Ubuntu 18.04でプライベートDockerレジストリを設定する方法
Ubuntu 18.04を使用していませんか?別のバージョンまたはディストリビューションを選択してください。
著者は「Write for DOnations」プログラムの一環として、寄付を受けるためにApache Software Foundationを選びました。
紹介
Docker Registryは、Dockerコンテナイメージの保存と配信を管理するアプリケーションです。レジストリはコンテナイメージを集中管理し、開発者のビルド時間を短縮します。Dockerイメージは仮想化を通じて同じ実行環境を保証しますが、イメージをビルドするにはかなりの時間投資が必要です。たとえば、Dockerを使用するために依存関係やパッケージを別々にインストールするのではなく、開発者はすべての必要なコンポーネントを含む圧縮イメージをレジストリからダウンロードできます。さらに、開発者はTravisCIなどの継続的インテグレーションツールを使用して、イメージをレジストリにプッシュする作業を自動化し、プロダクションや開発中にイメージをシームレスに更新できます。
Dockerには、カスタムDockerイメージをホストできる無料のパブリックレジストリであるDocker Hubもありますが、イメージを公開したくない状況がある場合もあります。イメージには、アプリケーションを実行するために必要なすべてのコードが含まれているため、独自のソフトウェアを使用する際にはプライベートレジストリを使用する方が望ましいです。
このチュートリアルでは、独自のプライベートDockerレジストリを設定し、安全に保護します。Docker Composeを使用してDockerアプリケーションを実行するための設定を定義し、Nginxを使用してHTTPSから実行中のDockerコンテナへのサーバートラフィックを転送します。このチュートリアルを完了すると、カスタムDockerイメージをプライベートレジストリにプッシュし、リモートサーバーから安全にイメージをプルできるようになります。
前提条件
このガイドを始める前に、次のものが必要です:
- Ubuntu 18.04初期サーバー設定ガイドに従ってセットアップされた2つのUbuntu 18.04サーバーがあります。これには、sudo非ルートユーザーとファイアウォールが含まれます。一方のサーバーはプライベートDockerレジストリをホストし、もう一方がクライアントサーバーになります。
- DockerとDocker-Composeは、Ubuntu 18.04にDocker-Composeをインストールする方法のチュートリアルに従って、両方のサーバーにインストールされています。このチュートリアルの最初のステップを完了するだけでDocker Composeをインストールすることができます。このチュートリアルでは、必要な前提条件の一部としてDockerのインストール方法について説明しています。
- Ubuntu 18.04にNginxをインストールする方法に従って、あなたのプライベートDockerレジストリサーバーにNginxがインストールされました。
- あなたのサーバー上のプライベートDockerレジストリのために、Let’s Encryptで保護されたNginxを設定するには、「Let’s EncryptでNginxを保護する方法」に従います。ステップ4ですべてのトラフィックをHTTPからHTTPSにリダイレクトすることを確認してください。
- プライベートDockerレジストリのために使用しているサーバーに解決されるドメイン名。これは、Let's Encryptの前提条件の一部として設定します。
ステップ1 — Dockerレジストリのインストールと構成
Dockerのコマンドラインツールは、1つまたは2つのDockerコンテナを起動および管理するのに便利ですが、フルデプロイメントには、Dockerコンテナ内で実行されるほとんどのアプリケーションが他のコンポーネントを並行して実行する必要があります。例えば、多くのWebアプリケーションは、アプリケーションのコードを提供するNginxのようなWebサーバー、PHPのようなインタプリタ型スクリプト言語、およびMySQLのようなデータベースサーバーで構成されています。
Docker Composeを使用すると、各コンテナの設定やコンテナ間で通信するために必要な情報を設定するための1つの.ymlファイルを書けます。その後、docker-composeコマンドラインツールを使用して、アプリケーションを構成するすべてのコンポーネントにコマンドを発行できます。
Docker Registryは複数のコンポーネントを持つアプリケーションであるため、構成を管理するためにDocker Composeを使用します。レジストリのインスタンスを開始するには、レジストリがデータを保存する場所を定義するdocker-compose.ymlファイルをセットアップします。
あなたがプライベートDockerレジストリをホストするために作成したサーバー上で、docker-registryディレクトリを作成し、その中に移動してから、次のコマンドを使ってdataサブフォルダを作成することができます:
- mkdir ~/docker-registry && cd $_
- mkdir data
テキストエディタを使用してdocker-compose.yml構成ファイルを作成してください:
- nano docker-compose.yml
以下の内容をファイルに追加してください。これはDocker Registryの基本構成を説明しています。
version: '3'
services:
registry:
image: registry:2
ports:
- "5000:5000"
environment:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
volumes:
- ./data:/data
このenvironmentセクションは、Docker Registryコンテナ内に/dataパスの環境変数を設定します。Docker Registryアプリケーションは、起動時にこの環境変数をチェックし、その結果、データを/dataフォルダーに保存し始めます。
しかし、あなたがvolumes: - ./data:/dataの行を含めたので、Dockerはそのコンテナ内の/dataディレクトリをレジストリサーバーの/dataにマッピングし始めます。結果として、Dockerレジストリのデータはすべてレジストリサーバーの~/docker-registry/dataに保存されます。
ポートセクションの設定5000:5000は、Dockerにサーバーのポート5000を実行中のコンテナのポート5000にマッピングするように指示します。これにより、サーバーのポート5000にリクエストを送信し、そのリクエストがレジストリアプリケーションに転送されるようになります。
これで、設定を確認するためにDocker Composeを開始できます:
- docker-compose up
出力にDockerがDockerのレジストリからDocker Registryイメージをダウンロードしていることを示すダウンロードバーが表示されます。1分か2分以内に、以下のような出力が表示されるでしょう(バージョンは異なる場合があります)。
Output of docker-compose upStarting docker-registry_registry_1 ... done
Attaching to docker-registry_registry_1
registry_1 | time="2018-11-06T18:43:09Z" 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.7.6 instance.id=c63483ee-7ad5-4205-9e28-3e809c843d42 version=v2.6.2
registry_1 | time="2018-11-06T18:43:09Z" level=info msg="redis not configured" go.version=go1.7.6 instance.id=c63483ee-7ad5-4205-9e28-3e809c843d42 version=v2.6.2
registry_1 | time="2018-11-06T18:43:09Z" level=info msg="Starting upload purge in 20m0s" go.version=go1.7.6 instance.id=c63483ee-7ad5-4205-9e28-3e809c843d42 version=v2.6.2
registry_1 | time="2018-11-06T18:43:09Z" level=info msg="using inmemory blob descriptor cache" go.version=go1.7.6 instance.id=c63483ee-7ad5-4205-9e28-3e809c843d42 version=v2.6.2
registry_1 | time="2018-11-06T18:43:09Z" level=info msg="listening on [::]:5000" go.version=go1.7.6 instance.id=c63483ee-7ad5-4205-9e28-3e809c843d42 version=v2.6.2
このチュートリアルの後半で、No HTTP secret provided 警告メッセージに対処します。出力はコンテナが起動していることを示しています。出力の最後の行は、ポート 5000 で正常にリスニングを開始したことを示しています。
デフォルトでは、Docker Composeはあなたの入力を待ち続けるので、Docker RegistryコンテナをシャットダウンするにはCTRL+Cを押してください。
ポート5000でリッスンする完全なDockerレジストリをセットアップしました。この時点では、レジストリは手動で起動しない限り起動しません。また、Dockerレジストリには組み込みの認証メカニズムがないため、現在は安全ではなく、完全に一般公開されています。次の手順では、これらのセキュリティ上の懸念に対処します。
ステップ2 — Nginxポートフォワーディングの設定
あなたはすでにNginxを使用してDocker RegistryサーバーにHTTPSを設定しているため、Nginxからポート5000へのポート転送を設定することができます。このステップを完了すると、レジストリに直接example.comでアクセスできるようになります。
Let’s Encryptを使用してNginxを安全にするための前提条件の一部として、すでにサーバー構成を含む/etc/nginx/sites-available/example.comファイルを設定しています。
このファイルをテキストエディタで開いてください。
- sudo nano /etc/nginx/sites-available/example.com
既存の location 行を見つけてください。それはこのように見えます:
...
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;
}
...
この $http_user_agent セクションは、クライアントの Docker バージョンが 1.5 以上であることを確認し、UserAgent が Go アプリケーションでないことを保証します。あなたが 2.0 バージョンのレジストリを使用しているため、古いクライアントはサポートされていません。詳細については、Docker の Registry Nginx ガイドにある nginx ヘッダー設定を参照してください。
ファイルを保存して終了します。Nginxを再起動して変更を適用します。
- sudo service nginx restart
レジストリを実行することで、Nginxがポート5000にトラフィックを転送していることを確認できます。
- cd ~/docker-registry
- docker-compose up
ブラウザウィンドウで、次のURLを開いてください:
https://example.com/v2
空のJSONオブジェクト、または次のように表示されます:
{}
ターミナルには、次のような出力が表示されます:
Output of docker-compose upregistry_1 | time="2018-11-07T17:57:42Z" level=info msg="response completed" go.version=go1.7.6 http.request.host=cornellappdev.com http.request.id=a8f5984e-15e3-4946-9c40-d71f8557652f http.request.method=GET http.request.remoteaddr=128.84.125.58 http.request.uri="/v2/" http.request.useragent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/604.4.7 (KHTML, like Gecko) Version/11.0.2 Safari/604.4.7" http.response.contenttype="application/json; charset=utf-8" http.response.duration=2.125995ms http.response.status=200 http.response.written=2 instance.id=3093e5ab-5715-42bc-808e-73f310848860 version=v2.6.2
registry_1 | 172.18.0.1 - - [07/Nov/2018:17:57:42 +0000] "GET /v2/ HTTP/1.0" 200 2 "" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/604.4.7 (KHTML, like Gecko) Version/11.0.2 Safari/604.4.7"
最後の行から、/v2/にGETリクエストが送信されたことがわかります。これは、ブラウザからリクエストを送信したエンドポイントです。コンテナは、ポートフォワーディングを通じてあなたが送信したリクエストを受信し、{}というレスポンスを返しました。出力の最後の行にある200というコードは、コンテナがリクエストを正常に処理したことを意味します。
ポートフォワーディングを設定したので、レジストリのセキュリティを向上させることに進むことができます。
ステップ3 — 認証の設定
Nginxがリクエストを適切にプロキシしているので、Dockerレジストリへのアクセスを管理するためにHTTP認証でレジストリを保護することができます。これを実現するために、htpasswdを使用して認証ファイルを作成し、そのファイルにユーザーを追加します。HTTP認証は設定が迅速で、HTTPS接続上で安全であり、レジストリが使用するものです。
以下のコマンドを実行することで、htpasswdパッケージをインストールできます:
- sudo apt install apache2-utils
今から、認証情報を保存するディレクトリを作成し、そのディレクトリに移動します。$_ は前のコマンドの最後の引数に展開されます。この場合は ~/docker-registry/auth です。
- mkdir ~/docker-registry/auth && cd $_
次に、次のように最初のユーザーを作成します。usernameは、使用したいユーザー名に置き換えてください。-Bフラグは、デフォルトの暗号化よりも安全なbcrypt暗号化を指定します。プロンプトが表示されたら、パスワードを入力してください。
- htpasswd -Bc registry.password username
注意: さらにユーザーを追加するには、-cオプションなしで前のコマンドを再実行してください。(cは作成用です):
- htpasswd registry.password username
次に、docker-compose.ymlファイルを編集して、Dockerにユーザーを認証するために作成したファイルを使用するよう指示します。
- cd ~/docker-registry
- nano docker-compose.yml
作成したauth/ディレクトリのために環境変数とボリュームを追加することができます。ユーザーの認証方法をDockerに伝えるために、docker-compose.ymlファイルを編集してください。ファイルに次の強調表示された内容を追加します:
version: '3'
services:
registry:
image: registry:2
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
REGISTRY_AUTHには、使用している認証スキームであるhtpasswdを指定し、REGISTRY_AUTH_HTPASSWD_PATHを認証ファイルのパスに設定しました。最後に、REGISTRY_AUTH_HTPASSWD_REALMはhtpasswdレルムの名前を示しています。
認証が正しく機能していることを確認するために、レジストリを実行し、ユーザーにユーザー名とパスワードの入力を促すかどうかを確認できます。
- docker-compose up
ブラウザウィンドウで、https://example.com/v2を開いてください。
ユーザー名とそれに対応するパスワードを入力した後、{}が再び表示されます。基本的な認証設定が確認されました:レジストリは正しいユーザー名とパスワードを入力した後のみ結果を返しました。これでレジストリのセキュリティが確保され、レジストリの使用を続けることができます。
ステップ4 — サービスとしてのDockerレジストリの開始
システムが起動するたびにレジストリが開始されるようにしたいと思っています。予期しないシステムクラッシュが発生した場合、サーバーが再起動する際にレジストリが再起動することを確認したいです。docker-compose.ymlを開きましょう:
- nano docker-compose.yml
registry:の下に以下の内容を追加してください:
...
registry:
restart: always
...
レジストリをバックグラウンドプロセスとして開始することができ、これにより ssh セッションを終了してもプロセスを持続させることができます:
- docker-compose up -d
バックグラウンドでレジストリが動作している状態で、ファイルアップロードのためにNginxを準備できます。
ステップ5 — Nginxのファイルアップロードサイズを増加させる
レジストリにイメージをプッシュする前に、大きなファイルのアップロードを処理できるようにレジストリを設定する必要があります。Dockerは大きなイメージのアップロードを別々のレイヤーに分割しますが、時には1GBを超えることがあります。デフォルトで、Nginxはファイルアップロードに1MBの制限があるため、nginxの設定ファイルを編集して最大ファイルアップロードサイズを2GBに設定する必要があります。
- sudo nano /etc/nginx/nginx.conf
httpセクションを見つけて、次の行を追加してください:
...
http {
client_max_body_size 2000M;
...
}
...
最後に、構成変更を適用するためにNginxを再起動します:
- sudo service nginx restart
Nginxエラーなしで、大きな画像をDockerレジストリにアップロードできるようになりました。
ステップ6 — プライベートDockerレジストリへの公開
あなたは今、自分のプライベートDockerレジストリに画像を公開する準備が整いましたが、まずは画像を作成する必要があります。このチュートリアルでは、Docker Hubのubuntu画像に基づくシンプルな画像を作成します。Docker Hubは、迅速にアプリケーションをDocker化するために利用できる多くの事前構成された画像を持つ公共ホスト型レジストリです。ubuntu画像を使用して、レジストリへのプッシュとプルをテストします。
あなたのクライアントサーバーから、新しいレジストリにプッシュするための小さな空のイメージを作成します。-iおよび-tフラグを使用すると、コンテナ内にインタラクティブなシェルアクセスが得られます。
- docker run -t -i ubuntu /bin/bash
ダウンロードが完了すると、Dockerプロンプトに入ります。root@の後にあるコンテナIDは異なる場合があることに注意してください。ファイルシステムにSUCCESSという名前のファイルを作成して、素早く変更を加えます。次のステップでは、このファイルを使って公開プロセスが成功したかどうかを判断することができます。
- touch /SUCCESS
Dockerコンテナから退出する:
- exit
以下のコマンドは、実行中の画像に基づいて、あなたが行った変更を加えた新しい画像を test-image という名前で作成します。この場合、/SUCCESS ファイルの追加が新しい画像に含まれています。
変更をコミットする:
- docker commit $(docker ps -lq) test-image
この時点では、イメージはローカルにのみ存在します。これで、作成した新しいレジストリにプッシュできます。Dockerレジストリにログインしてください:
- docker login https://example.com
以前のユーザー名とそれに対応するパスワードを入力してください。次に、プッシュするためにプライベートレジストリの場所で画像にタグを付けます:
- docker tag test-image example.com/test-image
新しくタグ付けされた画像をレジストリにプッシュします:
- docker push example.com/test-image
あなたの出力は以下のようになります:
The push refers to a repository [example.com/test-image]
e3fbbfb44187: Pushed
5f70bf18a086: Pushed
a3b5c80a4eba: Pushed
7f18b442972b: Pushed
3ce512daaf78: Pushed
7aae4540b42d: Pushed
...
あなたは、レジストリがユーザー認証を処理し、認証されたユーザーがレジストリに画像をプッシュできることを確認しました。次に、レジストリから画像をプルできることを確認します。
ステップ7 — プライベートDockerレジストリからのプル
レジストリサーバーに戻って、クライアントサーバーから画像をプルするテストを行うことができます。また、別のサーバーからこれをテストすることも可能です。
以前に設定したユーザー名とパスワードでログインしてください:
- docker login https://example.com
画像を取得する準備が整いました。前のステップでタグを付けたドメイン名と画像名を使用してください:
- docker pull example.com/test-image
Dockerはイメージをダウンロードし、プロンプトに戻ります。レジストリサーバー上でイメージを実行すると、以前作成したSUCCESSファイルが存在するのが確認できます:
- docker run -it example.com/test-image /bin/bash
bashシェル内のファイルをリストします:
- ls
この画像のために作成したSUCCESSファイルを見ることができます:
SUCCESS bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
ユーザーがカスタムイメージをプッシュおよびプルできる安全なレジストリの設定が完了しました。
結論
このチュートリアルでは、自分のプライベートDockerレジストリを設定し、Dockerイメージを公開します。イントロダクションで述べたように、TravisCIや同様のCIツールを使用して、プライベートレジストリへのプッシュを自動化することもできます。Dockerとレジストリをワークフローに取り入れることで、コードを含むイメージが本番環境でも開発環境でも同じ動作をすることを保証できます。Dockerファイルの作成に関する詳細情報は、このプロセスを説明するDockerチュートリアルを読むことができます。
