GitLab の Container Registry で S3 を使う

## まえがき

[GitLab](https://about.gitlab.com/) には [Container Registry](https://docs.gitlab.com/ce/user/project/container_registry.html) という Docker コンテナを保管するためのサービスがあります。
これを使うと、Dockerfile を GitLab に push すると、[GitLab CI](https://docs.gitlab.com/ee/ci/README.html) がコンテナのビルドを行い、出来上がったコンテナが Container Registry にアップロードされるということを、全自動で行うことができます。
また、GitLab CI では Container Registry にあるコンテナを使ってテストを行うこともできるので、あらかじめ環境構築を済ませたコンテナを置いておけば、テストを高速化することもできます。

そんな便利な Container Registry ですが、Docker のコンテナはソースコードと比べるとサイズがとても大きく、ストレージ容量を圧迫します。
うちでは GitLab を [Omnibus GitLab](https://docs.gitlab.com/omnibus/README.html) を使って VPS にインストールして使っているのですが、SSD プランだとあっという間に容量が足りなくなってしまい、急遽データをお引っ越しすることになりました。

## 設定

GitLab ではバックアップファイルなどの容量の大きなファイルは[クラウドストレージに置ける](https://docs.gitlab.com/ce/raketasks/backup_restore.html#uploading-backups-to-a-remote-cloud-storage)ように作られているのですが、Container Registry のデータも同様に[クラウドストレージに置ける](https://docs.gitlab.com/ce/administration/container_registry.html#container-registry-storage-driver)ように作られておりました。

Omnibus GitLab では設定を `/etc/gitlab/gitlab.rb` というファイルに書くのですが、その 440 行目付近にこのような設定項目が書かれています。

```ruby:/etc/gitlab/gitlab.rb
### Registry backend storage
###! Docs: https://docs.gitlab.com/ce/administration/container_registry.html#container-registry-storage-driver
# registry['storage'] = {
#   's3' => {
#     'accesskey' => 'AKIAKIAKI',
#     'secretkey' => 'secret123',
#     'bucket' => 'gitlab-registry-bucket-AKIAKIAKI'
#   }
# }
```

ぱっと見てわかる通り、AWS で発行した IAM ユーザのアクセスキー＆シークレットキーと、データを保存するバケットを記述します。
またここには書かれていませんが、`region` という項目もあった方がいいようです。

```ruby:/etc/gitlab/gitlab.rb
### Registry backend storage
###! Docs: https://docs.gitlab.com/ce/administration/container_registry.html#container-registry-storage-driver
registry['storage'] = {
  's3' => {
    'accesskey' => 'AKIAKIAKI',
    'secretkey' => 'secret123',
    'bucket' => 'gitlab-registry-bucket-AKIAKIAKI',
    'region' => 'ap-northeast-1'
  }
}
```

こんな感じ。

設定ができたら、reconfigure します。

```bash
$ sudo gitlab-ctl reconfigure
```

これでうまくいくかなあと思ったんですけど、そう簡単にはいきませんでした。


## 問題

上記の設定をして、コンテナのビルドを行うテストを実行すると、こんなエラーが出てしまいました。

```bash
$ docker login -u "gitlab-ci-token" -p "$CI_JOB_TOKEN" "$CI_REGISTRY"
Error response from daemon: login attempt to https://registry.example.com:4567/v2/ failed with status: 503 Service Unavailable
ERROR: Job failed: exit code 1
```

GitLab CI では GitLab アカウントの ID とパスワードではなく、テストの度に発行されるトークンを使ってログインするのでこんな書き方になっているんですけど、そのやり方ではなく、手元で普通にログインしようとしても、やっぱり同じエラーになってしまいました。

```bash
$ docker login registry.example.com:4567
Username (yuuAn): yuuan@example.com
Password:
Error response from daemon: Login: {"errors":[{"code":"UNAVAILABLE","message":"service unavailable","detail":"health check failed: please see /debug/health"}]}
 (Code: 503; Headers: map[Server:[nginx] Date:[Sun, 23 Jul 2017 07:05:12 GMT] Content-Type:[application/json; charset=utf-8] Content-Length:[125]])
```

ここで表示された情報をヒントに日本語でも英語でも検索してみたんですけど、なかなか欲している答えが見付からず。
偶然見付けたこの [Issue](https://gitlab.com/gitlab-com/infrastructure/issues/262) に書かれていたコメントを参考にしてやっと解決することができました。

> I've begun the sync to sync `/var/opt/gitlab/gitlab-rails/shared/registry/docker/` to `s3://registry-gitlab-com/` meaning that the structure on the s3 end will be `s3://registry-gitlab-com/registry/v2/<and so on>`. 


## データ移行

どうやら S3 のバケットが空っぽなのがエラーの原因らしく、S3 を設定する前に使っていたファイルを S3 にアップロードする必要があったようです。
アップロードするのは `/var/opt/gitlab/gitlab-rails/shared/registry/docker/` にあるファイルで、私は [AWS CLI](https://aws.amazon.com/jp/cli/) の `aws` コマンドを使って次のようにしてアップロードしました。

```bash
$ sudo aws s3 sync /var/opt/gitlab/gitlab-rails/shared/registry/docker/ s3://gitlab-registry-bucket-AKIAKIAKI/
```

`sudo` しているのは、普段ログインして使っているユーザだと、このディレクトリにアクセスする権限がなかったからです。
所有者はデフォルトで `registry` みたいなので、そのユーザでアップロードしてもよかったかも。


## おわりに

ひとまずこれで無事、ログイン時のエラーも出なくなり、Container Registry に push したファイルが S3 に置かれるようになりました。
日本では Container Registry を使ってる人が少ないのか、そもそも GitLab を自分でインストールして使ってる人が少ないのか、それとも単にアウトプットの少ない人達にばかり使われているのかはわかりませんが、なかなか情報を見つけられず解決にかなり時間を要したので、Qiita に記しておくことにしました。