Nexus Repository Manager による Dockerリポジトリ構築
この記事は、筆者が独自に調査、検証したものであり、内容を保証するものではありません。
記事の内容を利用する場合は、個人・業務を問わず自己責任でお願いします。
はじめに
※ この記事は過去記事のリライトで、情報が古くなっている可能性があります。ただし、この手順で構築した NXRM は現在 ( 2026/9 ) も稼働しています。
Docker 利用が増えているようですが、Docker イメージの共有ができておらず、利用する場合には Dockerfile から個々の開発者がビルドする必要があるなど、利便性の点で難がありました。
この問題を解決するためのDockerリポジトリを構築したので、構築/利用手順を以下に示します。
リポジトリ構築には Nexus Repository OSS ( NXRM ) を利用しました。
NXRM は Docker リポジトリ以外にも、maven 等の各種ライブラリ/イメージのリポジトリとして利用可能な OSS です。
Docker-hub のプロキシとして動作する事も可能、且つ、グループリポジトリ機能で独自(hosted)リポジトリとマージする事で、内部と外部(キャッシュ)リポジトリを透過的に扱う事も可能です。
ECR ( Elastic Container Registry) 等を利用することもできますが、コスト面などで採用しにくいチームなどでも採用しやすい。
※ 社内 LAN 等では Docker Imageや各種ライブラリをキャッシュしてトラフィックを削減する事も可能になる。
※ gitlab ci 等でイメージビルド後 NXRM に登録 ( push ) といった運用も可能。gitlab も self hosted 可能。
利用する事で、リポジトリ問題を解決する事が可能かもしれません。上記のような問題がある場合、試してみるとよいでしょう。
環境
- OS :
CentOS8( KVM ゲスト で構築)
現在はRocky9にアップグレードしてある - リポジトリディレクトリは ホストのディレクトリをマウントする。
キャッシュはリポジトリのサイズは肥大化しがちなので、KVMゲスト上で管理するよりホストディレクトリをマウントする方が管理がしやすい。 - 最初 9p_virtio を使おうとしたが、うまくいかなかったので NFS を使う。
- Configuring Blob Stores に NFS は v4 使えとあったので NFSv4 を利用。
- 事前にKVMホスト側でNFSサーバを設定しておく。
- ホストのディレクトリは /var/opt/nexus
前提
- 内部アクセスのみの閉じた環境で運用するためTLS(HTTPS)化は行っていません。
※NXRMをTLS化する場合、複数の方法があります。公式ドキュメント等を参考にしてください。 nexus_hostnameが Nexsus Repository Managerをインストールしたホスト名です。
ホスト名アクセスが不可の場合IPアドレスでも構いません。
インストール手順(サーバ)
dnf update
dnf update
nfsクライアント設定
KVMホストのディレクトリをNFSマウントします。
パッケージインストール
dnf -y install nfs-utils
設定ファイルの編集
vi /etc/idmapd.conf
以下部分を編集
Domain = mosaos.local
マウント
マウントできるか確認し、
mount -t nfs kvmhost:/var/opt/nexus /mnt
OKなら umount
umount /mnt
/etc/fstab に設定を記載し、automount されるようにする。
vi /etc/fstab
以下を追記
kvmhost:/var/opt/nexus /opt/sonatype-work nfs4 rw,sync,tcp,hard,intr,rsize=32768,wsize=32768 0 0
マウント。
mount -a
NXRMインストール
ansible-playbook を利用してインストールする。
mosaos/sonatype-nexus3-ansible
プロジェクトをclone
git clone https://github.com/mosaos/sonatype-nexus3-ansible
ansible-playbook
cd sonatype_nexus3_ansible
ansible-playbook -i hosts site.yml
NXRM設定
ほぼ以下の通り
Using Nexus 3 as Your Repository - Part 3: Docker Images
以下リポジトリを作成
| 名前 | 種類 | ポート(HTTP) |
|---|---|---|
| docker-private | docker(hosted) | 5001 |
| docker-hub | docker(proxy) | - |
| docker-group | docker(group) | 5000 |
それぞれの作成時の設定内容は以下
docker-private
- docker(hosted)で作成
- HTTPをチェックしポート番号を指定 (5001)
Enable Docker V1 API:をチェック
docker-hub
- docker(proxy)で作成
Remote storage:にhttps://registry-1.docker.ioを指定Docker Index:はUser Docker Hubを選択Enable Docker V1 API:をチェック
docker-group
- docker(group)で作成
- HTTPをチェックしポート番号を指定 (5000)
Member repositories:に先に作成した二つのリポジトリを追加Enable Docker V1 API:をチェック
※ 私の環境では Enable Docker V1 API: をチェックしてないと push できませんでした。
firewall設定(追加)
リポジトリ作成後は NXRMをインストールしたホストで 5000/tcp, 5001/tcp を許可する。
firewall-cmd --add-port=5000/tcp --permanent --zone=public
firewall-cmd --add-port=5001/tcp --permanent --zone=public
firewall-cmd --reload
インストール手順(クライアント)
docker 環境を構築してください。
非TLSのリポジトリに接続させるためには設定が必要です。
環境
以下環境で確認しています。
- OS : CentOS8
- Docker : 19.03.8
疎通確認
curlでNXRMにリクエストを投げた際に、Dockerリクエストではない旨レスポンスが返ってくればOK。
curl http://nexus_hostname:5000/
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Error 400 Not a Docker request</title>
</head>
<body><h2>HTTP ERROR 400 Not a Docker request</h2>
<table>
<tr><th>URI:</th><td>/</td></tr>
<tr><th>STATUS:</th><td>400</td></tr>
<tr><th>MESSAGE:</th><td>Not a Docker request</td></tr>
<tr><th>SERVLET:</th><td>-</td></tr>
</table>
<hr><a href="http://eclipse.org/jetty">Powered by Jetty:// 9.4.30.v20200611</a>< hr/>
</body>
</html>
daemon.json の設定
daemon.jsonを作成し、Dockerの設定を記述します。
vi /etc/docker/daemon.json
TLS化していないリポジトリを利用する場合 insecure-registriesで指定する必要があります。
※毎回コマンドラインで指定する事も可能ですが、面倒なのでdockerのdeamon設定がお勧め。
insecure-registries で、NXRMで追加したレジストリを追加してください。
{
"debug": true,
"insecure-registries": ["nexus_hostname:5000", "nexus_hostname:5001"]
}
※ミラー設定も行った場合の設定は下で記載しています。
daemon.json作成後はDockerデーモンを再起動。
systemctl restart docker
Dockerイメージの登録(push)
NXRMにログイン
ユーザ / パスワードは NXRMのユーザを指定します。
docker login nexus_hostname:5000
docker login nexus_hostname:5001
実行すると Username, password 入力を求められるので、自身のアカウントでログインして下さい。
実施時に WARNING 出力されますが、Login Succeeded になれば login 成功です。
※コマンド実行後は ~/.docker/config.json に認証情報が記録されます。
push
hosted リポジトリに pushします。
docker tag イメージ名:バージョン nexus_hostname:5001/イメージ名:バージョン
docker push nexus_hostname:5001/library/イメージ名:バージョン
何故か、library付けないとpull時点で 404(NOT FOUND)になった。
※おそらく docker-hub と同じように accountname/imagename:version のようなタグ付けをしないと駄目な気がする。
公式イメージの場合には accountname部分がないが、こういうタグ付けの場合には library 以下になっている風。実際に公式mariadbを落としてきてproxyリポジトリに格納されているのを見ると name = library/mariadb となっている。
pull
push したイメージが取得できるか確認
取得先は登録したhostedではなくhostedとproxyをまとめているgroupリポジトリを指定しています。
docker pull nexus_hostname:5000/イメージ名:バージョン
ミラー設定
ここまでの設定で、独自に作成したDockerイメージをリポジトリサーバに登録(push)、及び、取得(pull)できる事が確認できましたが、 この状態では独自イメージと外部(docker-hubでホストされている)イメージを透過的に扱う事ができません。
これを透過的に扱えるように設定を変更します。
具体的には、自分でホストしているイメージ取得の場合でも以下のように、リポジトリサーバ:ポート を指定しないで取得できるようにします。
※push時には リポジトリサーバ:ポートを指定しなければいけません。
docker pull イメージ名:バージョン
docker では、registry-mirrors を利用する事で、docker-hubのミラーとして設定できるので、 作成したgroupリポジトリを設定する事で対応可能なはずですが、
ミラーサイトに対して正しく認証情報が渡されない問題があるため、仕掛けが必要になります。
対応方法としては、 Docker is not passing auth informations when pulling from a mirror registry #30880 にある認証情報をNginx(proxy)側で上書きしてNexusにアクセスさせる方法を採ります。
nginx設定の編集
最終的に以下のような設定にしました。
events {
worker_connections 1024;
}
http {
proxy_send_timeout 120;
proxy_read_timeout 300;
proxy_buffering off;
keepalive_timeout 5 5;
tcp_nodelay on;
log_format upstreamlog [$time_local] $remote_addr - $host - $upstream_addr - $request;
access_log /var/log/nginx/upstream.log upstreamlog;
server {
listen *:80;
server_name www.example.com;
# allow large uploads of files
client_max_body_size 1G;
# optimize downloading files larger than 1G
#proxy_max_temp_file_size 2G;
location ~ ^/(v1|v2)/ {
if ($authorization = '') {
set $authorization "Basic XyzAbCdefg1234567890Mzk2Zi00NWYxLWJiNjYtN2ExOWRkM2EyOTVk";
}
proxy_pass http://127.0.0.1:5000;
proxy_set_header Authorization $authorization;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
}
location / {
# Use IPv4 upstream address instead of DNS name to avoid attempts by nginx to use IPv6 DNS lookup
proxy_pass http://127.0.0.1:8081/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
リポジトリアクセスする場合 /v1 or /v2 で始まるURLにアクセスされるので、この場合にgroupリポジトリに転送します。
この際、proxy_set_header Authorization ... で認証情報を設定しています。
$authorization に設定する基本認証の値は以下のようにして生成可能です。
echo -n 'username:password' | base64
また、上記設定では実際の転送先を確認するためupstreamlogを出力するように設定しています。
上述した、libraryを付けてpushしないと pull できなかった問題はこのログの確認で気づきました。
pushは出来たがpullできない場合で、404が発生している場合、dockerクライアントがmanifestsを取得するURLがpushした先と異なっている可能性があります。
この場合にはuplstreamlogの確認で解決するかもしれません。
nginx再起動
設定に問題ないか確認し、
nginx -t
問題なければ再起動します。
systemctl restart nginx
クライアント側設定
/etc/docker/daemon.json を以下に書き換えます。
registry-mirrors、及び、insecure-registries で nexusのホストを指定しています。
{
"debug": true,
"insecure-registries": [
"nexus_hostname",
"nexus_hostname:5000",
"nexus_hostname:5001"
],
"registry-mirrors": [
"http://nexus_hostname"
]
}
トラブルシュート
ミラー設定が一番ハマりました。
問題発生時には以下内容の確認をお勧めします。
- docker(クライアント側)ログ
私の構築した環境ではsyslog(/var/log/message/)に出力 - nexus repository managerのログ
/opt/sonatype-work/nexus3/logが出力先です。 - nginxのログ
proxyしている場合、上述のupstreamlogも利用しましょう。
リクエスト先が正しくない
library付けずにpushした場合、hostedリポジトリに以下のような構造でpushされました
/v2
|- blobs
| |- sha256:.....
| |- sha256:.....
| |- sha256:.....
| |- sha256:.....
|- image_name
|- manifests
| |- sha256:...
|- tags
|- latest
この状態でミラー設定を行い docker pull image_name するとdockerログには以下のエラーが出力
Oct 13 02:36:40 localhost dockerd[17647]: time="2020-10-13T02:36:40.054981034-04:00" level=info msg="Attempting next endpoint for pull after error: Get https://nexus_hostname:5000/v2/library/image_name/manifests/latest: unauthorized: access to the requested resource is not authorized"
/v2/library/image_name/... にアクセスしている(らしい)事が判りました。
(実際はこの前にupstreamlogで気づく)
以下でpushし直した後、pullできる事を確認しました。
docker push nexus_hostname:5001/library/image_name:latest
docker pull image_name