著者は、 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の前提条件の一部として、サンプルのサービスとイングレスを作成しました。 このチュートリアルではそれらは必要ないため、次のコマンドを実行して削除できます。

  1. kubectl delete -f hello-kubernetes-first.yaml
  2. kubectl delete -f hello-kubernetes-second.yaml
  3. kubectl delete -f hello-kubernetes-ingress.yaml

kubectl deleteコマンドは、-fパラメーターが渡されると、削除するファイルを受け入れます。

S3ストレージプロバイダーで問題が発生する公式のDockerレジストリを使用する代わりに、GitLabの Container Registry フォークを使用します。これは、ダウンロードしてビルドする必要があります。

ワークスペースとして機能するフォルダーを作成します。

  1. mkdir ~/k8s-registry

次のコマンドを実行して移動します。

  1. cd ~/k8s-registry

次のコマンドを実行して、gitを使用してコンテナレジストリリポジトリをダウンロードします。

  1. git clone https://gitlab.com/gitlab-org/container-registry.git

出力は次のようになります。

Output
Cloning into 'container-registry'... remote: Enumerating objects: 1706, done. ... Resolving deltas: 100% (13955/13955), done.

これで、リポジトリはcontainer-registryディレクトリにあります。 そこに移動します:

  1. cd container-registry

これで、コンテナレジストリのソースコードができました。 クラスタから使用するには、そこからDockerイメージを構築し、DockerHubなどのパブリックレジストリにプッシュする必要があります。

次のコマンドを実行して、最新の安定バージョンのブランチに切り替えます。

  1. git checkout v2.13.1-gitlab

次のコマンドを実行して、レジストリのDockerイメージを作成し、your_dockerhub_usernameをDockerHubユーザー名に置き換えます。

  1. docker build -t your_dockerhub_username/registry:dev .

このコマンドは、完了するまでに時間がかかる場合があります。 出力は長くなり、次のようになります。

Output
... Successfully built 27322ec15cf7 Successfully tagged your_dockerhub_username/registry:dev

イメージが作成されたので、それをアカウントにプッシュするには、最初にログインする必要があります。

  1. docker login

プロンプトが表示されたら、DockerHubのユーザー名とパスワードを入力します。 出力の終わりは次のようになります。

Output
... Login Succeeded

これで、画像をプッシュできます。

  1. docker push your_dockerhub_username/registry:dev

最終的な出力は次のようになります。

Output
The 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

レジストリを作成してプッシュしたので、ワークスペースに戻ります。

  1. cd ~/k8s-registry

お気に入りのテキストエディタを使用して、chart_values.yamlファイルを作成します。

  1. 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ルートがクラスター内の内部サービスを指すようにするため、外部からの通信が可能になります。 オーバーライドされる値は次のとおりです。

  • enabledtrueに設定して、入力を有効にします。
  • 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に追加します。

  1. helm repo add twuni https://helm.twun.io

そこから何かをインストールする前に、キャッシュを更新する必要があります。 これにより、チャートリポジトリに関する最新情報が更新されます。 これを行うには、次のコマンドを実行します。

  1. helm repo update

次に、次のコマンドを実行して、Helmを介してこのカスタム構成でDockerレジストリチャートをデプロイします。

  1. helm install docker-registry twuni/docker-registry -f chart_values.yaml

次の出力が表示されます。

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

まず、DockerHubからmysqlをプルします。

  1. docker pull mysql

出力は次のようになります。

Output
Using 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にプッシュする場所を通知するには、次のようにホスト名でタグ付けする必要があります。

  1. docker tag mysql registry.your_domain/mysql

次に、イメージを新しいレジストリにプッシュします。

  1. docker push registry.your_domain/mysql

このコマンドは正常に実行され、新しいレジストリが適切に構成され、新しいイメージのプッシュを含むトラフィックを受け入れていることを示します。 エラーが表示された場合は、手順1と2に対して手順を再確認してください。

レジストリからのプルをきれいにテストするには、最初に次のコマンドを使用してローカルmysqlイメージを削除します。

  1. docker rmi registry.your_domain/mysql && docker rmi mysql

次に、レジストリからプルします。

  1. docker pull registry.your_domain/mysql

このコマンドは、完了するまでに数秒かかります。 正常に実行された場合は、レジストリが正しく機能していることを意味します。 エラーが表示された場合は、前のコマンドに対して入力した内容を再確認してください。

次のコマンドを実行して、ローカルで使用可能なDockerイメージを一覧表示できます。

  1. docker images

ローカルマシンで使用可能なイメージと、それらのIDおよび作成日を一覧表示する出力が表示されます。

Dockerレジストリが構成されています。 画像をプッシュして、プルダウンできることを確認しました。 次に、認証を追加して、特定の人だけが画像にアクセスできるようにします。

ステップ3—アカウント認証の追加とKubernetesアクセスの設定

このステップでは、htpasswdユーティリティを使用して、レジストリのユーザー名とパスワードの認証を設定します。

htpasswdユーティリティはApacheWebサーバーから提供され、HTTPユーザーの基本認証用のユーザー名とパスワードを格納するファイルを作成するために使用できます。 htpasswdファイルの形式はusername:hashed_password(1行に1つ)で、他のプログラムでも使用できるように十分に移植可能です。

簡単にするために、htpasswdのDocker化されたバリアントを使用します。 次のコマンドを実行して、ログインの組み合わせをhtpasswd_fileに追加し、usernamepasswordを目的の資格情報に置き換えます。

  1. docker run --rm -ti xmartlabs/htpasswd username password >> htpasswd_file

Dockerでは、 bcrypt アルゴリズムを使用してパスワードをハッシュする必要があります。これは、ここで暗黙的に使用されています。 bcryptアルゴリズムは、Blowfishブロック暗号に基づくパスワードハッシュ関数であり、 work factor パラメーターを使用して、ハッシュ関数のコストを指定します。

追加したい数のユーザーに対してこのコマンドを繰り返すことができます。

完了したら、次のコマンドを実行してhtpasswd_fileの内容を表示します。

  1. cat htpasswd_file

表示されている内容を選択してコピーします。

Dockerレジストリに認証を追加するには、chart_values.yamlを編集し、htpasswd変数にhtpasswd_fileの内容を追加する必要があります。

chart_values.yamlを開いて編集します。

  1. nano chart_values.yaml

次のような行を見つけます。

chart_values.yaml
  htpasswd: ""

htpasswd\_file\_contentsを、htpasswd_fileからコピーしたコンテンツに置き換えて、次のように編集します。

chart_values.yaml
  htpasswd: |-
    htpasswd_file_contents

インデントに注意してください。ファイルの内容の各行の前には4つのスペースが必要です。 空白行がある場合は、削除できます。

コンテンツを追加したら、ファイルを保存して閉じます。

変更をクラスターに伝達するには、次のコマンドを実行します。

  1. helm upgrade docker-registry twuni/docker-registry -f chart_values.yaml

出力は、Dockerレジストリを最初にデプロイしたときに表示されるものと同様になります。

Output
Release "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)をアップグレードするように指示します。

ここで、レジストリからイメージを再度プルしてみます。

  1. docker pull registry.your_domain/mysql

出力は次のようになります。

Output
Using default tag: latest Error response from daemon: Get https://registry.your_domain/v2/mysql/manifests/latest: no basic auth credentials

クレデンシャルを指定しなかったため、正しく失敗しました。 これは、Dockerレジストリがリクエストを正しく承認することを意味します。

レジストリにログインするには、次のコマンドを実行します。

  1. docker login registry.your_domain

registry.your_domainをドメインアドレスに置き換えることを忘れないでください。 ユーザー名とパスワードの入力を求められます。 エラーが表示された場合は、htpasswd_fileの内容を再確認してください。 この手順の前半で作成したhtpasswd_fileからユーザー名とパスワードの組み合わせを指定する必要があります。

ログインをテストするには、次のコマンドを実行して、プルを再試行できます。

  1. docker pull registry.your_domain/mysql

出力は次のようになります。

Output
Using default tag: latest latest: Pulling from mysql Digest: sha256:f2dc118ca6fa4c88cde5889808c486dfe94bccecd01ca626b002a010bb66bcbe Status: Image is up to date for registry.your_domain/mysql:latest

これでDockerが構成され、安全にログインできます。 レジストリにログインするようにKubernetesを設定するには、次のコマンドを実行します。

  1. 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_usernameyour_passwordの代わりに以前に作成したログインクレデンシャルの1つを配置することを忘れないでください。

次の出力が表示されます。

Output
secret/regcred created

htpasswdを使用してログイン設定ファイルを作成し、リクエストを認証するようにレジストリを設定し、ログイン認証情報を含むKubernetesシークレットを作成しました。 次に、Kubernetesクラスターとレジストリ間の統合をテストします。

ステップ4—サンプルデプロイメントを実行してKubernetes統合をテストする

このステップでは、クラスタ内レジストリに保存されているイメージを使用してサンプルデプロイを実行し、Kubernetesクラスタとレジストリ間の接続をテストします。

最後のステップで、プライベートレジストリのログイン資格情報を含むregcredというシークレットを作成しました。 複数のレジストリのログイン資格情報が含まれている場合があります。その場合は、それに応じてシークレットを更新する必要があります。

imagePullSecretsを指定することで、ポッド定義でコンテナをプルするときにKubernetesが使用するシークレットを指定できます。 この手順は、Dockerレジストリで認証が必要な場合に必要です。

次に、サンプルのHelloWorldイメージをプライベートDockerレジストリからクラスターにデプロイします。 まず、プッシュするために、次のコマンドを実行してマシンにプルします。

  1. docker pull paulbouwer/hello-kubernetes:1.8

次に、次のコマンドを実行してタグを付けます。

  1. docker tag paulbouwer/hello-kubernetes:1.8 registry.your_domain/paulbouwer/hello-kubernetes:1.8

最後に、それをレジストリにプッシュします。

  1. docker push registry.your_domain/paulbouwer/hello-kubernetes:1.8

ローカルで不要になったため、マシンから削除します。

  1. docker rmi registry.your_domain/paulbouwer/hello-kubernetes:1.8

次に、サンプルのHelloWorldアプリケーションをデプロイします。 まず、テキストエディタを使用して、新しいファイルhello-world.yamlを作成します。

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

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

  1. kubectl apply -f hello-world.yaml

次の出力が表示されます。

Output
ingress.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 page

Hello Worldページには、Linuxカーネルのバージョンやリクエストが提供されたポッドの内部IDなど、いくつかの環境情報が一覧表示されます。 Webインターフェイスを介してスペースにアクセスし、このチュートリアルで使用した画像を確認することもできます。

テスト後にこのHelloWorldデプロイメントを削除する場合は、次のコマンドを実行します。

  1. kubectl delete -f hello-world.yaml

このステップでは、サンプルのHello Worldデプロイメントを作成して、Kubernetesがプライベートレジストリからイメージを適切にプルしているかどうかをテストします。

結論

これで、DigitalOcean Spacesを下のストレージレイヤーとして使用して、独自のプライベートDockerレジストリをDigitalOceanKubernetesクラスターに正常にデプロイできました。 保存できる画像の数に制限はありません。Spacesは無限に拡張でき、同時に同じセキュリティと堅牢性を提供します。 ただし、本番環境では、Dockerイメージを可能な限り最適化するよう常に努力する必要があります。本番環境用にDockerイメージを最適化する方法チュートリアルをご覧ください。