CircleCI2.0でRailsアプリをdocker multi stage buildをする
※2017/05/29 時点くらいの話しです。
現在は改善されているかもしれません。
multi stage buildとは
https://speakerdeck.com/toricls/understanding-dockers-multi-stage-builds こちらが詳しい。
build環境と、実行環境を別イメージとしてbuildしたい。 それを簡単に出来るようになったよ。という話し。
なぜ別イメージにしたいかと言うと、Goの例がわかりやすいが、 Goはワンバイナリで実行可能な為、 Goをコンパイルするために必要なライブラリ等は、実行環境のイメージには必要がない。 dockerのイメージは小さいほど、(docker pull含めて)実行が早いので軽いほうがいい。
CircleCI2.0 で実行する方法
CircleCI2.0では、build時に自分で好きなDockerのvesionを使えるよ。とうたっている。 https://circleci.com/docs/2.0/building-docker-images/
- run: name: Install Docker client command: | set -x VER="17.03.0-ce" curl -L -o /tmp/docker-$VER.tgz https://get.docker.com/builds/Linux/x86_64/docker-$VER.tgz tar -xz -C /tmp -f /tmp/docker-$VER.tgz mv /tmp/docker/* /usr/bin
ここで 17.03.0-ce
を指定しているのですが、multi-stage buildを行うためには、17.05.0-ce
が必要になる。
そのため、ここで 17.05.0-ce
と指定すると、そのバージョンのdockerをinstalできる。
出来るのだが、そのバージョンは使えない。
なぜかと言うと docker は client 側と docker daemon側で2つのversionがあり、
setup_remote_docker
では、docker daemon側のバージョンは 17.03.0-ce
で固定されている。
daemon側が低いとそちらに合わせられるため、multi-stage buildは行うことができない。
では、どうするのが良いか。 machine実行で、自分で docker を install する。
deploy: working_directory: ~/app machine: true steps: - checkout - run: name: update docker command: | docker version sudo service docker stop curl -fsSL https://get.docker.com/ | sudo sh docker version
上記では、(おそらく)最新のdockerを自分で入れている。
Client: Version: 17.05.0-ce API version: 1.29 Go version: go1.7.5 Git commit: 89658be Built: Thu May 4 22:06:06 2017 OS/Arch: linux/amd64 Server: Version: 17.05.0-ce API version: 1.29 (minimum version 1.12) Go version: go1.7.5 Git commit: 89658be Built: Thu May 4 22:06:06 2017 OS/Arch: linux/amd64 Experimental: false
build時に、debugでversionを出すとこんな感じに 17.05.0-ce
が入っていることがわかる。
あとは普通に、docker imageをbuildして push したりすればよい。 雑な例を出すとこんな感じ。
- deploy: name: run build & deploy command: | $(aws ecr get-login --region ap-northeast-1) $REPO={{ECRのリポジトリとか}} $PUSH_TAG=latest docker build -t hoge:latest . docker tag hoge:latest $REPO:$PUSH_TAG docker push $REPO
最後に
Railsでmulti-stage buildに使っているDockerfileを載せておく。 あんまり詰めれていないので、もっと良く出来るとは思う。 考えとしては、gemをbuildするために必要なものを、最初のイメージで入れて 実行に必要なものだけど後半のイメージにいれている。
FROM ruby:2.4.1-alpine as builder RUN apk --update add --virtual build-dependencies build-base curl-dev linux-headers RUN apk --update add mariadb-dev RUN echo 'gem: --no-document' > /etc/gemrc WORKDIR /app ADD . /app RUN bundle install --jobs=4 RUN apk del build-dependencies FROM ruby:2.4.1-alpine ENV LANG ja_JP.UTF-8 COPY --from=builder /usr/local/bundle /usr/local/bundle RUN apk --update add tzdata imagemagick RUN apk --update add mariadb-dev && rm /usr/lib/libmysqld* RUN apk del openssl-dev mariadb-client-libs mariadb-common ADD . /app RUN chown -R nobody:nogroup /app USER nobody WORKDIR /app EXPOSE 9292 CMD [ "bundle", "exec", "unicorn", "-c", "config/unicorn.rb" ]