GCP: Cloud Build

CLI

push なしで直接 CI を実行

PROJECT_ID="my-project"
gcloud builds --project $PROJECT_ID submit --config cloudbuild.yaml .

ビルドを取得

# ビルドの一覧を表示
gcloud builds list

# QUEUED or WORKINGのビルドのみ表示
gcloud builds list --ongoing

ログを取得

gcloud builds log --stream <build id>

GitHub 連携

https://github.com/apps/google-cloud-build

すべてのリポジトリを選択する。

CI が動く対象のリポジトリをあとから変更したいときは次の URL からRepository access -> Saveをクリックする。 https://github.com/settings/installations/1494878

以降、GitHub リポジトリを push すると自動で CI が動くようになる。

Docker イメージを自動ビルドする

リポジトリにDockerfileを作成して push するだけ。 gcr.io/<プロジェクトID>/<リポジトリ名>がイメージ名になる。

Docker ビルドをカスタマイズしたいときは構成ファイルを書く。
imagesを指定することでタスク内で生成したイメージを Container Registry に保存できるので、docker pushを書く必要はない。

Docker イメージを Container Regisry に保存するcloudbuild.yamlの例:

steps:
  - name: "gcr.io/cloud-builders/docker"
    args: ["build", "-t", "gcr.io/$PROJECT_ID/my-image", "."]
timeout: 600s
images: ["gcr.io/$PROJECT_ID/my-image"]

実際は次のようにイメージのキャッシュを使ったほうが時間の節約にもなって良い。

steps:
  - name: "gcr.io/cloud-builders/docker"
    entrypoint: "bash"
    args:
      - "-c"
      - |
        docker pull gcr.io/$PROJECT_ID/my-image:latest || exit 0
  - name: "gcr.io/cloud-builders/docker"
    args:
      [
        "build",
        "-t",
        "gcr.io/$PROJECT_ID/my-image:latest",
        "--cache-from",
        "gcr.io/$PROJECT_ID/my-image:latest",
        ".",
      ]
images: ["gcr.io/$PROJECT_ID/my-image:latest"]

構成ファイルを書いてビルドする

各タスクを実行するイメージには GCP が提供するイメージ(クラウドビルダー)か、一般公開されているイメージを使用できる。
クラウドビルダー

steps:
  - name: "gcr.io/cloud-builders/docker"
    args: ["build", "-t", "gcr.io/hako1912-dev/dev", "."]
  - name: "gcr.io/cloud-builders/docker"
    args: ["push", "gcr.io/my-project/my-image"]
  - name: "gcr.io/cloud-builders/kubectl"
    args:
      [
        "set",
        "image",
        "deployment/my-deployment",
        "my-container=gcr.io/my-project/my-image",
      ]
    env:
      - "CLOUDSDK_COMPUTE_ZONE=us-east4-b"
      - "CLOUDSDK_CONTAINER_CLUSTER=my-cluster"
options:
  machineType: "N1_HIGHCPU_8"
timeout: 660s
tags: ["mytag1", "mytag2"]

Ref:
https://cloud.google.com/cloud-build/docs/configuring-builds/create-basic-configuration?hl=ja

シークレット

Ref:
https://cloud.google.com/cloud-build/docs/securing-builds/use-encrypted-secrets-credentials?hl=ja

必要な IAM 権限を設定する

ownerまたはcloudkms.adminが必要。

Ref:
https://cloud.google.com/kms/docs/iam?hl=ja#modifying_iam_permissions

キーを作成する

KeyRing を作成する。

KEYRING_NAME=test-keyrings
gcloud kms keyrings create $KEYRING_NAME --location=global

CryptoKey を作成する。

KEY_NAME=test-key
gcloud kms keys create $KEY_NAME \
  --location=global \
  --keyring=$KEYRING_NAME \
  --purpose=encryption

Cloud Build のサービスアカウントに権限を設定する。

KEY_NAME=test-key
KEYRING_NAME=test-keyrings
gcloud kms keys add-iam-policy-binding \
    $KEY_NAME --location=global --keyring=$KEYRING_NAME \
    --member=serviceAccount:[PROJECT_NUMBER]@cloudbuild.gserviceaccount.com \
    --role=roles/cloudkms.cryptoKeyDecrypter

自身のアカウントにも復号権限を設定する。

USER="hoge@gmail.com"
gcloud kms keys add-iam-policy-binding \
    $KEY_NAME --location=global --keyring=$KEYRING_NAME \
    --member=user:$USER \
    --role=roles/cloudkms.cryptoKeyDecrypter

環境変数を暗号化する

MY_SECRET="my-password"
echo -n $MY_SECRET | gcloud kms encrypt \
  --plaintext-file=- \
  --ciphertext-file=- \
  --location=global \
  --keyring=$KEYRING_NAME \
  --key=$KEY_NAME | base64 | tr -d '\n'

環境変数を復号する

gcloud kms decrypt \
  --location=global \
  --keyring=$KEYRING_NAME \
  --key=$KEY_NAME \
  --ciphertext-file=- \
  --plaintext-file=-

動作確認

steps:
  - name: "ubuntu"
    entrypoint: "bash"
    args: ["-c", 'echo "$$APP_NAME HAS A VALUE $$HELLO_WORLD"']
    env:
      - "APP_NAME=test"
    secretEnv:
      - "HELLO_WORLD"
secrets:
  - kmsKeyName: projects/<PROJECT_ID>/locations/global/keyRings/<KEY_RING_NAME>/cryptoKeys/<KEY_NAME>
    secretEnv:
      HELLO_WORLD: CiQAIO2k2R4udI/pjuGN+u+ep4EG24pCm5DUkSH0K3Tx+ZdO8x8SKgBew579JJbB7srB0C1wDBxnmzIuoy9T7B4LYZ26huuoVURIYo1G8HqUTg==

Ref:
https://stackoverflow.com/questions/56936520/google-cloud-build-doesnt-substitute-values-in-secrets-section-of-cloudbuild-ya

Tips

組み込みの環境変数を使用

Ref:
https://cloud.google.com/cloud-build/docs/configuring-builds/substitute-variable-values?hl=ja#using_default_substitutions

Asciidoc を HTML 化して GCS にアップロード

docs/index.adocgs://<バケット名>/doc/<ブランチ名>/index.htmlに配置する。
https://storage.cloud.google.com/<バケット名>/docs/dev/index.htmlで Web ブラウザで表示できる。(プロジェクトの閲覧権限は必要)

steps:
  - name: asciidoctor/docker-asciidoctor:1.0.0
    entrypoint: sh
    # asciidoctor-diagramで生成した画像をimgタグから相対パスで指定すると表示できないので、画像ファイルをbase64にしてhtmlファイルに組み込んでいる
    args:
      - -c
      - |
        asciidoctor -r asciidoctor-diagram -D dist/ docs/index.adoc
        for FILE in $$(find dist -name "*.png"); do
          encoded_image=$$(base64 $$FILE | tr -d '\n')
          sed -i -e "s~diag-.*\.png~data:image/png;base64,$${encoded_image}~g" "dist/index.html"
        done
  - name: gcr.io/cloud-builders/gsutil
    args:
      - cp
      - dist/index.html
      - gs://${_GCS_PATH}/${BRANCH_NAME}/

substitutions:
  _GCS_PATH: my-bucket/docs