著者は、Write for DOnationsプログラムの一部として寄付を受け取るためにFree and Open Source Fundを選択しました。
前書き
Docker registryは、コンテナ化されたアプリケーションの業界標準である名前付きDockerイメージのストレージおよびコンテンツ配信システムです。 プライベートDockerレジストリを使用すると、パブリックのものと比較して、柔軟性と制御性を高めて、チームまたは組織内で画像を安全に共有できます。 KubernetesクラスターでプライベートDockerレジストリを直接ホストすることで、レジストリを制御しながら、高速化、低遅延化、可用性の向上を実現できます。
基礎となるレジストリストレージは外部ドライバーに委任されます。 デフォルトのストレージシステムはローカルファイルシステムですが、これをクラウドベースのストレージドライバーに交換できます。 DigitalOcean Spacesは、大量のデータを保存および提供するためのスケーラブルでシンプルかつ手頃な方法を必要とする開発者チームおよび企業向けに設計されたS3互換のオブジェクトストレージであり、Dockerイメージの保存に非常に適しています。 CDNネットワークが組み込まれているため、頻繁に画像にアクセスする際の待ち時間を大幅に削減できます。
このチュートリアルでは、データを保存するためにDigitalOcean SpacesによってバックアップされたHelmを使用して、プライベートDockerレジストリをDigitalOcean Kubernetesクラスターにデプロイします。 指定されたスペースのAPIキーを作成し、カスタム構成でクラスターにDockerレジストリをインストールし、Kubernetesで適切に認証するように構成し、クラスターでサンプルデプロイを実行してテストします。 このチュートリアルの最後に、DigitalOcean Kubernetesクラスターに安全なプライベートDockerレジストリをインストールします。
前提条件
このチュートリアルを始める前に、次のものが必要です。
-
クラスターにアクセスするマシンにインストールされたDocker。 Ubuntu 18.04の場合は、How To Install and Use Docker on Ubuntu 18.04にアクセスしてください。 最初のステップを完了するだけです。 それ以外の場合は、他のディストリビューションについてDockerのwebsiteにアクセスしてください。
-
接続構成が
kubectl
のデフォルトとして構成されたDigitalOceanKubernetesクラスター。kubectl
を構成する方法の説明は、クラスターの作成時に表示されるConnect to your Clusterステップの下に表示されます。 DigitalOceanでKubernetesクラスターを作成する方法については、Kubernetes Quickstartを参照してください。 -
APIキー(アクセスおよびシークレット)を持つDigitalOcean Space。 DigitalOcean SpaceおよびAPIキーを作成する方法については、How To Create a DigitalOcean Space and API Keyを参照してください。
-
ローカルマシンにインストールされたHelmパッケージマネージャー、およびクラスターにインストールされたTiller。 How To Install Software on Kubernetes Clusters with the Helm Package Managerのステップ1と2を完了します。 最初の2つのステップを完了するだけで済みます。
-
クラスタにインストールされたNginx Ingress ControllerおよびCert-Manager。 これを行う方法のガイドについては、How to Set Up an Nginx Ingress with Cert-Manager on DigitalOcean Kubernetesを参照してください。
-
Ingressが使用するDigitalOceanロードバランサーを指す2つのDNS Aレコードを持つドメイン名。 DigitalOceanを使用してドメインのDNSレコードを管理している場合は、How to Manage DNS Recordsを参照してAレコードを作成してください。 このチュートリアルでは、Aレコードを
registry.example.com
およびk8s-test.example.com
と呼びます。
[[step-1 -—- configuring-and-installing-the-docker-registry]] ==ステップ1—Dockerレジストリの構成とインストール
この手順では、レジストリ展開の構成ファイルを作成し、Helmパッケージマネージャーを使用して、指定された構成でクラスターにDockerレジストリをインストールします。
このチュートリアルの過程で、chart_values.yaml
という構成ファイルを使用して、DockerレジストリHelmchartのデフォルト設定の一部を上書きします。 Helmはそのパッケージをチャートと呼びます。これらは、Kubernetesリソースの関連する選択を概説するファイルのセットです。 設定を編集して、DigitalOcean Spacesを基になるストレージシステムとして指定し、TLS証明書を暗号化することでHTTPSアクセスを有効にします。
前提条件の一部として、テスト目的でecho1
およびecho2
サービスとecho_ingress
入力を作成します。このチュートリアルではこれらは必要ないため、削除できます。
次のコマンドを実行して、イングレスを削除することから始めます。
kubectl delete -f echo_ingress.yaml
次に、2つのテストサービスを削除します。
kubectl delete -f echo1.yaml && kubectl delete -f echo2.yaml
kubectldelete
コマンドは、-f
パラメータが渡されると、削除するファイルを受け入れます。
ワークスペースとして機能するフォルダーを作成します。
mkdir ~/k8s-registry
以下を実行してナビゲートします。
cd ~/k8s-registry
次に、テキストエディタを使用して、chart_values.yaml
ファイルを作成します。
nano chart_values.yaml
次の行を追加し、強調表示された行を詳細に置き換えます。
chart_values.yaml
ingress:
enabled: true
hosts:
- registry.example.com
annotations:
kubernetes.io/ingress.class: nginx
certmanager.k8s.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-body-size: "30720m"
tls:
- secretName: letsencrypt-prod
hosts:
- registry.example.com
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
最初のブロックingress
は、Helmチャートのデプロイの一部として作成されるKubernetesIngressを構成します。 Ingressオブジェクトは、外部HTTP / HTTPSルートがクラスター内の内部サービスを指すようにし、外部からの通信を許可します。 オーバーライドされる値は次のとおりです。
-
enabled
:Ingressを有効にするには、true
に設定します。 -
hosts
:入力がトラフィックを受け入れるホストのリスト。 -
annotations
:Ingressの処理方法に関するKubernetesの他の部分へのさらなる指示を提供するメタデータのリスト。 Ingress Controllerをnginx
に設定し、Let's Encryptクラスター発行者を本番バリアント(letsencrypt-prod
)に設定し、nginx
コントローラーに最大サイズ30GBのファイルを受け入れるように指示します。最大のDockerイメージでさえも賢明な制限です。 -
tls
:このサブカテゴリはLet's EncryptHTTPSを構成します。 このIngressがHTTPSトラフィックを受け入れるセキュアホストを定義するhosts
リストに、この例のドメイン名を入力します。
次に、ファイルシステムストレージをs3
に設定します—他の利用可能なオプションはfilesystem
です。 ここで、s3
は、DigitalOceanSpacesが満たす業界標準のAmazonS3APIと互換性のあるリモートストレージシステムを使用していることを示しています。
次のブロックsecrets
では、s3
サブカテゴリの下にあるDigitalOceanスペースにアクセスするためのキーを設定します。 最後に、s3
ブロックで、スペースを指定するパラメーターを構成します。
ファイルを保存して閉じます。
ここで、まだ行っていない場合は、前提条件のチュートリアルでNginx Ingress Controllerのインストールの一部として作成したロードバランサーを指すようにAレコードを設定します。 DigitalOceanでDNSを設定する方法については、How to Manage DNS Recordsを参照してください。
次に、スペースが空でないことを確認します。 スペースにファイルがない場合、Dockerレジストリはまったく実行されません。 これを回避するには、ファイルをアップロードします。 [スペース]タブに移動し、スペースを見つけて、[Upload File]ボタンをクリックし、必要なファイルをアップロードします。 作成した構成ファイルをアップロードできます。
Helmを介して何かをインストールする前に、キャッシュを更新する必要があります。 これにより、チャートリポジトリに関する最新情報が更新されます。 これを行うには、次のコマンドを実行します。
helm repo update
次に、次のコマンドを実行して、Helmを介してこのカスタム構成でDockerレジストリチャートをデプロイします。
helm install stable/docker-registry -f chart_values.yaml --name docker-registry
次の出力が表示されます。
OutputNAME: docker-registry
...
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/ConfigMap
NAME DATA AGE
docker-registry-config 1 1s
==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
docker-registry-54df68fd64-l26fb 0/1 ContainerCreating 0 1s
==> v1/Secret
NAME TYPE DATA AGE
docker-registry-secret Opaque 3 1s
==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
docker-registry ClusterIP 10.245.131.143 5000/TCP 1s
==> v1beta1/Deployment
NAME READY UP-TO-DATE AVAILABLE AGE
docker-registry 0/1 1 0 1s
==> v1beta1/Ingress
NAME HOSTS ADDRESS PORTS AGE
docker-registry registry.example.com 80, 443 1s
NOTES:
1. Get the application URL by running these commands:
https://registry.example.com/
Helmは、Dockerレジストリチャート展開の結果として作成したすべてのリソースを一覧表示します。 これで、以前に指定したドメイン名からレジストリにアクセスできます。
KubernetesクラスターにDockerレジストリを構成してデプロイしました。 次に、新しくデプロイされたDockerレジストリの可用性をテストします。
[[step-2 -—- testing-pushing-and-pulling]] ==ステップ2—プッシュとプルのテスト
このステップでは、新しくデプロイされたDockerレジストリをテストし、そこに画像をプッシュしたりプルしたりします。 現在、レジストリは空です。 プッシュするものを使用するには、作業元のマシンで画像を使用できるようにする必要があります。 mysql
Dockerイメージを使用してみましょう。
Docker Hubからmysql
をプルすることから始めます。
sudo docker pull mysql
出力は次のようになります。
OutputUsing default tag: latest
latest: Pulling from library/mysql
27833a3ba0a5: Pull complete
...
e906385f419d: Pull complete
Digest: sha256:a7cf659a764732a27963429a87eccc8457e6d4af0ee9d5140a3b56e74986eed7
Status: Downloaded newer image for mysql:latest
これで、ローカルで画像を利用できるようになりました。 Dockerにプッシュする場所を通知するには、次のようにホスト名でタグ付けする必要があります。
sudo docker tag mysql registry.example.com/mysql
次に、イメージを新しいレジストリにプッシュします。
sudo docker push registry.example.com/mysql
このコマンドは正常に実行され、新しいレジストリが適切に構成され、新しいイメージのプッシュを含むトラフィックを受け入れていることを示します。 エラーが表示された場合は、手順1と2に対して手順を再確認してください。
レジストリからのプルをきれいにテストするには、最初に次のコマンドを使用してローカルmysql
イメージを削除します。
sudo docker rmi registry.example.com/mysql && sudo docker rmi mysql
次に、レジストリから取得します。
sudo docker pull registry.example.com/mysql
このコマンドの完了には数秒かかります。 正常に実行される場合、それはレジストリが正しく機能していることを意味します。 エラーが表示された場合は、以前のコマンドに対して入力した内容を再確認してください。
次のコマンドを実行して、ローカルで利用可能なDockerイメージを一覧表示できます。
sudo docker images
ローカルマシンで使用可能な画像のリストと、IDおよび作成日が出力されます。
Dockerレジストリが構成されました。 画像をプッシュし、プルダウンできることを確認しました。 次に、認証を追加して、特定のユーザーのみがコードにアクセスできるようにします。
[[step-3 -—- adding-account-authentication-and-configuring-kubernetes-access]] ==ステップ3—アカウント認証の追加とKubernetesアクセスの設定
このステップでは、htpasswd
ユーティリティを使用して、レジストリのユーザー名とパスワードの認証を設定します。
htpasswd
ユーティリティはApache Webサーバーから提供され、HTTPユーザーの基本認証用のユーザー名とパスワードを格納するファイルを作成するために使用できます。 htpasswd
ファイルの形式はusername:hashed_password
(1行に1つ)であり、他のプログラムでも使用できるように十分に移植可能です。
システムでhtpasswd
を使用できるようにするには、次のコマンドを実行してインストールする必要があります。
sudo apt install apache2-utils -y
[。注意]##
Note:
Macからこのチュートリアルを実行している場合は、次のコマンドを使用して、マシンでhtpasswd
を使用できるようにする必要があります。
docker run --rm -v ${PWD}:/app -it httpd htpasswd -b -c /app/htpasswd_file sammy password
次のコマンドを実行して作成します。
touch htpasswd_file
ユーザー名とパスワードの組み合わせをhtpasswd_file
に追加します。
htpasswd -B htpasswd_file username
Dockerでは、bcryptアルゴリズムを使用してパスワードをハッシュする必要があります。そのため、-B
パラメーターを渡します。 bcryptアルゴリズムは、Blowfishブロック暗号に基づくパスワードハッシュ関数であり、ハッシュ関数のコストを指定するwork factorパラメーターがあります。
username
を希望のユーザー名に置き換えることを忘れないでください。 実行すると、htpasswd
は付随するパスワードの入力を求め、その組み合わせをhtpasswd_file
に追加します。 追加するユーザーの数だけこのコマンドを繰り返すことができます。
ここで、次のコマンドを実行して、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 stable/docker-registry -f chart_values.yaml
出力は、Dockerレジストリを最初にデプロイしたときに表示されるものと同様です。
OutputRelease "docker-registry" has been upgraded. Happy Helming!
LAST DEPLOYED: ...
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/ConfigMap
NAME DATA AGE
docker-registry-config 1 3m8s
==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
docker-registry-6c5bb7ffbf-ltnjv 1/1 Running 0 3m7s
==> v1/Secret
NAME TYPE DATA AGE
docker-registry-secret Opaque 4 3m8s
==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
docker-registry ClusterIP 10.245.128.245 5000/TCP 3m8s
==> v1beta1/Deployment
NAME READY UP-TO-DATE AVAILABLE AGE
docker-registry 1/1 1 1 3m8s
==> v1beta1/Ingress
NAME HOSTS ADDRESS PORTS AGE
docker-registry registry.example.com 159.89.215.50 80, 443 3m8s
NOTES:
1. Get the application URL by running these commands:
https://registry.example.com/
このコマンドはHelmを呼び出し、chart_values.yaml
ファイルを適用した後、既存のリリース(この場合はdocker-registry
)を、チャートリポジトリのstable/docker-registry
で定義されたチャートでアップグレードするように指示します。
ここで、レジストリから画像をもう一度取得してみます。
sudo docker pull registry.example.com/mysql
出力は次のようになります。
OutputUsing default tag: latest
Error response from daemon: Get https://registry.example.com/v2/mysql/manifests/latest: no basic auth credentials
資格情報を提供しなかったため、正しく失敗しました。 これは、Dockerレジストリがリクエストを正しく承認することを意味します。
レジストリにログインするには、次のコマンドを実行します。
sudo docker login registry.example.com
registry.example.com
をドメインアドレスに置き換えることを忘れないでください。 ユーザー名とパスワードの入力を求められます。 エラーが表示された場合は、htpasswd_file
に何が含まれているかを再確認してください。 このステップの前半で作成したhtpasswd_file
で、ユーザー名とパスワードの組み合わせを定義する必要があります。
ログインをテストするには、次のコマンドを実行して再度プルを試行できます。
sudo docker pull registry.example.com/mysql
出力は次のようになります。
OutputUsing default tag: latest
latest: Pulling from mysql
Digest: sha256:f2dc118ca6fa4c88cde5889808c486dfe94bccecd01ca626b002a010bb66bcbe
Status: Image is up to date for registry.example.com/mysql:latest
これでDockerが構成され、安全にログインできます。 レジストリにログインするようにKubernetesを構成するには、次のコマンドを実行します。
sudo kubectl create secret generic regcred --from-file=.dockerconfigjson=/home/sammy/.docker/config.json --type=kubernetes.io/dockerconfigjson
次の出力が表示されます。
Outputsecret/regcred created
このコマンドは、クラスター内にregcred
という名前のシークレットを作成し、Dockerが認証情報を格納するJSONファイルの内容を取得し、それをdockerconfigjson
として解析します。これにより、Kubernetesのレジストリ認証情報が定義されます。
htpasswd
を使用してログイン設定ファイルを作成し、リクエストを認証するようにレジストリを設定し、ログイン認証情報を含むKubernetesシークレットを作成しました。 次に、Kubernetesクラスターとレジストリ間の統合をテストします。
[[step-4 -—- testing-kubernetes-integration-by-running-a-sample-deployment]] ==ステップ4—サンプルデプロイメントを実行してKubernetes統合をテストする
この手順では、クラスター内のレジストリに保存されているイメージを使用してサンプル展開を実行し、Kubernetesクラスターとレジストリ間の接続をテストします。
最後のステップで、プライベートレジストリのログイン資格情報を含むregcred
というシークレットを作成しました。 複数のレジストリのログイン認証情報が含まれている場合があります。その場合、それに応じてシークレットを更新する必要があります。
imagePullSecrets
を指定することで、ポッド定義でコンテナをプルするときにKubernetesが使用するシークレットを指定できます。 この手順は、Dockerレジストリで認証が必要な場合に必要です。
次に、サンプルのHello World imageをプライベートDockerレジストリからクラスタにデプロイします。 まず、それをプッシュするには、次のコマンドを実行してマシンにプルします。
sudo docker pull paulbouwer/hello-kubernetes:1.5
次に、次を実行してタグを付けます。
sudo docker tag paulbouwer/hello-kubernetes:1.5 registry.example.com/paulbouwer/hello-kubernetes:1.5
最後に、レジストリにプッシュします。
sudo docker push registry.example.com/paulbouwer/hello-kubernetes:1.5
ローカルで不要になったため、マシンから削除します。
sudo docker rmi registry.example.com/paulbouwer/hello-kubernetes:1.5
次に、サンプルHello Worldアプリケーションをデプロイします。 まず、テキストエディタを使用して、新しいファイルhello-world.yaml
を作成します。
nano hello-world.yaml
次に、サービスをIngressに定義して、クラスターの外部からアプリにアクセスできるようにします。 次の行を追加し、強調表示された行をドメインに置き換えます。
hello-world.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: hello-kubernetes-ingress
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: k8s-test.example.com
http:
paths:
- path: /
backend:
serviceName: hello-kubernetes
servicePort: 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.example.com/paulbouwer/hello-kubernetes:1.5
ports:
- containerPort: 8080
imagePullSecrets:
- name: regcred
最初に、Hello World展開のIngressを定義します。これは、Nginx Ingress Controllerが所有するロードバランサーを介してルーティングします。 次に、展開で作成されたポッドにアクセスできるサービスを定義します。 実際の展開仕様では、レジストリにあるものとして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.example.com
)に移動できます。 KubernetesHello world!ページが表示されます。
Hello Worldページには、Linuxカーネルバージョンや、リクエストの提供元であるポッドの内部IDなど、いくつかの環境情報が一覧表示されます。 ウェブインターフェースからSpaceにアクセスして、このチュートリアルで使用した画像を表示することもできます。
テスト後にこのHello Worldデプロイメントを削除する場合は、次のコマンドを実行します。
kubectl delete -f hello-world.yaml
Kubernetesがプライベートレジストリからイメージを適切にプルしているかどうかをテストするサンプルHello Worldデプロイメントを作成しました。
結論
これで、DigitalOcean Spacesをその下のストレージレイヤーとして使用して、DigitalOcean Kubernetesクラスターに独自のプライベートDockerレジストリを正常にデプロイできました。 保存できる画像の数に制限はありません。スペースは無限に拡張でき、同時に同じセキュリティと堅牢性を提供します。 ただし、本番環境では、Dockerイメージを可能な限り最適化するように常に努力する必要があります。How To Optimize Docker Images for Productionチュートリアルを参照してください。