Help us understand the problem. What is going on with this article?

GKEでArgoを使ったカナリアリリースを実現する(完結編)

概要

GKEでArgoを使ったカナリアリリースを実現する(セットアップ編)の続編になります。
前回は、Argoをセットアップして、GitOpsできるところまで確認しました。
今回は完結編となります。

構成図

argocd.png

構成要素

前提条件

環境

  • macOS Majave
  • kubectl Client v1.15.1
  • kubectl Server v1.12.8-gke.10
  • Google Cloud SDK 259.0.0(gcloudはこれに含まれる)

今回の流れ

完結編の大まかな流れがこちらです。

  1. 前回のおさらい
  2. Dockerイメージの更新された時はどうするのか?
  3. HELMのセットアップ
  4. GCRのセットアップ
  5. Dockerfileの修正
  6. CloudBuildのセットアップ
  7. ようやくカナリアリリース

手順

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ボタンを押すと、すぐに同期してくれます。
スクリーンショット 2019-08-28 15.20.00.png
スクリーンショット 2019-08-28 15.24.19.png

無事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

GUI上でもPathが修正されているいことを確認できます。
スクリーンショット 2019-08-29 15.04.53.png

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が有効化されていない場合は、有効化してください。
スクリーンショット 2019-08-28 18.22.47.png

2. トリガーの設定
2-1. トリガーの作成

スクリーンショット 2019-08-28 18.23.28.png

2-2. ソースを選択(GitHubを選択)

スクリーンショット 2019-08-28 18.23.58.png

2-3. リポジトリを選択(Forkしたexamplesリポジトリを選択)

スクリーンショット 2019-08-28 18.24.33.png

2-4. トリガーの作成

スクリーンショット 2019-08-29 9.57.40.png

無視されるフィルタに入れているのは、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リソースが作成されていることが分かります。
スクリーンショット 2019-08-29 15.44.35.png

次にカナリアリリースできているかどうかを確認するためにguestbook/php-redis/index.htmlGuestbook ver.2に修正し、Commit,Pushします。

# before
      <h2>Guestbook</h2>
# before
      <h2>Guestbook ver.2</h2>

しばらくするとカナリアリリースが走るので、ブラウザで何度かアクセスするとGuestbook ver.2が何度か1度表示されるようになると思います。
スクリーンショット 2019-08-29 11.37.19.png

最終的には、Guestbook ver.2のみ表示されるようになるはずです。

参考文献

この記事は以下の情報を参考にして執筆しました。
Helmの概要とChart(チャート)の作り方

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away