リソースロケーションに制約のある環境でCloud Buildがエラーになる(HTTPError 412)

記事タイトルとURLをコピーする

G-gen の武井です。組織のポリシーでリソースロケーションに制約のあるプロジェクトで gcloud builds submit (Cloud Build のビルドコマンド) を実行したところ、HTTPError 412 エラーが出力され Docker イメージのビルドに失敗する事象が起きました。

当記事では、事象の原因と対策をご紹介します。

事象

構成

今回問題のあった環境は、組織 example.co.jp のツリー構造に属するプロジェクトの一つです。

組織レベルで constraints/gcp.resourceLocations を下図のように設定し、組織全体でリソースロケーションを東京リージョン (asia-northeast1)大阪リージョン (asia-northeast2)に制限しています。

実行内容

以下のソースコードを用いて gcloud builds submit コマンドを以下の通り実行したところエラーが発生しました。

この際、API の有効化、Cloud SDK の認証、ならびに実行ユーザーや Cloud Build サービスアカウントの IAM 設定等に不備はありませんでした。

# ソースコード一覧
  
$ tree
.
├── image_build.sh
└── source
    ├── cloudbuild.yaml
    ├── Dockerfile
    ├── go.mod
    ├── go.sum
    └── main.go
# image_build.sh
  
#!/bin/bash
  
# 変数定義
project_id=$(gcloud config get-value project)
location="asia-northeast1"
repository_name="image-build-test"
image_name="test"
  
# APIがまだ有効でない場合はチェックして有効化する
services=(
   artifactregistry.googleapis.com
   cloudbuild.googleapis.com
)
  
for service in "${services[@]}"; do
    if gcloud services list --enabled --filter="name:${service}" --format="value(name)" | grep "${service}"; then
        echo "Service ${service} is already enabled."
    else
        echo "Enabling service ${service}..."
        gcloud services enable "${service}"
    fi
done
  
# コンテナイメージ用のリポジトリを確認
if gcloud artifacts repositories describe "$repository_name" --location=$location &>/dev/null; then
    echo "Artifact repository $repository_name already exists, skipping creation."
else
    # 存在しない場合は作成
    gcloud artifacts repositories create "$repository_name" \
        --location $location \
        --repository-format "docker"
    echo "Artifact repository $repository_name created."
fi
  
# イメージビルド
gcloud builds submit ./source \
    --config=./source/cloudbuild.yaml \
    --region=$location \
    --substitutions=_LOCATION=$location,_REPOSITORY=$repository_name,_IMAGE=$image_name
# cloudbuild.yaml
  
# 変数は`image_build.sh`の`gcloud builds submit --substitutions`から動的に渡される
steps:
  - id: docker build and push
    name: 'gcr.io/cloud-builders/docker'
    args: [ 'build', '-t', '${_LOCATION}-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/${_IMAGE}:latest', '.' ]
images:
  - '${_LOCATION}-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/${_IMAGE}:latest'
# Dockerfile
  
# Goのバージョン1.22.2を使用
FROM golang:1.22.2 as builder
  
# 作業ディレクトリの設定
WORKDIR /app
  
# 依存関係のコピーとダウンロード
COPY go.mod go.sum ./
RUN go mod download
  
# ソースコードのコピー
COPY main.go ./
  
# アプリケーションのビルド
RUN CGO_ENABLED=0 GOOS=linux go build -o main .
  
# 実行環境の設定
# Distroless base image を使用
FROM gcr.io/distroless/base-debian10
WORKDIR /
  
# ビルドしたバイナリを実行環境にコピー
COPY --from=builder /app/main .
  
# コンテナ起動時のコマンド
CMD ["/main"]

エラーの詳細

エラーメッセージは ERROR: (gcloud.builds.submit) HTTPError 412: 'us' violates constraint 'constraints/gcp.resourceLocations' でした。出力の詳細は以下のとおりです。

# 実行ログ
  
$ ./image_build.sh
Your active configuration is: [cloudshell-1976]
projects/111111111111/services/artifactregistry.googleapis.com
Service artifactregistry.googleapis.com is already enabled.
projects/111111111111/services/cloudbuild.googleapis.com
Service cloudbuild.googleapis.com is already enabled.
Artifact repository image-build-test already exists, skipping creation.
ERROR: (gcloud.builds.submit) HTTPError 412: 'us' violates constraint 'constraints/gcp.resourceLocations'

原因

調査

'us' violates と示されていることから、gcloud builds submit コマンド実行時に何らかのリソースが US マルチリージョン に作成されようとした結果、エラーが発生したのではないかと推察しました。

判明した原因

以下の公式ドキュメントを確認したところ、以下のような記載がありました。

Cloud Build はデフォルトで、ビルドを実行するロケーションとは異なる Google 指定リージョンにビルドログを保存します。

つまり、US マルチリージョンにビルド実行ログ保存用の Cloud Storage バケットを作成しようとした際、リソースロケーションの制約に違反してエラーが発生したことがわかりました。

対処法

gcloud builds submit コマンドに --default-buckets-behavior="regional-user-owned-bucket" を付与して実行します。

これにより、ビルドログ保存先リージョンを US からビルド実行先 (今回で言えば東京リージョン) に変更してリソースロケーションの制約を回避します。

# image_build.sh (修正後、gcloud builds submit 部分のみ記載)
  
gcloud builds submit ./source \
    --config=./source/cloudbuild.yaml \
    --default-buckets-behavior="regional-user-owned-bucket" \
    --region=$location \
    --substitutions=_LOCATION=$location,_REPOSITORY=$repository_name,_IMAGE=$image_name
# 実行ログ (image_build.sh 修正後)
 
$ ./image_build.sh 
Your active configuration is: [cloudshell-772]
projects/111111111111/services/artifactregistry.googleapis.com
Service artifactregistry.googleapis.com is already enabled.
projects/111111111111/services/cloudbuild.googleapis.com
Service cloudbuild.googleapis.com is already enabled.
Artifact repository image-build-test already exists, skipping creation.
Creating temporary archive of 5 file(s) totalling 19.7 KiB before compression.
Uploading tarball of [./source] to [gs://yutakei-test-prj_asia-northeast1_cloudbuild/source/1714488853.161679-ce5f2b25b04146be8f8bd54f8d053851.tgz]
Created [https://cloudbuild.googleapis.com/v1/projects/yutakei-test-prj/locations/asia-northeast1/builds/48b469e7-243e-4fc6-b490-ec43c1403414].
Logs are available at [ https://console.cloud.google.com/cloud-build/builds;region=asia-northeast1/48b469e7-243e-4fc6-b490-ec43c1403414?project=111111111111 ].
Waiting for build to complete. Polling interval: 1 second(s).
------------------------------------------------------------------ REMOTE BUILD OUTPUT ------------------------------------------------------------------
starting build "48b469e7-243e-4fc6-b490-ec43c1403414"
  
※ 途中省略
  
Step 10/10 : CMD ["/main"]
 Running in dae2b25a7765
Removing intermediate container dae2b25a7765
 2b7d07a95edc
Successfully built 2b7d07a95edc
Successfully tagged asia-northeast1-docker.pkg.dev/yutakei-test-prj/image-build-test/test:latest
PUSH
Pushing asia-northeast1-docker.pkg.dev/yutakei-test-prj/image-build-test/test:latest
The push refers to repository [asia-northeast1-docker.pkg.dev/yutakei-test-prj/image-build-test/test]
89648652935c: Preparing
91f7bcfdfda8: Preparing
05ef21d76315: Preparing
91f7bcfdfda8: Layer already exists
05ef21d76315: Layer already exists
89648652935c: Pushed
latest: digest: sha256:03a23554f16b2ed46c7125d6eafbce97e8d7582a4a29d3c8d759891c58174f49 size: 950
DONE
---------------------------------------------------------------------------------------------------------------------------------------------------------
ID: 48b469e7-243e-4fc6-b490-ec43c1403414
CREATE_TIME: 2024-04-30T14:54:14+00:00
DURATION: 2M1S
SOURCE: gs://yutakei-test-prj_asia-northeast1_cloudbuild/source/1714488853.161679-ce5f2b25b04146be8f8bd54f8d053851.tgz
IMAGES: asia-northeast1-docker.pkg.dev/yutakei-test-prj/image-build-test/test (+1 more)
STATUS: SUCCESS

リソースロケーションの制約を守りつつDockerイメージの作成に成功

--default-buckets-behavior="regional-user-owned-bucket によりビルドログ保存バケットが US ではなくビルド実行先リージョンに作成された

その他

今回記事にしたCloud Build ですが、サービスアカウントとして使われていた <PROJECT_NUMBER>@cloudbuild.gserviceaccount.com の仕様が2024年4月29日から変更されます。

詳しくは当社の記事で解説しています。ぜひご確認ください。

blog.g-gen.co.jp

武井 祐介 (記事一覧)

2022年4月にジョイン。クラウドソリューション部所属。G-gen唯一の山梨県在住エンジニア

Google Cloud Partner Top Engineer 2024 に選出。IaC や CI/CD 周りのサービスやプロダクトが興味分野です。

趣味はロードバイク、ロードレースやサッカー観戦です。