DigitalOceanスペースの上にプライベートDockerレジストリを設定し、DigitalOcean Kubernetesで使用する方法

著者は、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]ボタンをクリックし、必要なファイルをアップロードします。 作成した構成ファイルをアップロードできます。

Empty file uploaded to empty Space

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レジストリをテストし、そこに画像をプッシュしたりプルしたりします。 現在、レジストリは空です。 プッシュするものを使用するには、作業元のマシンで画像を使用できるようにする必要があります。 mysqlDockerイメージを使用してみましょう。

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\_contentshtpasswd_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を指定し、前の手順で作成したimagePullSecretsregcredに設定します。

ファイルを保存して閉じます。 これをクラスターにデプロイするには、次のコマンドを実行します。

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 page

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チュートリアルを参照してください。

Related