GCP Cloud Build で Docker イメージを build する時のテクニック(Rails)

本記事は、Cloud Build を試したうえでの気付きをメモするものです。 何らかの目的を完遂するための一連のストーリー仕立てにはなっていないことから、各セクションの情報は独立しており、記事としてまとまりはありません。 もしヒントがあれば活用するくらいの程度でご参照ください。

試みたこと

Rails アプリケーションを開発していて、GitHub への Push を契機に CI(ビルド)して、最終的には GKE (Kubernetes) への CD(デリバリー)をしたい。ここで CI は以下のように使い分ける。

  • Rails のテストは CircleCI で実施したい
  • Docker Image のビルドやコンテナレジストリ(GCR)への格納は CloudBuild で実施したい(GCP でまとめてしまった方が、Credentials 管理に関する余計な手間が減るので楽かつセキュリティ面でも少し安心)

CloudBuild は数回使っているのですが、CI/CD 系のものって 1 回設定して安定動作してしまうとしばらく触らないため、新プロジェクトで新たに設定しようとするとかなり忘れていて戸惑ってしまいます。そのためこの記事では、CloudBuild の設計で悩んだ点をナレッジとして記録します。

なお、この記事では Rails に関する説明は省略しつつ CloudBuild の設定にフォーカスしています。

最初の悩み SECRET_KEY_BASE をどのように設定するか

Production 環境でビルドするにあたり assets:precompile をしたところコケました。

RUN bundle exec rails assets:precompile
 ---> Running in 8cb2873bd671
rails aborted!
ArgumentError: Missing `secret_key_base` for 'production' environment, set this string with `rails credentials:edit`
/usr/local/bundle/gems/railties-6.0.2.2/lib/rails/application.rb:580:in `validate_secret_key_base'
/usr/local/bundle/gems/railties-6.0.2.2/lib/rails/application.rb:423:in `secret_key_base'
/usr/local/bundle/gems/railties-6.0.2.2/lib/rails/application.rb:177:in `key_generator'
/usr/local/bundle/gems/railties-6.0.2.2/lib/rails/application.rb:201:in `message_verifier'

... 以下略

Rails 6.x のプロジェクトを新規で作成すると、環境変数 SECRET_KEY_BASE で設定はできず(?、軽く試しただけなので理解誤りかも)、rails credentials:edit にて設定される暗号化テキストファイルに設定する必要があるとのこと。そして、その暗号テキストを複合するためのキーがマスターキーと呼ばれるもので、master.key あるいは環境変数 RAILS_MASTER_KEY にて設定しておかなければならない。

なお、他の Rails プロジェクト(例 Mastodon)で使われている以下の記法も試したけどパスしませんでした。

SECRET_KEY_BASE=precompile_placeholder rails assets:precompile

mastodon/Dockerfile · tootsuite/mastodon

というわけで、素直にマスターキーを設定する方針に切り替えます。CloudBuild 自身が GitHub からレポジトリをクローンしてビルドするという性質上、当然 master.key をレポジトリに含むわけにはいかないことから、環境変数 RAILS_MASTER_KEY の選択をすることになります。

変数値の置換 (substitutions)

substitutions は、ビルド時に特定の変数を置き換える場合にビルド構成ファイルの中で使用します。

置換はビルド時まで値が不明な変数を設定する場合や、既存のビルド リクエストを別の変数値で再利用する場合に役立ちます。

変数値の置換  |  Cloud Build のドキュメント  |  Google Cloud

To Be: ビルドを早くするための Cache の一工夫

Caching Docker layers on serverless build hosts with multi-stage builds, –target, and –cache-from

Introducing kaniko: Build container images in Kubernetes and Google Container Builder without privileges | Google Cloud Blog

WaitForを書かない理由。先行するすべてのビルドステップを待つので、1 シーケンスを順次処理していくのならばあえて定義しなくてもよい(定義したほうが宣言的なので、よりベターではある) ビルドステップの順序の構成  |  Cloud Build のドキュメント  |  Google Cloud

便利そう(Docker のビルド キャッシュの改善): コンテナ構築のおすすめの方法  |  アーキテクチャ  |  Google Cloud

軽く参考になる記事: ビルドを高速化する際のおすすめの方法  |  Cloud Build のドキュメント  |  Google Cloud

id を振っておいたほうがログが見やすくなる

Starting Step #0 - "pull-builder-cache"
Step #0 - "pull-builder-cache": Already have image (with digest): gcr.io/cloud-builders/docker
Step #0 - "pull-builder-cache": Error response from daemon: manifest for ... not found
Finished Step #0 - "pull-builder-cache"
Starting Step #1 - "pull-rails-cache"Step #1 - "pull-rails-cache": Already have image (with digest): gcr.io/cloud-builders/docker
Step #1 - "pull-rails-cache": latest: Pulling from ...
Step #1 - "pull-rails-cache": 48839397421a: Pulling fs layer
Step #1 - "pull-rails-cache": e766eb7a99c4: Pulling fs layer
 ...
Finished Step #1 - "pull-rails-cache"

(以降、IDを降っていない場合)

Starting Step #2
Step #2: Already have image (with digest): gcr.io/cloud-builders/docker
Step #2: Sending build context to Docker daemon  1.467MB

Step #2: Step 1/9 : FROM ruby:2.6.5-stretch as builder
Step #2: 2.6.5-stretch: Pulling from library/ruby
Step #2: 56da78ce36e9: Pulling fs layer
Step #2: fbfe0f13ac45: Pulling fs layer
Step #2: 6254ff6d0e60: Pulling fs layer
Step #2: e0e1e13bd9f6: Pulling fs layer
Step #2: b86b38b40a24: Pulling fs layer