Argo CD & Rollouts を使って freee会計に canary release を導入しました!!

こんにちは、SRE で主に CI/CD 周りを担当している kumashun です。
この記事は freee Developers Advent Calendar 2021 20日目の記事になります。

私は今年11月に開催された、クラウドネイティブに関する国内でも大規模なテックカンファレンスである CloudNative Days Tokyo 2021 にオンライン登壇をしました。(人生初の社外登壇でもありました。)

speakerdeck.com

event.cloudnativedays.jp

登壇内容はタイトルの通りで、freee の中でも最大規模のサービスである freee会計に、Argo CD 及び Argo Rollouts を使って canary release を導入するまでの過程を述べています。コメントやアフタートークなどで反響を頂いたものの、登壇時点では Argo Rollouts の本番導入には至らなかったため、TBD な内容が多かったのが心残りでした。

しかし、一ヶ月後の12月中旬に満を辞して Argo Rollouts が本番環境に導入され、freee では初となる Kubernetes 環境での canary release が実装されました。

チームでの canary release 導入祝勝会の様子
チームでの canary release 導入祝勝会の様子

この記事では、登壇時点の導入方針から本番運用に載せるまでの過程で変更/追加された項目について説明します。なお、Argo Projects の概要や freee の Kubernetes cluster 戦略などは登壇スライドをご参照くださいm

Argo Rollouts の導入手順

導入手順の詳細はスライドにもあるので簡単におさらいすると、

  • kind: Deployment から kind: Rollout への移行
  • kind: Rollout 向けRBAC の追加
  • stable/canary ごとの bug や metrics の出し分け設定

となっています。

kind: Deployment から kind: Rollout への移行

Argo Rollouts 導入前後のインフラ構成
Argo Rollouts 導入前後のインフラ構成

freee では Helm/Helmfile を使った manifest 管理を行っており、サービス共通で利用できる common chart を運用しています。その common chart の values にオプションを渡すことで、Deployment の代わりに Rollout を使えるようにしました。

本番環境では以下の手順を踏むことで、ダウンタイムを発生させることなく移行を完了させています。(Deployment と Rollout が共存する間は、Service に紐付く Endpoints の中には両方の Pod が追加されていたので、常に Pod がリクエストを受けられる状態になっていることが確認できました。)

  1. chart version をあげて Deployment => Rollout にする PR をマージ & prune: false で Sync
  2. Rollout が作成されるが、Deployment もそのまま残る。
  3. Rollout の Pod が全て Running になったことを確認後prune: true で Sync し、Deployment を削除する。

prune は、manifest 上からは削除したが、実態が残っているリソースを削除するかどうかを選択できる Argo CD の Sync オプションです。

kind: Rollout 向けRBAC の追加

登壇時は Argo CD の GUI 上から Rollout に対する操作を行うための RBAC 追加の説明をしましたが、後述する CLI での操作のために以下のような RBAC も追加しました。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: argo-rollouts-clusterrole
rules:
- apiGroups:
  - argoproj.io
  resources:
  - rollouts/status
  verbs:
  - patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: argo-rollouts-crb
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: argo-rollouts-clusterrole
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: hoge-group

stable/canary ごとの bug や metrics の出し分け設定

DataDog での metrics や log の出し分けには、スライド通り Ephemeral Metadata を使用しました。加えて、Deployment で watch していた metrics は、Argo Rollouts controller が提供している metrics を Datadog openmetrics integrationで取得しています。

github.com

Deployment で取得できていた全ての metrics が Rollout でも取得できるわけではないですが、従来の monitoring の観点から大きなデグレは今のところありません。

Bugsnag での bug の出し分けについては、登壇時点では Downward API で環境変数 = Bugsnag 上の stage を書き換える手法を考えていました。しかしこの手順ではcanary release 終了後に再度デプロイを行う必要があり、デプロイ時間が増えてしまうのは避けたいということで、Bugsnag の version を利用する方法にシフトしました。

以下の仕組みによって、canary/stable の Pod ごとに version が分かれるようにしています。

  1. Docker image build の際に、commit hash をファイルに吐き出す。
  2. initializer の中で 1. で作ったファイルの中身を app_version としてセットする。

この仕組みは canary release を導入していないサービスでも、bug とリリースの対応付けが可能になる良い機能追加となりました。

要検証な課題について

TBD となっていた以下の課題についても進捗を共有します。

  • envoy proxy の設定変更
  • kind: Application レベルで canary を進められるか
  • canary をスキップしてデプロイしたい場合の対応

envoy proxy の設定変更

freee の Cluster が外部からのリクエストを受ける routing は ELB -> envoy proxy -> Service -> Pod となっています。service 以下の構成を変更するだけなので、現状の envoy proxy の設定変更は必要ない認識でしたが、要検証となっていました。

実際これは杞憂で、検証環境で問題なくリクエストを受けられることが確認できました。

kind: Application レベルで canary を進められるか

Argo CD の GUI から Rollout の promote (canary step を進める) や abort (canary step を全て rollback する) の操作を行うことは可能ですが、それを一つずつの Rollout に対してポチポチするのは楽ではありません。ましてや abort する際は canary に何か問題があった場合なので、極力少ない工数でできる必要があります。そのため、何らかの方法で、1つの Application が持つ Rollout に対して一括で操作できる方法を考えました。

promote については現状 canary step を2段階 (5% -> 30min経過 -> 自動的に 100%) にしているので、操作自体行う機会は無くなりました。

一方 abort については、kubectl からワンライナーで一括 abort する方針にしています。

canary をスキップしてデプロイしたい場合の対応

登壇時点では common chart に values として渡している canary step を上書きすれば ok としていました。しかしabort 同様、こちらも hotifx など緊急時のオペレーションになることが想定されるので、同一の方針によりワンライナーを用意しました。

ちなみに Argo CD の GUI を、 Custom Resouce を作ることで拡張できる Argo CD extensions という Custom Controller があります。

github.com

これを利用した GUI 拡張のサンプル を参考に、押すと一括 abort や promote-full してくれるボタンを自作することも頑張ればできそう! ...なのですが、現時点ではドキュメントなどがあまり充実していないので、実装はしばらく先になりそうです。

おわりに

今年中にやりきるぞ!! とチーム内でも高いモチベーションで挑み続けた結果、無事大きな障害を出すことなく canary release を本番運用に載せることができました。既にアプリエンジニアからの反響も多く、Datadog から canary release の進行具合を眺めていると達成感が得られました。

request 数から見れる canary release の進行具合。薄い青が stable、濃い青色が canary に対する request を示している。
request 数から見れる canary release の進行具合。 薄い青が stable、濃い青が canary に対する request を示している。

Argo CD に続いて Argo Rollouts の導入に成功したとはいえ、どちらもまだまだ使いこなせていない部分も多いです。ユーザーであるアプリエンジニアにとってより良い CI/CD となるように、改善を続けていきます。同時に、Argo Workflow など他の Argo projects 導入も今後のプロジェクトに盛り込んでいきたいです!

今回の canary release 導入のような Kubernetes 環境での CI/CD 開発に興味のある方を含め、freee ではエンジニア採用を実施中です!!

jobs.freee.co.jp

明日の Advent Calender 担当は @_kemuridama san です!!
freee会計の React 17 化についての記事になるそうです!! お楽しみに〜