Cloud Build を使用して Bitbucket のソースをデプロイする

# 概要

Bitbucket のソースを自動で Google Cloud にデプロイします。
Bitbucket には Cloud と Data Center の2種類がありますが、ここでは Cloud（よく使われている方）の方法を紹介します。

デプロイするサービスは何でもいいのですが、ここでは Cloud Run 関数（旧 Cloud Functions）をデプロイします。


# 構成ファイルの作成

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

```sh
.
├── cloudbuild.yaml
└── cloud_functions
    ├── main.py
    └── requirements.txt
```

このような cloud_functions ディレクトリ配下のソースをデプロイしたいとする。
まずは構成ファイル（cloudbuild.yaml）を作る。

Cloud Run 関数も含め、一部のサービスはドキュメントに[サンプル](https://cloud.google.com/build/docs/deploying-builds/deploy-functions?hl=ja#configuring_the_deployment)がある。

コンテナイメージを指定してCLIで実行するコマンドを書く。
他にも環境変数やタイムアウト時間を指定したりできるので細かい内容は[ドキュメント](https://cloud.google.com/build/docs/build-config-file-schema?hl=ja)を参照。

```yaml
steps:
  - name: "gcr.io/google.com/cloudsdktool/cloud-sdk"
    args:
      - gcloud
      - functions
      - deploy
      - demo-function
      - --region=asia-northeast1
      - --source=cloud_functions/ # cloudbuild.yaml からの相対パス
      - --trigger-http
      - --runtime=python312
      - --entry-point=hello_http

logsBucket: "for_blog" # ログを出力するバケットの名前
```

デフォルトではロギングとGCSの両方にビルドログが保存される。
options で指定することで、保存場所を限定できる。

```yaml
# ロギングにのみログを保存したいとき
options:
  logging: CLOUD_LOGGING_ONLY

# GCSにのみログを保存したいとき
options:
  logging: GCS_ONLY

# ログを保存しないとき
options:
  logging: NONE
```

# サービスアカウントを用意する（省略可）
https://cloud.google.com/build/docs/securing-builds/configure-user-specified-service-accounts?hl=ja

ビルドに使用するサービスアカウントを用意する。
[デフォルトのサービスアカウント](https://cloud.google.com/build/docs/cloud-build-service-account?hl=ja&_gl=1*1kalpq7*_ga*MTkwNDk2NjIyMy4xNjYxNDc0MDAy*_ga_WH2QY8WWF5*MTczNTE5MTk5NS4xNDIuMS4xNzM1MTkyNTkwLjAuMC4w)を使用することもできますが、推奨されていないので極力ビルド用のサービスアカウントを作った方がいい。

今回サービスアカウントに当てるロールは下記の通り。
デプロイするサービスに合わせて適切なロールを当てる。

| IAM ロール                     | 用途                                           |
|-------------------------------|------------------------------------------------|
| Cloud Functions 開発者         | Cloud Functions の作成、更新、デプロイ         |
| Logging 管理者                 | Cloud Build のビルドログを Cloud Logging に出力するため |
| サービス アカウント ユーザー   | Cloud Functions のデプロイ時にサービスアカウントを使用するため |
| ストレージ管理者               | Cloud Storage へのソースコードのアップロード、ログの出力 |


# Bitbucket のアクセストークンを作成する

:::note warn
今回は単一のリポジトリを監視すればいいので、リポジトリに対するアクセストークンを発行する。
プロジェクトやワークスペース単位でアクセストークンを発行するには、プレミアムプランの契約が必要らしいので注意。
https://support.atlassian.com/bitbucket-cloud/docs/access-tokens/
:::


まず Bitbucket 側で2つのアクセストークンを作成。

- 管理者アクセストークン：リポジトリの接続と切断に使用する
- 読み取りアクセストークン：Cloud Build にソースコードへのアクセスを許可する

リポジトリの設定ページからアクセストークンを発行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1217064/651f2a9a-c916-7916-52e9-ed75816df441.png)


権限は以下の通り

- 管理者アクセス トークン
    - リポジトリ：読み取りと管理者
    - プルリクエスト：読み取り
    - Webhook：読み取りと書き込み
- 読み取りアクセス トークン
    - リポジトリ：読み取り

![Google Chrome 2024-12-01 18.27.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1217064/848dd8ba-b392-0dce-1935-a0ba88379884.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1217064/6324b13b-6d36-1f4a-5d64-643519e26136.png)

作成時に表示されたトークンは控えておく。

# Google Cloud 側の操作

## Bitbucket Cloud ホストを作る

https://cloud.google.com/build/docs/automating-builds/bitbucket/connect-host-bitbucket-cloud?hl=ja

コンソールから Cloud Build のリポジトリページを開き、「第2世代」→「ホスト接続を作成」をクリック。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1217064/375033a8-09b9-7201-044a-8fd502e2b85b.png)

新しい接続の作成画面で Bitbucket を選び、項目を埋めていく。
ワークスペースは Bitbukcet のワークスペースIDを入れる。
`www.bitbucket.org/<ワークスペース ID>/<リポジトリ名>`

先ほど発行したアクセストークンをここでセット。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1217064/ec042236-7f6c-cb43-fdae-6ba5bc8c0a4f.png)

「接続」をクリックして、このようにホストが追加されていたらOK。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1217064/ccbaba74-1277-750c-b358-e8b830030445.png)


## Bitbucket Cloud リポジトリに接続する

https://cloud.google.com/build/docs/automating-builds/bitbucket/connect-repo-bitbucket-cloud?hl=ja

リポジトリのページから「リポジトリをリンク」をクリック。
先ほど作ったホストと対象のリポジトリを選択。
リポジトリ名は Cloud Build 上で識別する用の名前。「手動」を選ぶと任意の名前をつけられる。

![プレビュー 2024-12-03 22.42.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1217064/2f7b5564-3028-ae53-1299-f934b16ef3c5.png)

リポジトリが表示されていればOK

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1217064/25f8175d-bc71-b91f-400b-814fef7f8366.png)



## トリガーを作る

https://cloud.google.com/build/docs/automating-builds/bitbucket/build-repos-from-bitbucket-cloud?hl=ja

コンソールからトリガーのページを開き、「トリガーを作成」をクリック。

![Google Chrome 2024-12-26 14.48.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1217064/7e1f4ddd-214a-5cbd-6a26-8f31b2710ada.png)

設定を入れていく。

- 名前
- リージョン
    - 先ほど作ったホストと同じリージョンを指定
- イベント
    - 今回は指定のブランチに push されると発火するようにする
    - ここの設定に関わらず、作ったトリガーを手動で実行することも可能
- ソース
    - 第2世代を選択
- リポジトリ
    - 先ほど作ったやつが選択できるはず
- ブランチ
    - 今回は main ブランチを指定
- ロケーション
    - 先ほど作った構成ファイルを指定
- 代入変数
    - 今回は使用しない
    - 構成ファイルを使い回して、本番環境と開発環境で指定するリソースを分けたりする時に使う
- 承認
    - 今回は承認なしでビルドできるようにする
- サービスアカウント
    - 先ほど権限を用意したサービスアカウントを指定


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1217064/9e513478-40e0-8a3a-0821-8a802578520d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1217064/f1e7a8a1-cf9a-4ab1-0708-893a19ed6281.png)


作成したトリガーが表示されていればOK。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1217064/7afa3265-a0af-ebd5-c038-4ccc3c5b743e.png)



# 動作確認

main ブランチにプッシュする。
もしくは、トリガーを手動実行してもOK。
ビルド履歴から結果とログを確認できる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1217064/c2e17a83-eb0d-7095-c52a-34dfdb8c503c.png)



# ビルドに失敗していたら

https://cloud.google.com/build/docs/troubleshooting?hl=ja

よくあるエラーはトラブルシューティングをまとまっている。
ここでは自分がぶつかったエラーを紹介。

## ログの保存先に関する設定が不足している

:::note alert
ビルドを実行できませんでした: 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
:::

[構成ファイルの作成](#構成ファイルの作成)でも書いたように、ビルドのログはロギングまたはGCSに保存される。
ログの保存場所と、GCSに保存するならバケットを指定する必要がある。


## リージョンの割当制限に引っかかる

:::note alert
ビルドを実行できませんでした: failed precondition: due to quota restrictions, cannot run builds in this region, see https://cloud.google.com/build/docs/locations#restricted_regions_for_some_projects
:::

> 使用状況に応じて、特定のプロジェクトでは次のリージョンでしか Cloud Build が使用できないよう制限されることがあります。
us-central1
us-west2
europe-west1
asia-east1
australia-southeast1
southamerica-east1


自分の場合は asia-northeast1 を指定して割当制限に引っかかってしまった。
ホストとトリガーのリージョンを asia-east1 に変更すると成功した。
どうしても制限対象のリージョンを使いたい場合は割当の上限を引き上げを検討した方がいい。


## サービスアカウントに権限が足りない
ビルドを実行するサービスアカウントにサービスへのアクセス権がないケース。
GCSにログを保存するなら、保存先のバケットへのアクセス権も必要になるため、デプロイするサービスに関係なくGCSの「ストレージ管理者」という強いロールが必要なことに注意。


## デプロイ内容に不備がある
ビルドはスタートしているのにコマンドが間違っていたりして失敗するケース。
手動でデプロイして成功するか確かめた方がいい。