概要
GKEでArgoを使ったカナリアリリースを実現する(セットアップ編)の続編になります。
前回は、Argoをセットアップして、GitOpsできるところまで確認しました。
今回は完結編となります。
構成図
構成要素
- GKE
- GCR
- Cloud Build
- Cloud Load balancer(ingress)
- GitHub
- argo cd
- argo rollouts
前提条件
- GKEでArgoを使ったカナリアリリースを実現する(セットアップ編)でのセットアップが終わっていること
環境
- macOS Majave
- kubectl Client v1.15.1
- kubectl Server v1.12.8-gke.10
- Google Cloud SDK 259.0.0(gcloudはこれに含まれる)
今回の流れ
完結編の大まかな流れがこちらです。
- 前回のおさらい
- Dockerイメージの更新された時はどうするのか?
- HELMのセットアップ
- GCRのセットアップ
- Dockerfileの修正
- CloudBuildのセットアップ
- ようやくカナリアリリース
手順
1. 前回のおさらい
前回のおさらいで、GitOpsができているか確認します。
guestbook/frontend-deployment.yaml
でコンテナに割り当てるリソースを変更し、Commit、Pushしてみます。
# before
- name: php-redis
image: gcr.io/google-samples/gb-frontend:v4
resources:
requests:
cpu: 100m
memory: 100Mi
# after
- name: php-redis
image: gcr.io/google-samples/gb-frontend:v4
resources:
requests:
cpu: 110m # スケールアップ
memory: 110Mi # スケールアップ
ArgoのGUIで確認すると、新たにReplicasetが作成され、最終的に切り替わっていることが確認できます。
ArgoのREFRESH
ボタンを押すと、すぐに同期してくれます。
無事GitOpsによるデプロイが完了しました。
裏では、ArgoがManifestファイルを基にkubectl apply
を実行しているようです。
2. Dockerイメージの更新された時はどうするのか?
ここまでやってみて、1つ疑問が浮かびます。
確かにManifestファイルが更新された時は、ArgoでGitHubにあるManifestファイルとの差分を検知し、
自動でデプロイが走ります。
しかし、 Dockerfileやアプケーションコードに変更があった場合はどうでしょうか?
例えばここ。
- name: php-redis
image: gcr.io/google-samples/gb-frontend:v4 # タグがハードコードされている
resources:
requests:
cpu: 110m
memory: 110Mi
gb-frontend:v4
という形でDockerイメージのタグがハードコードされてしまっています。
例えばCloud build等のCIを使っている場合、アプリケーションに変更があったら自動でテスト、ビルドを行い、GCR等にDockerイメージをPushするかと思います。
しかし、この書き方だと毎回Dockerイメージのタグを書き換え(例えばv4→v5)ないと新しいDockerイメージがデプロイされません。
せっかくCIを使って自動化しているのに、毎回Dockerイメージのタグ変更をPull-Requestを出すのはしんどいな と思いました。
GitOpsじゃないと言われそうですが、どうせならここも自動化したいです。
そこで出てくるのがHelmのtemplate使った方法です。
3. HELMのセットアップ
Helmにはテンプレートの機能があり、公式のGitHubリポジトリではそのテンプレート機能を使ってタグを書き換えています。
サンプルはblue-greenですが、やることは基本的には同じです。
サンプル:https://github.com/argoproj/argocd-example-apps/tree/master/blue-green
やってみたが方がわかりやすいのこのまま進めます。
# Helmのインストール
$ brew install kubernetes-helm
# tillerのインストール
$ cd path/to/sourcecode # 今回の例だとexamples
$ cd guestbook
$ helm init
$ kubectl get pods --namespace kube-system | grep tiller
# HELMチャートの作成
$ APPNAME=guestbook # アプケーション名を設定
$ helm create $APPNAME
$ ls $APPNAME
Chart.yaml charts templates values.yaml
# デフォルトで作成されるManifestファイルを削除
$ rm -rf guestbook/templates/*
# Manifestファイルをtemplateに移動
$ mv *.yaml guestbook/templates/
$ ls guestbook/templates/
frontend-deployment.yaml frontend-service.yaml ingress.yaml redis-master-deployment.yaml redis-master-service.yaml redis-slave-deployment.yaml redis-slave-service.yaml
上記の修正が終わったら、Commit,Pushしておきます。
次にArgoが同期するManifestファイルのパスが変わったので、修正しておきます。
$ argocd app edit $APPNAME
変更内容は以下の通りです。
# before
destination:
namespace: default
server: https://35.194.96.41
project: default
source:
path: guestbook/
repoURL: git@github.com:<your GitHub account>/examples.git
targetRevision: master
syncPolicy:
automated:
prune: true
# after
destination:
namespace: default
server: https://35.194.96.41
project: default
source:
path: guestbook/guestbook/ # ここのパスを修正
repoURL: git@github.com:<your GitHub account>/examples.git
targetRevision: master
syncPolicy:
automated:
prune: true
Argoは、Helmに対応しているため特別な変更なく同期されました。
Helmの他、Kustomizeにも対応しているようです。詳細はこちらを参照してください。
4. GCRのセットアップ
次にDockerイメージをGCRにPushして、リポジトリを作成します。
guestbook/php-redis/Dockerfile
をビルドしてイメージをGCRにPushします。
# setup
$ PROJECT_ID=<your GCP Project ID>
$ gcloud config set project ${PROJECT_ID}
$ gcloud auth configure-docker
# Build
$ cd path/to/sourcecode
$ cd guestbook/php-redis/
$ docker build -f Dockerfile -t asia.gcr.io/${PROJECT_ID}/${APPNAME}:stable .
$ docker images | grep ${APPNAME}
asia.gcr.io/<your GCP Project ID>/guestbook stable 0700c57dd8bc 26 seconds ago 357MB
# Push
$ docker push asia.gcr.io/${PROJECT_ID}/${APPNAME}:stable
$ gcloud container images list --repository asia.gcr.io/${PROJECT_ID}
NAME
asia.gcr.io/<your GCP Project ID>/guestbook
DockerイメージがGCRにPushされました。
次にGCRからGKEにデプロイしてみます。
以下のようにguestbook/guestbook/templates/frontend-deployment.yaml
修正し、Commit、Pushします。
変更内容は以下の通りです。
# before
- name: php-redis
image: gcr.io/google-samples/gb-frontend:v4
# after
- name: php-redis
image: asia.gcr.io/<your GCP Project ID>/<your APPNAME>:stable
ArgoのGUIでも前回と同じように新たなReplicasetが作成され、デプロイされたことが確認できると思います。
5. Dockerfileの修正
現状guestbook/php-redis/Dockerfile
のままだとCloud Buildでbuildする際にコケるので、以下の箇所を修正し、Commit,Pushします。
# bofore
ADD guestbook.php /var/www/html/guestbook.php
ADD controllers.js /var/www/html/controllers.js
ADD index.html /var/www/html/index.html
# after
ADD guestbook/php-redis/guestbook.php /var/www/html/guestbook.php
ADD guestbook/php-redis/controllers.js /var/www/html/controllers.js
ADD guestbook/php-redis/index.html /var/www/html/index.html
6. CloudBuildのセットアップ
続いて、この処理をCloud Buildを使って自動化していきます。
ArgoのIPアドレス確認
$ kubectl get service argocd-server -n argocd | tail -n 1 | awk '{print $4}'
34.85.104.22
cloudbuild.yamlの作成
リポジトリのroot階層に、cloudbuild.yaml
を作成し、Commit,Pushします。
timeout: 780s
steps:
- name: gcr.io/kaniko-project/executor
args:
- --destination=asia.gcr.io/$PROJECT_ID/guestbook:${COMMIT_SHA}
- --cache=true
- --cache-ttl=6h
- --dockerfile=guestbook/php-redis/Dockerfile
- name: argoproj/argocd
entrypoint: 'bash'
args: ['-c', 'argocd login --insecure ${_ARGO_SERVER} --username admin --password ${_ARGOCD_PW} && argocd app set ${_PRJ} -p image.tag=${COMMIT_SHA} && argocd app sync ${_PRJ}']
substitutions:
_PRJ: guestbook
_ARGO_SERVER: <確認したArgoのIPアドレス>
処理内容をざっくり解説すると、指定したDockerfileを基にビルドし、guestbook:${COMMIT_SHA}
という名前でGCRにPushしています。
${COMMIT_SHA}
は、Cloud Buildで用意された変数です。詳細はは、こちらを参照してください。
次に、argocdコマンドを用いてログインし、argocd app set ${_PRJ} -p image.tag=${COMMIT_SHA}
で新たにPushしたDockerイメージに変更しています。
Cloud Buildの設定
1. Cloud Build APIの有効化
Cloud Buildに移動します。APIが有効化されていない場合は、有効化してください。
2. トリガーの設定
2-1. トリガーの作成
2-2. ソースを選択(GitHubを選択)
2-3. リポジトリを選択(Forkしたexamplesリポジトリを選択)
2-4. トリガーの作成
無視されるフィルタ
に入れているのは、Manifestファイルに更新があった場合、GitHubとの同期で更新されるためです。
_ARGOCD_PW
に関しては、Cloud Build内のArgoコマンドで使用しています。ここなんとかして暗号化したい。。
保存したら、トリガーを実行
ボタンを押して、Cloud Buildを走らせてみます。
2つのビルドステップが成功すればOKです。
最後にguestbook/guestbook/templates/frontend-deployment.yaml
のタグ部分をstable
から{{ .Values.image.tag }}
に修正し、Commit、Pushします。
変更内容は以下の通りです。
# before
containers:
- name: php-redis
image: asia.gcr.io/<your GCP Project ID>/<your APPNAME>:stable
# after
containers:
- name: php-redis
image: asia.gcr.io/<your GCP Project ID>/<your APPNAME>:{{ .Values.image.tag }}
ちなみにこの{{ .Values.image.tag }}
という値は、guestbook/guestbook/values.yaml
に設定されているものです。
このあたりは、Helmの話なので割愛します。
7. ようやくカナリアリリース
お待たせしました。
guestbook/guestbook/templates/frontend-deployment.yaml
を以下のように修正し、Commit,Pushします。
apiVersion: argoproj.io/v1alpha1 # 変更
kind: Rollout # 変更。ここでようやくargo-rolloutsを登場
metadata:
name: frontend
spec:
selector:
matchLabels:
app: guestbook
tier: frontend
replicas: 3
template:
metadata:
labels:
app: guestbook
tier: frontend
spec:
containers:
- name: php-redis
image: asia.gcr.io/<your GCP Project ID>/guestbook:{{ .Values.image.tag }} # GCP Project IDを修正
resources:
requests:
cpu: 110m
memory: 110Mi
env:
- name: GET_HOSTS_FROM
value: dns
# If your cluster config does not include a dns service, then to
# instead access environment variables to find service host
# info, comment out the 'value: dns' line above, and uncomment the
# line below:
# value: env
ports:
- containerPort: 80
# ここから下を追記
minReadySeconds: 30 # 30秒待つ?
revisionHistoryLimit: 3 # 3世代保持。つまり3世代までロールバック可能。
strategy:
canary:
steps:
- setWeight: 20 # 全体トラフィックの20%を
- pause:
duration: 20 # 20秒間新しいアプケーションに流す
- setWeight: 50 # 次に全体トラフィックの50%を
- pause:
duration: 20 # 20秒間新しいアプケーションに流す
しばらくすると、rollouts
リソースが作成されていることが分かります。
次にカナリアリリースできているかどうかを確認するためにguestbook/php-redis/index.html
をGuestbook ver.2
に修正し、Commit,Pushします。
# before
<h2>Guestbook</h2>
# before
<h2>Guestbook ver.2</h2>
しばらくするとカナリアリリースが走るので、ブラウザで何度かアクセスするとGuestbook ver.2
が何度か1度表示されるようになると思います。
最終的には、Guestbook ver.2
のみ表示されるようになるはずです。
参考文献
この記事は以下の情報を参考にして執筆しました。
Helmの概要とChart(チャート)の作り方