Dockerでgoのテストを実行する環境をつくる

1つのJenkinsの環境で複数のプロジェクトのテストが実行されることは、ままあると思う。Jenkins上で動作するすべてのプロジェクトが同時に動作するようにJenkins環境を整えるのは難しいことがある。あるプロジェクトのためにライブラリのバージョンを更新したことで、別のプロジェクトのテストが落ちるとか。

Ruby なら Bundler やrbenvを使って環境を切り替えるとだいたい解決するけど、libhogeみたいな共有ライブラリやBundlerやrbenv自体の更新とか、どうしても共有される部分はあって、だいたい普段は問題はないが、まれに困ると言う感じだと思う。

そこで、テストごとにDockerのコンテナを立ち上げてその中でテストを実行するようにすれば、環境を独立させることができるので、環境をこわさないように丁寧に設定するみたいなことに気を使わなくてよくなるので便利。

miyagawaさんのdocker-plenv-vanillaを使うとplenv環境がととのったDokcer imageを作ったりできる。こういう感じでgoが動くDocker imageを作ってgoのテストを動かしてみたので、ご紹介します。

Dockerfile になんか以下のように書く

FROM ubuntu
RUN apt-get -y update
RUN apt-get -y install curl sudo git build-essential

RUN mkdir -p /opt/local
RUN curl https://go.googlecode.com/files/go1.2.linux-amd64.tar.gz -o /tmp/go.tar.gz
RUN tar -C /opt/local -xzf /tmp/go.tar.gz

RUN mkdir -p /gobuild/src
RUN mkdir -p /gobuild/go/src/example.com/hakobe
RUN ln -sf /gobuild/src /gobuild/go/src/example.com/hakobe/goproject

RUN echo 'export GOPATH="/gobuild/go"' >> /gobuild/env
RUN echo 'export GOROOT="/opt/local/go"' >> /gobuild/env
RUN echo 'export PATH="/opt/local/go/bin:$PATH"' >> /gobuild/env

以下のようにしてimageを作る。

$ docker build -t gobuild .

テストは以下の様なスクリプトを用意しておいて実行する

docker run \
    -name gotest \
    -v "/path/to/goproject:/gobuild/src" \
    gobuild \
    sh -c 'cd /gobuild; . /gobuild/env; go test -v ./...'
result="$?"
docker rm gotest
exit $result

ポイントは以下の様な感じです。

  • /path/to/goprojectにあるソースコードはdocker run するときに /gobuild/src にmountされる
  • goがパッケージ名を解決できるように、/gobuild/srcから /gobuild/go/src/example.com/hakobe/goproject にシンボリックリンクを張っておく
    • パッケージ名が go get できる感じになっている場合は、こんなことする必要ないと思う
    • 直接 $GOPATH/src以下にmountしてもいいかもしれない
  • goの実行環境はバイナリのtarball持ってきて展開すれば作れる
  • 実行用の環境変数は /gobuild/env にとっておいて、テスト実行時に source する
    • /etc/bashrc? とかで勝手に読み込まれるようにしといても良いかもしれない
  • 最後にdocker rm しないとテストを実行するたびにコンテナがmountされたままになる
    • コンテナのmountはカーネルプロセスがやっていて、メモリを消費するので、ほうっておくとDockerのホストが爆発する
  • Jenkins上でdocker buildしているような体だけど、実際はdocker repositoryとかを活用したら良いと思う