こんにちは。CADDi SWE の飯迫です。この記事は キャディ Advent Calendar 2020 の3日目です。昨日は、村上さんによる「良いチーム開発」を実現するための「組織的学習」と「自己組織化」の話でした! 今Q は Rust で Backend を書いているのですが、前Q は主に DevOps 周りの改善をしていました。そのときにやったことのひとつ「Argo Rollouts を使った Blue-Green Deployment 移行」について紹介します。
背景と概要
まず前提として、弊社の生産管理システムは GKE 上で運用しており、大まかな 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
- 昇格後、不要になった Rollout(ReplicaSet)を破棄するまでの時間
- 最小 30sec 推奨
- Fast Rollback に関係する設定
- https://argoproj.github.io/argo-rollouts/FAQ/#how-does-bluegreen-rollback-work
- 昇格後、不要になった Rollout(ReplicaSet)が残っている状態で、ひとつ前の Rollout manifest を反映するとすぐに戻すことができます
Argo Rollouts コマンド
実際そんなに使う機会はないのですが、Argo Rollouts Plugin のコマンドをいくつか紹介します。
準備
- Kubectl Plugin Installation のインストール
一覧の参照
% 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 argo rollouts undo
が最近追加されたようです
運用小ネタ
昇格したかどうかを確認する
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 で結合テストすることができない
- 自動昇格の優先順が指定できない
- 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を採用している社内プロダクトのリファクタリングをしたときの話」です。