Argo Rollouts で Blue-Green Deployment

こんにちは。CADDi SWE の飯迫です。この記事は キャディ Advent Calendar 2020 の3日目です。昨日は、村上さんによる「良いチーム開発」を実現するための「組織的学習」と「自己組織化」の話でした! 今Q は Rust で Backend を書いているのですが、前Q は主に DevOps 周りの改善をしていました。そのときにやったことのひとつ「Argo Rollouts を使った Blue-Green Deployment 移行」について紹介します。

背景と概要

まず前提として、弊社の生産管理システムは GKE 上で運用しており、大まかな Tech Stack はこのようになっています。

https://caddi.tech/tech_stack

上記の Frontend/BFF/Backend は、今回移行するまでは、Deployment リソースとして管理されていました。これらは RollingUpdate でデプロイしていたため、互換性のない変更をしたとき、新旧 Pod の混在によりそこそこエラーが出てしまう問題がありました。そのため、リリースの作業時は Staging 環境で動作確認後ユーザーへ事前告知し、30 分後に本番デプロイ開始するというフローになっていました。つまり、リリースリードタイムが 30 分無駄に長くなってしまっており、これを改善するために BlueGreen Deployment ができるようにしました。

Argo Rollouts とは

Argo Rollouts is a Kubernetes controller and set of CRDs which provide advanced deployment capabilities such as blue-green, canary, canary analysis, experimentation, and progressive delivery features to Kubernetes.

https://argoproj.github.io/argo-rollouts/

Argo CD で有名な Argo ファミリーのひとつで、 k8s 標準ではできない Blue-Green や Canary など高度なデプロイが簡単にできるようになります。特に、

  • Deployment リソースとほぼ使用感が変わらない
  • GitOps フレンドリー

というところが、一般的な k8s オペレーションと親和性が高く、導入を容易にしています。また、Blue-Green Deployment は、Service と Deployment を 2 セット用意し、Service から Deployment への向き先を変えることで実現されています。これ自体はシンプルなので自前で用意することも可能ですが、Argo Rollouts では不要になったリソースの破棄や昇格条件などの細かい設定が可能となっています。

v1 でアプリケーションが動いていると仮定して、v2 をデプロイするとまずこうなります。

自動または手動で v2 が昇格する(Active Service からの向き先が変わる)ことで、混在状態がなくデプロイでき、不要になった v1 は自動的に破棄されます。

Argo Rollouts への移行の流れ

おおまかな移行の流れは下記の通りです。

  • k8s に Helm で argo-rollouts を追加
  • Service を preview 用 と active 用で2つ追加
    • preview 用:昇格(入れ替え)前の Rollout(ReplicaSet)を参照するために利用
    • active 用:実際に各アプリケーションから参照する(既存 Service を置き換えるもの)
  • 既存の Deployment を Rollout に書き換え
    • 詳細は後述
  • 上記の Service と Rollout をデプロイ
  • Service の参照元の向き先を新しい方に変更
  • 不要になったリソース(参照されなくなった Service と Deployment)を削除

注意点として、Rollout の初回デプロイ時に参照元の向き先も変更していると、Rolloutが生成されるまでの間ダウンタイムが発生してしまいます。向き先はあとで変更しましょう。

Deployment から Rollout への書き換え例

Before

apiVersion: apps/v1
kind: Deployment
~~ 中略 ~~
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 100%
      maxUnavailable: 50%

After

apiVersion: argoproj.io/v1alpha1
kind: Rollout
~~ 中略 ~~
  strategy:
    blueGreen:
      # Name of the service that the rollout modifies as the active service.
      activeService: sample-service-active
      # Name of the service that the rollout modifies as the preview service. +optional
      previewService: sample-service-preview
      # The number of replicas to run under the preview service before the switchover. Once the rollout is resumed the new replicaset will be full scaled up before the switch occurs +optional
      previewReplicaCount: 1
      # Indicates if the rollout should automatically promote the new ReplicaSet to the active service or enter a paused state. If not specified, the default value is true. +optional
      autoPromotionEnabled: true
      # Adds a delay before scaling down the previous replicaset. If omitted, the Rollout waits 30 seconds before scaling down the previous ReplicaSet. A minimum of 30 seconds is recommended to ensure IP table propagation across the nodes in a cluster. See https://github.com/argoproj/argo-rollouts/issues/19#issuecomment-476329960 for more information
      scaleDownDelaySeconds: 30

詳しくは公式資料に書いてありますが、Blue-Green strategy 設定はこのようになっています。

  • autoPromotionEnabled
    • 自動昇格フラグ
    • off のときは kubectrl rollouts promote [rollout name] で手動昇格できます
  • previewReplicaCount
    • Preview Rollout(ReplicaSet)のレプリカ数
    • 1 にしておけばリソースの節約になります
    • 昇格の直前に自動的にスケールしてくれます
  • scaleDownDelaySeconds

Argo Rollouts コマンド

実際そんなに使う機会はないのですが、Argo Rollouts Plugin のコマンドをいくつか紹介します。

準備

一覧の参照

% kubectl get rollouts
NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE
sample-rollout   1         1         1            1

特定 Rollout の状態取得

% kubectl argo rollouts get ro sample-rollout
Name:            sample-rollout
Namespace:       default
Status:          ✔ Healthy
Strategy:        BlueGreen
Images:          gcr.io/sample-dev/sample-image:v1.0.26 (active)
Replicas:
  Desired:       1
  Current:       1
  Updated:       1
  Ready:         1
  Available:     1

NAME                                            KIND        STATUS        AGE    INFO
⟳ sample-rollout                            Rollout     ✔ Healthy     26h
├──# revision:14
│  └──⧉ sample-rollout-5bfcfbd578           ReplicaSet  ✔ Healthy     130m   active
│     └──□ sample-rollout-5bfcfbd578-h6dh5  Pod         ✔ Running     130m   ready:2/2
├──# revision:13
│  └──⧉ sample-rollout-845f687b6            ReplicaSet  • ScaledDown  149m

その他

運用小ネタ

昇格したかどうかを確認する

kubectl apply されてから、実際に参照元に反映される(昇格される)まではタイムラグがあります。正確に preview Service や active Service が切り替わったタイミングを外から観測できるようにするため、Datadog logs に SavedView を用意しています。(諸事情あってまだ Argo CD や k8s view tool は入れていません)

time="2020-11-27T04:46:53Z" level=info msg="Switched selector for service 'sample-service-active' to value '764cd777bd'" namespace=sample rollout=sample-bff-rollout

特定 Rollout の再起動

Config の修正だけを反映するため、特定の Rollout を再起動したいときがあります。しかし、kubectl-argo-rollouts restart コマンドは、指定されている strategy (Blue-Green)とは関係なく Pod を再起動するだけです。せっかく Blue-Green Deployment を導入しているので、それを使って再起動相当のこと(つまり、同じ Image Version で新しい Pod 群をつくって入れ替え)をしてほしいのですが、Argo Rollouts コマンドではいまのところできません。ユーザー影響の少ないときは移行前と同様に Pod の再起動をしています。どうしてもダウンタイムが許容できない場面では、ワークアラウンドとして、Rollout の適当な annotations value を書き換えることで通常のフローで Blue-Green Deployment するようにしています。

移行後

移行後は、ユーザーへの価値提供が早くなったと同時に、開発チームが他のことに時間を割きやすくなりました。リリース回数 * 30min * 人数 なので、なかなかコスパのよい改善だったと思います。(もちろん移行前は何もせずに待っていたというわけではないですが)

  • 本番リリースの予告はやめ、事後に自動通知
  • 無駄な待ち時間がなくなり、リリースリードタイムが短縮

わかっている課題

それほど大きなものではありませんが、下記のような課題が残っています。

  • BFF からの gRPC 接続が Cache されているので、Rollout の昇格と同時には切り替わらない
    • 暫定的に scaleDownDelaySeconds を短くすることで強制的に再接続させている
  • preview Service で結合テストすることができない
    • 現状、自動昇格を採用しているが、将来的に手動昇格にして preview Service で事前に動作確認できたほうがよい
    • これをやるためには preview Service 同士をつなぎ合わせる変更やルートの設計が必要
  • 自動昇格の優先順が指定できない
    • Service 間には依存関係があるので、Backend -> BFF -> Frontend の順に昇格させたい

参考

おわりに

あまりにもあっさりできてしまうため、ほとんど書くことがなかったのですが、Argo Rollouts で簡単に Blue-Green Deployment できるということが伝われば幸いです。もし間違いやもっといい方法があれば教えていただけると助かります。また、CADDi では SRE/技術基盤エンジニアも募集しています。まだまだたくさんやるべきことがあるので、少しでも興味がありましたら Twitter からでも採用ページからでも気軽にご連絡ください!

https://caddi-careers.studio.site/jobs-tech-sre

明日の CADDi Advent Calendar は、agate-pris さんによる「Rustを採用している社内プロダクトのリファクタリングをしたときの話」です。