• トップ
  • ブログ一覧
  • Cloud BuildとKanikoで効率的な継続的デリバリー(CD)を構築する方法
  • Cloud BuildとKanikoで効率的な継続的デリバリー(CD)を構築する方法

    ストレスのないCDを構築しよう

    皆さんこんにちは。
    Google Cloudが好きな小山こと"こやまん"です。
    Google Cloud を利用する多くのプロジェクトでは、アプリケーションのデプロイが重要な作業の一つです。今回は、Cloud Buildを利用して、アプリケーションをデプロイする継続的デリバリー(CD)に関する記事となります。

    CD(継続的デリバリー/継続的デプロイメイント)とは?

    ITエンジニアなら一度は耳にしたことがある「CD」ですが、具体的な内容を忘れてしまっている方もいるかもしれませんので、ここで簡単におさらいします。CD とは、開発したアプリケーションを自動で公開・配布するプロセスで、作業の効率化を図るための手法です。

    CDのメリット

    • 迅速なリリース
      自動化されたデプロイメントによる効率的なリリース
    • ヒューマンエラーのリスクの減少
      手作業を無くすことによりエラーのリスクを減少
    • 運用コストの削減
      人的リソースを削減し、コスト効率が向上

    実際にCDを構築してみよう

    CDを実現するために、さまざまな方法がありますが、今回はGoogle CloudのCloud Buildと呼ばれるサービスを利用して実現していきます。
    構成は以下のような形となります。
    Cloud Build
    処理の流れは以下の通りです。

    1. Github上にコードをPush
    2. プルリクエストの作成
    3. Cloud Buildにより、ビルドを実行
    4. Secret Managerより環境変数を設定
    5. Artifact RegistryにDockerイメージを格納
    6. Cloud Runにデプロイ

    Artifact Registry

    Artifact RegistryはDockerイメージを格納するサービスです。
    Cloud Runにアプリケーションをデプロイする上で必須ではありませんが、一般的に使用されることが多いので、今回も利用していきます。
    Google CloudのコンソールでArtifact Registryと検索すると初回のみ、以下の画面が表示されるので、有効にするボタンをクリックします。
    ArtifactRegistryAPI
    有効後に、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と検索すると初回のみ、以下の画面が表示されるので、有効にするボタンをクリックします。
    CloudBuildAPI
    有効後に、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 に保存されます。
    kaniko
    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が構築できるように今後も触っていきたいと思います。

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background