CircleCIでbuildからGKEへのdeployまで
tl;dr
GKEが楽しいので、趣味productを以下のように環境にした
- Scala(sbt)とTypeScript(npm/angular2)で書かれたアプリケーション
- CircleCIでmaster push時に以下の処理
- テスト回す
- docker repositoryにimageをpush
- kubectlで最新imageを用いてdeploy
- sslはkube-legoを使う
kubenetes 設定
基本的にkube-legoのサンプルと同じようなファイルがある。このあたりは予めGKEのclusterを作成した上で既に適用してる。
- app
namespace.yaml
,deployment.yaml
,service.yaml
,ingress-tls.yaml
- nginx: ...
- kube-lego: ...
- ...
applicationの変更時はappのdeployment.yaml
の内容を以下のように変えて反映する。
kubectl patch deployment app -p '{"spec":{"template":{"spec":{"containers":[{"name":"app","image":"asia.gcr.io/project/app:${CIRCLE_SHA1}"}]}}}}'
kubectl set image ...
でも良い
後述するが、常にlatest
のdocker imageを用意するようにしてるので、deployment.yaml
の中のimageにはlatest
を指すようにしている。
他にもCloudSQLを利用するためにcloud_sql_proxy立てたり、ScheduleJobを設定してたりする。
CircleCI
CIでテスト後にdocker imageのpush、最新imageを用いたdeployまで行う。
実行環境
テストを通す+buildのために必要な環境が整ったDocker Imageを作成し、CircleCIではそちらのimageを利用。以下のdocker imageはsbt
, npm
, ng
, gcloud
, docker
といったコマンドが利用出来る環境になっている。正直な所実際にdeployされるDocker Imageはもっと軽量な別物になるし、このImageは適当で良い。
https://hub.docker.com/r/krrrr38/sbt-ng-gcloud-kubectl/
GCP Service Account
CIの中で、docker imageのpush、GKEのclusterへの更新処理を行うため、それらの機能のみを持つGCP Service Accountを予め作成。認証情報を含んだjsonをbase64して、GCLOUD_SERVICE_KEY
という環境変数へ設定。
Authentication with Google Cloud Platform - CircleCI
.circleci/config.yaml
stepsとしては以下のものがある。
- checkout - restore_cache: - run: cat /dev/null | sbt test:compile - run: cat /dev/null | npm --prefix ui/web install - save_cache: - run: cat /dev/null | sbt test:test - run: cat /dev/null | npm --prefix ui/web test - setup_remote_docker - deploy: ./build.sh -d -l -s ${CIRCLE_SHA1}
build.sh
について- frontのコードをbuild:
ng build --prod
- server側のコードをbuild+実行に必要なファイルを特定のdirectory以下にまとめる:
sbt stage
- 本番環境用のdocker imageを作成
- 先に作成したdeploy用のdirectoryとjava実行環境のみあるdocker imageを作成
docker build -t "asia.gcr.io/${PROJECT}/${MODULE}:${SHA1}" -f "deploy/${MODULE}/Dockerfile" .
- service accountの認証情報を読み込み
echo $GCLOUD_SERVICE_KEY | base64 --decode > gcp-authorization.json
gcloud auth activate-service-account --key-file gcp-authorization.json
- 作成したdocker imageをGCP Container Registryへpush
${SHA1}
とlatest
の2つのimageをpushしているlatest
がきちんと最新のものになるようにしつつ、反映時には${SHA1}
を利用している
gcloud docker -- push "asia.gcr.io/${PROJECT}/${MODULE}:${SHA1}"
gcloud docker -- push "asia.gcr.io/${PROJECT}/${MODULE}:latest"
- deploy
- k8s cluster情報の取得
gcloud container clusters get-credentials ${CLUSTER_NAME} --project $PROJECT --zone $PROJECT_ZONE
- 変更の反映
kubectl patch deployment app -p '{"spec":{"template":{"spec":{"containers":[{"name":"app","image":"asia.gcr.io/project/app:${CIRCLE_SHA1}"}]}}}}'
- k8s cluster情報の取得
- frontのコードをbuild:
build.sh
といいながらoption付けて、deployまでしてしまった
余談
cloud-buildersでimageの作成など考えていたが、依存libraryのcacheをどこに置くかなど悩んだ結果あまり答えがでなかった。CircleCIなら必要なデータは普通にcache出来るし、CI環境からそのままdocker imageを作成してpush出来るので、まぁこれで良いかという気持ち。
k8s、学習コストはあるもの、今後はdocker imageを手軽にほいほいGKEに投げていけるので、雑多な趣味環境を扱うにも良い。そもそも雑多なやつならgolangとかのがメモリ消費少なくなりやすいし良いんですけどね。