TOC
dockerのプライベートレジストリ
dockerをローカルやパブリックでない場所で管理する、プライベートレジストリにはいくつか種類があります。
- Project Quay
- Docker Registry
- Harbor
- Gitlab Container Registry
- JFrog Artifactory
- Sonatype Nexus
それぞれの特徴と個人的な感触は以下の通りです。今回はローカルで管理することを目的としているので、インターネット上のプライベートリポジトリは外しています。
Project Quay
- 元Quay.ioで有料サービスを提供していた
- CoreOSに買収され、CoreOSがRed Hatに買収され、今年の2月にProject Quayとしてオープンソース化された
- 将来的にはDockerイメージのリポジトリとして広く使われる気がしている
色々読んでみたもののイメージが掴めなかったため今回は見送り。
https://github.com/quay/quay/blob/master/docs/getting_started.md
Docker Registry
- 謹製のプライベートレジストリだけど管理がしにくそう
ということで今回は見送り。(あまり調べてない)
Harbor
- 元々VMware内で使われていたらしい
- かなり評価が高い
arm用のバイナリがなかったため今回は見送り。
Gitlab Container Registry
GitLab Container Registry | GitLab
- オンプレミスで構築できるGitlab CEの一機能としてDockerイメージを管理できるらしい
あっという間にdisk fullになるという情報に尻込みして今回は見送り。
JFrog Artifactory
- Dockerに限らず色々なリポジトリを管理できる
- OSS版ではMaven, Gradle, Ivy, SBT, Genericのみ
なので今回は見送り。
Sonatype Nexus(採用)
- OSS版でもDocker含め結構たくさん扱えるリポジトリがある
- OSS版はDockerでイメージを配布している
今回試してみた。
作業方針
今回の作業方針は以下です。
- LAN内で使うことを前提とし、通信を平文で行う
- Nexus上でのユーザー管理はせず、(Nexusの)adminユーザーで操作する
- Proxy、Group、Hosted(Nexusの用語)を構築する
- 使用するハードウェアはNanoPi M4V2、OSはArmbian Bionic server
完成時のシーケンスは以下のようになります。
Dockerイメージのダウンロード・RAM使用量削減
githubからcloneします。ちなみに、dockerhubにもイメージが登録されていますが、アーキテクチャがamd64しかないため、今回使うarmでは利用できません。
sonatype/nexus3 Tags - Docker Hub
docker(root権限が必要な)場合
# mkdir nexus3docker
# cd nexus3docker
# git clone https://github.com/sonatype/docker-nexus3.git
# cd docker-nexus3/
podman(一般ユーザーで実行)の場合
$ mkdir nexus3podman
$ cd nexus3docker
$ git clone https://github.com/sonatype/docker-nexus3.git
$ cd docker-nexus3/
家庭内での利用なのでRAMの使用量をデフォルトから減らします。
ここに書かれているように、最低ラインが物理RAM8GBになっていますが、 リポジトリは20個もないしblobのサイズも20GBに届くこともないので、
- -Xms2703M
- -Xmx2703M
- -XX:MaxDirectMemorySize=2703M
から
- -Xms350M
- -Xmx350M
- -XX:MaxDirectMemorySize=350M
に変更します。編集するのはcloneしたdocker-nexus3
の中のDockerfile
です。
--- Dockerfile.org 2020-09-15 01:06:52.139808059 +0900
+++ Dockerfile 2020-09-21 09:02:13.632456748 +0900
@@ -73,6 +73,6 @@
EXPOSE 8081
USER nexus
-ENV INSTALL4J_ADD_VM_PARAMS="-Xms2703m -Xmx2703m -XX:MaxDirectMemorySize=2703m -Djava.util.prefs.userRoot=${NEXUS_DATA}/javaprefs"
+ENV INSTALL4J_ADD_VM_PARAMS="-Xms350m -Xmx350m -XX:MaxDirectMemorySize=350m -Djava.util.prefs.userRoot=${NEXUS_DATA}/javaprefs"
CMD ["sh", "-c", "${SONATYPE_DIR}/start-nexus-repository-manager.sh"]
この後、docker build
/ podman build
をします。私の場合1回同じ名前でdockerイメージを作ってしまっているため、事前にdockerイメージを削除しています。
# docker image rm sonatype/nexus3
# nice docker build --rm=true --tag=sonatype/nexus3 .
nice
を付けるのは重い作業でLoad Averageが下がるのを防ぐためで、実質的な実行時間はさほど変わりません。
podmanの場合は以下のコマンドを実行します。
$ nice podman build --rm=true --tag=sonatype/nexus3 .
これで、sonatype/nexus3
という名前のDockerイメージがビルドされます。
永続ボリュームの事前作成
Dockerコンテナがデータを保存する永続ボリュームについて、事前に作成します。
# docker volume create --name nexus-data
nexus-data
podmanの場合は以下の通りです。–nameオプションは存在せず、-lを指定しています。volumeの名前もディレクトリ名を取ったものを使用するようなので、あわせて指定します。
$ podman volume create -l io.podman.compose.project=docker-nexus3 docker-nexus3_nexus-data
nexus-data
Dockerコンテナの起動(docker-compose版)
githubからcloneした中にdocker-compose.yml
があるので、以下の通り変更します。restart: always
はdocker-compose up -d
で正しく起動、docker-compose down
で正しく終了することを確認してから追加することをお勧めします。
--- docker-compose.yml.org 2020-09-15 01:06:52.139808059 +0900
+++ docker-compose.yml 2020-09-15 09:33:48.695926250 +0900
@@ -1,12 +1,17 @@
-version: "2"
+version: "3.5"
services:
nexus:
image: sonatype/nexus3
+ container_name: nexus3oss
volumes:
- "nexus-data:/nexus-data"
+ restart: always
ports:
- "8081:8081"
+ - "18080:18080"
+ - "28080:28080"
volumes:
- nexus-data: {}
+ nexus-data:
+ name: nexus-data
docker-compose.ymlのあるディレクトリで以下のコマンドを実行します。
# docker-compose up -d
Creating network "docker-nexus3_default" with the default driver
Creating nexus3oss ... done
Dockerコンテナの起動(docker-compose使わない版)
コンテナを起動します。引数として以下の情報を指定します。
- -p 8081:8081 -p 18080:18080 -p 28080:28080 Nexus3のWeb管理画面用のポートとして8081、Group用のポートとして18080、Hosted用のポートとして28080
- -v nexus-data:/nexus-data 永続データの格納場所をDocker Volumeで指定
–name nexus イメージ名をnexusとして指定
# docker run -d -p 8081:8081 -p 18080:18080 -p 28080:28080 -v nexus-data:/nexus-data --name nexus sonatype/nexus3 35cb31b88a24e4e493442bac3ede1a0997a02022028ad39ce975a5595b0870bc
Podmanコンテナの起動(podman-compose版)
コンテナが起動した状態でOS再起動するとネットワークから繋がらないため、1回停止してもう一度up -dする必要があります
恐らくこのバグが原因だと思います。
(色々やってるうちにインストールしました。効果があるかどうかわかりません)
# apt-get install slirp
docker-compose版と同様Dockerfileを修正してから起動します。
$ podman-compose up -d
using podman version: podman version 2.0.6
podman pod create --name=docker-nexus3 --share net -p 28080:28080 -p 18080:18080 -p 8081:8081
b18a1773f9c7329df5d6dbd6e8d9bcebef31d04af9113a2dcf9a41c6c852c88f
0
podman volume inspect docker-nexus3_nexus-data || podman volume create docker-nexus3_nexus-data
podman run --name=nexus3oss -d --pod=docker-nexus3 --label io.podman.compose.config-hash=123 --label io.podman.compose.project=docker-nexus3 --label io.podman.compose.version=0.0.1 --label com.docker.compose.container-number=1 --label com.docker.compose.service=nexus -v docker-nexus3_nexus-data:/nexus-data --add-host nexus:127.0.0.1 --add-host nexus3oss:127.0.0.1 sonatype/nexus3
80c3eff26540c7e123640687e6bab6cab78a4498e3daae774d219beb9b433e3b
0
停止。
$ podman-compose down
Podmanコンテナの起動(podman-compose使わない版)
$ podman run -d -p 8081:8081 -p 18080:18080 -p 28080:28080 -v docker-nexus3_nexus-data:/nexus-data --name nexus sonatype/nexus3
adminユーザーのパスワード確認
コンテナが起動したら1、永続マウントしているnexus-dataの下にあるadmin.password
にパスワードが書かれているので、中身を確認します。
# docker inspect nexus-data
[
{
"CreatedAt": "2020-04-02T19:52:19+09:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/autofs/usbhdd1/var/docker-image/volumes/nexus-data/_data",
"Name": "nexus-data",
"Options": {},
"Scope": "local"
}
]
Mountpointがマウントされているディレクトリなので、(btrfsなら)そのままcatで読み出せます。2
# cat /autofs/usbhdd1/var/docker-image/volumes/nexus-data/_data/admin.password
bb815373-127b-42e9-bb0b-dc26eecf3e89
或いは、上に記載したdocker-compose.yml
を使用した場合は正攻法で以下のコマンドでも確認可能です。
# docker container exec nexus3oss cat nexus-data/admin.password
bb815373-127b-42e9-bb0b-dc26eecf3e89
ブラウザからの初期設定
コンテナが起動し終わると、ブラウザから接続できるようになります。ブラウザから以下のURLへ接続します(Nexusのインストール先は192.168.1.210と仮定)
右上のSign inをクリックし、ユーザadmin、パスワードはadmin.passwordに書かれている値でログインします。
設定タスクをこなしていきます。
adminユーザーのパスワードを改めて設定します。ここで設定したパスワードを次回以降のadminユーザーのログインで使用します。
ここでは匿名アクセスを許可します。
リポジトリの追加
aminユーザーでログインし、中央上の⚙アイコンをクリックしてリポジトリの管理画面に移動し、Repositoriesをクリックします。
とりあえずNuGetのリポジトリは当面使わないので、削除します。
クリックして移動した先で「Delete repository」を押して、確認ダイアログでYesを押して削除します。
これをnugetで始まる3つ繰り返して、nuget関連のリポジトリを削除します。
Proxy(docker)リポジトリの作成
Repositoriesの画面で「+ Create repository」をクリックします。
Recipeからdocker (proxy)を選択。以下の値を設定します。
- Name: docker-proxy
- Enable Docker V1 APIのAllow clients to use the V1 API to interact with this repositoryにチェック
- ProxyのRemote storage: https://registry-1.docker.io
- ProxyのDocker Index: Use Docker Hubを選択
一番下のCreate repositoryをクリックすると、Proxy(docker)リポジトリが作成されます。
Group(docker)リポジトリの作成
同じように「+ Create repository」をクリックし、Recipeからdocker (group)を選択します。
以下の値を設定します。
- Name: docker-group
- Repository ConnectorsのHTTP: 18080
- GroupのMember repositories:にdocker-proxy(作成したProxyリポジトリ)を追加
一番下のCreate repositoryをクリックすると、Group(docker)リポジトリが作成されます。
Proxyとしての動作確認
ここまででプロキシリポジトリにアクセスするとDocker Hubからイメージを取得できます。
本当ならHTTPS/TLSで起動すべきところですが、JettyをTLSで起動しようとすると証明書が必要なので今回はまず平文で使用することに。ログは管理画面左側のSupport→Logging→Log Viewerで確認可能。こんなログが出ます。
2020-04-02 01:33:53,677+0000 INFO [qtp1531125840-163] admin org.sonatype.nexus.repository.manager.internal.RepositoryManagerImpl - Creating repository: docker-group -> OrientConfiguration{repositoryName='docker-group', recipeName='docker-group', attributes={docker={httpsPort=18443.0, forceBasicAuth=true, v1Enabled=false}, storage={blobStoreName=default, strictContentTypeValidation=true}, group={memberNames=[docker-proxy]}}}
2020-04-02 01:33:53,864+0000 WARN [qtp1531125840-163] admin org.sonatype.nexus.repository.docker.internal.DockerConnectorFacetImpl - Could not configure HTTPS connector on port 18443 for docker repository docker-group
org.sonatype.nexus.bootstrap.jetty.UnsupportedHttpSchemeException: Unsupported HTTP Scheme: https
at org.sonatype.nexus.internal.jetty.ConnectorRegistrarImpl.validate(ConnectorRegistrarImpl.java:128)
具体的には以下のURLで設定手順が書かれています。
curlコマンドで以下のような結果が得られればProxyとして動作していることが確認できます。
$ curl -v -k http://192.168.1.210:18080/v2/_catalog
* Trying 192.168.1.210:18080...
* TCP_NODELAY set
* Connected to 192.168.1.210 (192.168.1.210) port 18080 (#0)
> GET /v2/_catalog HTTP/1.1
> Host: 192.168.1.210:18080
> User-Agent: curl/7.65.3
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Sun, 05 Apr 2020 05:10:06 GMT
< Server: Nexus/3.22.0-02 (OSS)
< X-Content-Type-Options: nosniff
< Content-Security-Policy: sandbox allow-forms allow-modals allow-popups allow-presentation allow-scripts allow-top-navigation
< X-XSS-Protection: 1; mode=block
< Docker-Distribution-Api-Version: registry/2.0
< Content-Type: application/json
< Content-Length: 19
<
* Connection #0 to host 192.168.1.210 left intact
{"repositories":[]}
Dockerクライアントへの平文設定(HTTPS/TLS設定しない場合)
ここまでの手順の通りHTTPS/TLSを使わずにGroupを設定した場合はDockerクライアントで平文レジストリであることを設定します。この後Hosted(docker)リポジトリの作成も行う場合で使用するポートも平文で設定するので、28080ポートも記載します。
Dockerクライアントで/etc/docker/daemon.json
を作成します。
{
"insecure-registries" : ["192.168.1.210:18080", "192.168.1.210:28080"]
}
Dockerクライアントでdockerを再起動します。
# systemctl restart docker.service
また、Nexusが動作している端末でdockerを再起動すると当然ながらNexusも動作が停止するため、その場合はNexusコンテナを起動します。
# docker start nexus
Dockerクライアントでログインします。ログイン時のユーザー名とパスワードはNexusで管理する時のadminユーザーのものを使います。
# docker login http://192.168.1.210:18080
Username: admin
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
Docker HubからDockerイメージをProxy経由でpullします。イメージ名の前にNexusが動作しているIPアドレスとGroupで使用しているポート(18080)を指定し、/で区切ります。
# docker pull 192.168.1.210:18080/alpine
Using default tag: latest
latest: Pulling from alpine
Digest: sha256:b276d875eeed9c7d3f1cfa7edb06b22ed22b14219a7d67c52c56612330348239
Status: Downloaded newer image for 192.168.1.210:18080/alpine:latest
192.168.1.210:18080/alpine:latest
# docker images | grep 192.168.1.210
192.168.1.210:18080/alpine latest a187dde48cd2 10 days ago 5.59MB
これでDocker Hubに登録されていたDockerイメージをProxy経由でダウンロードできるようになりました。同一イメージであれば2回目以降はProxy上のキャッシュが使われます。
Hosted(docker)リポジトリの作成
同じように「+ Create repository」をクリックし、Recipeからdocker (hosted)を選択します。
以下の値を設定
- Name: docker-hosted
- Repository ConnectorsのHTTP: 28080
- Enable Docker V1 APIのAllow clients to use the V1 API to interact with this repositoryにチェック
一番下のCreate repositoryをクリックすると、Hosted(docker)リポジトリが作成されます。
Groupリポジトリ(docker-group)に戻って、一番下のGroupのMember repositoriesに作成したdocker-hostedをMembersに追加します。
DockerクライアントからHosted(docker)にログイン
Group(docker)と同じように、Hosted(docker)にもDockerクライアントからログインします。ユーザー名とパスワードは同じようにNexusのadminユーザーのものを使います。
# docker login http://192.168.1.210:28080
Username: admin
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
Docker Hubからダウンロードしたイメージをリネームしてプライベートレジストリに登録
よく使うイメージの通信量を削減するため、ここではサンプルとして以下のシナリオで操作します。
- Ubuntu 18.04のARM64イメージとAMD64イメージをDocker Hubから取得
- 取得したイメージをタグ付け
- プライベートリポジトリに登録する
ARM環境でDockerHubからイメージを取得(Groupから)
# docker pull 192.168.1.210:18080/ubuntu:bionic
Docker Hubではmulti-archサポートが入っていて、同じイメージ名でもDockerクライアントのアーキテクチャに合わせたイメージがダウンロードされます。
# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
192.168.1.210:18080/ubuntu bionic 428b2f74b0fb 13 days ago 57.7MB
念の為、プラットフォームに応じたイメージを取得できたか確認します。
# docker run -it 192.168.1.210:18080/ubuntu:bionic
root@81f0757bf857:/# ls
bin dev home media opt root sbin sys usr
boot etc lib mnt proc run srv tmp var
root@81f0757bf857:/# ldd /bin/ls
linux-vdso.so.1 (0x0000ffff9e11a000)
libselinux.so.1 => /lib/aarch64-linux-gnu/libselinux.so.1 (0x0000ffff9e08c000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffff9df33000)
/lib/ld-linux-aarch64.so.1 (0x0000ffff9e0ef000)
libpcre.so.3 => /lib/aarch64-linux-gnu/libpcre.so.3 (0x0000ffff9dec1000)
libdl.so.2 => /lib/aarch64-linux-gnu/libdl.so.2 (0x0000ffff9deac000)
libpthread.so.0 => /lib/aarch64-linux-gnu/libpthread.so.0 (0x0000ffff9de80000)
タグ付けをします。タグ付け時にイメージ前にリポジトリを指定し、192.168.1.210:28080/arm64ubuntu18.04:20200403
というイメージにします。
# docker tag 192.168.1.210:18080/ubuntu:bionic 192.168.1.210:28080/arm64ubuntu18.04:20200403
# docker image ls
192.168.1.210:18080/ubuntu bionic 428b2f74b0fb 13 days ago 57.7MB
192.168.1.210:28080/arm64ubuntu18.04 20200403 428b2f74b0fb 13 days ago 57.7MB
ARM環境でイメージを登録(Hostedへ)
タグ付けした192.168.1.210:28080/arm64ubuntu18.04:20200403
をHosted(docker)へpushします。
# docker push 192.168.1.210:28080/arm64ubuntu18.04:20200403
The push refers to repository [192.168.1.210:28080/arm64ubuntu18.04]
5769cbaa7f7a: Pushed
210d0dc42c58: Pushed
93d5d356eaa3: Pushed
e7e46d8023a0: Pushed
20200403: digest: sha256:12b9106d200061c8eb2179c984e63cc17d5a4e7a34f6e9ad03360b8efe492e96 size: 1152
AMD64環境でDockerHubからイメージを取得(Groupから)
実行するコマンドはarmでの場合と全く同じです。
# docker pull 192.168.1.210:18080/ubuntu:bionic
# docker image ls
192.168.1.210:18080/ubuntu bionic 4e5021d210f6 13 days ago 64.2MB
# docker run -it 192.168.1.210:18080/ubuntu:bionic
root@5deb505bef2e:/# ldd /bin/ls
linux-vdso.so.1 (0x00007ffc9cba6000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f3c7ddcb000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3c7d9da000)
libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f3c7d768000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f3c7d564000)
/lib64/ld-linux-x86-64.so.2 (0x00007f3c7e215000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f3c7d345000)
依存ライブラリの違いでイメージ自体が実は違うことがわかります。192.168.1.210:28080/amd64ubuntu18.04:20200403
というイメージ名でタグ付けします。
# docker tag 192.168.1.210:18080/ubuntu:bionic 192.168.1.210:28080/amd64ubuntu18.04:20200403
# docker image ls
192.168.1.210:18080/ubuntu bionic 4e5021d210f6 13 days ago 64.2MB
192.168.1.210:28080/amd64ubuntu18.04 20200403 4e5021d210f6 13 days ago 64.2MB
AMD64環境でイメージを登録(Hostedへ)
192.168.1.210:28080/amd64ubuntu18.04:20200403
をHosted(docker)へpushします。
# docker push 192.168.1.210:28080/amd64ubuntu18.04
The push refers to repository [192.168.1.210:28080/amd64ubuntu18.04]
16542a8fc3be: Pushed
6597da2e2e52: Pushed
977183d4e999: Pushed
c8be1b8f4d60: Pushed
20200403: digest: sha256:e5dd9dbb37df5b731a6688fa49f4003359f6f126958c9c928f937bec69836320 size: 1152
管理しているイメージを確認する
Nexusで管理しているDockerイメージを管理画面で確認します。左側のSearch→Dockerをクリックすると、管理しているイメージのうちFormatがDockerのものが表示されます。
イメージをクリックすると、イメージの概要が表示されます。さらにイメージをクリックするとより詳しい情報が表示されます。
イメージを削除する場合は「Delete asset」ボタンをクリックします。
Podmanクライアントからイメージを検索する
Dockerクライアントからは検索できる方法がわかりませんでしたが、Docker(OCI)と互換性のあるPodmanクライアントからは/etc/containers/registries.conf
を以下のように指定することで検索可能になります。
[registries.search]
registries = ['docker.io', 'quay.io', '192.168.1.210:18080']
[registries.insecure]
registries = ['192.168.1.210:18080']
$ podman search arm64 | grep 18080
(snip)
1.210:18080 192.168.1.210:18080/arm64alpine 0
1.210:18080 192.168.1.210:18080/arm64ubuntu18.04 0
管理しているイメージをダウンロードする
Dockerクライアントからイメージをダウンロードする場合は、Webの管理画面に表示されているUsageの部分のイメージの先頭にNexus3のIPアドレスとGroup(docker)のポート番号を指定します。これはProxy経由でpullする場合と全く同じ手順で、Hosted(docker)がGroupと連携していることを表しています。
# docker pull 192.168.1.210:18080/arm64alpine:20200404
Sonatype Nexus3/NXRM OSS版をDocker Registryとして使ってみた感想
個人的に感じた長所と短所を上げると以下のようになります。
長所
- ちゃんと動く
- Webからの管理画面が見やすい
- Docker以外にも物凄くたくさんのリポジトリを管理できる
短所
- RAMがかなり必要(要件としては8GB以上) 3
- Nexusでのリポジトリの概念を理解しないと使えない
- 管理しているDockerイメージ自体のサイズがわからない4
- Webの管理画面が日本語にならない(個人的には気にならないんですがお約束)
現実的に使える選択肢ではあると思うので、Dockerのイメージのサイズにお悩みなら使ってみるのも良いと思います。
但し、Dockerに限ったリポジトリならarmイメージが公開されればHarbor、ハンドリングしやすくなればProject Quayも有力な選択肢になると思います。
スポンサーリンク
- たまにコンテナ内でJavaが異常終了するので、
docker logs -f nexus
やdocker container ls -a
でコンテナが起動していることを確かめましょう [return] - 記載されるパスワードは環境によって異なります [return]
- https://help.sonatype.com/repomanager3/system-requirements#SystemRequirements-Memory [return]
- File Sizeとして書かれているのはjsonファイルのサイズ [return]