12
7

More than 5 years have passed since last update.

Multi-stageのDockerfileをCloud Buildでビルドするコツ

Last updated at Posted at 2018-11-22

Cloud BuildでMulti-stageのDockerfileをキャッシュを効かせて適用するにはちょっとした工夫が必要です。

Multi-stage buildsに関しては以下が参考になります。
Docker Multi-Stage BuildsでRailsのイメージを軽量化してみる

通常、dockerをローカルでビルドする場合は、Multi-stageでもあまり気にする事はなく、Multi-stageのメリットの一つであるdockerのキャッシュをうまく利用してビルドを高速化することができます。

しかし、Cloud Buildでは単純なビルドの仕方ではdockerのキャッシュを活用する事ができません。

Dockerfile

便宜上、このDockerfileを利用する前提で話を進めます。(先ほど共有した記事の中で使っているものと一緒です)
細かい処理は気にしないでください。
Fromが二つ入っていてMulti-stageになっている事だけがポイントです。

Dockerfile
FROM ruby:2.3.2-alpine as builder
RUN apk --update add --virtual build-dependencies \
    build-base \
    curl-dev \
    mysql-dev \
    linux-headers
RUN gem install bundler
WORKDIR /tmp
COPY Gemfile Gemfile
COPY Gemfile.lock Gemfile.lock
ENV BUNDLE_JOBS=4
RUN bundle install
RUN apk del build-dependencies

FROM ruby:2.3.2-alpine
ENV LANG ja_JP.UTF-8
RUN apk --update add \
    bash \
    nodejs \
    mariadb-dev \
    tzdata \
    && rm /usr/lib/libmysqld*
RUN gem install bundler

WORKDIR /tmp
COPY Gemfile Gemfile
COPY Gemfile.lock Gemfile.lock
COPY --from=builder /usr/local/bundle /usr/local/bundle

ENV APP_HOME /myapp
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME
COPY . $APP_HOME

キャッシュが効かない例

Cloud Buildのカスタムビルドで以下のような単純にマニュフェストを用意します。

cloudbuild.yml
steps:
- name: 'gcr.io/cloud-builders/docker'
  args:
  - 'build'
  - '-t'
  - 'gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA'
images:
- 'gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA'

これではキャッシュが効かず毎回1からビルドされます。

キャッシュを効かせる書き方

cloudbuild.yml
steps:
- name: 'gcr.io/cloud-builders/docker'
  args: ['pull', 'gcr.io/$PROJECT_ID/builder:latest']
  waitFor: ['-']
  id: 'pull-builder'
- name: 'gcr.io/cloud-builders/docker'
  args: ['pull', 'gcr.io/$PROJECT_ID/$REPO_NAME:latest']
  waitFor: ['-']
  id: 'pull-app'
- name: 'gcr.io/cloud-builders/docker'
  args:
  - 'build'
  - '--cache-from'
  - 'gcr.io/$PROJECT_ID/builder:latest'
  - '-t'
  - 'gcr.io/$PROJECT_ID/builder:latest'
  - '--target'
  - 'builder'
  - '.'
  waitFor: ['pull-builder']
  id: 'build-builder'
- name: 'gcr.io/cloud-builders/docker'
  args:
  - 'build'
  - '--cache-from'
  - 'gcr.io/$PROJECT_ID/builder:latest'
  - '--cache-from'
  - 'gcr.io/$PROJECT_ID/$REPO_NAME:latest'
  - '-t'
  - 'gcr.io/$PROJECT_ID/$REPO_NAME:latest'
  - '.'
  waitFor: ['build-builder']
  id: 'build-app'
- name: 'gcr.io/cloud-builders/docker'
  args: ["push", "gcr.io/$PROJECT_ID/builder:latest"]
  waitFor: ['build-builder']
- name: 'gcr.io/cloud-builders/docker'
  args: ["push", "gcr.io/$PROJECT_ID/$REPO_NAME:latest"]
  waitFor: ['build-app']

ポイントは4つ

  • ステージ毎にステップを分ける
  • ステージ毎にイメージをビルドしてpushする
  • --cache-fromで明示的に参照するキャッシュを指定する
  • idwaitForで可能な限り並列化

これでアプリケーションのソースの更新だけの場合など、キャッシュが有効になり高速にビルドされるようになります。

注意点

  • 各ステージ毎にイメージをpull, pushしてるのでその分の時間がかかります。
  • この例のpull-builderのイメージをpullしてくるところは初回はイメージが存在しないのでビルドに失敗します。初回は、コメントアウトしておき、二回目以降からpullするようにします。

ステージを分けすぎると、オーバーヘッドがでかくなるので必要であればステージを統合するなどする必要があります。

さいごに

キャッシュやMulti-stageなど、dockerのエコシステムは素晴らしい設計がされていると思います。

dockerのビルドツールなどでもそれをうまく活用していきたいですね。

12
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
7