Istio + Argo Rolloutとカナリアリリース
本記事について
Argo Rolloutを導入すると、様々なデプロイ戦略を実践することができる。 ただ少し実際の運用イメージが見えづらかったので整理してみる。
Argo Rolloutについての概要
Argo Rolloutとは通常のk8sのDeploymentに加え、カナリアリリースなどの様々なアップデート戦略をサポートするためのコンポーネント。 Rolloutという、Deploymentに対し、様々なPodのアップデート戦略をつけ加えたCRDを用いる。 Deployment同様に内部ではReplicaSetを管理していて、 アップデートのたびに新しいReplicaSetを作り、設定の通りにPodを移行させる。
カナリアリリース
一部のリクエストを新しいバージョンに流し、なにかあればロールバック、何も無ければ徐々にトラフィックを増やし、最終的に100%に切り替える。 ArgoRolloutでは、Rolloutリソースにどのように移行していくかをステップごとに宣言する。 設定できるのは下記のような項目。
- setWeightでカナリアバージョンの比重を設定する
- pauseでその状態でどのくらい待機するかを定義する
- analysisで同期的な新バージョンのヘルスチェックを挟む
ヘルスチェックについて
B/Gやカナリアの場合、定期的にヘルスチェックを行い、失敗した場合にロールバックすることができる。 実際にはAnalysisTemplateに記述し、Rolloutで指定することで、実行時にAnalysisRunリソースが作られ、 指定したヘルスチェックを実行する。 AnalysisTemplateでは、下記の実行形式をサポートしている。
Prometheusサーバからのメトリクス取得
- 一定時間内の200エラーが95%以上など
- 一定時間内の5xx系エラーが1%以下など
Job Metrics
WebHook
ちなみにAnalysisを定義しない場合はロールバックは自動で行わない。
結局Argo Rolloutを使って担保されるものはなに?
- 自動的な比重の変更と自動的なモニタリング + ロールバック
カナリアリリースの想定実行フロー
- 新バージョンデプロイ => 数%のトラフィックを新バージョンへ流す => 一定時間経って問題なければ自動的に新バージョンをStableバージョンを切り替え
- 新バージョンデプロイ => 数%のトラフィックを新バージョンへ流す => manual approveで新バージョンをStableバージョンへ切り替え
やらないといけないこと
- DeploymentはRolloutに置き換える
- カナリアリリース用のServiceを作成する
- VirtualServiceでカナリアリリースサービスとStableリリース用サービスで比重を0%, 100%に設定しておく
- ヘルスチェック用のリソース定義(AnalysisTemplate)
- Rolloutでstable用Service, canary用Service, virtualService, AnalysisTemplateを指定しておく
挙動FAQ
Podが存在しないときはどうなる?
- Deploymentと同様で、UpdatePolicyによらず、新規のReplicaSetおよびPodが作られる
どのようにWeightをコントロールしている?
Istioと連携するとどうなる?
- ルーティングがリクエストの割合ベースで分散できる
- WeightはArgoRolloutのPodがVirtualServiceの設定を随時書き換えている
カナリア用のServiceの向き先はStableと同じでいいの?
- 良い。Argo Rolloutが随時Serviceのセレクタに条件を付け足している
How to Use
- https://argoproj.github.io/argo-rollouts/getting-started/
- https://argoproj.github.io/argo-rollouts/features/canary/#canaryservice
- https://argoproj.github.io/argo-rollouts/features/analysis/#job-metrics
- https://argoproj.github.io/argo-rollouts/features/traffic-management/istio/
設定例
- stable = v1, canary = v2のケース
- AnalysisTemplateを使って、500エラーが一定期間内に3回でたらNGという定義を行っている
- istioが持つprometheusに対してクエリを投げている
- この設定では、比重を10%にし10分待ち、以降徐々に10分毎に20%ずつ増やす。50%になったら、そのまま待ち手動によるResumeを待つ。
apiVersion: v1 kind: Service metadata: name: reviews labels: app: reviews service: reviews spec: ports: - port: 9080 name: http selector: app: reviews --- apiVersion: v1 kind: Service metadata: name: reviews-canary labels: app: reviews service: reviews spec: ports: - port: 9080 name: http selector: app: reviews --- apiVersion: argoproj.io/v1alpha1 kind: Rollout metadata: name: reviews labels: app: reviews spec: replicas: 1 selector: matchLabels: app: reviews template: metadata: labels: app: reviews version: v2 spec: serviceAccountName: bookinfo-reviews containers: - name: reviews image: docker.io/istio/examples-bookinfo-reviews-v2:1.15.0 volumes: - name: wlp-output emptyDir: {} - name: tmp emptyDir: {} strategy: canary: analysis: templates: - templateName: failure-count args: - name: service-name value: reviews-canary.default.svc.cluster.local stableService: reviews canaryService: reviews-canary trafficRouting: istio: virtualService: name: reviews routes: - primary steps: - setWeight: 10 - pause: { 10m } - setWeight: 30 - pause: { 10m } - setWeight: 50 - pause: { } --- apiVersion: argoproj.io/v1alpha1 kind: AnalysisTemplate metadata: name: failure-count spec: args: - name: service-name metrics: - name: failure-count interval: 30s failureCondition: result >= 3 failureLimit: 3 provider: prometheus: address: "http://prometheus.istio-system.svc.cluster.local:9090" query: | sum(irate( istio_requests_total{reporter="source",destination_service=~"{{args.service-name}}",response_code!~"5.*"}[30s] ))
GitOpsとの兼ね合い
カナリアリリースが完了し、100%新バージョンに移行したあとに同期されたらどうなる?
- 結論: 何も変わらない
- ArgoRolloutがいじった結果とリソースが一緒になる
- StableとCanaryのServiceの向き先が両方共新Podになっていて、VirtualServiceもStableに100%向ける設定になっている
- 結論: 何も変わらない
カナリアリリース実行中に同期されたらどうなる?
- 結論: 一度ルーティングの割合がStable 100%, Canary 0%にリセットされた後、Rolloutによって同期される直前の状態に戻される
カナリアリリース時と通常リリース時でのフローの違い
デバッグ方法
- RolloutとAnalysisRunリソースを見るとデプロイの挙動が分かる
kd rollouts.argoproj.io kd analysisruns.argoproj.io
Analysisの設定は実運用上どうすれば良い?
IstioでPrometheusを有効にしてインストールし、Istioのメトリクスによって判別する
- --set values.prometheus.enabled=trueをつけてistioctl manifest applyすればprometheusが有効になるので、これをつかってメトリクスを取得することはできる
- ただ、素直に入れても可用性は担保されていないので、別途冗長化するなどの可用性対応が必要になってくる
Stackdriverなどにメトリクスを集約している場合、Goなどでデータを引っ張ってきた上で判定する
カナリアリリースした結果失敗してロールバックしたよね、というのはどのように検知すればよいか?
- Argo Rolloutの機能では通知できないので、自前の検証プログラムを使ってAnalysisRunを動かす場合は、プログラムの中にエラーケースでアラートするように記載しておく
- 例えばStackdriverならCloud Monitoringを活用して、ロールバック条件と同じ条件でアラートが飛ぶようにポリシー設定しておく