はじめに
CI では毎回まっさらな環境でテストやビルドができます。 これには、多くは Docker が用いられています。 さらには、Docker で実現されたまっさらな環境でも Docker ビルドなど Docker を利用できます。
前提知識: Docker クライアントと dockerd
アーキテクチャの理解 — Docker-docs-ja 1.11.0 ドキュメント
Docker コマンドとdockerd
は別物で、API クライアントと API サーバーのような関係です。
dockerd
は、Docker デーモンのことです。
例えば、Docker for Mac ではこのようなイメージです。
Docker は Linux カーネルを必要とするため、Mac 単体では動きません。
そこで、HyperKit を使い VM 上に LinuxKit を立ち上げています。
Mac のターミナルから、LinuxKit の dockerd
に命令を送っています。
CI では Docker コンテナで Docker コマンドを使う
EC2 上で Docker により Jenkins を動かす時などでは Docker コンテナ内で Docker コマンドを使う必要があります。
docker build
でイメージをビルドして、docker push
でレジストリにイメージをプッシュすることが多々あります。
どのようにコンテナ上で Docker を利用するべきでしょうか。
docker in docker
docker in docker は以下のようなイメージです。
docker in docker は元々、Docker の開発自体のために作られました。 Docker インストール済みのコンテナを使用しコンテナ内でホストとは別に dockerd を動かす方法です。 dindと一般的に呼ばれています。
$ docker run --privileged --name dind -d docker:dind
--privileged
オプションにより、コンテナ内からホストのリソースを扱える権利を持たせます。
GOOD
- ホストの Docker とは完全に別物の Docker 環境が利用可能
BAD
-privileged
を使う必要があり脆弱性があります/var/lib/docker
をマウントするので Data Volume がファイルが溜まっていきます- CI が再ビルドを行う時、dind コンテナを再起動するたびにそのキャッシュを無効になります
/var/run/docker.sock
をマウント
ホストの dockerd のソケットファイルをマウントして、そこに処理をリクエストします。 イメージは以下です。
Docker out of Docker(dood)と呼ばれます。
GOOD
- dockerd はホストのものを使うので、毎回同じでありキャッシュが利用できます
- ディスクスペースを節約できます
BAD
- ホストで動いている他のコンテナも見えます(完全に独立していない)
- ホストマシンのルートを取れます
$ docker run -t -i -v /var/run/docker.sock:/var/run/docker.sock debian:jessie /bin/bash $ apt-get update && apt-get install wget -y && wget -qO- https://get.docker.com | sh $ docker images # ホストのイメージが表示される $ docker run -t -i -v /:/host debian:jessie /bin/bash $ chroot /host # ホストのルートが取れる
どっちを使うべき
EC2などで自分でCIを構成する場合は、doodを使うのが好ましいです。 また、GitHub ActionsなどカスタマにCIを提供する場合では、dindが好ましいです。