Ubuntu 22.04でプライベートなDockerレジストリをセットアップする方法
筆者は、「Write for Donations(寄付のための執筆)プログラム」の一環として、寄付を受けるためにフリーかつオープンソースファンドを選択しました。
導入文
Docker Registryは、Dockerコンテナイメージの保存と配信を管理するアプリケーションです。レジストリはコンテナイメージを集中管理し、開発者のビルド時間を短縮します。Dockerイメージは仮想化により同じランタイム環境を保証しますが、イメージの構築には時間がかかることがあります。例えば、Dockerを使用するために依存関係やパッケージを個別にインストールする代わりに、開発者は必要なすべてのコンポーネントを含んだ圧縮イメージをレジストリからダウンロードできます。さらに、開発者はTravisCIなどの継続的インテグレーションツールを使用して、イメージをレジストリに自動的にプッシュし、本番や開発中にイメージをシームレスに更新することができます。
Docker Hubは、カスタムのDockerイメージをホストできる無料のパブリックレジストリですが、イメージを公開したくない場合もあります。通常、イメージにはアプリケーションを実行するために必要なすべてのコードが含まれているため、プロプライエタリなソフトウェアを使用する場合にはプライベートレジストリを使用する方が好ましいです。
このチュートリアルでは、独自のプライベートDockerレジストリをセットアップしてセキュリティを確保します。Docker Composeを使用して、Dockerコンテナを実行するための設定を定義し、Nginxを使用してインターネットからサーバートラフィックを実行中のDockerコンテナに転送します。このチュートリアルを完了すると、カスタムDockerイメージをプライベートレジストリにアップロードし、リモートサーバーから安全にイメージを取得することができます。
前提条件
このチュートリアルを完了するためには、以下が必要です:
- Two Ubuntu 22.04 servers set up by following the Ubuntu 22.04 Initial Server Setup Guide, including a sudo non-root user and a firewall. One server will host your private Docker Registry and the other will be your client server.
- Docker installed on both servers, which you can set up by following Steps 1 and 2 of How To Install and Use Docker on Ubuntu 22.04.
ホストサーバーで必要な設定を行う必要があります。
- Docker Compose installed on the host server, which you can set up by following Step 1 of How To Install and Use Docker Compose on Ubuntu 22.04.
- Nginx installed on your host server, which you can set up by following the steps in How To Install Nginx on Ubuntu 22.04.
- Nginx secured with Let’s Encrypt on your host server for the private Docker Registry, which you can set up by following the How To Secure Nginx with Let’s Encrypt on Ubuntu 22.04 tutorial. Make sure to redirect all traffic from HTTP to HTTPS in Step 4.A registered domain name that resolves to the server you’re using to host the private Docker Registry. You will set this up as part of the Let’s Encrypt prerequisite. In this tutorial, we’ll refer to it as your_domain.
ステップ1 — Docker Registryのインストールと設定
コマンドラインでDockerを実行することは、初めてコンテナを起動したりテストしたりする際に便利ですが、複数のコンテナが並列で実行されるような大規模な展開になると取り回しが難しくなります。
Docker Composeを使用すると、各コンテナの設定とコンテナ同士の通信に必要な情報を記述するための.ymlファイルを1つ作成します。docker composeツールを使用して、アプリケーションを構成するすべてのコンポーネントに対してコマンドを発行し、まとめて制御することができます。
Dockerレジストリは、複数のコンポーネントを持つアプリケーションですので、管理にはDocker Composeを使用します。レジストリのインスタンスを開始するためには、docker-compose.ymlファイルをセットアップして、データを保存するディスク上の場所やレジストリの定義を行います。
ホストサーバーでdocker-registryというディレクトリに設定を保存します。次のコマンドを実行して、それを作成してください。
- mkdir ~/docker-registry
それに移動してください。 (Sore ni idō shite kudasai.)
- cd ~/docker-registry
その後、データという名前のサブディレクトリを作成し、そこにレジストリが画像を保存するようにしてください。
- mkdir data
次のようにして、docker-compose.ymlという名前のファイルを作成して開きます。
- nano docker-compose.yml
基本的なDockerレジストリのインスタンスを定義する以下の行を追加してください。
~/docker-registry/docker-compose.ymlを日本語で言い換えると、以下のようになります。
〜/docker-registry/docker-compose.yml
version: '3'
services:
registry:
image: registry:latest
ports:
- "5000:5000"
environment:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
volumes:
- ./data:/data
最初に、最初のサービス登録を指定し、そのイメージを最新バージョンのレジストリに設定します。次に、ホスト上のポート5000をコンテナのポート5000にマッピングします。これにより、サーバーのポート5000にリクエストを送信し、リクエストをレジストリに転送することができます。
環境セクションでは、REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY変数を/dataに設定し、どのボリュームにデータを保存するかを指定します。そして、ボリュームセクションでは、ホストファイルシステムの/dataディレクトリをコンテナ内の/dataにマッピングして、パススルーとして機能させます。実際にデータはホストのファイルシステムに保存されます。
ファイルを保存し、閉じてください。
「実行することで、設定を開始できます。」
- docker compose up
レジストリコンテナとその依存関係はダウンロードされ、起動されます。
[+] Running 2/2 ⠿ Network docker-registry_default Created 0.1s ⠿ Container docker-registry-registry-1 Created 0.1s Attaching to docker-registry-registry-1 docker-registry-registry-1 | time=”2022-11-19T14:31:20.40444638Z” level=warning msg=”No HTTP secret provided – generated random secret. This may cause problems with uploads if multiple registries are behind a load-balancer. To provide a shared secret, fill in http.secret in the configuration file or set the REGISTRY_HTTP_SECRET environment variable.” go.version=go1.16.15 instance.id=4fb8d420-eaf8-4a69-b740-bdc94fa52d91 service=registry version=”v2.8.1+unknown” docker-registry-registry-1 | time=”2022-11-19T14:31:20.404960549Z” level=info msg=”redis not configured” go.version=go1.16.15 instance.id=4fb8d420-eaf8-4a69-b740-bdc94fa52d91 service=registry version=”v2.8.1+unknown” docker-registry-registry-1 | time=”2022-11-19T14:31:20.412312462Z” level=info msg=”using inmemory blob descriptor cache” go.version=go1.16.15 instance.id=4fb8d420-eaf8-4a69-b740-bdc94fa52d91 service=registry version=”v2.8.1+unknown” docker-registry-registry-1 | time=”2022-11-19T14:31:20.412803878Z” level=info msg=”Starting upload purge in 52m0s” go.version=go1.16.15 instance.id=4fb8d420-eaf8-4a69-b740-bdc94fa52d91 service=registry version=”v2.8.1+unknown” docker-registry-registry-1 | time=”2022-11-19T14:31:20.41296431Z” level=info msg=”listening on [::]:5000″ go.version=go1.16.15 instance.id=4fb8d420-eaf8-4a69-b740-bdc94fa52d91 service=registry version=”v2.8.1+unknown” …
このチュートリアルの後で、No HTTPシークレットが提供されていない警告メッセージについては後で対処します。
出力の最後の行には、ポート番号5000で正常に開始し、リスン中であることが示されています。
実行を停止するには、CTRL+Cを押すことができます。
このステップでは、Dockerレジストリをポート5000でリッスンするDocker Composeの設定を作成しました。次のステップでは、ドメインで公開し、認証を設定します。
ステップ2 – Nginxのポートフォワーディングの設定
必要条件の一部として、お客様のドメインでHTTPSを有効にしました。セキュアなDockerレジストリを公開するために、Nginxを設定してドメインからレジストリコンテナにトラフィックを転送する必要があります。
あなたはすでに/etc/nginx/sites-available/your_domainファイルを設定しており、サーバーの構成を含んでいます。次のコマンドを実行して、編集するためにそれを開いてください。
- sudo nano /etc/nginx/sites-available/your_domain
現在の場所ブロックを見つける。
...
location / {
...
}
...
以下の行を使って、トラフィックをポート5000に転送する必要があります。そこで、レジストリがトラフィックを受け付けるためにヘッダーをリクエストに追加することも望んでいます。以下の行で、locationブロックの既存の内容を置き換えてください。
...
location / {
# Do not allow connections from docker 1.5 and earlier
# docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
return 404;
}
proxy_pass http://localhost:5000;
proxy_set_header Host $http_host; # required for docker client's sake
proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
}
...
もしブロックは、リクエストのユーザーエージェントをチェックし、Dockerクライアントのバージョンが1.5以上であり、アクセスを試みるのがGoアプリケーションではないことを検証します。これについての詳しい説明は、DockerのレジストリNginxガイドのnginxヘッダーの設定を参照してください。
作業が終わったら、ファイルを保存して閉じてください。Nginxを再起動することで変更を適用してください。
- sudo systemctl restart nginx
エラーメッセージが表示された場合は、追加した設定を再確認してください。
ポート5000でレジストリコンテナにトラフィックが適切に転送されていることを確認するために、Nginxを実行してください。
- docker compose up
その後、ブラウザウィンドウでドメインに移動し、v2のエンドポイントにアクセスしてください。
https://your_domain/v2
ブラウザは空のJSONオブジェクトを読み込みます。
{}
あなたの端末では、次のような出力を受け取ります。
docker-registry-registry-1 | time=”2022-11-19T14:32:50.082396361Z” level=info msg=”response completed” go.version=go1.16.15 http.request.host=your_domain http.request.id=779fe265-1a7c-4a15-8ae4-eeb5fc35de98 http.request.method=GET http.request.remoteaddr=87.116.166.89 http.request.uri=”/v2″ http.request.useragent=”Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36″ http.response.contenttype=”text/html; charset=utf-8″ http.response.duration=”162.546µs” http.response.status=301 http.response.written=39 docker-registry-registry-1 | 172.19.0.1 – – [19/Nov/2022:14:32:50 +0000] “GET /v2 HTTP/1.0” 301 39 “” “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36” docker-registry-registry-1 | 172.19.0.1 – – [19/Nov/2022:14:32:50 +0000] “GET /v2/ HTTP/1.0” 200 2 “” “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36″ docker-registry-registry-1 | time=”2022-11-19T14:32:50.132472674Z” level=info msg=”response completed” go.version=go1.16.15 http.request.host=your_domain http.request.id=0ffb17f0-c2a0-49d6-94f3-af046cfb96e5 http.request.method=GET http.request.remoteaddr=87.116.166.89 http.request.uri=”/v2/” http.request.useragent=”Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36″ http.response.contenttype=”application/json; charset=utf-8″ http.response.duration=2.429608ms http.response.status=200 http.response.written=2 docker-registry-registry-1 | 172.19.0.1 – – [19/Nov/2022:14:32:50 +0000] “GET /favicon.ico HTTP/1.0” 404 19 “your_domain/v2/” “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36”
最後の行からわかるように、リクエストは/v2/エンドポイントに対して行われました。ポートフォワーディングにより、コンテナはあなたが送信したリクエストを受け取り、{}というレスポンスを返しました。コード200は、コンテナがリクエストを正常に処理したことを意味します。
実行を停止するには、CTRL+Cを押してください。
ポートフォワーディングの設定が完了したので、レジストリのセキュリティが向上します。
ステップ3- 認証の設定を行います。
Nginxは、管理するサイトに対してHTTP認証を設定することができ、これを使用してDockerレジストリへのアクセスを制限することができます。これを実現するために、htpasswdを使用して認証ファイルを作成し、その中に受け入れられるユーザー名とパスワードの組み合わせを追加します。このプロセスによって、レジストリへの認証が有効になります。
「apache2-utils」パッケージをインストールすることで、htpasswdユーティリティを入手することができます。次のコマンドを実行してください。
- sudo apt install apache2-utils -y
~/docker-registry/authに認証ファイルを保存しましょう。次のコマンドを実行して作成してください。
- mkdir ~/docker-registry/auth
それに移動してください。 (Sore ni idō shite kudasai.)
- cd ~/docker-registry/auth
最初のユーザーを作成し、ユーザー名を希望するユーザー名で置き換えてください。-Bフラグは、Dockerが必要とするbcryptアルゴリズムの使用を指示します。
- htpasswd -Bc registry.password username
指示された場合は、パスワードを入力してください。認証情報の組み合わせは、registry.passwordに追加されます。
Note
htpasswd -B registry.password ユーザー名
-cは新しいファイルを作成するため、それを削除すると既存のファイルが更新されます。
以下の日本語での表現は一つの選択肢です:
認証情報のリストが作成されたので、docker-compose.ymlを編集して、Dockerに作成したファイルを使用してユーザの認証を行うよう指示します。編集するためにファイルを開いてください。
- nano ~/docker-registry/docker-compose.yml
ハイライトされた行を追加してください。
version: '3'
services:
registry:
image: registry:latest
ports:
- "5000:5000"
environment:
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry
REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.password
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
volumes:
- ./auth:/auth
- ./data:/data
ユーザーが環境変数を追加しました。これにより、HTTP認証の使用と、作成したhtpasswdファイルへのパスが指定されます。REGISTRY_AUTHにおいて、値としてhtpasswdを指定し、これが使用される認証スキームです。また、REGISTRY_AUTH_HTPASSWD_PATHは認証ファイルのパスを指定します。さらに、REGISTRY_AUTH_HTPASSWD_REALMはhtpasswdの領域名を表します。
あなたは./authディレクトリもマウントして、そのファイルをレジストリコンテナ内で利用できるようにしました。ファイルを保存して閉じてください。
今、あなたの認証が正しく機能するかを確認できます。最初に、メインディレクトリに移動してください。
- cd ~/docker-registry
それでは、次のコマンドを実行してレジストリを起動してください。
- docker compose up
あなたのブラウザで、ドメインのページをリフレッシュしてください。ユーザー名とパスワードが求められます。
正しいクレデンシャルの組み合わせを提供すると、空のJSONオブジェクトが含まれたページにアクセスすることができます。
{}
レジストリへの認証が成功し、アクセス権を取得しました。ターミナルでCTRL+Cを押して終了してください。
あなたのレジストリは現在、認証後にのみアクセス可能で安全になりました。次に、自動的に起動して再起動に対して強固であるようにバックグラウンドプロセスとして設定します。
ステップ4 – Dockerレジストリをサービスとして起動する
Docker Composeに常に実行指示を出すことで、システムの起動時やクラッシュ後にレジストリコンテナが常に起動するように確認できます。
docker-compose.ymlを編集するために開く。
- nano docker-compose.yml
レジストリブロックに次の行を追加してください。 (Rejisutori burokku ni tsugino gyō o tsuika shite kudasai.)
docker-compose.ymlを日本語で自然に言い換えると、次のようになります。
ドッカー・コンポーズ・ファイル
...
registry:
restart: always
...
常に再起動を設定することで、コンテナは再起動後も生存することが保証されます。作業が完了したら、ファイルを保存して閉じてください。
「-d」を指定して、バックグラウンドプロセスとしてレジストリを開始することができます。
- docker compose up -d
バックグラウンドでレジストリを実行しているため、このSSHセッションとターミナルを自由に閉じることができます。レジストリは影響を受けません。
Dockerイメージは非常に大きい場合があるため、次にNginxがアップロードを受け入れる最大ファイルサイズを拡大します。
ステップ5 – Nginxのファイルアップロードサイズを増やす
イメージをレジストリにプッシュする前に、レジストリが大きなファイルのアップロードを処理できるようにする必要があります。Nginxのデフォルトのファイルアップロードサイズ制限は1mですが、これはDockerイメージには十分ではありません。アップロード制限を上げるには、/etc/nginx/nginx.confにある主なNginxの設定ファイルを変更します。
編集のために開いてください。
- sudo nano /etc/nginx/nginx.conf
httpセクションにハイライトされた行を追加してください。
/etc/nginx/nginx.conf
/エティーシーナインジンエックス/ナンジンエックスナジェスティフコンフ
...
http {
client_max_body_size 16384m;
...
}
...
client_max_body_sizeパラメータは現在16384mに設定されており、最大のアップロードサイズは16GBとなります。
ファイルを保存して閉じる。
設定の変更を適用するために、Nginxを再起動してください。
- sudo systemctl restart nginx
この手順では、Nginxによって許可されるファイルサイズを更新しました。これにより、Nginxが転送をブロックせず、エラーが発生せずに大きな画像をDocker Registryにアップロードすることができます。
6 ステップ – プライベートなDockerレジストリへの公開
あなたのDockerレジストリサーバーが稼働し、大容量ファイルを受け付けるようになったので、イメージをプッシュすることを試してみることができます。使用できるイメージがないため、テストには公開されているDockerレジストリであるDocker Hubからubuntuイメージを使用します。
クライアントサーバーの新しいターミナルセッションで、次のコマンドを実行してUbuntuイメージをダウンロードし、実行し、そのシェルにアクセスしてください。
- docker run -t -i ubuntu /bin/bash
「-i」と「-t」フラグを使用すると、コンテナに対してインタラクティブなシェルアクセスが可能です。
一度ログインしたら、実行して成功という名前のファイルを作成してください。 (Ichido roguin shitara, jikkō shite seikō to iu namae no fairu o sakusei shite kudasai.)
- touch /SUCCESS
このファイルを作成することで、あなたは自分専用のコンテナをカスタマイズしました。後で、まったく同じコンテナを使用していることを確認するために使用します。
「コンテナシェルを実行して終了してください。」
- exit
今、ちょうどカスタマイズしたコンテナから新しいイメージを作成してください。
- docker commit $(docker ps -lq) test-image
新しいイメージは現在地で利用可能ですので、それをコンテナレジストリにプッシュします。まず、ログインする必要があります。
- docker login https://your_domain
指示が表示されたら、ステップ3で定義したユーザー名とパスワードを入力してください。
出力は以下の通りです: (Shutsuryoku wa ika no toori desu)
… Login Succeeded
ログインしたら、作成した画像を名前を変更してください。
- docker tag test-image your_domain/test-image
最後に、新たにタグ付けされた画像をレジストリにプッシュしてください。
- docker push your_domain/test-image
次のような出力を受け取ります:
Using default tag: latest The push refers to a repository [your_domain/test-image] 1cf9c9034825: Pushed f4a670ac65b6: Pushed latest: digest: sha256:95112d0af51e5470d74ead77932954baca3053e04d201ac4639bdf46d5cd515b size: 736
あなたは、ログインしてユーザー認証を処理するレジストリが正常に動作していることを確認し、認証されたユーザーがイメージをレジストリにプッシュできることも確認しました。次に、レジストリからイメージを取得することを試してみます。
ステップ7 – プライベートなDockerレジストリから取り出す
あなたがプライベートレジストリにイメージをプッシュしたので、それからプルしてみましょう。
メインサーバーで、以前に設定したユーザー名とパスワードでログインしてください。
- docker login https://your_domain
実行することで、テスト画像を取得してください。
- docker pull your_domain/test-image
Dockerはイメージをダウンロードします。次のコマンドでコンテナを実行してください。
- docker run -it your_domain/test-image /bin/bash
コンテナのシェルが読み込まれます。
実行して現在のファイルをリストアップしてください。
- ls
以前に作成したSUCCESSファイルを含むファイルのリストには、このコンテナが作成したイメージと同じものを使用していることを確認できます。
- SUCCESS bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
コンテナシェルを終了する。
- exit
あなたはイメージのプッシュとプルをテストし、カスタムイメージを保存するために使用できる安全なレジストリの設定を完了しました。
結論
このチュートリアルでは、独自のプライベートなDockerレジストリを設定し、Dockerイメージを公開します。導入で述べたように、TravisCIや同様のCIツールを使って、プライベートレジストリに直接プッシュする自動化も可能です。
Dockerコンテナをワークフローに組み込むことで、本番環境や開発環境においても、コードを含んだイメージが同じ振る舞いをすることが保証されます。Dockerファイルの作成方法に関する詳細は、ベストプラクティスに関する公式ドキュメントをご覧ください。