DockerHub でイメージを自動ビルドする際の Tips

DockerHub で GitHub や BitbucketGitHub と連携して Docker イメージを自動ビルドすること自体は、既に先人たちが道を切り開いてくださっているので、そちらを参照されたし。

* [Docker HubでAutomated Buildを設定する](https://qiita.com/Brutus/items/19f02df409e859406914)
* [【Docker】GitHubと連携してDockerイメージを自動ビルドする手順](https://qiita.com/terufumi1122/items/91871075cfb9cff4617e)
* [Qiita 内を "dockerhub 自動ビルド" で検索](https://qiita.com/search?utf8=%E2%9C%93&sort=&q=dockerhub+%E8%87%AA%E5%8B%95%E3%83%93%E3%83%AB%E3%83%89)

本稿では、ちょっと捻くれたことをしている自分が、作業を進める中で得た知見を Tips として共有する。

# Dockerfile がソースリポジトリのトップではなく、サブディレクトリの下にある場合

一つのソースリポジトリにディレクトリを掘って、複数の Dockerfile を管理している構成で、例えば directory1 の下にある Dockerfile を自動ビルドしたい場合、

```
├─ LICENSE
├─ README.md
├─ directory1
│   └─ Dockerfile ← これを自動ビルドしたい
└─ directory2
    └─ Dockerfile
```

BUILD RULE の **Build Context の欄に `/directory1` と入力**すれば良い。以下に例を示す。

Source Type|Source|Docker Tag |Dockerfile location|Build Context|
-----------|------|-----------|-------------------|-------------|
Branch   |dir1_dev|latest     |Dockerfile         |/directory1  |

ただし、directory2 など他のディレクトリの Dockerfile を編集した場合に自動ビルドが暴発しないよう、Source Type 欄や Source 欄の設定内容は注意すること。この例だと、ソースリポジトリの `dir1_dev` ブランチに push した時だけ自動ビルドが発動する。

ちなみに、自動ビルドすると DockerHub におけるイメージの紹介ページの Overview の内容が、[ソースリポジトリにある README.md の内容で強制的に上書きされる](http://docs.docker.jp/docker-hub/builds.html#understand-the-build-process)。もし、ソースリポジトリのトップに README.md ファイルが一つだけある構成だったりすると、そのファイルが参照されてしまう。
それだと困る場合の対応策としては、**各ディレクトリそれぞれに README.md を保存**しておけば良い。Dockerfile と同じディレクトリ内に README.md があれば、そちらが優先して参照される。

```
├─ LICENSE
├─ README.md ← 同じディレクトリ内になければ、これが参照される
├─ directory1
│   ├─ Dockerfile
│   └─ README.md ← これが優先的に参照される
└─ directory2
    ├─ Dockerfile
    └─ README.md
```

# 一度の自動ビルドで複数のタグをつける

例えば、毎回の自動ビルド結果に対して個別のタグを付けると同時に、最新のものは常に latest タグを付けたい…ということを実現したい場合は、BUILD RULE の **Docker Tag 欄にカンマ区切りで複数設定する**と良い。以下に例を示す。

Source Type|Source|Docker Tag        |Dockerfile location|Build Context|
-----------|------|------------------|-------------------|-------------|
Tag   |/^[0-9.]+$/|latest,{sourceref}|Dockerfile         |/directory1  |

この例だと、`1.0` というタグを打ってソースリポジトリに push した場合、ビルド結果に対して `1.0` と `latest` のタグが付けられる。

参考: https://github.com/docker/hub-feedback/issues/341#issuecomment-263705074

## hooks/build を上書きした上で、一度の自動ビルドで複数のタグをつける

Dockerfile 内の `ARG` に動的に変化する値を渡せるように、[自動ビルドのスクリプトを上書きできる仕組み](https://docs.docker.com/docker-hub/builds/advanced/#override-build-test-or-push-commands)があるのだが、上述の「一度の自動ビルドで複数のタグをつける」と組み合わせる場合、以下のようなスクリプトではうまくいかない。[^1]

[^1]: そもそも、build スクリプトを上書きしなかった場合は、どんなスクリプトで処理してるのだろうか? どこかに情報は無いものか。

```bash:.../hooks/build
#!/bin/bash
docker build --build-arg CUSTOM=$VAR -f $DOCKERFILE_PATH -t $IMAGE_NAME .
```

エラーの内容から考察すると、恐らく、環境変数 `DOCKER_TAG` には `latest,1.0` という文字列が設定されていると考えられるので、for 文を利用して複数のイメージ名を指定するように細工する。加えて、ネットで調べたところ、どうも環境変数 `DOCKERFILE_PATH` が空になるケースがあるようなので、念のためその対策もしておく。

以下にスクリプトの例を示す。

```bash:.../hooks/build
#!/bin/bash

set -eu

if [ -z "$DOCKERFILE_PATH" ]
then
	file_path_arg=""
else
	file_path_arg="-f $DOCKERFILE_PATH"
fi

image_name_arg=""
tags=`echo "$DOCKER_TAG" | sed -e 's/,/ /g'`
for tag in $tags
do
	image_name_arg="$image_name_arg -t $DOCKER_REPO:$tag"
done

docker build --build-arg CUSTOM=$VAR $file_path_arg $image_name_arg .
```

# MicroBadger と連携

[MicroBadger](https://microbadger.com/) を利用することで、以下のようなバッジを README.md などに貼り付けることができる。[^2]
[^2]: MicroBadger のイメージ詳細情報ページの右側にある "Get the badges" からマークダウン記述例を取得できる。

[![](https://images.microbadger.com/badges/version/obono/arduboy-env:latest.svg)](https://microbadger.com/images/obono/arduboy-env:latest "Get your own version badge on microbadger.com")  [![](https://images.microbadger.com/badges/commit/obono/arduboy-env:latest.svg)](https://microbadger.com/images/obono/arduboy-env:latest "Get your own commit badge on microbadger.com") [![](https://images.microbadger.com/badges/image/obono/arduboy-env:latest.svg)](https://microbadger.com/images/obono/arduboy-env:latest "Get your own image badge on microbadger.com")
イメージのタグ名は、URL 内に埋め込まれているのでそれはそれとして、そのタグを付けたイメージをビルドした際の、ソースリポジトリにおける commit ID を取得するためには、[Dockerfile に適切にラベルを設定](https://microbadger.com/labels)しておく必要がある。
また、イメージのサイズについては、[対象の Docker イメージが更新されたタイミングで webhook を呼び出すように設定](https://medium.com/microscaling-systems/microbadger-keep-your-metadata-fresh-with-a-webhook-651ee26cd4a6)しておき、メタ情報の更新の契機を与える必要がある。

…のだが、ちゃんと webhook の設定をしたつもりなのに、イメージのサイズがいつまで経っても "0B" の表示のまま変化しない。MicroBadger のイメージ詳細情報ページを見ても、レイヤ情報の欄が "No matching base image" と表示されてしまう。何故だ!?
![shot.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152579/59e81455-274b-c28f-e6af-7690c57c3e9b.jpeg)
公式サイトの "Support" のリンク先となっている GitHub ページの [Issues](https://github.com/microscaling/microbadger/issues) を覗いたら、昨年(2019年)からレイヤ情報やサイズ情報の更新がうまくできていないっぽい。なんてこった。

どうにかならないかと調べた結果、shields.io を利用することで、レイヤ情報だけのバッジを作れるようなので、仕方がないのでそれで代用する。
URL の例: https://img.shields.io/microbadger/layers/obono/arduboy-env:latest.svg → [![MicroBadger Layers](https://img.shields.io/microbadger/layers/obono/arduboy-env:latest.svg)](https://microbadger.com/images/obono/arduboy-env:latest)