https://cloud.google.com/cloud-build/?hl=ja
コンテナイメージを作る用のサービスらしいが、他にも色々できる。
- https://cloud.google.com/cloud-build/docs/cloud-builders?hl=ja
- https://github.com/GoogleCloudPlatform/cloud-builders
機能
- トリガーには GitHub の場合はブランチかタグを正規表現で指定できる
- 特定のファイルに変更がある場合のみトリガーしたり、変更があっても無視するファイルを指定したりできる。
ドキュメントファイルのみの更新などに便利そう - Dockerfile を使ったビルド、または cloudbuild.yaml ファイルを使ったビルドを行える。
docker build 以外のことをしたい場合はおそらく cloudbuild.yaml 一択になる
せっかくなので今回は cloudbuild.yaml を使ってみる。
幸いにも AppEngine にデプロイするときの内部での Cloud Build のログが残っているのでそれを参考に作ってみる
リポジトリのディレクトリ構成は以下のようになっている
- docker/ 開発環境構築用のDockerfileなどを管理
- src/ プロジェクトのソースコード本体
- cloudbuild.yaml [NEW]
cloudbuild.yaml はこんな感じにしてみた。
_から始まる変数はトリガー作成時に変数として作成した値。
詳しい内容はリンク先を参照 https://cloud.google.com/cloud-build/docs/build-config?hl=ja
steps:
- name: "gcr.io/cloud-builders/docker:latest"
args: [
"build", "--network=cloudbuild",
"--build-arg", "env=$_RAILS_ENV",
"--build-arg", "master_key=$_MASTER_KEY",
"-t", "asia.gcr.io/$PROJECT_ID/build/$BRANCH_NAME:latest",
"."
]
dir: src
images: ["asia.gcr.io/$PROJECT_ID/build/$BRANCH_NAME"]
Dockerfile は AppEngine の CodeBuild 時に生成される Dockerfile を確認して作成した。ほぼコピペしてコメント消したくらい。
実際には app.yaml によって生成される Dockerfile は異なるので参考程度に。
############
# STEP1
############
FROM gcr.io/gcp-runtimes/ruby/ubuntu16:latest AS augmented-base
ARG ruby_version="2.5.1"
COPY --from=gcr.io/gcp-runtimes/ruby/ubuntu16/prebuilt/ruby-2.5.1:latest \
/opt/rbenv/versions/${ruby_version} \
/opt/rbenv/versions/${ruby_version}
RUN rbenv global ${ruby_version} \
&& rbenv rehash \
&& (bundle version > /dev/null 2>&1 \
|| gem install bundler --version ${DEFAULT_BUNDLER_VERSION}) \
&& rbenv rehash
ENV TZ="Asia/Tokyo"
############
# STEP2
############
FROM augmented-base AS app-build
COPY --from=gcr.io/gcp-runtimes/ruby/ubuntu16/build-tools:latest /opt/ /opt/
ENV PATH /opt/bin:/opt/google-cloud-sdk/bin:/opt/yarn/bin:${PATH}
RUN ln -s /opt /build_tools \
&& ln -s /opt/bin/cloud_sql_proxy /opt/cloud_sql_proxy \
&& ln -s /opt/bin/access_cloud_sql /opt/access_cloud_sql
COPY . /app/
ARG BUILD_CLOUDSQL_INSTANCES="xxxxxxxxxxxxxxxx"
RUN mkdir /cloudsql
RUN bundle install --deployment --without="development test" && rbenv rehash
# ベースのイメージは RAILS_ENV=productionなので、ステージング環境にも対応させる
# CloudBuildのトリガーでmasterブランチの場合はproduction、stagingブランチの場合はstagingを渡すようにトリガーを2つ作成してある
ARG env="production"
ARG master_key
# AppEngineでRailsをデプロイするときは assets:precompile をやってくれていなかったので追加した。
# yarnをデフォルトで入れているならやってくれてもいいのに…
RUN RAILS_ENV=${env} RAILS_MASTER_KEY=${master_key} bundle exec rails assets:precompile
############
# STEP3
############
FROM augmented-base
# cloudbuild.yamlから渡した値で環境変数を上書きする。
ARG env="production"
ARG master_key
ENV RAILS_ENV $env
ENV RAILS_MASTER_KEY $master_key
COPY --from=app-build /app/ /app/
CMD exec bundle exec rails server -p $PORT -e $RAILS_ENV
まとめ
なんとなくやっていた App Engine でのデプロイの仕組みがわかった。
Cloud Build → Container Registry (本体はCloud Storageに) → イメージを GCE上にデプロイ
(実際にはもっと複雑だけど。ロードバランサーとか。)
今回使わなかったけど便利な機能
基本的には順番に実行されていくが、 waitFor で順番を調整できる
https://cloud.google.com/cloud-build/docs/configuring-builds/configure-build-step-order?hl=ja
以下の場合は foo のビルドが終わってから bar をビルドする
steps:
- name: foo
- name: bar
以下の場合は A と B が同時にビルドされ、両方が終わってから baz のビルドが始まる
steps:
- name: foo
id: A
- name: bar
id: B
waitFor: ['-']
- name: baz
以下の場合は A のビルドが終わってから B と C のビルドが同時に始まる
steps:
- name: foo
id: A
- name: bar
id: B
waitFor:
- A
- name: baz
id: C
waitFor:
- A