DigitalOceanスペースの上にプライベートDockerレジストリを設定し、DigitalOceanKubernetesで使用する方法
著者は、 Write for DOnations プログラムの一環として、 Free and Open SourceFundを選択して寄付を受け取りました。
序章
Dockerレジストリは、コンテナ化されたアプリケーションの業界標準である名前付きDockerイメージのストレージおよびコンテンツ配信システムです。 プライベートDockerレジストリを使用すると、パブリックのものと比較して、チームまたは組織内でイメージをより柔軟に制御して安全に共有できます。 プライベートDockerレジストリをKubernetesクラスタで直接ホストすることで、レジストリを制御しながら、より高速、低レイテンシ、より優れた可用性を実現できます。
基盤となるレジストリストレージは、外部ドライバに委任されます。 デフォルトのストレージシステムはローカルファイルシステムですが、これをクラウドベースのストレージドライバーと交換できます。 DigitalOcean Spaces は、大量のデータを保存および提供するためのスケーラブルでシンプルかつ手頃な方法を求める開発者チームや企業向けに設計されたS3互換のオブジェクトストレージであり、Dockerイメージの保存に非常に適しています。 CDNネットワークが組み込まれているため、画像に頻繁にアクセスする際の待ち時間を大幅に短縮できます。
このチュートリアルでは、データを保存するためにDigitalOceanSpacesによってバックアップされたHelm を使用して、プライベートDockerレジストリを DigitalOceanKubernetesクラスターにデプロイします。 指定したスペースのAPIキーを作成し、カスタム構成を使用してDockerレジストリをクラスターにインストールし、Kubernetesを適切に認証するように構成し、クラスターでサンプルデプロイメントを実行してテストします。 このチュートリアルを終了すると、DigitalOceanKubernetesクラスターに安全なプライベートDockerレジストリがインストールされます。
前提条件
このチュートリアルを開始する前に、次のものが必要です。
クラスタにアクセスするマシンにインストールされているDocker。 Ubuntu 18.04については、 Ubuntu18.04にDockerをインストールして使用する方法をご覧ください。 ステップ1とステップ2を完了するだけです。 それ以外の場合は、Dockerのウェブサイトにアクセスして他のディストリビューションを入手してください。
このチュートリアルで作成するDockerイメージを保存するためのDockerHubのアカウント。
クラスターへのアクセスに使用するマシンにGitがインストールされています。 Ubuntu 18.04の場合は、 Ubuntu18.04チュートリアルにGitをインストールする方法のステップ1に従ってください。 その他のプラットフォームについては、公式ウェブサイトにアクセスしてください。
接続構成が
kubectl
デフォルトとして構成されているDigitalOceanKubernetesクラスター。kubectl
の構成方法については、クラスターの作成時に表示されるクラスターへの接続の手順を参照してください。 DigitalOceanでKubernetesクラスタを作成する方法については、 KubernetesQuickstartをご覧ください。APIキー(アクセスとシークレット)を備えたDigitalOceanスペース。 DigitalOceanスペースとAPIキーを作成する方法については、DigitalOceanスペースとAPIキーを作成する方法を参照してください。
ローカルマシンにインストールされているHelmパッケージマネージャー。 これを行うには、Helm3パッケージマネージャーを使用してKubernetesクラスターにソフトウェアをインストールする方法チュートリアルのステップ1を完了します。
クラスターにインストールされているNginxIngressControllerとCert-Manager。 これを行う方法のガイドについては、Helmを使用してDigitalOceanKubernetesでNginxIngressを設定する方法を参照してください。
Ingressが使用するDigitalOceanロードバランサーを指す2つのDNSAレコードを持つドメイン名。 DigitalOceanを使用してドメインのDNSレコードを管理している場合は、DNSレコードの管理方法を参照してAレコードを作成してください。 このチュートリアルでは、Aレコードを
registry.your_domain
およびk8s-test.your_domain
と呼びます。注:このチュートリアルで使用するドメイン名は、 DigitalOceanKubernetes前提条件チュートリアルでNginxIngressを設定する方法で使用するドメイン名とは異なる必要があります。
ステップ1—Dockerレジストリの構成とインストール
このステップでは、レジストリ展開用の構成ファイルを作成し、Helmパッケージマネージャーを使用して、指定された構成でレジストリをクラスターにインストールします。
このチュートリアルの過程で、chart_values.yaml
という構成ファイルを使用して、DockerレジストリHelm chartのデフォルト設定の一部を上書きします。 ヘルムはそのパッケージをチャートと呼んでいます。 これらは、関連するKubernetesリソースの選択の概要を示すファイルのセットです。 設定を編集して、基盤となるストレージシステムとしてDigitalOcean Spacesを指定し、Let'sEncryptTLS証明書を接続してHTTPSアクセスを有効にします。
Nginx Ingress Controllerの前提条件の一部として、サンプルのサービスとイングレスを作成しました。 このチュートリアルではそれらは必要ないため、次のコマンドを実行して削除できます。
kubectl delete -f hello-kubernetes-first.yaml kubectl delete -f hello-kubernetes-second.yaml kubectl delete -f hello-kubernetes-ingress.yaml
kubectl delete
コマンドは、-f
パラメーターが渡されると、削除するファイルを受け入れます。
S3ストレージプロバイダーで問題が発生する公式のDockerレジストリを使用する代わりに、GitLabの Container Registry フォークを使用します。これは、ダウンロードしてビルドする必要があります。
ワークスペースとして機能するフォルダーを作成します。
mkdir ~/k8s-registry
次のコマンドを実行して移動します。
cd ~/k8s-registry
次のコマンドを実行して、git
を使用してContainerRegistryリポジトリをダウンロードします。
git clone https://gitlab.com/gitlab-org/container-registry.git
出力は次のようになります。
OutputCloning into 'container-registry'... remote: Enumerating objects: 1706, done. ... Resolving deltas: 100% (13955/13955), done.
これで、リポジトリはcontainer-registry
ディレクトリにあります。 そこに移動します:
cd container-registry
これで、コンテナレジストリのソースコードができました。 クラスタから使用するには、そこからDockerイメージを構築し、DockerHubなどのパブリックレジストリにプッシュする必要があります。
次のコマンドを実行して、最新の安定バージョンのブランチに切り替えます。
git checkout v2.13.1-gitlab
次のコマンドを実行して、レジストリのDockerイメージを作成し、your_dockerhub_username
をDockerHubのユーザー名に置き換えます。
docker build -t your_dockerhub_username/registry:dev .
このコマンドは、完了するまでに時間がかかる場合があります。 出力は長くなり、次のようになります。
Output... Successfully built 27322ec15cf7 Successfully tagged your_dockerhub_username/registry:dev
イメージが作成されたので、それをアカウントにプッシュするには、最初にログインする必要があります。
docker login
プロンプトが表示されたら、DockerHubのユーザー名とパスワードを入力します。 出力の終わりは次のようになります。
Output... Login Succeeded
これで、画像をプッシュできます。
docker push your_dockerhub_username/registry:dev
最終的な出力は次のようになります。
OutputThe push refers to repository [docker.io/your_dockerhub_username/registry] c3baf7582a54: Pushed bc49969a328b: Pushed 0694fbf8288a: Pushed 3e207b409db3: Mounted from library/alpine dev: digest: sha256:02399157107a1d72312fb4f383f4c8c53a08f3e206d787a9c9380f446b008184 size: 1156
レジストリを作成してプッシュしたので、ワークスペースに戻ります。
cd ~/k8s-registry
お気に入りのテキストエディタを使用して、chart_values.yaml
ファイルを作成します。
nano chart_values.yaml
次の行を追加して、強調表示された行を詳細に置き換えてください。
chart_values.yaml
ingress: enabled: true hosts: - registry.your_domain annotations: kubernetes.io/ingress.class: nginx cert-manager.io/cluster-issuer: letsencrypt-prod nginx.ingress.kubernetes.io/proxy-body-size: "30720m" tls: - secretName: docker-registry-prod hosts: - registry.your_domain storage: s3 secrets: htpasswd: "" s3: accessKey: "your_space_access_key" secretKey: "your_space_secret_key" s3: region: your_space_region regionEndpoint: your_space_region.digitaloceanspaces.com secure: true bucket: your_space_name image: repository: your_dockerhub_username/registry tag: dev
最初のブロックingress
は、Helmチャートのデプロイの一部として作成されるKubernetesIngressを構成します。 Ingressオブジェクトは、外部HTTP / HTTPSルートがクラスター内の内部サービスを指すようにするため、外部からの通信が可能になります。 オーバーライドされる値は次のとおりです。
enabled
:true
に設定して、入力を有効にします。hosts
:Ingressがトラフィックを受け入れるホストのリスト。annotations
:イングレスの処理方法に関するKubernetesの他の部分へのさらなる方向性を提供するメタデータのリスト。 IngressControllerをnginx
に設定し、Let's Encryptクラスター発行者を本番バリアント(letsencrypt-prod
)に設定し、nginx
コントローラーに最大サイズ30のファイルを受け入れるように指示しますGB。これは、最大のDockerイメージでも適切な制限です。tls
:このサブカテゴリはLet'sEncryptHTTPSを構成します。 このイングレスがHTTPSトラフィックを受け入れるセキュアホストを定義するhosts
リストに、この例のドメイン名を入力します。secretName
(ここではdocker-registry-prod
に設定)は、証明書が保存されるシークレットの名前を指定します。通常、作成または展開するIngressごとに異なる必要があります。
次に、ファイルシステムストレージをs3
に設定します—他の利用可能なオプションはfilesystem
です。 ここで、s3
は、DigitalOceanSpacesが満たす業界標準のAmazonS3APIと互換性のあるリモートストレージシステムを使用していることを示しています。
次のブロックsecrets
では、s3
サブカテゴリの下でDOスペースにアクセスするためのキーを構成します。 最後に、s3
ブロックで、スペースを指定するパラメーターを構成します。
ファイルの最後で、プッシュしたレジストリイメージを、公式のDockerレジストリではなく、デプロイするレジストリイメージとして指定します。
ファイルを保存して閉じます。
ここで、まだ行っていない場合は、前提条件のチュートリアルでNginxIngressControllerのインストールの一部として作成したロードバランサーを指すようにAレコードを設定します。 DigitalOceanでDNSを設定する方法については、DNSレコードの管理方法を参照してください。
Dockerレジストリーをデプロイするチャートは、twuniリポジトリーにあります。 次のコマンドを実行して、Helmに追加します。
helm repo add twuni https://helm.twun.io
そこから何かをインストールする前に、キャッシュを更新する必要があります。 これにより、チャートリポジトリに関する最新情報が更新されます。 これを行うには、次のコマンドを実行します。
helm repo update
次に、次のコマンドを実行して、Helmを介してこのカスタム構成でDockerレジストリチャートをデプロイします。
helm install docker-registry twuni/docker-registry -f chart_values.yaml
次の出力が表示されます。
OutputNAME: docker-registry LAST DEPLOYED: ... NAMESPACE: default STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: 1. Get the application URL by running these commands: https://registry.your_domain/
これで、前に指定したドメイン名からレジストリにアクセスできます。
KubernetesクラスタにDockerレジストリを設定してデプロイしました。 次に、新しくデプロイされたDockerレジストリの可用性をテストします。
ステップ2—プッシュとプルのテスト
このステップでは、イメージをプッシュおよびプルして、新しくデプロイされたDockerレジストリをテストします。 現在、レジストリは空です。 何かをプッシュするには、作業しているマシンで画像を利用できるようにする必要があります。 そのためにmysql
Dockerイメージを使用してみましょう。
まず、DockerHubからmysql
をプルします。
docker pull mysql
出力は次のようになります。
OutputUsing default tag: latest latest: Pulling from library/mysql 27833a3ba0a5: Pull complete ... e906385f419d: Pull complete Digest: sha256:9643e9fbd6330d10686f8922292dcb20995e7b792c17d4e94ddf95255f1d5449 Status: Downloaded newer image for mysql:latest docker.io/library/mysql:latest
これで、画像をローカルで利用できるようになりました。 Dockerにプッシュする場所を通知するには、次のようにホスト名でタグ付けする必要があります。
docker tag mysql registry.your_domain/mysql
次に、イメージを新しいレジストリにプッシュします。
docker push registry.your_domain/mysql
このコマンドは正常に実行され、新しいレジストリが適切に構成され、新しいイメージのプッシュを含むトラフィックを受け入れていることを示します。 エラーが表示された場合は、手順1と2に対して手順を再確認してください。
レジストリからのプルをクリーンにテストするには、最初に次のコマンドを使用してローカルmysql
イメージを削除します。
docker rmi registry.your_domain/mysql && docker rmi mysql
次に、レジストリからプルします。
docker pull registry.your_domain/mysql
このコマンドは、完了するまでに数秒かかります。 正常に実行された場合は、レジストリが正しく機能していることを意味します。 エラーが表示された場合は、前のコマンドに対して入力した内容を再確認してください。
次のコマンドを実行して、ローカルで使用可能なDockerイメージを一覧表示できます。
docker images
ローカルマシンで使用可能なイメージと、それらのIDおよび作成日を一覧表示する出力が表示されます。
Dockerレジストリが構成されています。 画像をプッシュして、プルダウンできることを確認しました。 次に、認証を追加して、特定の人だけが画像にアクセスできるようにします。
ステップ3—アカウント認証の追加とKubernetesアクセスの設定
このステップでは、htpasswd
ユーティリティを使用して、レジストリのユーザー名とパスワードの認証を設定します。
htpasswd
ユーティリティはApacheWebサーバーから提供され、HTTPユーザーの基本認証用のユーザー名とパスワードを格納するファイルを作成するために使用できます。 htpasswd
ファイルの形式はusername:hashed_password
(1行に1つ)で、他のプログラムでも使用できるように十分に移植可能です。
簡単にするために、htpasswd
のDocker化されたバリアントを使用します。 次のコマンドを実行して、ログインの組み合わせをhtpasswd_file
に追加し、username
とpassword
を目的の資格情報に置き換えます。
docker run --rm -ti xmartlabs/htpasswd username password >> htpasswd_file
Dockerでは、 bcrypt アルゴリズムを使用してパスワードをハッシュする必要があります。これは、ここで暗黙的に使用されています。 bcryptアルゴリズムは、Blowfishブロック暗号に基づくパスワードハッシュ関数であり、 work factor パラメーターを使用して、ハッシュ関数のコストを指定します。
追加したい数のユーザーに対してこのコマンドを繰り返すことができます。
完了したら、次のコマンドを実行してhtpasswd_file
の内容を表示します。
cat htpasswd_file
表示されている内容を選択してコピーします。
Dockerレジストリに認証を追加するには、chart_values.yaml
を編集し、htpasswd
変数にhtpasswd_file
の内容を追加する必要があります。
chart_values.yaml
を開いて編集します。
nano chart_values.yaml
次のような行を見つけます。
chart_values.yaml
htpasswd: ""
htpasswd\_file\_contents
を、htpasswd_file
からコピーしたコンテンツに置き換えて、次のように編集します。
chart_values.yaml
htpasswd: |- htpasswd_file_contents
インデントに注意してください。ファイルの内容の各行の前には4つのスペースが必要です。 空白行がある場合は、削除できます。
コンテンツを追加したら、ファイルを保存して閉じます。
変更をクラスターに伝達するには、次のコマンドを実行します。
helm upgrade docker-registry twuni/docker-registry -f chart_values.yaml
出力は、Dockerレジストリを最初にデプロイしたときに表示されるものと同様になります。
OutputRelease "docker-registry" has been upgraded. Happy Helming! NAME: docker-registry LAST DEPLOYED: ... NAMESPACE: default STATUS: deployed REVISION: 2 TEST SUITE: None NOTES: 1. Get the application URL by running these commands: https://registry.your_domain/
このコマンドはHelmを呼び出し、chart_values.yaml
ファイルを適用した後、チャートリポジトリのstable/docker-registry
で定義されたチャートを使用して、既存のリリース(この場合はdocker-registry
)をアップグレードするように指示します。
ここで、レジストリからイメージを再度プルしてみます。
docker pull registry.your_domain/mysql
出力は次のようになります。
OutputUsing default tag: latest Error response from daemon: Get https://registry.your_domain/v2/mysql/manifests/latest: no basic auth credentials
クレデンシャルを指定しなかったため、正しく失敗しました。 これは、Dockerレジストリがリクエストを正しく承認することを意味します。
レジストリにログインするには、次のコマンドを実行します。
docker login registry.your_domain
registry.your_domain
をドメインアドレスに置き換えることを忘れないでください。 ユーザー名とパスワードの入力を求められます。 エラーが表示された場合は、htpasswd_file
の内容を再確認してください。 この手順の前半で作成したhtpasswd_file
からユーザー名とパスワードの組み合わせを指定する必要があります。
ログインをテストするには、次のコマンドを実行して、プルを再試行できます。
docker pull registry.your_domain/mysql
出力は次のようになります。
OutputUsing default tag: latest latest: Pulling from mysql Digest: sha256:f2dc118ca6fa4c88cde5889808c486dfe94bccecd01ca626b002a010bb66bcbe Status: Image is up to date for registry.your_domain/mysql:latest
これでDockerが構成され、安全にログインできます。 レジストリにログインするようにKubernetesを設定するには、次のコマンドを実行します。
sudo kubectl create secret docker-registry regcred --docker-server=registry.your_domain --docker-username=your_username --docker-password=your_password
このコマンドは、レジストリのログイン情報を含むregcred
という名前のシークレットをクラスタに作成し、それをdockerconfigjson
として解析します。これにより、Kubernetesのレジストリクレデンシャルが定義されます。
registry.your_domain
をレジストリドメインに置き換え、your_username
とyour_password
の代わりに以前に作成したログインクレデンシャルの1つを配置することを忘れないでください。
次の出力が表示されます。
Outputsecret/regcred created
htpasswd
を使用してログイン設定ファイルを作成し、リクエストを認証するようにレジストリを設定し、ログイン認証情報を含むKubernetesシークレットを作成しました。 次に、Kubernetesクラスターとレジストリ間の統合をテストします。
ステップ4—サンプルデプロイメントを実行してKubernetes統合をテストする
このステップでは、クラスタ内レジストリに保存されているイメージを使用してサンプルデプロイを実行し、Kubernetesクラスタとレジストリ間の接続をテストします。
最後のステップでは、プライベートレジストリのログイン資格情報を含むregcred
というシークレットを作成しました。 複数のレジストリのログイン資格情報が含まれている場合があります。その場合は、それに応じてシークレットを更新する必要があります。
imagePullSecrets
を指定することで、ポッド定義でコンテナをプルするときにKubernetesが使用するシークレットを指定できます。 この手順は、Dockerレジストリで認証が必要な場合に必要です。
次に、サンプルのHelloWorldイメージをプライベートDockerレジストリからクラスターにデプロイします。 まず、プッシュするために、次のコマンドを実行してマシンにプルします。
docker pull paulbouwer/hello-kubernetes:1.8
次に、次のコマンドを実行してタグを付けます。
docker tag paulbouwer/hello-kubernetes:1.8 registry.your_domain/paulbouwer/hello-kubernetes:1.8
最後に、それをレジストリにプッシュします。
docker push registry.your_domain/paulbouwer/hello-kubernetes:1.8
ローカルで不要になったため、マシンから削除します。
docker rmi registry.your_domain/paulbouwer/hello-kubernetes:1.8
次に、サンプルのHelloWorldアプリケーションをデプロイします。 まず、テキストエディタを使用して、新しいファイルhello-world.yaml
を作成します。
nano hello-world.yaml
次に、サービスと入力を定義して、クラスターの外部からアプリにアクセスできるようにします。 次の行を追加して、強調表示された行をドメインに置き換えます。
hello-world.yaml
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: hello-kubernetes-ingress annotations: kubernetes.io/ingress.class: nginx spec: rules: - host: k8s-test.your_domain http: paths: - pathType: Prefix path: "/" backend: service: name: hello-kubernetes port: number: 80 --- apiVersion: v1 kind: Service metadata: name: hello-kubernetes spec: type: NodePort ports: - port: 80 targetPort: 8080 selector: app: hello-kubernetes --- apiVersion: apps/v1 kind: Deployment metadata: name: hello-kubernetes spec: replicas: 3 selector: matchLabels: app: hello-kubernetes template: metadata: labels: app: hello-kubernetes spec: containers: - name: hello-kubernetes image: registry.your_domain/paulbouwer/hello-kubernetes:1.8 ports: - containerPort: 8080 imagePullSecrets: - name: regcred
最初に、Hello WorldデプロイメントのIngressを定義します。これは、NginxIngressControllerが所有するロードバランサーを介してルーティングされます。 次に、デプロイメントで作成されたポッドにアクセスできるサービスを定義します。 実際の展開仕様では、レジストリにあるものとしてimage
を指定し、前の手順で作成したimagePullSecrets
をregcred
に設定します。
ファイルを保存して閉じます。 これをクラスターにデプロイするには、次のコマンドを実行します。
kubectl apply -f hello-world.yaml
次の出力が表示されます。
Outputingress.extensions/hello-kubernetes-ingress created service/hello-kubernetes created deployment.apps/hello-kubernetes created
これで、テストドメイン(このチュートリアルの2番目のAレコードk8s-test.your_domain
)に移動できます。 Kubernetes Hello world!ページが表示されます。
Hello Worldページには、Linuxカーネルのバージョンやリクエストが提供されたポッドの内部IDなど、いくつかの環境情報が一覧表示されます。 Webインターフェイスを介してスペースにアクセスし、このチュートリアルで使用した画像を確認することもできます。
テスト後にこのHelloWorldデプロイメントを削除する場合は、次のコマンドを実行します。
kubectl delete -f hello-world.yaml
このステップでは、サンプルのHello Worldデプロイメントを作成して、Kubernetesがプライベートレジストリからイメージを適切にプルしているかどうかをテストします。
結論
これで、DigitalOcean Spacesを下のストレージレイヤーとして使用して、独自のプライベートDockerレジストリをDigitalOceanKubernetesクラスターに正常にデプロイできました。 保存できる画像の数に制限はありません。Spacesは無限に拡張でき、同時に同じセキュリティと堅牢性を提供します。 ただし、本番環境では、Dockerイメージを可能な限り最適化するよう常に努力する必要があります。本番環境用にDockerイメージを最適化する方法チュートリアルをご覧ください。