この記事は Enigmo Advent Calendar 2018 の20日目の記事です。
概要
GCP(GKE)を利用してログからユーザ属性を機械学習により予測し、 その結果をAPIで返却するシステムを構築しました。 運用していく上でCI/CDツールは何にしようかなぁというところで、 今後社内でGitlabを使っていくという流れがあったのと GKEとの相性も良さそうということでGitlab CIを利用することにしました。1
今回はGitlabとGCP(GKE)環境を連携する方法と、 Gitlab CIのキモとなる.gitlab-ci.ymlの内容についてまとめたいと思います。
システム構成
簡単にデプロイ対象のシステム構成を紹介します。 赤い枠で示したものが今回の作業対象になります。
環境条件
今回は下記の環境を使用しています。 Gitlabについては毎月バージョンが更新されるので、旧バージョンを利用されている方などは若干UI等が変わっているかもしれません。
- Gitlab
GitLab Community Edition 11.5.3 - GKE
クラスタバージョン 1.10.9-gke.5
CI/CDのフロー
CI/CDは下記のフローで行います。
テスト環境
- developブランチへのcommit&push
以降は自動
- dockerイメージのbuild
- dockerイメージをGCSへpush
- GKE(CronJon)へのデプロイ
- GKE(Deployment)へのデプロイ
本番環境
本番はすべて手動
- developブランチからmasterブランチへのMR作成
- マージ
- GKE(CronJon)へのデプロイ
- GKE(Deployment)へのデプロイ
設定手順
Gitlab CIを利用するにあたり必要な設定を行っていきます。
サービスアカウント作成
GCP(GKE)とGitlab CIとの連携のためにGCPコンソールからサービスアカウントを作成します。
- ここでは仮に「Gitlab CI」という名前のサービスアカウントを作成します。
- 役割(role)に下記の2つを付与してください。
- Kubernetes Engine 開発者
- ストレージ管理者
- キーのタイプでJSONを選択し、キーを作成し、ローカルにキーファイルをdownloadします。
変数の設定
GKEへの認証情報をGitlabに登録します。 対象のプロジェクト(リポジトリ)のSettings -> CI/CD -> 変数にて、 キーに「SERVICE_ACCOUNT_KEY」を入力し、 値に先ほどdownloadしたキーファイルの中身をそのままコピペします。
下記の例では「SERVICE_ACCOUNT_KEY_PROD」というキーもありますが、 これは本番とテストでプロジェクトを分けているためです 1つのプロジェクト配下の場合は1つのキーで問題ないと思います。
Specific Runnersの登録
下記コマンドにてSpecific Runnerを登録します。 今回はdocker in docker方式で行います。 shell executorを利用する場合はこちらを参考にしてください。
$ sudo gitlab-runner register -n \ > --url [URL] \ > --registration-token [TOKEN] \ > --executor docker \ > --description "docker-runner" \ > --tag-list "gke" \ > --docker-image "docker:stable" \ > --docker-privileged
※指定するURLとTOKENについてはGitlabのSettings -> CI/CD -> Runnerの項目を参照してください。
今回はオンプレ版のGitlab環境を使用していますが、 gitlab.comでShared Runnerを利用する場合には こちらの手順は不要です。
.gitlab-ci.ymlの作成
今回はCronjobへのデプロイ定義を例に説明します。 Deploymentへのデプロイ定義も基本的に同じです。
- 共通設定
.auth_gke: &auth_gke | gcloud auth activate-service-account --key-file=key.json gcloud config set project ${PRJ_ID} gcloud config set container/cluster ${CLUSTER_NAME} gcloud config set compute/zone ${CLUSTER_ZONE} gcloud container clusters get-credentials ${CLUSTER_NAME} --zone ${CLUSTER_ZONE} .setting_dev: environment: develop variables: CI_DEBUG_TRACE: 'true' PRJ_ID: 'XXXXX-dev' CLUSTER_NAME: 'dev-XXXX' CLUSTER_ZONE: 'asia-northeast1-a' GCS_KEY: 'XXXX.json' ENV_CONFIG: '/PATH/conf/settings_dev.json' .setting_prod: environment: production variables: PRJ_ID: 'XXXXX-prod' CLUSTER_NAME: 'XXXXX' CLUSTER_ZONE: 'asia-northeast1-a' GCS_KEY: 'XXXX.json' ENV_CONFIG: '/PATH/conf/settings_prod.json' services: - docker:dind stages: - build - deploy
- ジョブ定義①
ここではdockerイメージのbuildと、GCRへのコンテナイメージのpushを行っています。- extendsで先程定義した内容を継承しています。
- only:changesで該当のファイルに変更があった場合のみジョブが実行されるようになります
docker build_base: extends: .setting_dev image: 'docker:stable' stage: build tags: - gke script: - docker info # Build the image - docker build --cache-from ${IMAGE_TAG} -t ${IMAGE_TAG}:${IMAGE_VER} . # Log in to Google Container Registry - echo "$SERVICE_ACCOUNT_KEY" > key.json # - docker login -u _json_key --password-stdin https://asia.gcr.io < key.json - docker login -u _json_key -p "$SERVICE_ACCOUNT_KEY" https://asia.gcr.io # Push the image - docker push ${IMAGE_TAG}:${IMAGE_VER} only: refs: - develop changes: - Dockerfile - bin/* - conf/* - requirements.txt
- ジョブ定義②
ここではテスト/本番環境に対する2つのCronJobリソースへのデプロイを行います。- 本番環境へはwhen: manualを定義して手動でデプロイするようにしています。
deploy_model_builder_dev: extends: .setting_dev image: 'claranet/gcloud-kubectl-docker:1.2.2' stage: deploy tags: - gke script: # Authenticate with GKE - echo "$SERVICE_ACCOUNT_KEY" > key.json - *auth_gke # set up CronJob - cat kubernetes/XXXX-job.yaml | envsubst | kubectl apply -f - only: refs: - develop changes: - .gitlab-ci.yml - kubernetes/XXXX-job.yaml deploy_bulk_judgement_dev: extends: .setting_dev image: 'claranet/gcloud-kubectl-docker:1.2.2' stage: deploy tags: - gke script: - echo "$SERVICE_ACCOUNT_KEY" > key.json - *auth_gke # set up CronJob - cat kubernetes/XXXX-job.yaml | envsubst | kubectl apply -f - only: refs: - develop changes: - .gitlab-ci.yml - kubernetes/XXXX-job.yaml deploy_model_builder_prod: extends: .setting_prod image: 'claranet/gcloud-kubectl-docker:1.2.2' stage: deploy tags: - gke script: # Authenticate with GKE - echo "$SERVICE_ACCOUNT_KEY_PROD" > key.json - *auth_gke # set up CronJob - cat kubernetes/XXXX-job.yaml | envsubst | kubectl apply -f - only: - master when: manual deploy_bulk_judgement_prod: extends: .setting_prod image: 'claranet/gcloud-kubectl-docker:1.2.2' stage: deploy tags: - gke script: # Authenticate with GKE - echo "$SERVICE_ACCOUNT_KEY_PROD" > key.json - *auth_gke # set up CronJob - cat kubernetes/XXXX-job.yaml | envsubst | kubectl apply -f - only: - master when: manual
ロールバック
せっかくなのでロールバックの手順についても記載しておきます。 Gitlabの左メニューから運用 -> 環境とたどっていくと、下記のような画面が表示されます。
上記で記載した.gitlab-ci.ymlにenvironmentという定義があると思いますが、 そこで定義した文字列が環境のところに表示されているのがわかると思います。 たとえばここでproductionをクリックします。 すると下記の画面に遷移します。
この一覧はproductionに対するコミットの一覧です。 たとえば直前のコミットにロールバックしたい場合は赤枠のところをクリックすればOKです。 もちろん、この画面から任意のコミットを選択して再デプロイすることも可能です。
environmentの定義があることで環境毎のコミット履歴が一覧で見れるかつ、 そこからのデプロイ、ロールバックも簡単に行えます。 複数環境がある場合にはぜひ設定することをおすすめします。
まとめ
- 今回、Gitlab CIを初めて利用してみましたが、特に癖もなく使いやすかったです。
- Auto DevOpsや、先日発表されたGitLab Serverlessなど、まだまだアツい機能があるので、時間を見つけて検証してみようと思います。
- 当初はGitlabのAuto Devopsの利用を考えていましたが、検討時点では利用しているGitlabのバージョンが古かったため、ひとまずGitlab CIを利用することにしました。 ↩