概要
Cloud BuildでCDをしたいので、する。
ついでにkustomizeも組み合わせる。
※プロダクションに導入するには権限がガバガバなので適宜修正。
今回のリポジトリ
目次
- 概要
- やること
- 1. CircleCIでGitHubからCloud Source Repositoriesへpush
- 2. Cloud Source RepositoriesをトリガーにCloud Buildを走らせる
- 3. Cloud Buildでデプロイ
- 所感
やること
スタック
- GitHub
- Cloud Source Repositories
- リポジトリをGitHubとCloud Source Repositoriesの両方用いる理由は完全にお仕事に導入する場合この構成になるため。
- お仕事先はGitHub Enterpriseを使用しているが、GitHub EnterpriseとCloud Buildは直接連携できないため間にCloud Source Repositoriesを噛ます
- CircleCI
- GitHubからCloud Source Repositoriesにコードを上げる用途
- Cloud Build
- コンテナのビルドと配布・GKEへのデプロイ
- Container Registry
- コンテナの配布先
- GKE
- 雑に立てておく。
- 1.10-gkeで雑に立てた。
- 他に変更した設定はnodeのオートスケールを有効にして最小台数を1にした。
- プリエンプティブで立てようとしたら余ってないとか言われてつらい
- kustomize
- Kubernetesのマニフェストを複数環境に適用するために使用
CDの流れ
- CircleCIでGitHubからCloud Source Repositoriesへpush
- CircleCIはtagがpushされたら走らせる
- Cloud Source RepositoriesをトリガーにCloud Buildを走らせる
- Cloud Source Repositoriesへ上がったコードをCloud Buildを用いてContainer Registoryへデリバリ
- Cloud Buildでデプロイ
- kustomizeとkubectlを用いてGKEへデプロイを行う
1. CircleCIでGitHubからCloud Source Repositoriesへpush
1.1. コードを用意してGitHubへpush
雑にコードを書く。nodeが書きたかったのでnode
レスポンスに環境変数の STAGE
を返すようにする。
index.js
const express = require('express') const app = express() const ENV = process.env app.get('/', (req, res) => { res.send(`hello world. ${ENV.STAGE}`) }) app.listen(3000, () => { console.log('listening: 0.0.0.0:3000') })
package.json
{ "name": "cloudbuild-practice", "version": "1.0.0", "main": "index.js", "license": "MIT", "scripts": { "start": "node index.js" }, "dependencies": { "express": "^4.16.3" } }
Dockerfile
FROM node:alpine WORKDIR /app COPY . /app RUN yarn install ENV STAGE=develop EXPOSE 3000 CMD ["yarn", "run", "start"]
動作確認
$ docker build -t myapp . $ docker run -d -e STAGE=local -p 3000:3000 myapp $ curl localhost:3000 hello world. local
Dockerをrootで起動してたり細かい所がアレだけど動けばいいの精神。
雑にGitHubへpush.
https://github.com/y-ohgi/cloudbuild-practice
1.2. Cloud Source Repositoriesでリポジトリの作成
$ gcloud source repos create cloudbuild-practice $ gcloud source repos list REPO_NAME PROJECT_ID URL cloudbuild-practice xxxxxxxxxx https://source.developers.google.com/p/xxxxxxxxxx/r/cloudbuild-practice
1.3. GCPのサービスアカウントを作成
Cloud Source Repositoriesへアクセスするためのサービスアカウントを作成する。
$ export GCLOUD_PROJECT=$(gcloud config get-value project) $ gcloud iam service-accounts create repositories-access --display-name "repositories-access" $ gcloud projects add-iam-policy-binding ${GCLOUD_PROJECT} --member serviceAccount:repositories-access@${GCLOUD_PROJECT}.iam.gserviceaccount.com --role roles/source.admin $ gcloud iam service-accounts keys create repositories-access.json --iam-account repositories-access@${GCLOUD_PROJECT}.iam.gserviceaccount.com
roles/source.admin
の権限は与え過ぎな気がするけど同じく動けばいいの精神。
1.3. CircleCIのアクティベートと環境変数の登録
CircleCIのWebコンソールからGitHubで作成したプロジェクトを追加。
環境変数に先程取得した repositories-access.json
をbase64化したものとプロジェクト名を追加
$ base64 repositories-access.json | pbcopy # "SOURCE_REPOSITORY_SERVICE_ACCOUNT"のキー名で環境変数を追加 $ gcloud config get-value project | pbcopy # "GCP_PROJECT"のキー名で環境変数を追加 $ gcloud source repos list # "SOURCE_REPOSITORY_URL"のキー名でhttpsから始まるURLを環境変数へ追加
1.4. CircleCIのコンフィグを記述
.circleci/config.yml
へコンフィグを追加
version: 2 jobs: push_code: docker: - image: google/cloud-sdk:alpine steps: - checkout - run: name: setup gcloud command command: | echo "${SOURCE_REPOSITORY_SERVICE_ACCOUNT}" | base64 -d > /service-account.json gcloud auth activate-service-account --project=${GCP_PROJECT} --key-file=/service-account.json - run: name: push source code command: | git config credential.helper gcloud.sh git remote add google ${SOURCE_REPOSITORY_URL} git push --all google workflows: version: 2 push_code: jobs: - push_code: filters: tags: only: /.*/ branches: ignore: /.*/
gitへpushして試しにタグを付与して動作確認
$ git tag v0.0.0 $ git push origin v0.0.0
良さげ
この .circleci/config.yml
を一発で書けたの誰かほめて
2. Cloud Source RepositoriesをトリガーにCloud Buildを走らせる
3章でデプロイ。2章はRegistryへのデリバリまで。
PRはこれ。 https://github.com/y-ohgi/cloudbuild-practice/pull/1
2.1. cloudbuild.yamlの用意
Cloud Build用の設定ファイルである cloudbuild.yaml
を用意する。
cloudbuild.yaml
steps: - name: 'gcr.io/cloud-builders/docker' args: - 'build' - '--tag=asia.gcr.io/$PROJECT_ID/cloudbuild-practice:$TAG_NAME' - '--tag=asia.gcr.io/$PROJECT_ID/cloudbuild-practice:latest' - '.' images: - 'asia.gcr.io/$PROJECT_ID/cloudbuild-practice:$TAG_NAME' - 'asia.gcr.io/$PROJECT_ID/cloudbuild-practice:latest'
2.2. CircleCIでtagもpushするよう修正
- run: name: push source code command: | git config credential.helper gcloud.sh git remote add google ${SOURCE_REPOSITORY_URL} git push --all google + git fetch --tags + git push google --tags
2.3. Cloud Buildの設定
Webコンソールからぽちぽち
"Cloud Source Repositories"をソースに選択
コードをpushしたリポジトリを選択
トリガーをタグにしてcloudbuild.yamlを使用する
2.4. デリバリできている確認
$ git tag origin v0.0.4 $ git push origin v0.0.4
しばらく待ってContainer Registoryを確認。あった。
3. Cloud Buildでデプロイ
3章のPRはこれ。
CloudBuildでデプロイの実行 by y-ohgi · Pull Request #3 · y-ohgi/cloudbuild-practice · GitHub
3.1. GKEの構築
以下の条件でGKE環境を用意する。
- GKEのクラスタは用意済み
- developとproductionの2環境を用意
- web-develop/web-productionを用意する。
$ kubectl create namespace web-production
$ kubectl create namespace web-develop
- web-develop/web-productionを用意する。
- 環境はnamespaceで分ける
- 最低限の労力でやる。
$ gcloud container clusters get-credentials mycluster
デリバリしたコンテナの動作確認
$ cat <<EOL | kubectl apply -f apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: run: web name: web spec: replicas: 1 selector: matchLabels: run: web template: metadata: labels: run: web spec: containers: - name: web image: asia.gcr.io/ohgi-gcp-practice/cloudbuild-practice:latest env: - name: STAGE value: default ports: - containerPort: 3000 --- apiVersion: v1 kind: Service metadata: name: web labels: run: web spec: ports: - port: 3000 protocol: TCP selector: run: web EOL $ kubectl port-forward <web POD NAME> 3000:3000 & $ curl localhost:3000 hello world. default
※ なんか頑張ってGCPのプロジェクト名隠してた記憶あるけど、まあ一家。
3.2. kustomizeでGKEのマニフェストを作成
リポジトリにkubernetesディレクトリを作成し、マニフェストはここへ配置。
yamlペタペタすると長くなるので割愛。PRはこれ
https://github.com/y-ohgi/cloudbuild-practice/pull/2
ディレクトリ構成
$ tree kubernetes/ kubernetes/ |-- base | |-- kustomization.yaml | `-- manifest.yaml `-- overlays |-- develop | |-- env_stage.yaml | `-- kustomization.yaml `-- production |-- env_stage.yaml `-- kustomization.yaml
行っていることは以下の3つ
- コンテナのタグをkustomizeで管理
- 環境変数
STAGE
を環境毎に変更- develop環境:
STAGE=develop
- production環境:
STAGE=production
- develop環境:
- namespaceを環境毎に変更
- develop環境:
web-develop
- production環境:
web-production
- develop環境:
3.3. デプロイスクリプトを用意
CloudBuild公式で提供されているkubectl用イメージ gcr.io/cloud-builders/kubectl
を使いたいがkustomizeが入ってないので自分で入れる。
kubectlイメージはGKEへの認証系のスクリプトを提供してくれているのが魅力
#!/bin/bash # e.g.) ./scripts/kustomize-apply.sh kubernetes/overlays/develop/ KUSTOMIZE_OVERLAY=$1 KUSTOMIZE_VERSION=1.0.7 KUBECTL_CMD="/builder/kubectl.bash" if which kustomize ; then kustomize build $KUSTOMIZE_OVERLAY | ${KUBECTL_CMD} apply -f - exit 0 fi curl -s https://api.github.com/repos/kubernetes-sigs/kustomize/releases/latest |\ grep browser_download |\ grep linux |\ cut -d '"' -f 4 |\ xargs curl -L -o /usr/local/bin/kustomize chmod +x /usr/local/bin/kustomize kustomize build $KUSTOMIZE_OVERLAY | ${KUBECTL_CMD} apply -f -
3.4. CloudBuildへKubernetesへの権限を付与
Webコンソールへ移動し、CloudBuildのサービスアカウント 01234567@cloudbuild.gserviceaccount.com
へ「Kubernetes Engine管理者」を付与。
忘れていると以下のエラーと出会える
Running: gcloud container clusters get-credentials --project="ohgi-gcp-practice" --zone="asia-northeast1-a" "mycluster" Fetching cluster endpoint and auth data. ERROR: (gcloud.container.clusters.get-credentials) ResponseError: code=403, message=EXTERNAL: Required "container.clusters.get" permission(s) for "projects/ohgi-gcp-practice/zones/asia-northeast1-a/clusters/mycluster".
3.5. デプロイの実行
今までのコードをpush。
タグを付与してデプロイされるのを祈る。
$ git tag v1.0.0 $ git push origin --tags
くりあー!
3.6. 動作確認
develop環境の動作確認
$ kubens web-develop $ kubectl get svc,pods NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/web ClusterIP 10.47.250.163 <none> 3000/TCP 2m NAME READY STATUS RESTARTS AGE pod/web-7bbf794f95-bhwcl 1/1 Running 0 2m $ kubectl port-forward pod/web-7bbf794f95-bhwcl 3000:3000 & $ curl localhost:3000 hello world. develop $ fg C-c
production環境の動作確認
$ kubens web-production $ kubectl get svc,pods NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/web ClusterIP 10.47.252.144 <none> 3000/TCP 4m NAME READY STATUS RESTARTS AGE pod/web-d45b66c49-vlpxw 1/1 Running 0 4m $ kubectl port-forward pod/web-d45b66c49-vlpxw 3000:3000 & $ curl localhost:3000 hello world. production $ fg C-c
所感
思ってたよりCloud Buildがかゆいところに手が届くので便利。
お仕事でも導入したい。