CircleCI で定期的に Docker イメージをビルド

概要

Docker Hub には色々な Docker イメージが公開されていて便利なんですが、なかなか更新されないものもあります。

更新されない一因として手動で誰かがビルドして Docker Hub にプッシュする運用になっているということがあると思います。

そこで CircleCI で定期的にイメージをビルドして Docker Hub にプッシュするというジョブを作って自動化するのをやってみました。

リポジトリはこれです。

https://github.com/bigsleep/haskell-docker

Docker Hub はこれです。

https://hub.docker.com/r/tkaaad97/haskell-docker/

イメージ

https://hub.docker.com/_/haskell/

前にも少しブログに書きましたが haskell のイメージが公開されているのですがあまり更新されていません。

現在最新の GHC バージョンは 8.4.1 のようですが 8.2.1 で止まっています。

OS のバージョンも変わったり色々な依存パッケージも変わったりするので 完全に自動にするのは難しいと思いますがマイナーバージョンの更新ぐらいなら そんなに Dockerfile の内容は変えずにいけるんじゃないかと思います。

このイメージを定期的に新しい ghc バージョンで更新するというのが目標になります。

パッケージ情報の取得

GHC の開発に使われている git リポジトリは公開されていて GitHub にもミラーがあるので ここからリリースの情報を取得することもできると思います。

ただソースからビルドするのはなかなか大変で時間もかかりそうなので Ubuntu 用に公開されているパッケージを使っています。

Packages in “GHC” : GHC : Herbert V. Riedel

Ubuntu用のパッケージは launchpad.net に登録されているようです。

https://launchpad.net/+apidoc/1.0.html#archive

launchpad.net の API もあるようなのでここから情報を取得できそうです。

curl -fs "https://api.launchpad.net/1.0/~hvr/+archive/ubuntu/ghc?ws.op=getPublishedBinaries&distro_arch_series=https://api.launchpad.net/1.0/ubuntu/xenial/amd64&status=Published&order_by_date=true&binary_name=ghc"

これでパッケージ情報が取得できました。

API/Webhooks - Launchpad Help

Webhook というのもあってこれも使えるかもしれないんですが CircleCI ではまだ API からビルドを走らせることはできないらしいので上手く利用できないかもしれません。

CircleCI

Continuous Integration and Delivery - CircleCI

CircleCI は CI (継続的インテグレーション) のためのサービスで、開発に伴う色々な作業を自動化することに使うことができます。

他に Travis CI も有名だと思います。Jenkins や GitLab CI などサーバーをホスティングして使うものもあります。

CircleCI は GitHub との連携があって GitHub にアカウントがあれば簡単に試すことができました。

GitHub のプロジェクトのソースに CircleCI の設定ファイルを含めてこれに基づいてビルドやテストを行うようになっているようです。

ある程度は無料でも利用できるので便利そうです。

CircleCI の設定

Configuration Reference - CircleCI

ドキュメントは色々あるんですが読んでもいまいちわからないので 実験用のプロジェクトを作って色々試してみるのがいいような気がします。

version, jobs, workflows というのが一番上の階層にあります。 version は2を指定します。

jobs は CI で実行するジョブの内容を書きます。シェルスクリプトなども使えます。 build という名前のジョブは少し特別扱いされていて workflows が無い設定では build ジョブだけが実行されます (Orchestrating Workflows - CircleCI) 。

workflows はジョブ間の順番やジョブを実行するブランチのフィルタリング、コンテキストなどジョブの付属的な設定を書くようです。 workflows には triggers で cron 形式で定期的に実行する設定ができます。

色々試行錯誤して下の設定を作りました。 build ジョブでは単純にコミットされた Dockerfile を元にイメージをビルドして Docker Hub にプッシュしています。 crawl のジョブでは lanunchpad.net から最新の ghc パッケージ情報を取得して Dockerfile を生成して github にプッシュするようになっています。 build ジョブは master ブランチ以外、 crawl ジョブは master ブランチのみで実行されます。 crawl ジョブは triggers を使っていて週三回定期的に実行するようにしています。 ビルドが手元でやるより大分速いので毎日ビルドするのでもいいかもしれません。

version: 2
jobs:
    build:
        working_directory: /work
        docker:
            - image: docker:18.02.0-ce-git
        steps:
            - checkout
            - setup_remote_docker
            - run: docker login -u ${DOCKER_USER} -p ${DOCKER_PASS}
            - run: docker build -t ${DOCKER_USER}/${CIRCLE_PROJECT_REPONAME}:${CIRCLE_BRANCH} .
            - run: docker push ${DOCKER_USER}/${CIRCLE_PROJECT_REPONAME}:${CIRCLE_BRANCH}
            - run: docker tag ${DOCKER_USER}/${CIRCLE_PROJECT_REPONAME}:${CIRCLE_BRANCH} ${DOCKER_USER}/${CIRCLE_PROJECT_REPONAME}:latest
            - run: docker push ${DOCKER_USER}/${CIRCLE_PROJECT_REPONAME}:latest
    crawl:
        machine: true
        working_directory: ~/work
        steps:
            - checkout
            - add_ssh_keys:
                fingerprints:
                    - "20:ed:11:cf:92:b2:fa:ff:24:a5:00:f3:fe:00:d9:ac"
            - run: ./scripts/crawl.sh
workflows:
    version: 2
    docker-build:
        jobs:
            - build:
                filters:
                    branches:
                        ignore: master
                    tags:
                        ignore: /.*/
    scheduled-workflow:
        triggers:
            - schedule:
                cron: "0 0 * * 1,3,5"
                filters:
                    branches:
                        only: master
                    tags:
                        ignore: /.*/
        jobs:
            - crawl

まとめ

CircleCI を使って Docker イメージのビルドを定期的に行うようにしてみました。 まだ CI 設定作ったところで実際に新しいバージョンがリリースされて更新できるかなどは確認がとれてません。 しばらく続けて動かしてみて様子を見てみます。 例えばプロジェクトの依存ライブラリの新しいバージョンがリリースされていたらビルドしてテストを流してみるなども 似たような感じでできそうかなと思います。