実際に使うDocker超入門 (第2回)

こんにちはtatsyです。

今回は「実際に使うDocker超入門」の第2回です。今回は自前のDockerfileを作成し、Docker Hubにpush、そしてTravis CIでDockerを使ったテストを行う方法を紹介したいと思います。

前回の記事はコチラからご覧になれます。

自前のDockerfileを作る


では、前回ご紹介した内容を踏まえて自前のDockerfileを作って見たいと思います。

私が今回作成したDockerfileはUbuntu 14.04 LTS上でCMakeを用いたC++のビルドを実行するための環境です。

少し長いですが、実際に作成したDockerファイルは以下のようなものです。

#
# Dockerfile for building C++ project
#
# https://github.com/tatsy/dockerfile/ubuntu/cxx
#

# OS image
FROM ubuntu:14.04

# Install
RUN \
  sed -i 's/# \(.*multiverse$\)/\1/g' /etc/apt/sources.list && \
  apt-get update && \
  apt-get -y upgrade && \
  apt-get install -y build-essential clang-3.5 && \
  apt-get install -y software-properties-common && \
  apt-get install -y byobu curl git htop man unzip vim wget
RUN \
  apt-get install -y subversion cmake qt5-default && \
  rm -rf /var/lib/apt/lists/*

# Add files
ADD root/.bashrc /root/.bashrc

# Set environment variables.
ENV HOME /root

# Define working directory.
WORKDIR /root

# Define default command.
CMD ["bash"]

# Update gcc/g++ to v4.9
RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test
RUN apt-get update -qq
RUN apt-get install -qq gcc-4.9 g++-4.9
RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 90
RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 90
RUN update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-4.9 90

# Install LCOV
RUN git clone https://github.com/linux-test-project/lcov.git
RUN make -C lcov install
RUN apt-get install -y ruby
RUN gem install coveralls-lcov

# Install LLVM v.3.7
RUN apt-add-repository -y "deb http://llvm.org/apt/trusty llvm-toolchain-trusty main"
RUN apt-add-repository -y "deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.7 main"
RUN wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key|sudo apt-key add -
RUN apt-get update -qq
RUN apt-get install -qq clang-3.7 llvm-3.7
RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-3.7 90
RUN update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-3.7 90
RUN ln -fs /usr/bin/llvm-cov-3.7 /usr/bin/llvm-cov

# Install OpenMP for LLVM
RUN svn co http://llvm.org/svn/llvm-project/openmp/trunk openmp
RUN \
  cd openmp && \
  mkdir build && \
  cd build && \
  cmake .. && \
  make omp && \
  make install && \
  cd -

# Update CMake to v3.3.2
RUN git clone --depth 1 -b v3.3.2 https://github.com/Kitware/CMake.git
RUN \
  cd CMake && \
  mkdir build && \
  cd build && \
  cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr && \
  make -j4 && \
  make install && \
  ldconfig && \
  cd ../.. && \
  cmake --version

# Show environments
RUN echo "--- Build Enviroment ---"
RUN cat /etc/lsb-release
RUN gcc --version | grep gcc
RUN g++ --version | grep g++
RUN lcov --version | grep version
RUN clang --version | grep version
RUN clang++ --version | grep version
RUN cmake --version | grep version
RUN echo "------------------------"

若干前回ご紹介したものよりも長いですが、特別なことは何もしていません。

前回ご紹介したUbuntuのDockerfileに加えて、GCC 4.9.2, Clang 3.7.0, CMake 3.3.2をインストールして、最後にバージョンの確認を行っているだけです。

以後は、このDockerfileをTravis上でdocker buildして、Docker Hubに自動的にpushする方法を紹介します。

Docker Hubアカウントの作成


Docker Hubアカウントの作成は、

Docker Hub

にアクセスしてアカウント登録をするだけです。

私の個人的な見解として、Docker Hubのユーザ名、メールアドレス、パスワードはTravis上に打ち込まないといけない(他のユーザからはもちろん見えません)ので、あまり良くつかっているパスワードを入れない方がいいかもしれません。

他のサービスみたいに認証用トークンを発行してくれるとありがたいのですが、今のところは対応していない状況です。

Travis CIからDocker Hubへの自動push


Docker Hubアカウントが作成できたら、GitHub上にレポジトリを作成し、Travis CIとの連携をONにします。

私が今回作成したGitHubのレポジトリはこちらになります。

tatsy/docker-ubuntu-cxx – GitHub

.travis.ymlの書き方ですが、基本はdocker buildを実行してDockerfileからイメージを作り、それをDocker Hubにpushするという流れになります。

第一に、Travis CI上でDockerを使うためにはservicesにdockerを指定する必要があります。

services:
  - docker

後はinstallの箇所にdocker buildを指定してあげるだけです。

install:
  - docker build --tag=tatsy/ubuntu-cxx:latest

確認用にちゃんとビルドが出来ているかをafter_installで確認してあげるとよりGoodかもしれません。

after_install:
  - docker images

続いて、ビルドが上手く言った後にDocker Hubへpushする方法ですが、.travis.ymlに打ち込むべき情報は次のようになります。

after_success:
  - docker login -e="$DOCKER_EMAIL" -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
  - docker push tatsy/ubuntu-cxx

一行目ではDocker Hubへのログインを、二行目では実際のpushを実行しています。

なおDocker Hubへのpushでは、レポジトリの名前とローカルでのイメージ名をそろえてあげる必要があります。

そのため、今回のビルドではタグをtatsy/ubuntu-cxx:latestにして、それをpushするときにも使っています。

次にもう一つの注意点として、docker login時のアカウント情報の入力があります。

上の例ではメールアドレス、ユーザ名、パスワードが環境変数として設定されていますが、間違っても、パスワードの情報を.travis.ymlのenvなどに打ち込まないでください。

Travis CIではレポジトリごとに環境変数を設定することができて、ここに他のユーザに見せたくない環境変数を設定しておくことが出来ます。

設定したいレポジトリのSetteingsを開くと以下のような画面ができるので、Addを押して環境変数を入力します(下の画像ではもう入力済みです)。

travis_setting

これで準備は完了で、自分のGitHubにDockerfileをpushすればTravis上でビルドが始まり、ビルドが成功すればDocker Hubにpushされます。

なおpushには地味に時間がかかりまして、今回の私の例だとイメージのサイズが1.5GBで、pushにかかった時間は274秒でした。

Travis CI上でDockerを使ったビルド/テスト


これで自前のビルド環境をDocker Hubにpushすることができました。

あとは自分のサーバー上のJenkinsで使うなり、Travis CIで使うなり、Dockerさえインストールされていれば、どの環境でも同じビルド環境を使うことが出来ます。

Travis CIで使う場合にはいちいち必要なソフトウェアをインストールする必要がなくなるのでビルド時間も短縮できて良い感じです。

では、早速Travis CI上でDockerを使ったビルド/テストを実施する方法をご紹介します。

とはいえ、こちらも特に特別なことは必要ありません。単に作成したDocker Hub上のイメージをpullしてきて、そのイメージをコンテナとして実行した上で、ビルドとテストを行うだけです。では.travis.ymlの設定方法を順に見ていきます。

まず、DockerをTravis上で使うためにservicesにDockerを指定します。これは先ほどと同じですね。

services:
  - docker

次にdocker imageをpullします。

install:
  - docker pull tatsy/ubuntu-cxx

そうするとローカルにイメージが現れます。あとはビルドなどの情報を入れたDockerfileをdocker buildします。

このDockerfileはFROMに、落としてきた自前のイメージ名を入れて、ビルドやテストをRUNで実行するものです。

手前味噌ながら、自分の趣味レンダラーでは次のようなDockerfileを使っています。

#
## Load docker image
#
FROM tatsy/ubuntu-cxx

#
## Install freeglut and glew
#
RUN apt-get -qq install freeglut3 freeglut3-dev libglew-dev libglew1.5
RUN apt-get -qq install libxmu-dev libxi-dev

#
## Install Google Test
#
RUN git clone --depth=1 -b release-1.7.0 https://github.com/google/googletest.git /usr/src/gtest
RUN \
  cd /usr/src/gtest && \
  cmake . && \
  cmake --build . && \
  mkdir -p /usr/local/lib && \
  mkdir -p /usr/include && \
  mv libg* /usr/local/lib && \
  mv include/* /usr/include && \
  cd /
ENV GTEST_LIBRARY /usr/local/lib/libgtest.a
ENV GTEST_MAIN_LIBRARY /usr/local/lib/libgtest_main.a
ENV GTEST_INCLUDE_DIRS /usr/include

#
## Build spica
#
RUN git clone --depth 10 -b develop https://github.com/tatsy/spica.git
RUN \
  cd spica && \
  cmake . && \
  cmake --build .

#
## # of threads used by OpenMP
#
ENV OMP_NUM_THREADS 4

#
## Define working direcotry
#
WORKDIR /root/spica

#
## Run unit tests
#
RUN lcov --directory . --zerocounters

やっていることは、

  • プロジェクト特有のライブラリのインストール (上記の例ではfreeglutとGLEWおよびGoogleTest)
  • プロジェクトのビルド

です。

上記のDockerfileをビルドしてあげると、プロジェクトのビルドが終わった状態になっているので、あとはテストとカバレッジデータの集計を手動で行います。

docker execによるコマンドの逐次実行


テストとカバレッジデータの送信を順に行うためにはdocker execコマンドを使います。

なお、docker execは1.3以上の機能なので、使っているDockerのバージョンに注意してください。

docker execは起動しているコンテナ内でコマンドを実行するためのコマンドで使い方は次のようになります。

例えばmake checkでテストを実行したいとすると、

$ docker exec container-name make check

のようになります。

docker execに渡すコンテナ名はイメージ名ではないので、これはコンテナを起動する時に**–name**オプションで設定します。

$ docker run --name container-name -itd image-name

この後でカバレッジデータの集計等を行いたい場合には、同様にdocker execを使って逐次的にコマンドを実行してあげればOKです。

使い終わったコンテナは停止した後にdocker rmを使って破棄しておきましょう。

$ docker stop container-name
$ docker rm container-name

ビルドしたイメージを削除したい場合にはdocker rmiコマンドを使います。

$ docker rmi image-name

まとめ


今回は自前のDockerfileの作成から、そこから作成したイメージを使ってのビルド/テストおよびカバレッジデータの集計方法までをご紹介いたしました。

簡単にポイントをまとめておくと

  • Dockerfileを作成したらGitHub + Travis CIで自動的にDocker Hubへホスティングできる!
  • Docker Hub上のイメージを使えば、Travis CIでのビルド時間を結構短縮できる
  • Dockerコンテナ内でのコマンド実行にはdocker execを使う

という感じです。

それでは、これで今回の記事を終わりたいと思います。今回も最後までお読みいただき、ありがとうございました。