詩と創作・思索のひろば

ドキドキギュンギュンダイアリーです!!!

Dockerfile をベースイメージの更新に自動で追従させる

前回のエントリで作った Docker イメージ motemen/datastore-emulator は、google/cloud-sdk をベースにしているが、このベースイメージがけっこうな頻度で更新される。とうぜん自分はその追従に手を煩わせる気はなくて、全部自動でやってほしい。

やりたかったこと

  • google/cloud-sdk:x.y.z がリリースされたら、
  • リポジトリ中の ./Dockerfile と ./alpine/Dockerfile の FROM を google/cloud-sdk:x.y.z(-alpine) に更新し、
  • x.y.z タグを打って git push することで、
  • Docker Hub に x.y.z(-alpine) タグとしてリリースする

これを自動かつ無料で実現したい。

採用しなかった案: 自分でなんか作る

はじめは適当な GitHub Actions をこしらえて、Docker Hub のイメージ更新を検出する、ということをやるつもりだった。

更新フィードを Docker Hub が吐いてくれれば……と考えていたけど、イメージは日付が新しければよいというものではなく、*-slim みたいなバリエーションもある以上、タグをバージョンとして解釈して適切なものを選択する、としなくてはいけない。やればできそうなんだけど、Dockerflie の公式なパーザがないのはネックだった。それが理由じゃないんだけど、この案は不採用。もっと楽な方法があったからだ。

Renovate を使う

もうこれで説明しきったも同然なんだけど、最近試して気に入っていた Renovate が Dockerfile の更新に対応しているので、これを使う。簡単に言うと、依存関係を更新する Pull Request を作ってくれるやつです。

Whitesource Renovate - Automated Dependency Updates

Dockerfile を置いたリポジトリについて、Renovate を設定すれば勝手に更新を見つけてプルリクしてくれる。

docker:enableMajor という、Dockerfile でメジャーバージョンの更新を有効にする preset が効いていなかった不具合があったのだけど、つい最近解消されました

ちなみに Dependabot も Dockerfile の更新に対応しているが、複数の Dockerfile の更新をひとつの PR で処理する、というのができなさそうなので今回はそぐわなかった。他の理由もあって Renovate のほうが好みだというのもある。

自動マージを有効にするためにテスト書く

Renovate には automerge という設定項目があって、これを使うと(CI が通ったあとに)PR を自動マージするってことができる。CI をうまく構成すればハンズフリーで運用できそうだ。というわけで以下のようなテストを書く。

  • 全ての Dockerfile がベースイメージの同じバージョンに基づいていること
    • ./DockerfileFROM foo:1.2.3./alpine/DockerfileFROM foo:2.0.0-alpine だったらこまる
  • それぞれの Dockerfile が正しく派生イメージになっていること
    • ./alpine/Dockerfile*-alpine みたいなタグになってること、ということ

それぞれをテストするスクリプトを書いてGitHub Actions を仕込むFROM の解析は以下のような雑なワンライナーでお茶を濁している。

perl -nle 'print "$1\t$2\t$3" if /^FROM (\S+?):([0-9.]+)(?:-([^:-]+))?(?:$| )/' "$@"

うまく動いてマージされた Pull Request がこちらです。このエントリを書くために探すまで、バージョンが上がってたことに気づかなかったので成功ですね。

Update google/cloud-sdk Docker tag to v292 by renovate · Pull Request #6 · motemen/docker-datastore-emulator · GitHub

タグを打つ

最後に、master にマージされたら Dockerfile をベースに git tag を打ち、これをトリガに Docker Hub でビルドさせる。

これも先ほどの雑なワンライナーを使って、こんな感じにする。GitHub Actions の checkout@v2 であれば特に設定なしで git push できちゃうので、便利。

https://github.com/motemen/docker-datastore-emulator/blob/8aed69b/.github/workflows/tag.yml#L17-L23

tag=$(./scripts/parse-dockerfile-from Dockerfile | cut -f2)
echo "tag: $tag"
if ! git rev-parse --quiet --verify "refs/tags/$tag" > /dev/null; then
  git tag "$tag"
  git push --tags
fi

いかがでしたか? これが全部無料で実現できているだなんて、いったい自分は前世でどんな善行を積んできたんだ……とおののくばかりです。今生でもがんばりたいですね。

はてなで一緒に働きませんか?