Cloud Buildのアレコレ

Cloud Buildさわって、気づきを書いてみます。

#複数プロジェクトをまたいでPipelineを組む際の権限設定

##前提となる構成
GKE上にアプリケーションをデプロイする
Source code repositoryとCloud BuildをホストするCICDプロジェクトと、アプリケーションをホストするプロジェクト（本番、開発、ステージングなど複数環境があることを想定）が存在する

##結論
各プロジェクト、各サービスアカウントで適切なRole設定する必要がある

###CICDプロジェクト
####Cloud Buildのサービスアカウントに必要な権限
Cloud BuildのTrigger実行権限
GCRにImageをPushする権限
GKEのEndpointに対してkubectl applyを実行する権限


###アプリケーションをホストするプロジェクト
####GKEのワーカーノードのサービスアカウントに必要な権限
GCRからImageをPullしてくる権限
https://cloud.google.com/container-registry/docs/access-control#permissions_and_roles

上記を図にすると以下になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78184/416199a4-b3a7-62b8-640b-e2ff55b8797c.png)

注意点としてTriggerは、Cloud Buildのデフォルトのサービスアカウントである必要があります。
ただし、各Build Stepは任意のサービスアカウントが指定可能で、Stepごとに最小権限を付与すべきという観点で、任意のサービスアカウントを使うほうが好ましいです。
https://cloud.google.com/cloud-build/docs/securing-builds/configure-user-specified-service-accounts

以下のようにStepごとにサービスアカウントが定義できます。

```
steps:
- name: 'bash'
  args: ['echo', 'Hello world!']
logsBucket: 'LOGS_BUCKET_LOCATION'
serviceAccount: 'projects/PROJECT_NAME/serviceAccounts/SERVICE_ACCOUNT'
```

https://cloud.google.com/cloud-build/docs/securing-builds/configure-user-specified-service-accounts#running_builds


#任意のバージョンを連携する方法
いくつかやり方があると思いますが、git tagを使った例を記載します。
詳細は、以下のチュートリアルを参照してください。
https://cloud.google.com/kubernetes-engine/docs/tutorials/gitops-cloud-build

git tagしてPushすると、デフォルト変数の$TAG_NAMEにTAG情報が格納されます。

使用可能な環境変数は以下のマニュアルに記載があります。
https://cloud.google.com/cloud-build/docs/configuring-builds/substitute-variable-values#using_default_substitutions

```
$ git tag -a 1.0.3 -m 'test'
$ git push google 1.0.3
Enumerating objects: 1, done.
Counting objects: 100% (1/1), done.
Writing objects: 100% (1/1), 148 bytes | 148.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0)
To https://source.developers.google.com/p/xxx/r/hello-cloudbuild-app
 * [new tag]         1.0.3 -> 1.0.3
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78184/aa3043cd-ef74-e0e6-cae6-8b75ea5d11fd.png)

##バージョンをContainer imageのタグとして使用する方法
git tagで指定したタグ名が、デフォルト変数の$TAG_NAMEに格納されるので、cloudbuild.yamlのbuildステップでタグを指定します。

```
# This step builds the container image.
- name: 'gcr.io/cloud-builders/docker'
  id: Build
  args:
  - 'build'
  - '-t'
  - 'gcr.io/$PROJECT_ID/hello-cloudbuild:${TAG_NAME}'
  - '.'
```

##GKEにデプロイする際のバージョンの指定方法

パターン１
sedなどを使ってkubernetesのマニフェストファイルを置換する方法が可能です。

```
# This step generates the new manifest
- name: 'gcr.io/cloud-builders/gcloud'
  id: Generate manifest
  entrypoint: /bin/bash
  args:
  - '-c'
  - |
     sed "s/GOOGLE_CLOUD_PROJECT/${PROJECT_Id}/g" kubernetes.yaml.tpl | \
     sed "s/COMMIT_SHA/${TAG_NAME}/g" > kubernetes.yaml;

# This step deploys the new version of our container image
# in the hello-cloudbuild Kubernetes Engine cluster.
- name: 'gcr.io/cloud-builders/kubectl'
  id: Deploy
  args:
  - 'apply'
  - '-f'
  - 'kubernetes.yaml'
  - 'CLOUDSDK_COMPUTE_ZONE=asia-northeast1-a'
  - 'CLOUDSDK_CONTAINER_CLUSTER=cluster-1'
  - 'CLOUDSDK_CORE_PROJECT=xxx'
```

https://cloud.google.com/kubernetes-engine/docs/tutorials/gitops-cloud-build#modifying_the_continuous_integration_pipeline_to_trigger_the_continuous_delivery_pipeline

パターン2
イメージのタグを直接指定することも可能です。

```
    name: 'gcr.io/cloud-builders/kubectl'
    args:
    - 'set'
    - 'image'
    - 'deployment/hello-cloudbuild'
    - 'hello-cloudbuild=gcr.io/$PROJECT_ID/hello-cloudbuild:${TAG_NAME}''
```

#Cloud Buildの変数の文字操作

以下のBashの機能が使えます。
https://cloud.google.com/cloud-build/docs/configuring-builds/use-bash-and-bindings-in-substitutions#bash-style_string_operations



```
$ git tag -a prd-1.0.6 -m 'test'
$ git push google prd-1.0.6
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 4 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 433 bytes | 433.00 KiB/s, done.
Total 4 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2)
To https://source.developers.google.com/p/xxx/r/hello-cloudbuild-app
 * [new tag]         prd-1.0.6 -> prd-1.0.6
```


以下は、変数の先頭から４文字を削除しています。（${TAG:4}）

```
# This step generates the new manifest
- name: 'gcr.io/cloud-builders/gcloud'
  id: Generate manifest
  entrypoint: /bin/bash
  args:
  - '-c'
  - |
     echo "tag name is ${TAG_NAME}";
     TAG=${TAG_NAME};
     echo "tag is ${TAG:4}";
     sed "s/GOOGLE_CLOUD_PROJECT/${PROJECT_Id}/g" kubernetes.yaml.tpl | \
     sed "s/COMMIT_SHA/${TAG:4}/g" > kubernetes.yaml;
```

先頭４文字が削除されました。

```
Step #3 - "Generate manifest": tag name is prd-1.0.6
Step #3 - "Generate manifest": tag is 1.0.6
```

本番環境へのデプロイは、先頭にprd-を付与されたTAGがPushされると起動するTriggerを作成します。実際のデプロイでしているコンテナイメージのTAGは、上記のようにバージョンを文字列操作で取り出すといった事が可能になります。

>
デフォルト変数とユーザー定義変数に bash 形式の文字列操作を適用できます。

https://cloud.google.com/cloud-build/docs/configuring-builds/use-bash-and-bindings-in-substitutions#bash-style_string_operations

と書いてるけど、デフォルト変数は置換できなかった。。

```
     echo "tag name is ${TAG_NAME}";
     TAG=${TAG_NAME};
     echo "tag is ${TAG:4}";
     echo ${TAG_NAME:4}
     sed "s/GOOGLE_CLOUD_PROJECT/${PROJECT_Id}/g" kubernetes.yaml.tpl | \
     sed "s/COMMIT_SHA/${TAG_NAME}/g" > kubernetes.yaml;
     cat kubernetes.yaml
```

ログの４行目のように変数の値が削除される。

```
Step #3 - "Generate manifest": Already have image (with digest): gcr.io/cloud-builders/gcloud
Step #3 - "Generate manifest": tag name is prd-1.0.7
Step #3 - "Generate manifest": tag is 1.0.7
Step #3 - "Generate manifest": 
Step #3 - "Generate manifest": # Copyright 2018 Google LLC
```

#yamlファイルをどこまで分けるか？
アプリと同じレポジトリで管理するパターンとアプリと別のレポジトリでマニフェストファイルをまとめて１つのレポジトリで管理するパターンがありますが、
完全に個人の感想ですが、アプリと同じレポジトリで管理するパターンは、マイクロサービス的に向いていて、別に管理するパターンは、一元管理できるのでアプリとインフラ担当に分かれている組織、gitopsなどに向いている。
また、Pipelineの観点だとデプロイ自体は、docker buildが不要なのでマニフェストファイルのみで完結できるので、分けてると、tagを使ってデプロイ先を切り替えるのがより簡単に実装できそう。