Cloud Build ちゃんと理解したい
Cloud Build を利用してはいるが、やりたいことを実現するためのその場しのぎの知識しかないので、docs を読み進めて理解を深めていく。
Build and push a Docker image with Cloud Build
とりあえず quickstart をやる。
まずは、Build の quickstart。
Prepare source files to build
echo するだけのshell と、その shell を実行するだけの Dockerfile を作る。
echo "Hello, world! The time is $(date)."
FROM alpine
COPY quickstart.sh /
CMD ["/quickstart.sh"]
Create a Docker repository in Artifact Registry
Artifact Registry に repository を作る。
gcloud cli で作成
gcloud artifacts repositories create quickstart-docker-repo --repository-format=docker --location=asia-northeast1 --descri
ption="Docker repository"
gcloud cli で repository の一覧表示
gcloud artifacts repositories list
Build an image using Dockerfile
gcloud builds submit --tag asia-northeast1-docker.pkg.dev/{PROJECT_ID}/quickstart-docker-repo/quickstart-image:tag1
{PROJECT_ID} は自身の project の projectId に置き換える。
Build an image using a build config file
同様の Docker image の build を、build config file (cloudbuild.yaml) を使って行う。
cloudbuild.yaml を作成
steps:
- name: 'gcr.io/cloud-builders/docker'
args:
[
'build',
'-t',
'asia-northeast1-docker.pkg.dev/$PROJECT_ID/quickstart-docker-repo/quickstart-image:tag1',
'.',
]
images:
- 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/quickstart-docker-repo/quickstart-image:tag1'
At build time, Cloud Build automatically replaces $PROJECT_ID with your project ID.
$PROJECT_ID
はそのままでOK。build 時に自動的にその環境の projectId が利用される。
cloudbuild.yaml を使って gcloud builds submit する
gcloud builds submit --config cloudbuild.yaml
まとめ
-
gcloud builds submit
は、Dockerfile のみでも Docker image を build できる。 -
gcloud builds submit
は、 build の成果物を Artifact Registory の repository にアップロードする。 - tag の規約が大事。
${region}-docker.pkg.dev/${projectId}/${artifactRegistoryRepositoryName}/${imageName}:${tag}
- config file (cloudbuild.yaml) を使ってbuild の詳細設定を管理できる。
image の push について
quickstart の例では images
filed を指定することによって、build された image を自動で Artiafct Registry に push している。
以下の例のように、明示的に docker push で Artifact Registry に push する step を追加することで同様のことが実現できる↓
steps:
# Docker Build
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t',
'us-central1-docker.pkg.dev/${PROJECT_ID}/my-docker-repo/myimage',
'.']
# Docker Push
- name: 'gcr.io/cloud-builders/docker'
args: ['push',
'us-central1-docker.pkg.dev/${PROJECT_ID}/my-docker-repo/myimage']
Deploy a containerized application to Cloud Run using Cloud Build
次は、Deploy の quickstart やる。
以下の API を有効化
Grant permissions
下準備として権限周りを整える。
- projectId, projectNumber を環境変数にひかえる
PROJECT_ID=$(gcloud config list --format='value(core.project)')
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
- Cloud Build service account に Cloud Run Admin role を追加
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member=serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com \
--role=roles/run.admin
- Cloud Build service account に Cloud Run runtime service account の IAM Service Account User role を追加
gcloud iam service-accounts add-iam-policy-binding \
$PROJECT_NUMBER-compute@developer.gserviceaccount.com \
--member=serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com \
--role=roles/iam.serviceAccountUser
Deploy a prebuilt image
Artifact Registry に存在する Docker image を使って、Cloud Run へ deploy する。
cloudbuild.yaml 作成
gcr.io/cloud-builders/gcloud
Cloud Builder を使うと、gcloud cli を実行できる。
gcloud cli を使って、Cloud Run へ deploy するコマンドを記載。
steps:
- name: 'gcr.io/cloud-builders/gcloud'
script: |
gcloud run deploy cloudbuild-hello-example --image us-docker.pkg.dev/cloudrun/container/hello --region asia-northeast1 --platform managed --allow-unauthenticated
gcloud build submit で実行
cloudbuild.yaml に記載した deploy のフローを Cloud Build に submit して実行する。
gcloud builds submit --config cloudbuild.yaml
動作確認
GCP console > Cloud Run から確認。ちゃんと deploy できている。
まとめ
- Cloud Build は build だけでなく、 deploy 等のいろんな step を構築できる。
- gcloud cli でできることは、Cloud Build でもできる。
ここまでの流れを整理しとく
大雑把な deploy フロー
GCP に Docker を利用したアプリを deploy するおおまかなフローはこんな感じになる↓
- Docker Image を build する
- 1 で用意した Image をどっかに置いとく
- 2 の image を指定して deploy する
Cloud Build でできること
Cloud Build を使うと、この GCP への deploy までのフローを簡単に実現できる。
Cloud Run への deploy のケースを例にとると、
- Docker Image を build して tag 付け
- Artifact Registry の repository (= Docker Image の置き場所) に生成した image を push
- image をもとに Cloud Run へ deploy
この一連の処理を cloudbuild.yaml に記載しておけば、コマンド一発で deploy まで実現できる。
gcloud builds submit --config cloudbuild.yaml
Build & Deploy and more....
Cloud Build では、Cloud Builder という諸々のツールやプログラムの実行環境を土台に、いろいろな処理の "step" を積み重ねていくことができる。
ここまでの quickstart では、Build の step, Deploy の step のみを試したが、ここに test の step や通知の step を追加していけば包括的な CI/CD パイプラインを構築できそう。
gcloud builds submit
は何をしている...?
ここまでの quickstart で gcloud builds submit
で Dockerfile を build したり、Cloud Run へ deploy したりしてきたが、そもそも Cloud Build はなんで local の source code を build できるんだ...? source code をどっかにアップロードしてるのか...?
と、疑問が湧いてきたので調べる。
Submit a build via CLI and API を読む
gcloud builds submit
がやっていること
gcloud builds submit --region=us-west2 --tag gcr.io/PROJECT_ID/IMAGE_NAME .
The
gcloud builds submit
command:
- compresses your application code, Dockerfile, and any other assets in the current directory as indicated by .;
- uploads the files to a Cloud Storage bucket;
- initiates a build in the location us-west2 using the uploaded files as input;
- tags the image using the provided name
- pushes the built image to Container Registry.
local の source code を compress して Cloud Storaeg bucket にアップロードしてるみたい。
gcloud builds submit SOURCE
の形で、対象の source code の path を指定できる。何も指定しないとデフォルトで currrent directory が利用される。
その後、アップロードされた file をもとに build 実行 => tag 付け => image を registry に push の流れ。
Cloud Build が利用する Cloud Storage bucket
When you run gcloud builds submit for the first time in a Google Cloud project, Cloud Build creates a Cloud Storage bucket named [YOUR_PROJECT_NAME]_cloudbuild in that project. Cloud Build uses this bucket to store any source code that you might use for your builds. Cloud Build does not automatically delete contents in this bucket. To delete objects you're no longer using for builds, you can either set up lifecycle configuration on the bucket or manually delete the objects.
gcloud builds submit
の初回実行時に、[YOUR_PROJECT_NAME]_cloudbuild という bucket が作成され、Cloud Build が利用する source code がここにアップロードされる。
Cloud Storage 確認したら確かに作成されていた↓
local の source code のアップロードをスキップ
Cloud Build が参照する source code が、すでに Cloud Storage に存在する場合は、以下のように 直接 Cloud Storage の file を指定することで local の source code をアップロードするフローをスキップできる。直接 Cloud Storage 内の指定した file に圧縮された source code が利用される。
gcloud builds submit \
--config cloudbuild.yaml \
gs://BUCKET/SOURCE.tar.gz
Substitution
Cloud Build config 内で参照できる変数。
e.g.) $PROJECT_ID
: ID of your Cloud project
デフォで利用できる変数
$PROJECT_ID: ID of your Cloud project
$BUILD_ID: ID of your build
$PROJECT_NUMBER: your project number
$LOCATION: the region associated with your build
trigger による build で利用できる変数
$TRIGGER_NAME: the name associated with your trigger
$COMMIT_SHA: the commit ID associated with your build
$REVISION_ID: the commit ID associated with your build
$SHORT_SHA : the first seven characters of COMMIT_SHA
$REPO_NAME: the name of your repository
$REPO_FULL_NAME: the full name of your repository, including either the user or organization
$BRANCH_NAME: the name of your branch
$TAG_NAME: the name of your tag
$REF_NAME: the name of your branch or tag
$TRIGGER_BUILD_CONFIG_PATH: the path to your build configuration file used during your build execution; otherwise, an empty string if your build is configured inline on the trigger or uses a Dockerfile or Buildpack.
$SERVICE_ACCOUNT_EMAIL: email of the service account you are using for the build. This is either a default service account or a user-specified service account.
$SERVICE_ACCOUNT: the resource name of the service account, in the format projects/PROJECT_ID/serviceAccounts/SERVICE_ACCOUNT_EMAIL
github 特有の変数
$_HEAD_BRANCH : head branch of the pull request
$_BASE_BRANCH : base branch of the pull request
$_HEAD_REPO_URL : url of the head repo of the pull request
$_PR_NUMBER : number of the pull request
独自の変数
独自の変数を定義することも可能。
お決まり事
Substitutions must begin with an underscore (_) and use only uppercase-letters and numbers (respecting the regular expression [A-Z0-9]+). This prevents conflicts with built-in substitutions. To use an expression starting with $ you must use $$. Thus:
- 変数名は
_
で始めること - 利用可能な値は、uppercase-letters と数値のみ
_[A-Z0-9_]+
利用方法
You can specify variables in one of two ways: $_FOO or ${_FOO}:
$_FOO
or ${_FOO}
変数に値を指定する。
gcloud builld submit
時に、--substitutions
で指定してあげればOK。
gcloud builds submit --config=cloudbuild.yaml \
--substitutions=_NODE_VERSION_1="v6.9.4",_NODE_VERSION_2="v6.9.5" .
デフォルト値の指定
Cloud build config file の substitutions
でデフォルト値を指定可能。
# ....省略...
substitutions:
_NODE_VERSION_1: v6.9.1 # default value
_NODE_VERSION_2: v6.9.2 # default value
images: [
'gcr.io/$PROJECT_ID/build-substitutions-nodejs-${_NODE_VERSION_1}',
'gcr.io/$PROJECT_ID/build-substitutions-nodejs-${_NODE_VERSION_2}'
]
環境変数に map
substitutions で扱う変数は、あくまで Cloud Build Config 内で参照する値。
Build step 内で利用したい場合は、それぞれの step の環境変数に map してあげる必要がある。
e.g.) 以下の "$_USER" は bash script 実行時の環境変数。substitutions の $_USER を直接参照しているわけではない。
steps:
- name: 'ubuntu'
script: |
#!/usr/bin/env bash
echo "Hello $_USER"
一括で全ての変数を環境変数に map
Cloud build config で options.automapSubstitutions
を true にセットすればOK。
options:
automapSubstitutions: true
substitutions:
_USER: "Google Cloud"
step 内で全ての変数を環境変数に map
top level の options.automapSubstitutions
を true にすると、全ての step が対象になるが、step level で automapSubstitutions
を true にすると、その step 内のみ全ての変数を環境変数に map できる。
steps:
- name: 'ubuntu'
script: |
#!/usr/bin/env bash
echo "Your project ID is $PROJECT_ID"
automapSubstitutions: true
substitutions:
_USER: "Google Cloud"
手動で設定
各 step の env
でその step の環境変数を定義できる。ここで substitution 変数を step の環境変数に map してあげることも可能。
steps:
- name: 'ubuntu'
env:
- 'BAR=$PROJECT_ID'
script: 'echo $BAR'
Github と接続
Cloud Build は "Github の変更を起点として実行する" みたいなことが可能。
この "何かのイベントを起点に Build を実行するやつ” を Cloud Build trigger と言う。
Github の変更を起点とする Cloud Build trigger を作りたいのだが、まず下準備として、Cloud Build を Github と接続させる必要があるとのことなので進めていく。
Required IAM permissions
cloud build service agent (service-${PROJECT_NUMBER}@gcp-sa-cloudbuild.iam.gserviceaccount.com
) に "roles/secretmanager.admin" を付与する必要あり。
PN=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)")
CLOUD_BUILD_SERVICE_AGENT="service-${PN}@gcp-sa-cloudbuild.iam.gserviceaccount.com"
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member="serviceAccount:${CLOUD_BUILD_SERVICE_AGENT}" \
--role="roles/secretmanager.admin"
Connecting a GitHub host
connection 作成
gcloud builds connections create github $CONNECTION_NAME --region=$REGION
ULR が表示されるので、github に login した状態で URL を開き、Cloud Build GitHub App の認証を完了させる。
Please log in to https://github.com using a robot account and then follow this link to authorize Cloud Build to access that account. After authorization, your GitHub authorization token will be stored in Cloud Secret Manager.
https://accounts.google.com/AccountChooser?continue=https%3A%2F%2Fconsole.cloud.google.com....
完了すると、github oauth token が secret manager に保存される。
確認
gcloud builds connections describe $CONNECTION_NAME --region=$REGION
githubConfig
に接続した github の情報が入っているか確認↓
githubConfig:
appInstallationId: 'xxxxxxxxx'
authorizerCredential:
oauthTokenSecretVersion: projects/my-project-id/secrets/my-org-github-github-oauthtoken-xxxxx/versions/latest
username: my-robot-account
ここまでで Github host との "connection" が作成された状態で、特定の repository とは接続はされているわけではない。
Connecting a GitHub repository
先ほど作成した "connection" を利用し、repository と接続する。
gcloud builds repositories create $REPO_NAME \
--remote-uri=$REPO_URI \
--connection=$CONNECTION_NAME --region=$REGION
これで、接続した repository が Cloud Build trigger の repository 先として利用できる。
Cloud Build Trigger (Github)
gcloud cli で作成
gcloud builds triggers create github
を利用すると、Github と連携した Cloud Build trigger 作成可能。
e.g.) master branch への pull request を契機に Build を実行する trigger
gcloud builds triggers create github \
--name="my-trigger" \
--repository=projects/my-project/locations/asia-northeast1/connections/my-connection/repositories/my-repo \
--pull-request-pattern="^master$" \
--build-config="cloudbuild.yaml" \
--region=asia-northeast1
yaml から import
あらかじめ、設定値を記載した yaml file を用意しておき、その file から import する形で Cloud Build trigger を作成することも可能。
e.g.) master branch への pull request を契機に Build を実行する trigger
trigger.yaml
を作成し、
filename: cloudbuild.yaml
name: my-trigger
repositoryEventConfig:
pullRequest:
branch: ^master$
commentControl: COMMENTS_ENABLED
repository: projects/my-project/locations/asia-northeast1/connections/my-connection/repositories/my-repo
repositoryType: GITHUB
gcloud beta builds triggers import
を実行。
gcloud beta builds triggers import --source=./trigger.yaml --region asia-northeast1
Cloud Build trigger を yaml に export
すでに作成ずみの trigger を yaml に export することを可能。
export した yaml を編集し import することで trigger の内容を変更することができる。
gcloud beta builds triggers export sample-trigger --destination=./trigger.yaml --region asia-northeast1
approval を有効化
trigger.yaml に approvalConfig.approvalRequired true を追加すればOK
#...
+approvalConfig:
+ approvalRequired: true
custom service account を利用
cloud build config yaml の serviceAccount
を指定すればOK
serviceAccount: 'projects/$PROJECT_ID/serviceAccounts/my-cloud-build-service-account@$PROJECT_ID.iam.gserviceaccount.com'
必要な role
roles/cloudbuild.builds.builder
を利用すれば基本的な権限はカバーできる。
The email for the Cloud Build service account is [PROJECT_NUMBER]@cloudbuild.gserviceaccount.com. This service account may have permissions that are unnecessarily broad for your use case. You can improve the security posture by following the principle of least privilege. As part of this principle, we recommend creating your own service account to execute builds on your behalf, this can reduce the potential impact of misconfigurations or malicious users.
roles/cloudbuild.builds.builder
は Cloud Build の default service account が利用する role。場合によっては、必要以上の権限になっている場合もあるのでその場合は自前の custom role で対応した方が良さげ。
roles/cloudbuild.builds.builder permissions
description: Can perform builds
etag: AA==
includedPermissions:
- artifactregistry.aptartifacts.create
- artifactregistry.dockerimages.get
- artifactregistry.dockerimages.list
- artifactregistry.files.download
- artifactregistry.files.get
- artifactregistry.files.list
- artifactregistry.kfpartifacts.create
- artifactregistry.locations.get
- artifactregistry.locations.list
- artifactregistry.mavenartifacts.get
- artifactregistry.mavenartifacts.list
- artifactregistry.npmpackages.get
- artifactregistry.npmpackages.list
- artifactregistry.packages.get
- artifactregistry.packages.list
- artifactregistry.projectsettings.get
- artifactregistry.pythonpackages.get
- artifactregistry.pythonpackages.list
- artifactregistry.repositories.createOnPush
- artifactregistry.repositories.deleteArtifacts
- artifactregistry.repositories.downloadArtifacts
- artifactregistry.repositories.get
- artifactregistry.repositories.list
- artifactregistry.repositories.listEffectiveTags
- artifactregistry.repositories.listTagBindings
- artifactregistry.repositories.readViaVirtualRepository
- artifactregistry.repositories.uploadArtifacts
- artifactregistry.tags.create
- artifactregistry.tags.get
- artifactregistry.tags.list
- artifactregistry.tags.update
- artifactregistry.versions.get
- artifactregistry.versions.list
- artifactregistry.yumartifacts.create
- cloudbuild.builds.create
- cloudbuild.builds.get
- cloudbuild.builds.list
- cloudbuild.builds.update
- cloudbuild.operations.get
- cloudbuild.operations.list
- cloudbuild.workerpools.use
- containeranalysis.occurrences.create
- containeranalysis.occurrences.delete
- containeranalysis.occurrences.get
- containeranalysis.occurrences.list
- containeranalysis.occurrences.update
- logging.logEntries.create
- logging.logEntries.list
- logging.views.access
- pubsub.topics.create
- pubsub.topics.publish
- remotebuildexecution.blobs.get
- resourcemanager.projects.get
- resourcemanager.projects.list
- source.repos.get
- source.repos.list
- storage.buckets.create
- storage.buckets.get
- storage.buckets.list
- storage.objects.create
- storage.objects.delete
- storage.objects.get
- storage.objects.list
- storage.objects.update
name: roles/cloudbuild.builds.builder
stage: GA
title: Cloud Build Service Account
Cloud Run へ deploy する場合の role
追加で以下を付与すればOK。
-
roles/run.admin
role 付与 - Cloud Run runtime service account に対する Service Account User role 付与。
GCP console の Cloud Build > Settings page から GUI でポチポチ role を追加することも可能。(ただし、[PROJECT_NUMBER]@cloudbuild.gserviceaccount.com 以外の
service account では、roles/cloudbuild.builds.builder
を別途付与する必要あり)
エラーでた
gcloud builds submit したところ以下のエラーに遭遇
ERROR: (gcloud.builds.submit) INVALID_ARGUMENT: generic::invalid_argument: if 'build.service_account' is specified, the build must either (a) specify 'build.logs_bucket', (b) use the REGIONAL_USER_OWNED_BUCKET build.options.default_logs_bucket_behavior option, or (c) use either CLOUD_LOGGING_ONLY / NONE logging options
custom service accoun を利用する場合は、log に関する設定が必要とのこと
log に関する設定
When you specify your own service account for builds, you must store your build logs either in Cloud Logging or in a user-created Cloud Storage bucket. You can not store your logs in the default logs bucket.
service account を指定した場合、デフォの log bucket は利用できないので Cloud Build の log の保存先を設定する必要がある。選択肢は以下の二つ。
- Cloud Logging を利用
- 自前の Cloud Storage bucket を利用
詳細は、Sore and manage build logs を参照
Cloud Logging を利用する場合
#...
options:
logging: CLOUD_LOGGING_ONLY
Cloud Sorage buket を利用する場合
#...
logsBucket: 'gs://mylogsbucket'
options:
logging: GCS_ONLY
step 間でデータの引き継ぎ
Cloud Build runs your tasks as a series of build steps, which execute in isolated and containerized environments. After each step, the container is discarded. This allows you to have totally different tools and environments for each step, and by default, any data created in one step can't contaminate the next step. But sometimes you may need to persist state from one step of a build to use in subsequent steps.
Cloud Build の各 step は独立した container 環境なので基本的にデータは分離されてる。
が、これだと前の step の成果物に依存する step を作りたい場合に困る。
e.g.) dependencies を install する step => test を実行する step
For such cases, Cloud Build provides volumes, which are read-write file paths that you can attach to any build step. Volumes retain their contents throughout the duration of the build. You can define your own volume or use /workspace, which is the default volume that Cloud Build provides for you. Before executing a build, Cloud Build extracts the source code to /workspace. Anything written to user-defined volumes and /workspace by any step will be available to subsequent steps.
このような場合に対応するために、各 step を跨いで尊属し、共有される volume が存在する。デフォルトで /workspace
がこの volume として扱われる。
ちなみに、Cloud Build は user の source code をこの /workspace
下に置くので source code 内での変更は保持されるはず。なので install で生成される node_modules
とか、build で生成される dist
とかは次の step でも残っているはず。
node の例
特に明記されていないが、test や build を実行するためには install された node_modules が残っていないとおかしいので、install step で /workspace
volume 内の source code に node_modules
が生成されて、その上で test step, build step が実行されているはず。
steps:
- name: 'node'
entrypoint: 'npm'
args: ['install']
- name: 'node'
entrypoint: 'npm'
args: ['test']
- name: 'node'
entrypoint: 'npm'
args: ['run', 'build']
Cloud Build とは何か
時間経ってだいぶ理解が深まってきたので今の理解をメモ。
Cloud Build は何かというと、
"containerize された実行環境に、指定した source code を取り込んで、何かしらの処理を実行できるやつ"
で、この時の実行の単位が step
で、どの実行環境で実行するかを step.name
で指定している。この指定対象の"実行環境"が Cloud Builder と呼ばれているわけだが、要は docker image なので dockerhub の image を指定することもできる。
gcloud build submit
で source を指定するわけだけど、これが "実行環境"に取り込みたい source code" の指定になる。デフォで container 内の /workspace
に配置される。
あとは、"source code を取り込んだ実行環境内のどこで何を実行したいか" 等の実行に関する詳細を各 step の option で定義していく流れ。
Cloud Build はこの step を積み重ねて一つのワークフローを定義できる。この時、各 step の実行環境は分離されているけど、取り込んだ source code 内の変更は引き継がれる(/workspace
volume)ので、step を積み重ねて install/test/build みたいなフローが作成できる。
Cloud Build Default Service Account