Cloud BuildとKanikoで効率的な継続的デリバリー(CD)を構築する方法
IT技術
ストレスのないCDを構築しよう
皆さんこんにちは。
Google Cloudが好きな小山こと"こやまん"です。
Google Cloud を利用する多くのプロジェクトでは、アプリケーションのデプロイが重要な作業の一つです。今回は、Cloud Buildを利用して、アプリケーションをデプロイする継続的デリバリー(CD)に関する記事となります。
CD(継続的デリバリー/継続的デプロイメイント)とは?
ITエンジニアなら一度は耳にしたことがある「CD」ですが、具体的な内容を忘れてしまっている方もいるかもしれませんので、ここで簡単におさらいします。CD とは、開発したアプリケーションを自動で公開・配布するプロセスで、作業の効率化を図るための手法です。
CDのメリット
- 迅速なリリース
自動化されたデプロイメントによる効率的なリリース - ヒューマンエラーのリスクの減少
手作業を無くすことによりエラーのリスクを減少 - 運用コストの削減
人的リソースを削減し、コスト効率が向上
実際にCDを構築してみよう
CDを実現するために、さまざまな方法がありますが、今回はGoogle CloudのCloud Buildと呼ばれるサービスを利用して実現していきます。
構成は以下のような形となります。
処理の流れは以下の通りです。
- Github上にコードをPush
- プルリクエストの作成
- Cloud Buildにより、ビルドを実行
- Secret Managerより環境変数を設定
- Artifact RegistryにDockerイメージを格納
- Cloud Runにデプロイ
Artifact Registry
Artifact RegistryはDockerイメージを格納するサービスです。
Cloud Runにアプリケーションをデプロイする上で必須ではありませんが、一般的に使用されることが多いので、今回も利用していきます。
Google CloudのコンソールでArtifact Registryと検索すると初回のみ、以下の画面が表示されるので、有効にするボタンをクリックします。
有効後に、Artifact Registryの管理画面から、リポジトリを作成します。
- 名前
hoge(任意の名前を設定) - 形式
Docker - モード
標準 - ロケーションタイプ
リージョン(日本国内に在住の場合は、以下のいずれかがおすすめです)
- asia-northeast1(東京)
- asia-northeast2(大阪) - 説明
任意の内容を設定 - ラベル
必要に応じて設定 - 暗号化
Googleが管理する暗号鍵 - 不変のイメージタグ
無効 - クリーンアップポリシー
ドライラン
作成したリポジトリに移動すると、asia-northeast1-docker.pkg.dev/project-name/xxxxxx
といった感じのリポジトリのパスが確認できます。
このパスは、後続の作業で必要になるので控えておきます。(※1)
Cloud Build
Cloud Buildのトリガーを作成します。
Google CloudのコンソールでCloud Buildと検索すると初回のみ、以下の画面が表示されるので、有効にするボタンをクリックします。
有効後に、Cloud Buildの管理画面から、トリガーの作成を行います。
- 名前
fuga(任意の名前を設定) - リージョン
global(グローバル) - 説明
任意の内容を設定 - タグ
必要に応じて設定 - イベント
ブランチにpushする - ソース
第1世代
- リポジトリ : GitHub(Cloud Build GitHub アプリ)
- ブランチ : 必要に応じて変更 - invert Regex
チェックしない - 構成(形式)
Cloud Build 構成ファイル(yaml または json) - 構成(ロケーション)
リポジトリ - Cloud Build 構成ファイルの場所
任意の場所(例: gcp/cloudbuild.yml) - 承認
チェックしない - ビルドログ
チェックしない - サービスアカウント
xxxxxxxxxxxx-compute@developer.gserviceaccount.com
Cloud Run
Cloud Runの管理画面から、サービスを作成します。
- Artifact Registry & Docker Hub or Github or Functions
既存のコンテナ イメージから 1 つのリビジョンをデプロイする - コンテナイメージのURL
デモコンテナ / hello - サービス名
cloud-run-hoge(任意の内容を設定) - リージョン
リージョン(日本国内向けのサービスの場合は、以下のいずれかがおすすめです)
- asia-northeast1(東京)
- asia-northeast2(大阪) - 認証
未認証の呼び出しを許可 - CPUの割り当てと料金
リクエストの処理中にのみCPUを割り当てる - インスタンスの最小数
0 - 上り(上向き)の制御
すべて
Dockerfile
本記事ではRailsをデプロイするため、Railsで必要なDockerfileを作成し、Railsアプリケーションのルートディレクトリに配置します。
Dockerfileの内容はビルド時間に大きく影響するため、不要なキャッシュのクリーンアップを行い、イメージサイズを小さくするなどしています。
1# ベースイメージとして公式のRubyイメージを使用
2FROM ruby:3.2.2
3
4# Node.jsとYarnのインストール(Railsのアセットプリコンパイルに必要)
5RUN curl -sL https://deb.nodesource.com/setup_18.x | bash - && \
6 apt-get install -y nodejs && \
7 npm install -g yarn
8
9# 必要なパッケージをインストール、キャッシュをクリーンアップ
10# 1. パッケージリストを更新
11# 2. ビルドツールのインストール
12# 3. aptキャッシュをクリーンアップし不要なデータを削除
13# 4. ダウンロードしたパッケージリストを削除してイメージサイズを削減
14RUN apt-get update -qq && \
15 apt-get install -y build-essential && \
16 apt-get clean && \
17 rm -rf /var/lib/apt/lists/*
18
19# 作業ディレクトリを設定
20WORKDIR /rails
21
22# GemfileとGemfile.lockをコピーし、bundlerを実行
23COPY Gemfile Gemfile.lock /rails/
24RUN bundle install --jobs 4 --retry 3
25
26# アプリケーションのソースコードをコピー
27COPY . .
28
29# Railsのアセットをプリコンパイル(アセットパイプライン使用時)
30RUN bundle exec rake assets:precompile
31
32# ポートを8080で公開
33EXPOSE 8080
34
35# コンテナ起動時にRailsサーバーを自動的に起動、全てのインターフェース(0.0.0.0)でリッスンし、ポート8080を使用
36CMD ["./bin/rails", "server", "-b", "0.0.0.0", "-p", "8080"]
ビルド設定
ビルド設定は、cloudbuild.yamlで行います。
ファイル名は、Cloud Buildのトリガー設定のCloud Build 構成ファイルの場所
と一致させれば良いので他の名前でも構いません。
ファイルの内容は以下のような4ステップになります。
1# Cloud Buildで実行する設定
2steps:
3 # Dockerイメージをビルドし、タグを付ける
4 - name: "gcr.io/cloud-builders/docker"
5 args:
6 [
7 "build",
8 "-t",
9 "asia-northeast1-docker.pkg.dev/project-name/hoge/hoge:$COMMIT_SHA",
10 ".",
11 ]
12 id: Build
13
14 # docker pushコマンドを起動し、DockerイメージをArtifact Registry にPush
15 - name: "gcr.io/cloud-builders/docker"
16 args:
17 [
18 "push",
19 "asia-northeast1-docker.pkg.dev/project-name/hoge/hoge:$COMMIT_SHA",
20 ]
21 id: Push
22
23 # Secret Managerから値を取得
24 - name: "gcr.io/google.com/cloudsdktool/cloud-sdk"
25 entrypoint: bash
26 args:
27 - "-c"
28 - |
29 gcloud secrets versions access latest --secret="RAILS_MASTER_KEY" > /workspace/rails_master_key
30 id: GetSecret
31
32 # Cloud Run にデプロイ
33 - name: "gcr.io/google.com/cloudsdktool/cloud-sdk"
34 entrypoint: gcloud
35 args:
36 [
37 "run",
38 "deploy",
39 "hello", # サービス名
40 "--image=asia-northeast1-docker.pkg.dev/project-name/hoge/hoge:$COMMIT_SHA",
41 "--region=asia-northeast1",
42 "--platform=managed",
43 "--set-secrets", "RAILS_MASTER_KEY=RAILS_MASTER_KEY:latest",
44 "--set-env-vars", "RAILS_ENV=production",
45 ]
46 id: Deploy
47
48# ログをCloud Loggingのみに送信(stdoutやstderrには出力しない)
49options:
50 logging: CLOUD_LOGGING_ONLY
51
52# 20分経過しても処理が終わらない場合はタイムアウトさせる
53timeout: 1200s
上記のファイルをアプリケーションのgcp/cloudbuild.yaml
に配置します。
asia-northeast1-docker.pkg.dev/project-name/hoge
は(※1)で取得できる値を設定します。
本記事のデプロイ設定では、セキュアな値を利用するため、Secret Managerから値を取得するステップを設定し、Cloud Runにデプロイするステップでシークレット変数と変数を設定しています。
GitHub 上でプルリクエストを作成し、main ブランチにマージすると、自動的に Cloud Run にデプロイされます。作成直後の Rails アプリケーションをデプロイしたところ、約3分45秒で完了しました。
ただし、アプリケーションのサイズが大きくなると、デプロイ時間が延びる問題が発生します。例えば、私が関わったプロジェクトでは、ビルドに約40分かかるケースがありました。
ビルド時間の短縮
先ほどの設定では、ビルドが実行されるものの、時間がかかる状態となります。ビルド時間を短縮する手段の一つとして、Kaniko というツールがあります。Kaniko は、Docker Daemon を使わずにコンテナイメージをビルドできるツールで、キャッシュ機能を活用することで、ビルド時間を大幅に短縮することが可能です。
本記事の設定では、キャッシュは Artifact Registry に保存されます。
Cloud Buildの設定ファイルを以下のように変更するだけでKanikoを利用できます。
1# Cloud Buildで実行する設定
2steps:
3 # タスクを実行するコンテナイメージのタグ付け
4 - name: 'gcr.io/kaniko-project/executor:latest'
5 args:
6 - --destination=asia-northeast1-docker.pkg.dev/project-name/hoge/fuga:$COMMIT_SHA
7 - --cache=true
8 - --cache-ttl=100h
9 id: Build
10
11 # Secret Managerから値を取得
12 - name: "gcr.io/google.com/cloudsdktool/cloud-sdk"
13 entrypoint: bash
14 args:
15 - "-c"
16 - |
17 gcloud secrets versions access latest --secret="RAILS_MASTER_KEY" > /workspace/rails_master_key
18 id: GetSecret
19
20 # Cloud Run にデプロイし、シークレットを環境変数として設定
21 - name: "gcr.io/google.com/cloudsdktool/cloud-sdk"
22 entrypoint: gcloud
23 args:
24 [
25 "run",
26 "deploy",
27 "cloud-run-hoge", # サービス名
28 "--image=asia-northeast1-docker.pkg.dev/project-name/hoge/fuga:$COMMIT_SHA",
29 "--region=asia-northeast1",
30 "--platform=managed",
31 "--set-secrets", "RAILS_MASTER_KEY=RAILS_MASTER_KEY:latest",
32 "--set-env-vars", "RAILS_ENV=production",
33 ]
34 id: Deploy
35
36# ログをCloud Loggingのみに送信(stdoutやstderrには出力しない)
37options:
38 logging: CLOUD_LOGGING_ONLY
39
40# 20分経過しても処理が終わらない場合はタイムアウトさせる
41timeout: 1200s
- --cache=true
キャッシュを有効にし、ビルド時間を短縮する - --cache-ttl=100h
キャッシュの有効期限を100時間に設定
Kanikoを利用して複数回ビルドを実行してみます。
- 1回目:3分45秒
- 2回目:3分6秒
- 3回目:3分
最初のビルドはキャッシュが存在しないため、時間が短縮されませんが、2回目以降はキャッシュが利用されるため、ビルド時間が短縮されることが確認できます。作成直後の小規模な Rails アプリケーションでは、ビルド時間の短縮がそれほど実感できないかもしれませんが、例えば、1回のビルドに40分かかるアプリケーションでは、キャッシュの利用によって5分程度に短縮されるケースがあります。
次に、キャッシュの有効期限が切れた場合のビルド時間を確認します。本記事ではキャッシュの有効期限を100時間に設定しているため、最後のビルドから4日と4時間以上が経過するとキャッシュが無効になり、再度フルビルドが行われることになります。
- 1回目:3分43秒
- 2回目:2分58秒
- 3回目:3分13秒
1回目にキャッシュが切れてビルド時間が長い事が確認できます。
Kanikoのキャッシュについて
Kanikoでキャッシュを有効にしている場合、2回目以降のCloud Buildの実行時に Found cached layer, extracting to filesystem
というログが表示されることがあります。これは、過去のビルドで生成されたDockerイメージのレイヤーを再利用していることを示しており、ビルド時間の短縮に役立ちます。キャッシュは各ビルドステップごとに管理され、RailsアプリケーションのGemfileやDockerfileが更新された場合、該当するステップのキャッシュが無効化され、再ビルドが行われます。それ以外のステップは、引き続き有効期限内のキャッシュが再利用されます。
まとめ
今回は、Cloud BuildとKanikoを用いてCDを構築してみました。世の中には様々なCDが存在していますが、迅速かつ信頼性の高いCDが構築できるように今後も触っていきたいと思います。
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ