Argo Rolloutsを導入してカナリアリリースを実現する

OGP-image

はじめに

こんにちは。計測プラットフォーム開発本部SREブロックの纐纈です。今年の4月に入社し、ZOZOMATやZOZOGLASSの運用改善に取り組んでいます。また、今年の夏US向けにZOZOFITをリリースしましたが、そちらの機能追加にも今後関わっていく予定です。

計測システムでは最近Argo Rolloutsを導入してカナリアリリース、自動ロールバックを実現しました。本記事では、その具体的な導入方法と効果についてお伝えします。

目次

Argo Rollouts導入前のリリースの問題

Argo Rollouts導入前までは、以下の手順でロールバックしていました。

  1. リリース担当者が異常を検知
  2. リリースPRをリバートする
  3. リバートPRをマージする
  4. Argo CDが変更を検知し、アプリケーションを旧バージョンに戻す

私達のチームではArgoCDを導入しており、KubernetesリソースをGitHubで管理しています。このため、ロールバックは、アプリケーションのイメージタグを変更するリリースPRをリバートすることで完了します。しかしながら、旧バージョンのPodが起動し、トラフィックが流れるまでの間、ユーザー影響は避けられません。

こうした理由からユーザ影響を最小限に抑えるため、リリース戦略を見直す必要がありました。

上記の課題を解決するため、弊チームではカナリアリリースを導入することにしました。

カナリアリリースの導入

まずカナリアリリースについて簡単に説明します。カナリアリリースは、リリースの手法の1つです。初めからリリースするバージョンを全体に公開するのではなく、公開する対象を一部のユーザーだけに絞りつつテストを行い、定められた基準を満たした場合のみ全体に公開するリリース方法のことを指します。これによって、ユーザーのトラフィックありきのテストを本番環境で行いつつ、障害発生時にもユーザーへの影響を最小限にしながら自動ロールバックを実現できるようになります。

また段階的なリリース方法としてカナリアリリースとよく比較されるのが、Blue/Greenデプロイです。Blue/Greenではなくカナリアリリースを選択した理由は、実際にユーザートラフィックが流れる点にあります。カナリアリリースでは実際にユーザートラフィックが流れている状態でテストを行うため、Podの立ち上げ時にはわからない問題も対応できます。計測システムでは、ZOZOTOWNのAPIサーバーと接続して認証するため、実際にトラフィックを流してのテストを行う必要がありました。

導入後の効果

Argo Rolloutsを導入してカナリアリリースを実現してからは、より安心安全にリリースできるようになりました。リリース担当者の心理的負荷が減っただけでなく、エラー検知した際もPodが瞬時に入れ替わるため、障害による影響範囲を最小限に抑えられます。これによって、本番リリース直後のアラートに怯える必要性が大幅に緩和されました。

また副次的な効果として、今回ステージング環境にも同様にArgo Rolloutsを入れたため、エラーを伴う変更が本番環境でリリースするまでに発見できるという効果もありました。ステージング環境ではユーザーのトラフィックは流れていませんが、暖機運転と共に動作確認用の処理がPod起動時に走るため、エラーがあればこの動作確認で発見できます。

暖気運転の図

この時点でエラーを伴う変更を検知して自動ロールバックが走ったため、ユーザー影響を出す前にステージング環境で食い止めることができました。

カナリアリリースでの1つデメリットとして、Podが徐々に入れ替わって段階的にテストを行うため、全体的なリリース時間が伸びてしまうという特徴があります。例えばリリース後に動作確認やチェックを行う場合、リリース担当者が全てのPodが入れ替わるまで待つ必要があり、これはリリース担当者のリリースに費やす時間的制約を増やしてしまいます。そのため、できる限りリリース後の作業は自動化できると、よりカナリアリリースの恩恵を受けられるようになるかと思います。

ツールの選定

カナリアリリースの特徴や効果については一通り説明できたと思うので、なぜ今回Argo Rolloutsを選んだのかについて説明していきます。カナリアリリースは通常のKubernetesリソースであるDeploymentではサポートされておらず、外部ツールを使う必要があります。カナリアリリースをサポートする外部ツールはArgo Rollouts以外にも、SpinnakerFlaggerのようなものがあります。Flaggerについては社内の他チームで採用され、ブログ記事も公開されているので、気になる方はこちらの記事をご参照ください。

techblog.zozo.com

今回計測システム部でArgo Rolloutsを選んだ理由は3つあります。

まず、計測システムでは既にArgoCDの導入が済んでおりArgo Rolloutsとの親和性が高かったこと。これはArgoCDのWeb UI上でArgo Rolloutsの操作ができる、Slack通知用のトークンなどを共通して使えるなどの利点があります。

次に、Argo Rollouts以外のツールで必要となるIstioのようなサービスメッシュがまだ導入されていないこと。現在計測システムでは、サービスメッシュは使われておりません。そのため、カナリアリリースのためにサービスメッシュを導入すると、工数も増えるため導入のハードルが上がりました。

最後に、必要最小限の機能であるDatadogやCloudWatchの連携ができること。今回考慮したツールの中ではどれもサポートされていましたが、もしArgo Rolloutsがサポートしていなければ、選定されることはありませんでした。

Argo Rolloutsについて

Argo RolloutsはBlue/Greenやカナリアリリースのようなプログレッシブデリバリーをサポートしている、Kubernetesコントローラーです。CRDs(Custom Resource Definitions)でもあります。監視ツールと連携させて自動ロールバックや段階的リリースを行なったり、Ingressコントローラーやサービスメッシュと連携させて限定公開時のトラフィック制御ができます。

連携できる監視ツールはDatadog、CloudWatch、Prometheusなどがあります。事前に定めた基準から外れたメトリクスを検知した場合、自動でロールバック、基準内だった場合は段階的にリリースします。また、このリリースの段階やテスト項目などはKubernetesのマニフェストで定めることができます。

限定公開時のトラフィックの制御については、ALB IngressコントローラーやIstio、nginxなどと連携できます。連携する場合パーセント単位で制御できますが、連携しない場合でもPod数単位での段階的な切り替えが可能です。

カナリアリリース用にArgo Rolloutsで必要なリソースは、RolloutとAnalysis Templateの2つです。

Argo Rollouts 構成図

RolloutはArgo Rolloutsのカスタムリソースで、Deploymentリソースの項目に追加してカナリアリリースやBlue/Greenデプロイ用のフィールドが追加されています。DeploymentのExtensionだと考えてもらえれば良いです。Deploymentに含まれているフィールドに関しては対応されている他、カナリアリリースやBlue/Greenデプロイの細かい設定ができます。また、既存のDeploymentを参照して設定を読み込むこともできます。

Analysis Templateでは、Datadogなどの監視ツールで取得するメトリクスとテスト内容が記述できます。テスト内容は、頻度や取得したメトリクスのテスト条件、ロールバックまでの許容エラー数などが記載できます。

DeploymentからRolloutへの移行

Argo Rolloutsを導入すると、PodをRolloutリソースで管理することになります。新規プロジェクトで導入するのであれば、最初からDeploymentではなくRolloutリソースを作成すれば良いです。一方、既存のプロジェクトで導入する場合はDeploymentリソースから移行する必要があります。

今回弊チームでは、DeploymentからRolloutへ障害なく移行できたのでその方法を説明していきたいと思います。

Argo Rollouts導入前の計測システムでは、DeploymentでPodの設定、HPA(Horizontal Pod Autoscaler)でPod数を管理していました。

  1. 既存のDeploymentを参照するRolloutリソースを作成して、Podを立ち上げる
  2. HPAの対象をDeploymentからRolloutに変更する
  3. Deploymentのspec.replicasを0にする

以上のステップに切り分けてリリースすることで、移行は完了します。詳しく見ると、以下のようになります。

1. 既存のDeploymentを参照するRolloutリソースを作成して、Podを立ち上げる

移行手順1

まず、Rolloutリソースを用意します。この時点ではまだDeploymentからのPodは立ち上げたままにしているため、DeploymentとRollout両方からPodが立ち上がっている状態です。

Rolloutでは、このようにDeploymentを参照することでDeploymentの設定を読み込むことができます。Deploymentのイメージタグなどが変更された際、Rolloutが変更を検知して、デプロイを行います。

apiVersion: argoproj.io/v1alpha1
kind: Rollout
...
spec:
  workloadRef:
    apiVersion: apps/v1
    kind: Deployment
    name: <name for Deployment>

2. HPAの対象をDeploymentからRolloutに変更する

移行手順2

次に、オートスケールを利用してる場合は、オートスケールの対象をRolloutリソースに変更します。

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
...
spec:
  scaleTargetRef:
    apiVersion: argoproj.io/v1alpha1
    kind: Rollout
    name: <name for Rollout>

3. Deploymentのspec.replicasを0にする

移行手順3

無事RolloutからPodが立ち上がることを確認したら、DeploymentからのPodを落とします。これは、Deploymentのreplicasを0に設定することで実現できます。また、revisionHistoryLimitを0にすることによって、明示的な設定がない場合デフォルトで10個保持されてしまうreplicasetを削除できます。

apiVersion: apps/v1
kind: Deployment
...
spec:
  replicas: 0
  revisionHistoryLimit: 0

今回移行した方法以外にも、以下の方法でもDeploymentからRolloutへの移行はできます。

  1. 新規でRolloutリソースを作成して、Podを立ち上げる。この時点でPodが倍になる。
  2. Deploymentのspec.replicasを0にする。この時点でRolloutに紐づいたPodだけになる。
  3. 問題なければDeploymentリソースを削除する。

どちらの方法を取るかはこの方法だとDeploymentを完全に削除できる一方、Rolloutの中にDeploymentのテンプレートを入れるためRolloutの中身が煩雑になってしまいます。既に動いているDeploymentがあるのであれば、そちらを参照する方がそれぞれのテンプレートがシンプルになり管理しやすいかなと思います。どちらの方法でも移行の手間は変わらないです。

Datadogとの連携と注意点

今回計測システムでは、Datadogと連携させてメトリクスを取得しています。Datadogとの連携はシークレットを登録するだけで可能です。

apiVersion: v1
kind: Secret
metadata:
  name: datadog
type: Opaque
data:
  address: https://api.datadoghq.com
  api-key: <datadog-api-key>
  app-key: <datadog-app-key>

Argo Rollouts内のコードを読むと、Datadogとの連携部分ではメトリクスのAPIを叩いていることがわかります。なので、Datadogのログなどは通常取得できません。しかし、多くの場合ログからエラーなどを取得したいところだと思います。幸いなことにDatadogには、ログのクエリをメトリクスとして保存する機能があります。これを利用すると、Argo RolloutsからもDatadogのログを参照できます。

docs.datadoghq.com

ALBとの連携と注意点

Argo RolloutsはALBとの連携をサポートしているため、今回計測システムで使用しているALB Ingress Controllerとの連携を検討しました。しかし、最終的にトラフィック制御はしないことにしました。

Argo Rolloutsではロードバランサーやサービスメッシュなどと連携せずに、Pod数単位でユーザートラフィックを分散させるという選択肢があります。この場合先述した通り、ユーザーのトラフィックをパーセンテージ単位でリリースバージョンに流すのではなく、純粋にPod数単位での分散となります。例えば新しくリリースするバージョンのPodを1つ、リリース前のバージョンのPodが3つ立てている場合、25%のトラフィックがリリースバージョンに流れることとなります。

本来であればALBと連携させてリリースするバージョンにはユーザートラフィックをパーセンテージ単位で絞って、障害時のユーザー影響を極小化したいところなのですが、1箇所対応できない点がありました。

現状Argo Rolloutsの仕様として、RolloutとALB Ingressは1対1である必要があり、ALB Ingressが複数ある場合は想定されていません。ALB Ingressが複数ある場合、RolloutもALBの数に合わせて作成する必要があります。そうなるとパスごとにデプロイのタイミングを考える必要があり、ロールバックが発生する場合も考慮するとバージョンがパスによって異なるといった問題が起きます。運用上Podの管理が大変になることもあり、今回ALBとの連携は断念しました。

計測システムが複数のALB Ingressを使っているのは、クライアントによって接続するプロトコルが異なり、1つのALB Ingressで複数のプロトコルには紐付けられないためです。具体的には、ネイティブアプリからはgRPC、ZOZOTOWNサーバーからはRESTが使用されており、IPによってリスナーを分けています。また、RESTでの通信も計測システム内のEnvoyでgRPCに変換されるのですが、gRPCを使用している詳しい背景については以下の記事をご参照ください。

techblog.zozo.com

マルチテナントにおける横展開について

マルチテナント構成を取っている場合、Argo Rolloutsを他のプロダクトに導入するのは非常に簡単です。計測システムでは、ZOZOMATやZOZOGLASSを同じクラスタ内で運用するマルチテナント構成をとっています。そのため、ZOZOMATに次いでZOZOGLASSでもArgo Rolloutsを導入しましたが、少ない工数で横展開できました。

マルチテナント構成に関する詳しい背景などは、こちらの記事をご参照ください。

techblog.zozo.com

1つ目のプロダクトに導入する際は、事前にArgo Rolloutsリソース自体のインストールやSlack通知の設定、Datadogなど連携するツールの設定などを行う必要があります。ですが、2つ目以降は、構成が変わらない場合RolloutとAnalysisTemplateのみの追加で導入が可能です。ここはマルチテナント構成を取っている弊チームでは大きな利点となりました。

まとめ

Kubernetes上でカナリアリリースを実現するために、Argo Rolloutsは有用で楽に導入ができるツールでした。

カナリアリリースができるようになり、弊チームではより安全に定期リリースを行えるようなり、リリース担当者の負担も軽くなりました。

本記事がArgo Rolloutsの導入やカナリアリリースを検討している方への手助けになれば幸いです。

終わりに

計測プラットフォーム開発本部では、今夏に新しいサービスであるZOZOFITをUSにてローンチしました。更にスピード感を持った開発が求められますが、このような課題に対して楽しんで取り組み、サービスを一緒に盛り上げていける方を募集しています。少しでもご興味のある方は以下のリンクからぜひご応募ください。

hrmos.co

カテゴリー