はじめに
こんにちは。SRE部ECプラットフォーム基盤SREブロックの大澤と立花です。
本記事ではマイクロサービスのカナリアリリースに関して私達が抱えていた課題と、それをFlaggerによるプログレッシブデリバリー導入でどのように改善したのかを紹介します。
ZOZOTOWNのマイクロサービス基盤におけるカナリアリリース手段の変遷については以下のテックブログで紹介しておりますので気になった方はご参照ください。現在はIstio VirtualServiceの加重ルーティングを用いたカナリアリリースに一本化しております。
目次
カナリアリリースの運用課題
カナリアリリースすることで、新しいアプリケーションにバグがあった場合でもユーザへの影響を最小限に抑えられます。一方で、通常のリリースと比較して運用負荷が大きくなるデメリットも存在します。
こちらの図はカナリアリリースの進行を示したものです。
以下を繰り返すことでカナリア用Pod(以下、Canary Podとする)への加重を増やしていきます。
- n%デプロイ
- Canary Podのreplicas拡張・加重増加の設定変更
- 分析
- Canary Podに流れたリクエストが問題なく処理されているか、レイテンシが悪化していないか判断
- 問題がない場合、n%デプロイを進行する
- 問題があった場合、旧バージョンにロールバックする
- Canary Podに流れたリクエストが問題なく処理されているか、レイテンシが悪化していないか判断
従来のカナリアリリースでは、これらのサイクルを人が担うことになります。Canary Podへの加重変更プルリクエスト作成、レビュー、CI/CDの待ち時間、エラー発生やレイテンシ悪化の監視といったことが加重変更の都度発生します。このため、カナリアリリースだけでリリース担当者の稼働を半日費やしてしまうことも珍しくなく負担が非常に大きい状態でした。
また、私達のチームではメインのリソースとカナリアのリソースでKubernetesのDeploymentのマニフェストを分けて管理していました。これら2つのマニフェストを日頃からメンテナンスすることになりますので、変更やレビュー対象の増加に繋がり、設定漏れといった人的ミスのリスク要因になりがちです。
解決手段としてのプログレッシブデリバリー
私達のチームが管理する検索APIはリリース頻度が比較的高く、カナリアリリースによる時間的拘束が顕著でしたので改善を目指すことにしました。
そこで挙がったのがプログレッシブデリバリーです。
プログレッシブデリバリーはリリースプロセスを自動化するために、メトリクスの取得・分析を行い、問題がなければデプロイの進行、基準を満たさなければロールバック、といった判断と実行を備えた仕組みです。
このプログレッシブデリバリー用ツールには、Argo Rollouts、Flagger、Spinnakerなどがありますが、私達のチームではFlaggerを採用することにしました。
Flaggerとは?
Flaggerとはプログレッシブデリバリーを実現するKubernetes Operatorです。
私達のマイクロサービス基盤で採用しているツール(Istio, Datadog, Slack)の場合ですと、Flaggerはカナリアリリースを自動で進めるために次のように連携します。
- 加重変更 : Istio VirtualServiceの設定変更
- スケールアウト/イン : DeploymentをオートスケールするためのHorizontalPodAutoscalerの設定変更
- メトリクス取得 : Datadogにクエリ発行
- 通知・アラート : Slackに通知
連携可能なツールについては公式ドキュメントのIntroductionで紹介されております。
用途 | 連携可能なツール |
---|---|
トラフィックルーティング | Istio, App Mesh, Linkerd, Kuma, Open Service Mesh, Contour, Gloo, nginx, Skipper, Traefik |
リリース分析のためのメトリクス取得 | Datadog, Prometheus, InfluxDB, New Relic, CloudWatch, Stackdriver, Graphite |
通知、アラート | Slack, MS Teams, Discord, Rocket |
Flaggerを採用した理由については以下の通りです。
- Istioとの連携がサポートされていること
- FlaggerはIstioを活用したプログレッシブデリバリーをサポートしており、自動でVirtualServiceの加重を変更できます。マイクロサービス基盤はIstio導入済みで、カナリアリリースの際には元々手動でVirtualServiceを変更していました。加重変更の手段は今までと変わらないため、導入イメージが湧きやすいことは重要なポイントでした。
- Datadog、Slackとの連携がサポートされていること
- FlaggerはDatadogのメトリクス取得をサポートしています。私達のチームでは監視は主にDatadogを利用しており、カナリアリリースの進行・ロールバック判断もDatadogを活用していました。人が目視でチェックしていたグラフのメトリクスをFlaggerからも同じように利用できることは重要なポイントでした。
- モダンなツールであれば通知手段としてSlackが提供されていることは一般的かもしれませんが、リリースの成否通知をSlack連携できることは必須要件でした。
Flaggerによるプログレッシブデリバリーの進み方
Flaggerがどのようにプログレッシブデリバリーを進めていくかをご説明します。便宜上、旧バージョンをv1、新バージョンをv2として記載します。またわかりやすくするために加重変更は100%まで行う設定とし、Serviceなどのリソースは記載を省略しています。
まずリリース前の状態です。v1のPrimary Podのみが存在し、勿論加重はPrimary Podに100%となっております。
対象マイクロサービスのDeploymentが更新されてrevisionが上がったことをFlaggerが検知するとプログレッシブデリバリーが開始され、Progressingフェーズ
に入ります。このフェーズではまずCanary Podがv2としてPrimary Podと同じスケールで生成されます。v2のCanary Podが生成された後、メトリクスを取得し分析しながらCanary Podへの加重を徐々に増やしていきます。
加重切り替えが順調に進みCanary Podへの加重が100%になると、Promotingフェーズ
に入ります。このフェーズではPrimary Podがv2へアップデートされます。アップデート完了後、加重をCanary PodからPrimary Podに徐々に戻していきます。
加重が完全にPrimary Podに戻ると、Finalisingフェーズ
に入ります。このフェーズでは不要になったCanary Podを停止するためにスケールが0に変更されます。
Canary Podのスケールが0になると、Succeededフェーズ
になりプログレッシブデリバリーが完了となります。
Flagger導入時の検討ポイント
Flaggerを検索APIに導入するにあたり、基本機能の検証や日々のリリース業務に落とし込めるか検討しました。その中でも円滑な導入・運用のためにSREチームとして特に注力した3点について紹介します。
(1) サービスを瞬断させることなくFlaggerを導入する
最初にIstioサービスメッシュ内でのFlaggerの挙動を説明します。sample-appというDeploymentが存在する状態でカスタムリソースであるCanaryを適用すると、Flaggerは次の3つの動きをします。
- Flaggerは
app
ラベルがsample-app
と一致するDeployment(またはDaemonSet)をCanary適用のターゲットにする。ターゲットになった既存Deploymentはreplicasが0へ更新される。 - ターゲットの既存Deploymentを元に新しいDeploymentが作成される。このDeploymentは
name
とapp
ラベルがsample-app-primary
になる。Flaggerによるプログレッシブデリバリーの進み方
で説明したPrimary Podはここで作成されたDeploymentが対応している。 - 新しいDeployment用のService、VirtualService、DestinationRule、HorizontalPodAutoscalerがCanary定義に従い新規に作成される。
詳細な挙動については公式ドキュメントのHow it worksをご覧ください。
ここでCanaryを稼働中の検索APIへ適用する障害となったのが、Flaggerがターケットとするラベル名でした。Flaggerはデフォルトではapp
ラベルを使用します。そして、Flagger導入前の検索APIは以下のような通信経路を構築していました。
ここで重要なのは、ServiceとDeploymentの紐付けにapp
ラベルを使用していた点です。この状態の検索APIへCanaryを適用すると次の問題が発生します。
- 既存Deploymentはreplicasが0へ更新されているため通信できない。
- 新たに作成されたDeploymentは
app
ラベルがzozo-search-api-primary
へ更新されているため、既存Serviceのapp
ラベルとの不一致で通信できない。
このようなCanary適用後の振る舞いにより、デフォルト設定のままFlaggerを導入すると検索APIと通信できなくなる事象が発覚しました。
通信経路を確保しつつFlaggerを導入するためには、新たに作成されるDeploymentのapp
ラベルは更新を避ける必要があります。幸いなことにFlaggerはCanary適用のターゲットラベルを変更するオプションを提供しています。下記は検索APIにおいて自動的にラベルの書き換えが行われても影響を受けないrun
へ変更した時のコード事例です。selector-labels
を設定することでターゲットになるラベルを変更できます。
- name: flagger image: ghcr.io/fluxcd/flagger:1.11.0 imagePullPolicy: IfNotPresent args: - -log-level=info - -include-label-prefix=app.kubernetes.io - -mesh-provider=istio - -selector-labels=run
- 既存Serivceは
app
ラベルを元に通信する。 - 新たに作成されたServiceは
run
ラベルを元に通信する。
このように既存の通信経路を確保する方法でFlagger導入を進めました。安定稼働していることを確認した後、検索APIを呼び出す経路を新たな通信経路へと切り替えFlagger導入は完了となりました。
(2) Datadogのメトリクスを利用してカナリア分析する
FlaggerはカスタムリソースMetricTemplateを使用することで、Datadogなど様々な外部サービスをカナリア分析に利用できます。加えてMetricTemplateは、Datadogのメトリクスを作成するクエリをそのまま利用できます。
以下は99パーセンタイルのレイテンシが1sを超えたことを閾値とする場合の設定例です。
apiVersion: flagger.app/v1beta1 kind: MetricTemplate metadata: name: zozo-search-api-99tile-latency spec: provider: type: datadog address: https://api.datadoghq.com secretRef: name: zozo-search-api-datadog-secrets-20211228101017 query: | p99:trace.servlet.request{env:prd,service:zozo-search-api}
このテンプレートをCanaryのanalysisから参照します。
analysis: metrics: - name: "99tile-latency" templateRef: name: zozo-search-api-99tile-latency thresholdRange: max: 1
このように、クエリを自由に記述できるため様々な指標をカナリア分析に利用できます。
(3) リリース開始・完了をSlackに通知する
プログレッシブデリバリーにより自動でカナリア分析可能となったら、人間がカナリアリリース全体の状況を注視してる時間も可能な限り削減したいと考えます。そこでSREチームではFlaggerのイベントをSlackに通知し、カナリアリリースの開始・終了を検知しています。
プログレッシブデリバリー導入の効果
プログレッシブデリバリー導入により得られた効果を3点紹介します。
(1) リリース対応稼働の削減
冒頭でカナリアリリースの辛みとして記載した以下の対応を、プルリクエスト1つでFlaggerが実行するようになりました。
- n%デプロイ
- Canary Podのreplicas拡張・加重増加の設定変更
- 分析
- Canary Podに流れたリクエストが問題なく処理されているか、レイテンシが悪化していないか判断
- 問題がない場合、n%デプロイを進行する
- 問題があった場合、旧バージョンにロールバックする
- Canary Podに流れたリクエストが問題なく処理されているか、レイテンシが悪化していないか判断
Flaggerはn%デプロイ進行と同時に分析も実施し、エラー増加やレイテンシ悪化が発生していれば自動でロールバックを行ってくれます。
分析時のメトリクス取得は、これまで監視に利用していたDatadogダッシュボードのクエリを移植しております。これにより元々人間が判断していたのと同じロジックでFlaggerが判断できますので、リリース中の監視もFlaggerに任せています。別作業しながらSlackの成否通知を待っていれば良いため作業時間の捻出にも寄与できております。
また、2022年4月に実施したElasticsearchのアップグレードに伴うクラスタ切り替え時には、Elasticsearchの負荷を監視しながら加重を少しづつ上げたいニーズがありました。段階的な加重増加をFlaggerに任せられたおかげで、私達は負荷の監視に注力できました。
(2) リリースミスによる障害範囲の最小化
検索APIリリース時に、とある理由で新アプリケーションにエラーが発生してしまったことがありました。これは当時の検索APIのリクエスト・エラー数のグラフとSlackの様子になります。
カナリアリリース開始後10%リリースの時点で、Flaggerがメトリクス分析にてエラーを検知し自動でロールバックしてくれたため障害影響を最小限に抑えられました。
(3) カナリア用リソースのコスト削減
Flagger導入前は、カナリアリリースのためにリリース時以外でもCanary Podを最低1つは起動しておく必要がありました。オートスケーリングに利用しているHorizontalPodAutoscalerの仕様上、minReplicasを0にできないためです。
Flagger導入後は、Canary PodがProgressingフェーズ
からFinalisingフェーズ
の間しか起動しないため、コスト削減にも寄与できております。
また、カナリア用のマニフェストファイルも不要になりましたので、構成管理としてもDRYな状態を保てるようになりました。
最後に
Flaggerによるプログレッシブデリバリーを導入しカナリアリリースを自動化したことで、リリース作業の削減・障害範囲の最小化・コスト削減といった多くのメリットを享受できました。
ただし、これまでカナリアリリース不要と判断していた軽微な修正でも必ずカナリアリリースになるため、リリース時間が延びてしまうケースもありました。1
しかし、万が一の障害発生時に障害範囲を最小限にし自動ロールバックも備えた仕組みがあることは、リリースに対する心理的なハードルを下げ開発サイクルを高速化することに繋がっていると感じております。
引き続き、快適な開発・運用を実現すべく改善していきます。
ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。
-
skipAnalysis
をtrueにすることで通常のリリースに変更することも可能ですが、開発環境以外では変更しないようにしております。↩