30分でできる(当社比) Google Cloud Container Builderを利用した AppEngine SE環境への自動デプロイ

AppEngine FEのビルドにも使われている `Google Cloud Container Builder`。

ビルドトリガーとして、GitのレポジトリへのPushなども設定できるため、簡易なCIツールとしても利用できるのではないかと思ってやってみた。
使用感としては、割と手軽にCIっぽいことできて便利って感じ。

### 記事内で使用しているコード
* コード
https://github.com/ryutah/gccb-sample

* プロジェクト構成
今回の記事で使用するコードは、以下のような構成になっている

```
.
├── Dockerfile.deploy
├── Dockerfile.glide
├── README.md
├── app
│   └── default
│       ├── app.yaml
│       └── main.go
├── cloudbuild.yaml
├── deploy.sh
└── src
    ├── gccb_sample
    │   └── route.go
    ├── glide.lock
    └── glide.yaml
```

\# 上記の構成についての詳細は、[前回の記事(Qiita - GAE/Go+glide的な構成での環境構築 ~ローカルサーバー立ち上げまで~)](http://qiita.com/ryutah/items/d864310c62f0385d876d)を参照



# 手順簡易まとめ
1. GCPプロジェクトで、AppEngineのデプロイ権限を持つサービスアカウントを作成し、キーファイルを取得する
2. AppEngineデプロイ用のDockerコンテナを作成し、 `Google Container Registry` にpush
3. デプロイ用コンテナが実行されるカスタムビルドステップを作成
4. `Cloud Source Repositories` にレポジトリを作成する
5. `Cloud Source Repositories` の特定ブランチへのPushをイベントとした `Google Container Registry` のトリガーを作成する
6. `Cloud Source Repositories`　へソースをPushする


# Google Cloud Container Builder(GCCB)とは
`Google Cloud Storage` や `Google Cloud Source Repository` にあるソースコードからDockerイメージのビルドを行うサービス。
`AppEngine FE環境`　のビルドも実はGCCBを利用してDockerイメージがビルドされ、 `Google Container Registory` にビルドされたイメージが保存されるようになっている。

ビルドの開始は、 `gcloud` コマンドやREST APIなどで実行できるほか、 `Google Source Repository`, `GitHub`, `Bitbucket` へのPushもトリガーとすることが可能。


# サービスアカウント作成とキーファイル取得
GCPへデプロイするためのサービスアカウントを作成し、キーファイルを生成する

## サービスアカウント作成
`IAMと管理` > `サービスアカウント` > `サービスアカウントを作成` で、デプロイ用のサービスアカウントを作る。

![IAM と管理](https://qiita-image-store.s3.amazonaws.com/0/36379/77596246-8320-b125-8a60-78a470cbb9a1.png)

## キーファイル生成
作成したサービスアカウントのオプションから `キーを作成` を選択し、キーファイルをダウンロードする。
\# 今回はJSON形式を指定。

![IAM と管理](https://qiita-image-store.s3.amazonaws.com/0/36379/7e4a4c90-14ab-f296-8c57-55f8c46dfe5c.png)

ここで作成したサービスアカウントとキーファイルは、デプロイ時に使用する



# Dockerコンテナのビルド
GAE/Go SEへのデプロイを行うDockerコンテナをビルドする。
今回のサンプルでは、デプロイ前にglideを使用して依存ライブラリの取得も行いたいため、デプロイ用コンテナとは別にビルド用コンテナをビルドしている。


## ベースイメージのPull
ビルド用コンテナ、デプロイ用コンテナのベースイメージをpullしておく

```console
$ gcloud docker -- pull gcr.io/cloud-builders/glide
$ gcloud docker -- pull gcr.io/cloud-builders/gcloud
```


## ビルド用コンテナのビルド
依存ライブラリの取得を行うDockerイメージをビルドする。

### Dockerfile
```Dockerfile:Dockerfile.glide
# Google Cloud Container Builder 公式イメージ
# https://github.com/GoogleCloudPlatform/cloud-builders
FROM gcr.io/cloud-builders/glide

ENV GOPATH=$GOPATH:/workspace

# GCCBのビルドステップ中は、各ステップの成果物やソースコードは全て `/workspace`
# ディレクトリに保存され、ワークディレクトリが `/workspace` となるためローカル
# でも同様の状態を再現するためにワークディレクトリを `/workspace` に設定しておく
WORKDIR /workspace

ENTRYPOINT ["sh"]
CMD ["-c", "cd src && glide.bash install"]
```

### ビルド
```console
$ docker build -t gcr.io/{YOUR_PROJECT_ID}/glide -f Dockerfile.deploy .
```


## デプロイ用コンテナのビルド
`AppEngine SE` へアプリケーションのデプロイを行うDockerイメージをビルドする。

### Dockerfile
```Dockerfile:Dockerfile.deploy
# Google Cloud Container Builder 公式イメージ
# https://github.com/GoogleCloudPlatform/cloud-builders
FROM gcr.io/cloud-builders/gcloud

# デプロイ先のプロジェクト名
ARG PROJECT_NAME
# サービスアカウントのキーファイル名
ARG KEY_FILE_NAME
# サービスアカウントID
ARG SERVICE_ACCOUNT_ID

# デプロイ時の環境設定
ENV APPLICATION=${PROJECT_NAME} \
    GAE_GO_SDK_VERSION=1.9.56 \
    ACCOUNT=${SERVICE_ACCOUNT_ID} \
    KEY_FILE_NAME=${KEY_FILE_NAME} \
    PATH=/go_appengine:$PATH \
    GOPATH=/workspace

# サービスアカウントのキーファイルを、Dockerコンテナ内にコピーする
COPY ${KEY_FILE_NAME} /${KEY_FILE_NAME}

RUN apt-get update && apt-get install -y \
    wget \
    unzip \
 && apt-get clean \
 && rm -rf /var/lib/apt/lists/*

# AppEngine SDK For Go のインストール
RUN wget https://storage.googleapis.com/appengine-sdks/featured/go_appengine_sdk_linux_amd64-${GAE_GO_SDK_VERSION}.zip \
 && unzip /go_appengine_sdk_linux_amd64-${GAE_GO_SDK_VERSION}.zip \
 && rm /go_appengine_sdk_linux_amd64-${GAE_GO_SDK_VERSION}.zip

WORKDIR ${GOPATH}

# デプロイスクリプトの実行
ENTRYPOINT ["sh"]
CMD ["deploy.sh"]
```

### ビルド
```console
$ docker build -t gcr.io/{YOUR_PROJECT_ID}/deploy \
  -f Dockerfile.deploy \
  --build-arg PROJECT_NAME={YOUR_PROJECT_NAME} \
  --build-arg SERVICE_ACCOUNT_ID={SERVICE_ACCOUNT_ID} \
  --build-arg KEY_FILE_NAME={SERVICE_ACCOUNT_KEY_FILE_NAME} \
  .
```

## デプロイスクリプト
サービスアカウントを使用してデプロイしているだけ。

```sh:deploy.sh
gcloud auth activate-service-account $ACCOUNT --key-file /$KEY_FILE_NAME

appcfg.py update --application $APPLICATION --oauth2_access_token $(gcloud auth print-access-token 2> /dev/null) app/default
```


#### ローカルでビルドしたイメージを実行してみる
```
$ docker run --rm -v $PROJECT_ROOT:/workspac gcr.io/{YOUR_PROJECT_ID}/glide   # 依存ライブラリの取得
$ docker run --rm -v $PROJECT_ROOT:/workspac gcr.io/{YOUR_PROJECT_ID}/deploy  # デプロイ実行
```

上記実行してみて、特にエラーが発生せずにデプロイが成功していたら問題なくイメージのビルドができてる。



# DockerイメージのPush
`Google Container Registory`に、ビルドしたコンテナイメージをPushしておく

```console
$ gcloud docker -- push gcr.io/{YOUR_PROJECT_ID}/glide
$ gcloud docker -- push gcr.io/{YOUR_PROJECT_ID}/deploy
```


# カスタムビルドステップの作成
カスタムビルドステップを作成して、トリガーイベントの発生時に、作成したDockerイメージを利用してデプロイが行われるようにする。
カスタムビルドステップの設定ファイル(`cloudbuild.yaml`)はリポジトリに含めておくようにする。

```yaml:cloudbuild.yaml
steps:
  - name: gcr.io/$PROJECT_ID/glide
  - name: gcr.io/$PROJECT_ID/deploy
```


# Cloud Source Repositoriesの設定、トリガーの設定

## レポジトリの作成
`Source Repositories` > `レポジトリを作成`で、レポジトリを作成する

![Source Repositories   project ryutah.png](https://qiita-image-store.s3.amazonaws.com/0/36379/18dfe44e-b41c-69d6-e15d-af2090f273b9.png)


## Google Cloud Container Builderのトリガー設定
作成したレポジトリをイベントとした、トリガーを作成する

### トリガー作成手順
1. `Container Registory` > `トリガーを作成` > `トリガーを作成` を選択
![Container Registry](https://qiita-image-store.s3.amazonaws.com/0/36379/a0a8285b-81b7-d473-e772-f8709837e878.png)


2. `ソースを選択` で `Cloud Source Repositories` を選択
![Container Registry](https://qiita-image-store.s3.amazonaws.com/0/36379/8220b03d-b6b5-ddd7-fc0c-33d19de7e92c.png)


3. `リポジトリを選択` で、先ほど作成したレポジトリを選択
![Container Registry](https://qiita-image-store.s3.amazonaws.com/0/36379/4ab19bbd-67cd-8fc6-19ca-349719d482bd.png)


4. `トリガー設定` > `ビルド設定` で　`cloudbuild.yaml` を選択し、 `cloudbuild.yaml の場所` に `/cloudbuild.yaml` を設定する
![Container Registry](https://qiita-image-store.s3.amazonaws.com/0/36379/cf369ea6-ef89-3e09-5759-6ef76deec5bf.png)



# コードのPush、ビルドの確認
ソースを `Cloud Source Repositories` にPushし、ビルドを開始させる

```console
$ git remote add google https://source.developers.google.com/p/{project ID}
$ git config --local credential.helper gcloud.sh
$ git push google master
```

コードをPushすると、 `Container Registory` > `ビルド履歴` で、ビルドが実行されているのが確認できる。

![Container Registry](https://qiita-image-store.s3.amazonaws.com/0/36379/d0d173c5-9fdb-90c3-8c7f-703fcf8f1f24.png)

ビルドに成功すると、緑色のチェックマークがつく

![Container Registry](https://qiita-image-store.s3.amazonaws.com/0/36379/ab6e4077-a1ab-d4c4-046a-6787704984de.png)


---

以上が、GCCBを利用して `AppEngine Starndard Environment` にデプロイする方法。

実際に試してみると、結構仕組みも単純だし、ビルドにDockerコンテナを使用するためかなり自由度が高く、Circle CIなどを使用した場合と比べてもGCPないで完結するぶんお手軽感がある。
また、各ビルドステップでエラーコードが帰ると、途中でビルドが停止されるらしい(未検証)ので、デプロイ前にテストの実施を行うとかもできそうな感じ。

GAEの利用を考えていて、かつCIツールに何を使用するか決定していない場合は、選択肢の一つとして考えてもいいかもしれない。

# 参考
[Qiita - Google Container Builder ファイル作れるの作れないの！](http://qiita.com/atusi/items/61441ab8db5970c6adf4)
[Qiita - CircleCIでService Account使ってGAE/goへdeployする](http://qiita.com/koki_cheese/items/ee25795bcd85ef72889c)
[Qiita - 既存のGitローカルレポジトリをGoogle Cloud RepositoriesにPushする。](http://qiita.com/soundTricker/items/3a77f1053b24cca3ecec)