はじめに
CircleCIでSPA(それ単体でも動くもの)とGoAppEngineをデプロイできるようにしたので設定方法のメモです。
やりたいこと
こんなディレクトリ構成で
myrepo/
├── spa/
└── go-app/
SPAはそれ単体でも動いていて、GoAppEngine側にそのSPAを取り込んで動くようなWebアプリをデプロイしたい。
また、CI上のJobはSPAとGoAppでそれぞれ分けたい。
→JSとGo両方入ったイメージを作りたくないため。(自分でイメージを管理したくない等の理由。。。)
障害となったポイント
- SPAのJob(ビルド)の結果をGoAppのJobに渡すのに工夫が必要。
- Job間のImageが異なるので、キャッシュの復元時にPermission問題とかが出てくる。
- AppEngineにデプロイできて、Goも使えるイメージの用意が大変。
- これはやや手抜きですが今回メルカリのイメージを使って解決しました。
設定方法
ディレクトリ構成
ほぼ上の再掲ですが、こんな感じです。
myrepo/
├── .circleci/
├── spa/
│ └── public/
└── go-app/
└── public/
-
.circleci/
にはご存知の通りCircleCIのconfig.ymlが入ります。 -
spa/
には、Javascriptで作られたアプリが入っています。- npm scriptでビルド等ができるように準備されてるものとします。
- 成果物は
public/
に生成されます。 1
-
go-app/
には、GoAppEngineのアプリが入っています。- その下の
public/
に、SPAのビルド成果物を持ってくるようにします。
- その下の
config.yml
早速ですがymlファイルです。ポイントとなる箇所にコメントを入れています。
version: 2.1
jobs:
build:
# ポイント(1)
working_directory: /tmp/myapp/spa
docker:
- image: circleci/node:10
steps:
- checkout:
# ポイント(1)
path: /tmp/myapp
- run:
name: Update npm
command: "sudo npm install -g npm@latest"
- restore_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
- run:
name: Install npm wee
command: npm install
- save_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
paths:
- node_modules
- run:
name: Build SPA
command: npm run build
# ポイント(2)
- save_cache:
key: assets-cache-{{ .Environment.CIRCLE_SHA1 }}
paths:
- /tmp/myapp/spa/public
deploy:
working_directory: /go/src/github.com/myrepo/myapp/go-app
docker:
- image: mercari/appengine-go:1.11
steps:
- checkout:
path: /go/src/github.com/myrepo/myapp
- run:
name: Install dep
command: go get -u github.com/golang/dep/cmd/dep
- restore_cache:
key: dependency-cache-{{ checksum "Gopkg.lock" }}
- run:
name: Install go deps
command: make setup
- save_cache:
key: dependency-cache-{{ checksum "Gopkg.lock" }}
paths:
- vendor
# ポイント(3)
- restore_cache:
key: assets-cache-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: Get spa assets
command: mv /tmp/myapp/spa/public ./public
# ポイント(4)
- run:
name: Set gcloud credential
command: |
echo $GCLOUD_SERVICE_KEY | base64 --decode | gcloud auth activate-service-account --key-file=-
gcloud --quiet config set project ${GOOGLE_PROJECT_ID}
gcloud --quiet config set compute/zone ${GOOGLE_COMPUTE_ZONE}
- run:
name: Deploy to GAE
command: make deploy-ci
workflows:
version: 2
build-deploy:
jobs:
- build
- deploy:
requires:
- build
filters:
branches:
only:
- master
ポイント(1) 複数のジョブ間でキャッシュを復元できるようにSPA側は/tmpで作業する
これは上の方でも書きましたが、Permission問題などへの解決策です。
save_cache
でビルドの成果物を複数のJobで取り回したいとき、
例えば
- /home ディレクトリ以下でcacheをしてみた場合
- 複数Jobでユーザーが異なると、cacheを復元できない場合があります。
- / (root) ディレクトリ以下でcacheをしてみた場合
- root権限を持たないユーザーで動くImageの場合、cacheを置けない/復元できない場合があります。
今回のconfig.ymlだとnode.jsのImageはcircleciユーザーで動いていて、appengine-goのimageはrootユーザーで動いてるので、後者に当たります。(/goの下とかにsave_cacheできれば最高なんだけど、circleciユーザー権限なんでそれができない)
このことについてはこちらにも記載があります。
本当はジョブ間で共通のユーザー(またはイメージ)で動くようになってればこんなハマらないのですが
今回は自前でイメージを作りたくなかったのでこうしました。
ポイント(2) SPAの成果物をsave_cacheする
後にGoApp側でSPAを取ってこれるようにsave_cacheしています。
成果物はcommit単位で異なると想定して .Environment.CIRCLE_SHA1
でキャッシュしています。
ポイント(3) GoApp側Jobでrestore_cacheでSPAの成果物を取得
上記の通りsave_cacheした成果物を復元しています。
そのすぐ下のrun
で、GoApp側の欲しい場所に成果物public
をコピーしています。(ちょっとダサい・・・。)
ポイント(4) AppEngineのデプロイ用にCredentialを設定
CircleCIのドキュメントに書いてる通りですが、ここでGoogleCloudのCredentialを設定しています。
環境変数GCLOUD_SERVICE_KEY
には、base64エンコードしたGoogleCloudサービスアカウントのキーファイル内容を設定しておきます。
以下のようなコマンドで生成できるはず。
base64 my-service-account-key.json
AppEngineにデプロイするため、サービスアカウントには少なくとも以下のような権限が必要です。
- App Engine デプロイ担当者
- App Engine サービス管理者
- Cloud Build 編集者
- ストレージのオブジェクト管理者
肝心のデプロイ部分はmake deploy-ci
で誤魔化してますが、中身はgcloud app deploy --quiet
するだけです。
ポイントとしては以上です。
終わりに
今回のconfig.ymlではTestやLint等が抜けてるので、ちゃんとした物を作る場合は入れておきたいです。
SPAのtest等は、今のbuildの中でそのままやれば良いかなと思います。
Goのtest等は、ひとつjobを増やして今のdeployとは分けたいかな・・・。綺麗なやり方は模索中です
また、そんな風にjobが増えたりでキャッシュのsave/restoreがなんども行われる場合は、CircleCI2.0のcommandsを使うとDRYに書けるのでオススメです。今回は、そんなに複雑にならなかったので使いませんでした
以上、半分は自分向けの備忘録ですが、誰かの参考になれば幸いです。
-
ちなみに、今回は全く内容に出てきませんが自分はGatsbyJSで作った成果物を利用しています ↩