GCPのCloud Buildを試してみた

[Google、「Cloud Build」発表。ビルド／テスト／デプロイの実行をマネージドサービスで提供、1日あたり120分まで無料。GitHubと提携も](https://www.publickey1.jp/blog/18/googlecloud_build1120github.html)

と、上の記事にもあるように7末になってからCloud Buildのβ版が公開されていました。
使おう、使おうと思っていたら既に8月も半ばに……時間が経過するのが早すぎる。
という訳で使ってみました。

# 作成物
https://github.com/fumihiko-hidaka/cloud-build-sample

いくつかの罠を踏み越えて実装してみました。
ここはこう実装した方が良い！　等あればissueでもprでも貰えれば！

# 構成図
![CB構成図.jpg](https://qiita-image-store.s3.amazonaws.com/0/235100/24302d6a-3382-62e9-eedc-0f6b870e0aa9.jpeg)

VMを用意すると課金が走ってしまうので、App Engineの無償枠でやってみようと思います。
ここがGKEを使ってkubernetesになるかGCEになるかどうかは、サービスの内容次第ということで。

1. githubへpush!!
2. githubに登録したwebhookからCloud Source Repositoryへソースが同期
3. ソースの変更を感知してCloud Buildが実行
4. App Engineへデプロイ
5. Buildの実行データをPub/Subに登録しCloud Function経由でSlackへデプロイ内容を通知！

という5つの流れで。

# Cloud Source Repositoryの用意
正直説明することないです( ˘ω˘)
GUIからポチポチっとGithub連携してください。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/235100/b1a85135-25a5-9448-8868-e72876a955aa.png)

こんな感じになると思います。

# Cloud Build の用意
- masterブランチが更新されたらデプロイを実行
- 環境変数をとりあえず何か設定

上記の内容で構成してみました。
環境変数の頭には_がいるので、それだけつけるように。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/235100/ff58555c-b567-641e-a5a0-6c715b8c8f39.png)

## cloudbuild.yamlの設定

設定のcloudbuild.yamlのファイルはシンプルにこんな感じにしてあります。

```yaml
steps:
- name: 'alpine'
  args: ['echo', '${_ENVIRONMENT}']
- name: 'gcr.io/cloud-builders/yarn'
  args: ['install']
- name: 'gcr.io/cloud-builders/gcloud'
  args: ['app', 'deploy', '--version', '1', 'app.yaml']
```

yamlの中で使いたい変数を${}で囲ってあげれば、コンテナのコマンド実行時に該当部分を置換した上で実行してくれます。

yamlファイルの設定としては、

1. alpineのコンテナをpullしてきて、echoで環境変数を出してみる
2. [GCPで提供されているyarn用のコンテナ](https://github.com/GoogleCloudPlatform/cloud-builders/tree/master/yarn)で、yarn installを実行。
    3. 商用の場合はnodeなりyarnのバージョンをあわせるように調整が必要かな……
3. [GCPで提供されているgcloud用のコンテナ](https://github.com/GoogleCloudPlatform/cloud-builders/tree/master/gcloud)で、gcloudコマンドを使用、gaeへのデプロイを実行！

## 手動で実行したい場合

stagingや確認用の環境はともかく、本番は自動デプロイしたくないんだよ！　ということもある方にも安心。
CUIからコマンドを叩けば実行できるんですが、GUIからもちゃんと手動で実行開始できます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/235100/1bad344e-7ff8-14d5-78b8-54a10fc68932.png)

ステータスを無効にすれば、そのトリガーは自動では走らないので。
いざデプロイのタイミングでボタンからポチポチっとすることができます。

## 気をつけること
**DBのパスワード**等、漏れると不味いものは絶対に設定しないようにしましょう。
後述のCloudBuildのログ等に出てしまうので、Cloud Key Management Serviceをちゃんと使いましょう。
環境変数に応じて、事前に準備しておいてある.env.xxxのファイルを復号してコンテナの中で読み込むとか、
[セキュアに値を管理する方法](https://cloud.google.com/cloud-build/docs/securing-builds/use-encrypted-secrets-credentials?hl=ja)があるので、それを使うのが無難です。

# Pub/Subの設定

Cloud Buildの開始、実行中、完了のステータスを投げてもらうためのPub/Subの設定です。
Cloud Buildを実行するためのAPIを有効にすれば自動的に作成されるので、特に何も考える必要はないはずなんですが、
何故か自分の場合は作成されなかったので……自動的に作成されるものと同じPub/Subを作成してみました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/235100/466f6d4f-5ae5-7f2b-1306-c27716d0b9a4.png)

「cloud-builds」という名前のtopicを作成すればOKです。
他には特に設定する必要はありません。

## Pub/Subから連携されるデータ

```json
{
  "id": "d31aefd5-3e5e-4267-9b34-ae0763e8fe33",
  "projectId": "neko-tech-test",
  "status": "QUEUED",
  "source": {
    "repoSource": {
      "projectId": "neko-tech-test",
      "repoName": "cloud-build-sample",
      "branchName": "master"
    }
  },
  "steps": [
    {
      "name": "alpine",
      "args": [
        "echo",
        "production"
      ]
    },
    {
      "name": "gcr.io/cloud-builders/yarn",
      "args": [
        "install"
      ]
    },
    {
      "name": "gcr.io/cloud-builders/gcloud",
      "args": [
        "app",
        "deploy",
        "--version",
        "1",
        "app.yaml"
      ]
    }
  ],
  "createTime": "2018-08-19T16:11:10.482618374Z",
  "timeout": "600s",
  "logsBucket": "gs://131175276489.cloudbuild-logs.googleusercontent.com",
  "sourceProvenance": {
    "resolvedRepoSource": {
      "projectId": "neko-tech-test",
      "repoName": "cloud-build-sample",
      "commitSha": "f3583a8989a4fe586c441f6a9b4179a4ae683bfa"
    }
  },
  "buildTriggerId": "29ecb161-3f28-493d-9d48-a3624c77a9d0",
  "options": {
    "substitutionOption": "ALLOW_LOOSE"
  },
  "logUrl": "https://console.cloud.google.com/gcr/builds/d31aefd5-3e5e-4267-9b34-ae0763e8fe33?project=131175276489",
  "substitutions": {
    "_ENVIRONMENT": "production"
  },
  "tags": [
    "event-8cf659ed-4402-4f3d-a61e-e2fd9a4ba1f9",
    "trigger-29ecb161-3f28-493d-9d48-a3624c77a9d0"
  ]
}
```

こんな感じのデータが来ます。
実行待ちの場合はSTATUSが「QUEUE」、開始で「WORKING」、完了すると「SUCCESS」として設定されます。

## Pub/Sub連携の罠

GAEを使っているからなんですが、GAEのデプロイの時にもここのPub/Subが飛んできて、思い当たる節のないデータに悩まされました。
source.repoSource.repoNameあたりの項目から、自分のデプロイした対象のPub/Subかどうか判定する項目を入れておきましょう。

# Cloud Functionの設定
![image.png](https://qiita-image-store.s3.amazonaws.com/0/235100/4681a0e4-edcc-cdd4-1954-6afd3c58539c.png)

Cloud Buildのサンプル用のリポジトリの中にSlack通知用のCloud Functionの設定も入れてみました。
せっかくなのでNode.jsの8を使用してます。
後はcloud_functionディレクトリの下にindex.jsとpackage.jsonを用意すれば動いてくれます！

ちなみに「その他」の項目の中に「SLACK_WEBHOOK_URL」という環境変数とslackの連携用のURLを入れてます。
これは展開すると、ありとあらゆるところから通知が来てしまうので隠してます:sob:

## Cloud Functionが更新されないという罠

ソースを更新してもCloud Functionの内容が反映されない……！
という罠があったりしたので、別記事に書いてみました。
[デプロイしたCloud Functionsが更新されない問題をどうにかする](https://qiita.com/fumihiko-hidaka/items/149ac8ab95f1205bf26b)

regionを指定し忘れると、もう一つ別のCloud Functionができてしまうので、そこだけ注意しましょう。

# いざ実行

![cf.mov.gif](https://qiita-image-store.s3.amazonaws.com/0/235100/29e9ef31-d85c-65bc-a79e-d4d04475d0a5.gif)

# 最後に

[株式会社ネコカリ](https://www.nekokari.co.jp/)では猫の手も借りたい🔥炎上中🔥なお仕事を募集しています！
一緒に働くメンバーも募集していますので、よかったら是非！