概要
GAE/GoでAPI書いていると、必ずデプロイフローの問題にぶつかると思います。
CircleCIがDockerコンテナベースでのビルドなので、それと合わせてGAEのデプロイを成功させるにはどうすればいいのか、設定ファイルと合わせて共有します。
CircleCIとAppEngine
AppEngineにdeployするとき、必ずGoogle Cloud SDKをインストールしたり、deploy権限を持ったユーザーでのログインなどが問題になります。
なので、まずGoogle Cloud SDKが入ったdocker imageを用意する必要があります。
ローカルから各プロジェクトへのデプロイをする場合には、手元のCLIでauth loginすればいいのですが、CircleCIのdocker container上からは別の方法を取ることになります。
mercari/docker-appengine-go
自作でGAEにdeployするためのdockerイメージのDockerfileを書こうとしたこともあったのですが、ややだるかったので、ぼーっとしていたところ、メルカリさん(というか作者の@_zchee_ さんおよび@sotanard さん)が作ってくれていたようで、それを使うと簡単にdeployフローが構築できます。
サービスアカウント
デプロイに際して、デプロイ権限を持ったサービスアカウントのJSONを書き出す必要があります。
サービスアカウントの発行方法は、以下の記事に書いてありますので、ご確認ください。
ビルド用の環境変数の設定
サービスアカウントを用いたGAEへのデプロイで必要なパラメーターとして、「サービスアカウントのemail」と「アカウントのJSON」の2つを用意しなければいけません。
今回は試しにdevサーバーにdeployするためのフローを作るために、「DEV_SERVICE_ACCOUNT_CLIENT_EMAIL」と「DEV_SERVICE_ACCOUNT_KEY」という値を用意してみましょう。
例としては、
DEV_SERVICE_ACCOUNT_CLIENT_EMAIL: {PROJECT名}@appspot.gserviceaccount.com
DEV_SERVICE_ACCOUNT_KEY: サービスアカウントのJSON十数行の改行を無くして1行にまとめたもの
を、CircleCIの Environment Variables
から設定します。
circle.yml
circle.yml全文は下記になります。
version: 2.0
references:
container_config: &container_config
docker:
- image: mercari/appengine-go:1.8
environment:
PROJECT_ROOT: /go/src/github.com/timakin/sample_api
APP_NAME: sample_api
VENDORING_CACHE_PATH: /go/src/github.com/timakin/sample_api/vendor
working_directory: /go/src/github.com/timakin/sample_api
workspace_root: &workspace_root
/tmp/workspace
attach_workspace: &attach_workspace
attach_workspace:
at: *workspace_root
save_workspace: &save_workspace
run:
name: load code from temporary directory
command: |
mkdir -p /tmp/workspace/sample_api
mv * .[!.]* /tmp/workspace/sample_api/
load_code: &load_code
run:
name: load code from temporary directory
command: |
# Move all files and dotfiles to current directory
mv /tmp/workspace/sample_api/* /tmp/workspace/sample_api/.[!.]* .
go_vendoring_cache_params: &go_vendoring_cache_params
key: go-vendoring-{{ .Branch }}-{{ checksum "Gopkg.lock" }}
paths:
- /go/src/github.com/timakin/sample_api/vendor
dep_ensure: &dep_ensure
run:
name: Vendoring
command: |
if [ ! -e ${VENDORING_CACHE_PATH} ]; then
go get -u github.com/golang/dep/cmd/dep
dep ensure -v
fi
deploy_to_dev: &deploy_to_dev
run:
name: Hook an event of deployment
command: |
if [ "${CIRCLE_BRANCH}" == "develop" ]; then
echo $DEV_SERVICE_ACCOUNT_KEY > /tmp/secret.json
gcloud auth activate-service-account $DEV_SERVICE_ACCOUNT_CLIENT_EMAIL --key-file /tmp/secret.json
appcfg.py --oauth2_access_token $(gcloud auth print-access-token) update ./services/api/appengine/dev.yaml
fi
vet_and_test: &vet_and_test
run:
name: Validate go source
command: |
go vet $(go list ./... | grep -v /vendor/)
go test -race $(go list ./... | grep -v /vendor/)
jobs:
checkout_code:
<<: *container_config
steps:
- checkout
- *save_workspace
- persist_to_workspace:
root: *workspace_root
paths:
- sample_api
vendoring:
<<: *container_config
steps:
- *attach_workspace
- *load_code
- restore_cache:
<<: *go_vendoring_cache_params
- *dep_ensure
- save_cache:
name: Saving Cache - vendor
<<: *go_vendoring_cache_params
- *save_workspace
- persist_to_workspace:
root: *workspace_root
paths:
- sample_api
deployment:
<<: *container_config
steps:
- *attach_workspace
- *load_code
- *vet_and_test
- *deploy_to_dev
workflows:
version: 2
build_and_test:
jobs:
- checkout_code
- vendoring:
requires:
- checkout_code
- deployment:
requires:
- vendoring
重要なところだけピックアップしますと、下記がdeployを実行する箇所です
echo $DEV_SERVICE_ACCOUNT_KEY > /tmp/secret.json
gcloud auth activate-service-account $DEV_SERVICE_ACCOUNT_CLIENT_EMAIL --key-file /tmp/secret.json
appcfg.py --oauth2_access_token $(gcloud auth print-access-token) update ./services/api/appengine/dev.yaml
これは何をしているかというと、
/tmp/secret.json
に一時的に環境変数から取り出したサービスアカウントのJSONを格納しておく- JSONを使ってサービスアカウントを有効にする
- 有効にしたサービスアカウントを元に、指定したAppEngineサービスをデプロイする
という流れで、デプロイを行なっています。
まとめ
AppEngineのDockerコンテナからのdeployは多少工夫が必要ですが、適切なdocker imageさえ選んでおけば、あとはサービスアカウントの有効化さえすれば済みます。
上記で誤りがあれば、ご指摘ください。