Flaggerでも手動カナリアリリースがしたい!

Flaggerでも手動カナリアリリースがしたい!

はじめに

こんにちは。株式会社ZOZOのSRE部プラットフォームSREチームに所属しているはっちーと申します。

本記事では、Kubernetesクラスター上で自動カナリアリリース機能を提供するFlaggerが導入済みのマイクロサービスにおいて、手動カナリアリリースを実施する方法について紹介します。一見、矛盾するように思えるかもしれません。しかし、時にはそのような要件も発生することがあります。また、手動カナリアリリースで運用している状態からFlaggerの導入を検討している場合、導入後も念のために現行の手動カナリアリリースができるのか、という点は気になるかと思います。すでにFlaggerを導入している、これからの導入を検討している、という方の参考になりましたら幸いです。

目次

前提知識(Flagger)

Flaggerは、Progressive Deliveryのツールです。Progressive Deliveryは、カナリアリリースやA/Bテスト、Blue-Green Deploymentなどの手法を組み合わせて、リスクを最小限に抑えながらリリースを進める手法です。Flaggerを使うことで、カナリアリリースを自動化できます。より詳細については、Flaggerの公式ドキュメントや以下の弊社テックブログなどを参照してください。

techblog.zozo.com

本記事は、Flaggerの基本知識がある前提での説明となります。

Manual Gatingの基本

Manual Gatingによる手動カナリアリリースを実現するために必要な基本知識を説明します。

Manual Gatingとは

Manual Gatingとは、Flaggerで手動カナリアリリースをすることです。なお、「Manual Gating」という機能がFlaggerで特別に用意されているわけではありません。Flaggerのカスタムリソースである、CanaryリソースのWebhooksなどを利用して実現します。

Manual Gatingが必要な背景

FlaggerのProgressive Deliveryにより、カナリアリリースの進行におけるメトリクスの確認や判断、加重率の変更作業、切り戻し作業などを自動化し、リリースにおける工数を削減できます。自動化できるものをわざわざ手動で行う背景は何でしょうか。それは、より慎重にリリースをしたいケースがあるからです。たとえばZOZOの場合、内製しているzozo-api-gatewayの一部のリリースにおいて、以下のケースでManual Gatingを利用しています。

  • 1週間程度の長い期間をかけて手動カナリアリリースをしたいケース。
    • とくにリクエスト数が多い週末にはN%で留めて様子を見ておき、週明けにリリースを進めたい。
    • Flaggerの自動カナリアリリースでは、進行が速すぎるかつ、このような柔軟な進行を実現できない。
  • FlaggerのMetricTemplateリソースによる外部ツール(Datadogなど)のメトリクスを利用した機械的判断では不十分なケース。
    • たとえば、パーソナライズされた検索結果を返すような機能のリリースの場合、単純なHTTPステータスのエラー率では判断できない。誤ったパーソナライズでの検索結果でも200レスポンスであるため。人の目で丁寧に動作確認する必要がある。
    • なお、zozo-api-gatewayにそのような検索機能のビジネスロジックが実装されているわけではない。カナリアリリースを導入できないレガシーシステムへのルーティングもzozo-api-gatewayが担っている。zozo-api-gatewayのルーティングコンフィグを変更したうえで、zozo-api-gateway自体のカナリアリリースをして、そのレガシーシステムのカナリアリリースしている。

Manual Gatingを実現する要素

Manual Gatingは、CanaryリソースのWebhooksとloadtesterのいくつかのエンドポイントを組み合わせて、実現します。

Webhooks

Webhooksは、Flaggerのanalysisを拡張する機能です。各Webhookはそれぞれの実行タイミングで実行され、CanaryリソースはそのWebhookの応答ステータスコード(成功なら2xx)からカナリアリリースが失敗しているかを判断します。詳細はWebhookのページをご確認ください。

以下のように、Canaryリソースの spec.analysis.webhooks[].type でWebhookの種類を設定します。

spec:
  analysis:
    webhooks:
      - name: "gate"
        type: confirm-rollout
        url: http://flagger-loadtester.test/gate/halt

loadtester

loadtesterは、gateエンドポイントを通じてリリースの進行を制御するPodです。gateエンドポイントの一覧は以下です。

  • /gate/check
    • カナリアリリースの進行を確認する。
    • gateがopenであれば approved true として、カナリアリリースを進行する。closeであれば approved false として、進行しない。
  • /gate/open
    • gateをopenにする。
    • /gate/closeが実行されない限り、gateはopenのまま。
  • /gate/close
    • gateをcloseにする。
    • /gate/openが実行されない限り、gateはcloseのまま。
  • /gate/approve
    • 常にHTTPステータスコード202を返す。
    • トラフィックの進行を進めるために使用されるが、今回は使用しない(理由は後述)。
  • /gate/halt
    • 常にHTTPステータスコード403を返す。
    • トラフィックの進行を停止するために使用されるが、今回は使用しない(理由は後述)。

Webhooks + loadtester

Webhooksとloadtesterを組み合わせてManual Gatingを行う方法を2つ紹介します。

1つ目は、 type: confirm-rollout のurlを /gate/halt にしておき、カナリアリリースを進行する際に /gate/approve へ変更する方法です。一時停止するたびに /gate/halt へ戻します。

(1つ目)Manual Gatingでカナリアリリースを進行する流れ

YAMLファイルには以下の設定を追加します。

spec:
  analysis:
    webhooks:
      - name: "gate"
        type: confirm-rollout
        url: http://flagger-loadtester.test/gate/halt # 進行させる場合は/gate/approveにする

これは、公式ドキュメントで紹介されている方法です。しかしながら、この方法は実際の運用には難しいと個人的に考えています。理由は、手動でkubectlのapplyコマンドを実行する必要があるからです。N%で一時停止したい場合に、タイミングがシビアなので、CIでの反映は難しいです。本番環境で手動applyをする運用は緊急時以外にしたくないですし、タイミングが間に合わず、加重率が意図せずに進んでしまう可能性が容易に発生しうると考えています。したがって、今回は /gate/approve/gate/halt を使用しません。

2つ目は、定期的に /gate/check して、リリースを進行するタイミングになったら手動で /gate/open することで、1つ進行したら自動ですぐに /gate/close する方法です。基本的にgateはcloseにしておきます。checkしてopenだったらすぐにcloseする理由は、openのままだと次以降のcheckでもカナリアリリースが進行し続けてしまうからです。意図せず進行してしまわないように、自動でcloseするのは要件として必須としました。図にまとめると次の通りです。

(2つ目)Manual Gatingでカナリアリリースを進行する流れ

一時Podを起動してloadtesterの /gate/open を手動で叩きます(1)。Canaryリソースが定期的にloadtesterの /gate/check を叩きます。もし、gateがopenになっていれば、VirtualServiceのweightを操作してカナリアリリースを進行させます。closeであれば何もしません(2、2')。Canaryリソースが /gate/close を叩きます(3)。

YAMLファイルには以下の設定を追加します。なお、通知がノイジーになるため、muteAlertをtrueにしています。

- name: confirm-traffic-increase-gate-check
  type: confirm-traffic-increase
  url: http://flagger-loadtester.istio-system/gate/check
  muteAlert: true
- name: confirm-traffic-increase-gate-close
  type: confirm-traffic-increase
  url: http://flagger-loadtester.istio-system/gate/close
  muteAlert: true

type: confirm-traffic-increase でurlを /gate/check にしているので、トラフィック進行判断の直前にcheckされます。つまり、spec.analysis.interval で設定した間隔でcheckされます。進行させたいタイミングになったら手動で /gate/open を叩いてopenにします。そうすると、次のcheckでopenであることが確認され、カナリアリリースが進行されます。進行した場合は、自動で即座に /gate/close されます。同じ type: confirm-traffic-increase で設定されているため、 gate/check の後に /gate/close が実行されるためです。

また、ロールバック用に以下の設定もします。/rollback/check はgateエンドポイントと同様で、openであればロールバックをして、closeであればロールバックしません。ロールバックの通知はノイジーでないので、muteAlertをtrueにしません。

- name: rollback
  type: rollback
  url: http://flagger-loadtester.istio-system/rollback/check

今回は、この2つ目の方法でManual Gatingします。

Manual Gatingによる手動カナリアリリース手順

今回は、zozo-api-gatewayを例に、実際のリリース手順を紹介します。

正常系

Manual Gatingにより、0%から100%まで手動カナリアリリースを進行する手順です。

Step1. CanaryリソースのWebhooksの設定を追加する

以下の spec.analysis.webhooks の設定をCanaryリソースに追加します。

spec:
  analysis:
    webhooks:
      - name: confirm-traffic-increase-gate-check
        type: confirm-traffic-increase
        url: http://flagger-loadtester.istio-system/gate/check
        muteAlert: true
      - name: confirm-traffic-increase-gate-close
        type: confirm-traffic-increase
        url: http://flagger-loadtester.istio-system/gate/close
        muteAlert: true
      - name: rollback
        type: rollback
        url: http://flagger-loadtester.istio-system/rollback/check

この時点では、CanaryリソースのSTATUSに変化ありません。

Step2. Deploymentの変更

たとえば、Containerのイメージを変更します。

この変更により、CanaryリソースのSTATUSがProgressingになりますが、WEIGHTは0%のままです。つまり、期待通り、カナリアリリースが自動で進行せずに止まっています。

正常系の手順2におけるCanaryリソースの様子

loadtesterのPodで以下のようなログ(抜粋)を確認できます。approved false になっています。

{"level":"info","ts":"2024-04-24T06:19:28.806Z","caller":"loadtester/server.go:79","msg":"api-gateway.api-gateway gate check: approved false"}

Step3. トラフィック進行

Kubernetesクラスター内に一時的なPodを起動して、loadtesterの /gate/open を叩きます。この一時的なPodはcurlの実行が完了すると自動で削除されます。適切なnamespaceで起動してください。curlのBodyにはCanaryリソースの名前とnamespaceを指定します。

kubectl run tmp-$(date "+%Y%m%d-%H%M%S")-<your_name> --image alpine:latest -n <tmp_pod_namespace> --rm -it -- sh -c "apk add --no-cache curl; curl -v -m 10 -d '{\"name\": \"<canary_name>\",\"namespace\":\"<canary_namespace>\"}' http://flagger-loadtester.istio-system:80/gate/open"

curlが成功したら、CanaryリソースのWEIGHTが増えることを確認します。今回の例ですと、10%進行しています。この値は spec.analysis.stepWeight で決定されます。また、stepWeights で変動的な値にもできます。

正常系の手順3におけるCanaryリソースの様子(Progressing)

loadtesterのPodで以下のようなログ(抜粋)を確認できます。gate opened -> approved true -> gate closed になっています。この後、 approved false に戻ります。

{"level":"info","ts":"2024-04-25T05:51:38.011Z","caller":"loadtester/server.go:110","msg":"api-gateway.api-gateway gate opened"}
{"level":"info","ts":"2024-04-25T05:52:29.329Z","caller":"loadtester/server.go:79","msg":"api-gateway.api-gateway gate check: approved true"}
{"level":"info","ts":"2024-04-25T05:52:29.333Z","caller":"loadtester/server.go:141","msg":"api-gateway.api-gateway gate closed"}

目標のN%になるまで上記のcurlを実行する作業を複数回実施し、進行させます。 spec.analysis.maxWeight を50%にしている場合、50%まで進行させてからもう1回 /gate/open を叩くと、100%までカナリアリリースが進行します。CanaryリソースのSTATUSが Promoting -> Finalizing -> Succeeded と遷移します。

正常系の手順3におけるCanaryリソースの様子(Succeeded)

Step4. Webhooksの設定削除

次回のリリースではManual Gatingを利用しない場合、Step1のWebhooksの設定を削除します。続けて、Manual Gatingによる手動カナリアリリースする場合は、そのままで結構です。

ロールバック

Manual Gatingによるカナリアリリース進行の途中で、リリース作業をする人(以下、リリーサー)の判断により手動ロールバックする場合の手順です。Flaggerのanalysisにより自動でロールバックされる場合の話ではありません。したがって、正常系手順のStep3内で実行することを想定しています。

注意点

ロールバックをするとWEIGHTは0%に戻ってしまいます。たとえば、10%まで進行していたとして、20%に進行した段階でリリーサー判断によりロールバックをしたとすると、10%に戻るのではなく0%に戻ります。もし10%に戻したい場合は、再度、正常系の手順2の「マイクロサービスの変更」からやり直しです。

Step1. ロールバックする

/rollback/open を叩きます。

kubectl run tmp-$(date "+%Y%m%d-%H%M%S")-<your_name> --image alpine:latest -n <tmp_pod_namespace> --rm -it -- sh -c "apk add --no-cache curl; curl -v -m 5 -d '{\"name\": \"<canary_name>\",\"namespace\":\"<canary_namespace>\"}' http://flagger-loadtester.istio-system:80/rollback/open"

CanaryのSTATUSがFailedになり、WEIGHTが0%になります。

ロールバックの手順1におけるCanaryリソースの様子

loadtesterで以下のようなログ(抜粋)を確認できます。rollback opened -> approved true になっています。

{"level":"info","ts":"2024-04-24T09:56:27.253Z","caller":"loadtester/server.go:207","msg":"rollback.api-gateway.api-gateway rollback opened"}
{"level":"info","ts":"2024-04-24T09:56:28.802Z","caller":"loadtester/server.go:177","msg":"rollback.api-gateway.api-gateway rollback check: approved true"}

Step2. ロールバックを終了する

/rollback/close を叩きます。

kubectl run tmp-$(date "+%Y%m%d-%H%M%S")-<your_name> --image alpine:latest -n <tmp_pod_namespace> --rm -it -- sh -c "apk add --no-cache curl; curl -v -m 5 -d '{\"name\": \"<canary_name>\",\"namespace\":\"<canary_namespace>\"}' http://flagger-loadtester.istio-system:80/rollback/close"

loadtesterで以下のようなログ(抜粋)を確認できます。rollback closed になっています。

{"level":"info","ts":"2024-05-21T08:07:56.017Z","caller":"loadtester/server.go:237","msg":"rollback.api-gateway.api-gateway rollback closed"}

Step3. Deploymentの変更を戻す

Deploymentの変更を戻します。

運用の工夫

上記の手順を、より安全で効率的に実施するための運用面で工夫した点を紹介します。

周知

Manual Gatingによるカナリアリリース期間中は、そのマイクロサービスに関する他のリリースはできません。もしカナリアリリース期間中に対象マイクロサービスの新しい変更をリリースしてしまうと、その新しい変更で0%から振り出しに戻ってしまいます。したがって、その期間は他のリリース作業はストップとなることを、関係各所へ事前に連絡しておくルールとしています。

Manual Gating中に他のリリースをブロックする仕組み

上記の周知による方法では限界があります。そこで、仕組みで問題が発生しにくいようにしています。具体的には、Manual Gating中に対象マイクロサービスの変更Pull Request(以下、PR)が作成されると、CIでエラーとなるようにしています。具体的には、あるマイクロサービスがManual Gating中であるかを返すGitHub ActionsのReusable Workflowを実装し、それを使うようにしています。

Reusable Workflowの実装

以下は、そのReusable Workflowです。どのマイクロサービスでも利用できるように、Reusable Workflowとして汎用的に実装しています。

# This workflow checks if the microservice is under the process of manual gating by Flagger.
name: check-under-manual-gating-flagger
on:
  workflow_call:
    inputs:
      ...
      cluster_name:
        description: EKS cluster name
        required: true
        type: string
      canary_name:
        description: service name
        required: true
        type: string
      namespace:
        description: namespace
        required: true
        type: string
...
jobs:
  check-under-manual-gating-flagger:
    runs-on: ubuntu-latest
    steps:
      ...
      - name: check if under manual gating process
        run: |
          aws eks --region "$AWS_REGION" update-kubeconfig --name ${{ inputs.cluster_name}}
          WEBHOOKS=$(kubectl get canary ${{ inputs.canary_name }} -n ${{ inputs.namespace }} -o json | jq '.spec.analysis.webhooks')
          if echo "$WEBHOOKS" | jq 'map(select(.type == "confirm-traffic-increase" and .url == "http://flagger-loadtester.istio-system/gate/check")) | length' | grep -q 0; then
            echo "Not under manual gating process"
            exit 0
          else
            echo "Under manual gating process"
            exit 1
          fi

kubectl get canary の結果を確認しています。typeが confirm-traffic-increase かつ、urlが /gate/check であれば、1でexitします。1の場合はManual Gating中という意味です。

Reusable Workflowの利用

上記で定義したReusable Workflowを利用して、以下の2つの条件を満たす場合はエラーとするjobをマイクロサービスごとに用意します。ここではzozo-api-gatewayを例にします。

  • PRに対象のマイクロサービスの変更含まれる
  • 対象のマイクロサービスがManual Gating中である
k8s-block-update-under-manual-gating-api-gateway:
    uses: st-tech/zozo-platform-shared-infra/.github/workflows/check-under-manual-gating-flagger.yaml@v147
    if: ${{ contains(needs.k8s-directory-changes.outputs.changed_dir, 'api-gateway') }}
    needs:
      - set-env
      - k8s-directory-changes
    with:
      kubectl-version: ${{ needs.set-env.outputs.kubectl_version }}
      oidc_role_arn: arn:aws:iam::xxx:role/zozo-platform-infra-gha
      cluster_name: prd-zozo-platform
      canary_name: api-gateway
      namespace: api-gateway

1つ目の条件に関しては tj-actions/changed-files を利用した別のjob(k8s-directory-changes)を利用しています。これはFlaggerのManual Gatingとは趣旨がずれますので詳細は割愛します。興味がございましたらついに最強のCI/CDが完成した 〜巨大リポジトリで各チームが独立して・安全に・高速にリリースする〜をご一読ください。

エラー時は、以下のようになります。

CIエラー例

万が一、CIでエラーになっているにもかかわらずPRをマージしてしまった場合は、そのPRをRevertし、元のN%まで /gate/open を叩いて戻します。

なお、この方法ですとマイクロサービスの数が多いとjobの数も多くなり、きつくなりそうです。しかし、今のところはManual Gatingを利用するマイクロサービスがzozo-api-gatewayしか社内に存在しないため、問題になっていません。増えてきたら、より汎用的な実装にした方が良さそうです。

loadtesterのエンドポイントを叩く操作をスクリプト化する

上記の正常系の手順には、本番環境でPodを立てる手順が含まれます。しかしながら、本番環境のKubernetesクラスター上でPodを手動により作成する作業には、人為的ミスのリスクが伴います。そのリスクを少しでも軽減するために、操作を補助する以下のシェルスクリプトを開発しました。

#!/usr/bin/env bash
[[ -n $DEBUG ]] && set -x
set -e

# Required config:
# - config/global_config.sh
# - config/k8s.sh

SCRIPTS_DIR="$(cd "$(dirname "$0")"; pwd)"
. "${SCRIPTS_DIR}/config/global_config.sh"

function usage_exit() {
  echo "Usage: $0 -e prd -o zozo-api-gateway-ops -c api-gateway -n api-gateway [-g|-r] -a open" 1>&2
  echo "  Options:" 1>&2
  echo "    -e                         Env of EKS cluster(dev-onprem, dev, stg, prd)" 1>&2
  echo "    -o                         Ops namespace such as zozo-api-gateway-ops" 1>&2
  echo "    -c                         Canary resource name such as api-gateway" 1>&2
  echo "    -n                         Namespace of Canary resource such as api-gateway" 1>&2
  echo "    -g                         Gate" 1>&2
  echo "    -r                         Rollback" 1>&2
  echo "    -a                         Action of service(open, close)" 1>&2
  echo "                               NOTE: AWS Profile requires Power User or higher permissions" 1>&2
  echo " Prerequisites:" 1>&2
  echo "   AWS Credentials you need to connect each env:" 1>&2
  echo "     dev-onprem    - ${AWS_ACCOUNT_ALIAS_DEV}" 1>&2
  echo "     dev           - ${AWS_ACCOUNT_ALIAS_DEV}" 1>&2
  echo "     stg           - ${AWS_ACCOUNT_ALIAS_STG}" 1>&2
  echo "     prd           - ${AWS_ACCOUNT_ALIAS_PRD}" 1>&2
  exit 1
}

readonly REGION="ap-northeast-1"

while getopts e:o:c:n:gra:h OPT
do
  case $OPT in
    e) ENV=$OPTARG
      ;;
    o) OPS_NAMESPACE=$OPTARG
      ;;
    c) CANARY_NAME=$OPTARG
      ;;
    n) CANARY_NAMESPACE=$OPTARG
      ;;
    g) GATE=1
      ;;
    r) ROLLBACK=1
      ;;
    a) ACTION=$OPTARG
      ;;
    h) usage_exit
      ;;
    *) usage_exit
      ;;
  esac
done

# validation for args
if [[ -z "${ENV}" ]] || [[ -z "${OPS_NAMESPACE}" ]] || [[ -z "${CANARY_NAME}" ]] || [[ -z "${CANARY_NAMESPACE}" ]] || [[ -z "${ACTION}" ]]; then
  echo "ERROR: required arguments are missing" 1>&2
  usage_exit
fi

case "${ENV}" in
  dev-onprem|dev|stg|prd) ;;
  *) echo "ERROR: -e ${ENV} is invalid" 1>&2
    usage_exit
    ;;
esac

if [[ -z "${GATE}" ]] && [[ -z "${ROLLBACK}" ]]; then
  echo "ERROR: Either -g or -r option is required." 1>&2
  usage_exit
fi
if [[ -n "${GATE}" ]] && [[ -n "${ROLLBACK}" ]]; then
  echo "ERROR: Both -g and -r options are not able to pass at the same time." 1>&2
  usage_exit
fi

if [[ -n "${GATE}" ]]; then
  if [ "${ACTION}" = "close" ]; then
    echo "ERROR: Request /gate/close is not expected in this script." 1>&2
    usage_exit
  fi
  RESOURCE="gate"
else
  RESOURCE="rollback"
fi

# run bastion pod and curl
echo ""
echo "You're requesting to the loadtester pod by /${RESOURCE}/${ACTION}" .
echo ""

. "${SCRIPTS_DIR}/config/k8s.sh"
iam_username="$(aws sts get-caller-identity --query Arn --output text | awk -F "." '{print $NF}')"
aws eks --region "${REGION}" update-kubeconfig --name "${CLUSTER_NAME}"
  exec kubectl run "tmp-manual-gating-$(date "+%Y%m%d-%H%M%S")-${iam_username}" --image=alpine:latest -n "${OPS_NAMESPACE}" --rm -it --restart=Never -- \
    /bin/sh -c "apk add --no-cache curl > /dev/null 2>&1 ; \
    curl -v -m 10 -d '{\"name\": \"${CANARY_NAME}\",\"namespace\":\"${CANARY_NAMESPACE}\"}' http://flagger-loadtester.istio-system:80/${RESOURCE}/${ACTION}"

一見するとコード量が多く見えますが、実際には最後のawsコマンドのみが重要です。Podを起動してcurlをインストールし、loadtesterのエンドポイントを引数の情報に応じて実行します。他の処理は、usageの説明であったり、引数を取得してvalidationしているだけです。

使い方は以下の通りです。

# /gate/open
./scripts/flagger_loadtester.sh -e prd -o zozo-api-gateway-ops -c api-gateway -n api-gateway -g -a open

# /rollback/open
./scripts/flagger_loadtester.sh -e prd -o zozo-api-gateway-ops -c api-gateway -n api-gateway -r -a open

# /rollback/close
./scripts/flagger_loadtester.sh -e prd -o zozo-api-gateway-ops -c api-gateway -n api-gateway -r -a close

自動ロールバックの発生を防ぐ

Manual Gatingによる手動カナリアリリース期間中に意図せず、自動ロールバックされてしまう事象が発生しましたので、その対応です。

原因

ロールバックされた原因は、Datadogへの通信が一定数(Canaryリソースの spec.analysis.threshold の値)以上エラーになったことでした。弊社の場合、analysisのメトリクス取得で、MetricTemplateリソースにより1分の間隔(spec.analysis.interval)でDatadogのクエリを叩いています。この通信はEKSクラスターとDatadog間のインターネット経由のため、数日間にわたるカナリアリリース期間中に通信エラーが複数回発生することは想像に難くありません。実際に、今回のケースでは約1週間の間に3回発生しました。

なお、エラー発生時のloadtesterのログは以下のようになります。

{"stream":"stderr","logtag":"F","message":"{\"level\":\"error\",\"ts\":\"2024-07-03T18:25:59.976Z\",\"caller\":\"controller/events.go:39\",\"msg\":\"Metric query failed for error-count: request failed:(省略)

対応案

案1

Manual Gatingを実施する際は、Canaryリソースの spec.analysis.threshold を極端に大きな値に設定する案です。極端に大きな値とは、spec.analysis.interval が1分の場合かつ、10日間の期間でリリースする場合には、14400です。内訳は、 60m * 24h * 10days = 14400 です。

問題発生時はこの方法を最初に思いつき、暫定対応として採用しました。その後、恒久対応として採用されました。

なお、この案に限りませんが、Flaggerによるanalysisとロールバックは期待できなくなります。つまり、Manual Gatingによる手動カナリアリリース期間中には、Datadogのメトリクスを人間が確認し、適切なアラートを設定することが必要になります。ただし、これはFlagger導入前の手動カナリアリリースの時と同じ運用ですので、Flaggerを導入しても手動カナリアリリースをするのであれば、受け入れられるものかと思います。

案2

案1と同じような方法として、Canaryリソースの spec.analysis.metrics.thresholdRange.max を極端に大きな値にするという案もあります。しかし、あえて案1から変更するメリットはないため、選択しませんでした。

案3

Canaryリソースの spec.analysis.metrics を設定しない案です。この方法でも問題ありません。Canaryリソースの設定もシンプルになります。しかし、同じく、あえて案1から変更するメリットはないため、選択しませんでした。

NGな方法

意外かもしれませんが、Canaryリソースの spec.skipAnalysis をtrueにする方法はNGです。なぜならば、そもそもManual Gatingにならず、リリースを開始すると自動で100%まで進行してしまうからです。

まとめ

本記事では、Flaggerを導入したマイクロサービスにつきましても、手動カナリアリリースができることを紹介しました。方法は、CanaryリソースのWebhooksの設定とloadtesterのgateエンドポイントを利用することです。さらに、運用面における工夫も詳細に紹介しました。

普段はFlaggerによる自動カナリアリリースを利用し、必要に応じて手動カナリアリリースすることで、より安全かつ柔軟にリリースを進めることができます。もし、Flaggerの導入を検討している、運用で困っている、といった場合は参考にしていただけますと幸いです。

We are hiring

ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。

hrmos.co

corp.zozo.com

カテゴリー