privateなdockerレジストリを構築する

山道を歩きながらこう考えた。社内でよく使う環境を毎回セットアップするのは面倒だ。ansibleを作っても良いのだが、流すのにも時間がかかるし書き慣れない。構築手順を間違えれば失敗だ。とかくに検証環境の作成はやりにくい。

## やりたいこと

これを解決するため、自社専用のプライベートdockerレジストリを構築することを決めました。
つまり、検証環境や開発環境をdockerコンテナとして作成してしまい、レジストリを経由して社内の人々に使ってもらおうということです。

## 本記事の目標
* dockerプライベートレジストリを構築し、pull・pushをできるようにする
* 画面UIを用意する

## 構築手順
### サーバーについて
レジストリを構築するサーバーについてですが、レジストリ自体をdockerコンテナとして立ち上げます。
なので、centos7にしました。

今回のケースでは、グローバルIPアドレスとドメインを持っているVPSサーバーです。

ここではこのサーバーをdocker-hostと呼称します。

### dockerのインストール
前述の通り、レジストリ自体がdockerコンテナなので、dockerをインストールします。

```
yum install docker-io
systemctl start docker
```

### レジストリの構築
dockerレジストリはDocker Hubでimageを配布されています。それを使ってコンテナを起動させることで、レジストリを構築することができます。

なお、v2を採用します。

https://docs.docker.com/registry/deploying/#customize-the-storage-back-end

こちらのページで、構築方法などが詳細に記載されています。

今回は、外部からの通信をしたいので、localレジストリではいけません。
なので「Run an externally-accessible registry」のパートを参考にします。

local registry　との違いは、TLS通信を使うことです。

#### 構造
dockerコンテナに配置する証明書や、コンテナにマウントするディレクトリなどが発生するので、それらのディレクトリをまとめたいです。
今回は、以下のようなディレクトリ構成にしてみました。

```
/root/docker/registry
　　　　　　　　　　　　/certs/ ←証明書を配置する
　　　　　　　　　　　　/data/ ←dockerコンテナのデータをマウントする
```

#### 自己証明書を作って配置する
SSL証明書などはお金がかかるので持っていないですけれど、TLS通信を実現したいので、自己証明書を作成します。

```
cd /var/tmp
mkdir certs
cd certs
openssl req -newkey rsa:2048 -nodes -keyout domain.key -x509 -days 365 -out domain.crt
```

作成したファイルを指定の位置に配置します。

```
cp -p /var/tmp/certs/domain.key /root/docker/registry/certs/
cp -p /var/tmp/certs/domain.crt /root/docker/registry/certs/
```


#### dockerコンテナを起動

```
cd /root/docker/registry

docker run -d \
 --restart=always \
 --name registry \
 -v `pwd`/certs:/certs \
 -v `pwd`/data:/var/lib/registry \
 -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
 -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
 -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
 -e REGISTRY_STORAGE_DELETE_ENABLED=True \
 -p 443:443 \
 registry:2
```

プロセスを確認して、dockerコンテナが起動していることを確認します。

以上で、レジストリが構築できたはずです。

## 確認
面倒なので、その場で確認したいところですが、せっかくなので別のサーバーからpull・push等してみます。

### docker-client準備
imageをpullしたりするサーバーですが、なんでも良いので、vagrantで作ります。
このサーバーを「docker-client」と呼称します。

* centos7
* dockerをインストールし起動する

ができていれば良いです。

### 自己証明書を信用させる
今回のレジストリは自己証明書で作ったので、それを信用させる必要があります。

https://docs.docker.com/registry/insecure/

自分で作ったレジストリなので分かりきっているから別に良いので、このレジストリはインセキュアですよ～と認識させる方法でも構いません。
（上記URLの「Deploy a plain HTTP registry」の内容）


ただ、せっかくですしこちらの方法を記載している記事が多くなかった気がするので、別の方法を取ってみます。

1. 作成したdocker-hostの証明書をdocker-clientにコピーする
2. docker-clientで `/etc/docker/certs.d` 以下にdokcer-hostのドメインのディレクトリを作成する
3. docker-clientで 2で作成したディレクトリ以下に、1のファイルをコピーする
4. ファイル名は `ca.crt` とする

上記でうまくいかない場合は、下記のようにすると良いです。

1. 作成したdocker-hostの証明書をdocker-clientにコピーする
2. docker-clientで 1のファイルを `/etc/pki/ca-trust/source/anchors/` 以下にコピーする
4. ファイル名は `{docker-hostのドメイン名}.crt` とする
5. docker-clientで `update-ca-trust` を実行
6. docker-clientで `systemctl restart docker` を実行


確認のため、下記コマンドを実行してみます。

```
curl https://{docker-hostのdomain}/v2/_catalog
```

エラーが出なければOKです。


どうしてもだめなら、ドキュメントにある通り、`/etc/docker/daemon.json`にinsecure-registriesを記載する方針にしてしまいましょう。
自分で作成したレジストリなので良いでしょう、という前提付きで…。

ちなみにこちらの方法ですと、dockerでのレジストリ情報を書くことになりますので、上記のcurlコマンドは相変わらずエラーになると思います。
curlコマンドについても、今回はSSLのエラーを飛ばして良いと思いますので、この場合は「-k」オプションをつければ良いです。

`curl -k https://{docker-hostのdomain}/v2/_catalog`

### リポジトリの操作
準備が整ったので、レジストリを使ってみます＠docker-client

Docker Hubから適当なimageを取得します。

```
docker pull centos:7
```

取得したimageをプライベートレジストリにおくため、タグ打ちをします。

```
docker tag docker.io/centos:7 {docker-hostのドメイン}/{リポジトリ名}:{tag}
```

一例

```
[root@docker-client ~]# docker tag docker.io/centos:7 tekito-private-registry.com/test/tekito:latest
[root@docker-client ~]# docker images
REPOSITORY                                      TAG                 IMAGE ID            CREATED             SIZE
docker.io/centos                                7                   ff426288ea90        10 days ago         207.2 MB
tekito-private-registry.com/test/tekito          latest              ff426288ea90        10 days ago         207.2 MB
```

IMAGE ID が同じですが、それぞれ違うimageとして作成されました。

tekito-private-registry.com/test/tekitoのように階層をつくることができます。適宜パスを設定しましょう。


pushしてみます。

```
docker push {タグ打ちしたimage名}:{tag}
```

確認のため、いったんimageを削除します。

```
docker rmi {docker-hostのドメイン}:{tag}
docker images
```

pullします。

```
docker pull {docker-hostのドメイン}:{tag}
docker images
```

pullできればOKです。


以上で、一通りプライベートレジストリを使ってリポジトリを公開したり取得したりすることができるようになりました。


## APIを使ってレジストリを操作する
ここまででプライベートレジストリにimageをプッシュしたりすることができました。

しかし、これではレジストリのなかにどのようなimageがあるのか、どのようなパスで存在しているかわかりません。

レジストリではAPIを用意しており、それを使うことで、レジストリやその中のリポジトリの操作ができます。
APIについては、以下がドキュメントです。

https://docs.docker.com/registry/spec/api/

ちなみに、レジストリv1とv2ではAPIの仕様がだいぶ違うらしいです。今回のレジストリはv2なので、v2のAPIの仕様を確認します。

一つだけAPIを紹介します。

### catalog
レジストリ内にどのようなリポジトリが存在しているかリストするAPIです。先程自己証明書を信頼させる下りで使ったのも同じAPIです。

```
curl https://{docker-hostのdomain}/v2/_catalog
```

JSONデータが応答されます。

以下のようなイメージです。

```
[root@docker-client ~]# curl https://tekito-private-registry.com/v2/_catalog
{"repositories":["test/tekito","test/yurui",""dev/develop01"]}
```

どのようなパスでどのようなリポジトリがあるか、なんとなくわかりますね。

他にもタグを追加削除したり、様々なAPIが用意されています。


## UIを使う
APIを使ってどのようなリポジトリがレジストリ内にあるかなどを確認することができました。

しかしUIでも確認できると便利です。

### docker-registry-frontend
今回は紹介されている記事が多かったように見受けられる、docker-registry-frontendというものでUIを見ます。

https://hub.docker.com/r/konradkleine/docker-registry-frontend/

### 構築
これもdocker-host上にdockerコンテナとして立ち上げます。

基本的にはドキュメントに記載されている通りで問題ありません。

```
docker run \
  -d \
	-e ENV_DOCKER_REGISTRY_HOST=ENTER-YOUR-REGISTRY-HOST-HERE \
	-e ENV_DOCKER_REGISTRY_PORT=ENTER-PORT-TO-YOUR-REGISTRY-HOST-HERE \
	-e ENV_DOCKER_REGISTRY_USE_SSL=1 \
	-p 8080:80 \
	--name docker_registry_frontend \
	konradkleine/docker-registry-frontend:v2
```

今回のレジストリはTLSでの通信なので、「Docker registry using SSL encryption」のブロックを採用しました。

ENV_DOCKER_REGISTRY_PORTの設定でハマりました。
ここに5000を指定していたのですが、443です。
考えるだに愚かだとは思うのですが、一生懸命5000を指定していたのです。


### 確認
ブラウザで確認してみましょう。
8080ポートでポートフォワーディングしていますので、そちらにアクセスします。

http://{docker-hostのdomain}:8080

画面が表示され、「Browse repositories」ボタンを押すと、リポジトリがずらりと表示されます。

ちゃんとパスで分割されています。



以上で、一通りのプライベートレジストリの構築と操作とUIを使っての確認ができました。

## 懸念点
このままですと、urlを知っていると誰でもpullやpushができてしまいます。
そこがちょっと気になりますね。

それを解決するために、docker_authを使って権限設定をしたいと思いますが、それは別記事とすることにします。

### 追記（2018/3/20）
別記事を書きました。
→　[docker_authを使ってプライベートレジストリの権限管理をする](https://qiita.com/zknzfz/items/61909e9a577e1b2a731b)


## 参考
参考にした記事などを紹介します。

https://qiita.com/rsakao/items/617f54579278173d3c20
https://qiita.com/stbmp23/items/64fd20eeb44e4992c594
http://mao-instantlife.hatenablog.com/entry/2015/08/14/%E7%A4%BE%E5%86%85%E3%82%B5%E3%83%BC%E3%83%90%E3%81%ABDocker_Registry%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%82%92%E3%82%84%E3%82%8A%E5%8F%96%E3%82%8A

公式のドキュメントが充実していますので、そこも必見です。

ここまで読んでくださった方はありがとうございました。参考になれば幸いです。