Progressive Deliveryについてまとめる

目次

はじめに

Continuous Deliveryの次のステップだと言われているProgressive Deliveryについて

そもそもContinuous Deliveryとは

継続的デリバリとは、新機能、構成変更、バグ修正、実験など、あらゆる種類の変更を、安全かつ迅速に、持続可能な方法で本番環境やユーザーの手元に届ける能力のことです。 私たちの目標は、大規模な分散システム、複雑な本番環境、組み込みシステム、アプリケーションなどのデプロイメントを、必要に応じて実行できる、予測可能な日常業務にすることです。 何千人もの開発者が毎日のように変更を加えても、コードが常にデプロイ可能な状態であることを保証することで、これを実現しています。これにより、従来のように "dev complete "の後に続く統合、テスト、ハードニングのフェーズや、コードのフリーズを完全に排除することができます。 ソフトウェアをより頻繁にデプロイするためには、システムの安定性や信頼性が低くても仕方がないと思われがちです。実際、査読付きの研究によると、そうではないことがわかっています。ハイパフォーマンスのチームは、パフォーマンスの低い競合他社よりも常に速く、信頼性の高いサービスを提供しています。これは、金融サービスや政府機関など、規制の厳しい分野でも同じです。この能力は、それを追求するための努力を惜しまない組織にとって、驚くべき競争上の優位性をもたらします。

  • メリット
    • Low risk releases
    • Faster time to market
    • Higher quality
    • Lower costs
    • Better products
    • Happier teams

引用: Continuous Delivery

デプロイ方法の種類

そもそも代表的なデプロイ方法についてまとめ

Rolling Update

デフォルトでk8sがサポート、名前の通り稼働中のPodを随時新しいPodに置き換えていく
(もう一つはRecreateだが多分基本使わない)

シンプルではあるが、全断なしでPodの更新が行えるので、ツール系や重要度の低いシステムのデプロイはRolling Updateで十分
ただ以下のようなデメリットもあるため、他デプロイ手法と比較すると一部システムにおいてはリスクは高くなる

  • デプロイにかかる時間が長くなる
    • 基本的に1つずつPodを更新するので、Podが大量に存在する場合は完了までに非常に時間がかかる場合がある
    • maxSurge maxUnavailableを変更する事で同時に更新を行うPodの割合を設定することも出来るのである程度の時間短縮は出来る
      • 例えばPodを半分に減らす事を許容したいとして、アップデート中にリクエストが跳ねるなどがあった場合に捌けるのかどうかの確認が別途必要
      • そんなに余裕のある状態で運用できているケースがあるのかもわからないが
  • prod環境での一台目動作確認が出来ない
    • stg環境で動作確認は出来るが、1podだけ反映して動作確認、ログ確認したいなどの要求には答えられない
  • 異なるバージョンが混在する瞬間が発生する
    • 新旧を切り替えて互換性をなくすようなデプロイは行えない
      • 例えば、新しいバージョンで書き込まれたデータが、古いバージョンでは扱えずにエラーとなるような修正を行ってしまうと、Rolling Update完了までの間に新→旧の順でリクエストを受け付けたユーザーはエラーとなってしまう
    • アプリケーション側でハンドリングするなら別
      • Blue/Green Deploymentなら綺麗に切り替えられるがロールバックも難しくなる
      • そもそもアプリケーションでハンドリングしないでこれを行うなら、デプロイ方法をどうするかよりもシステム毎分けるなりした方が良さそう

Blue/Green Deployment

新しいバージョンのアプリケーションが起動する環境を作成し、通信の向き先を新環境に切り替えることでデプロイを行う
API Gatewayなどが前段にあれば、向き先を変えるだけで即座に反映、ロールバックが行える
Rolling Updateと違い、リクエストは受け付けていない新環境が事前に作成されるので、事前に本番環境での動作確認などが行いやすい

主なデメリットとしては

  • 必要なリソース量が多い
    • 単純に2環境分デプロイするためのリソース容量が必要
      • spring bootで構築してるアプリケーションで数十Pod用意しないといけないシステムでブルーグリーンデプロイメントを行う際のリソースの使い具合はなかなかに贅沢
      • Goがマイクロサービスやコンテナ環境で愛用されるのを実感

Canary Release

一部環境orユーザーにだけ新しいバージョンをまず提供し、問題なければ全環境orユーザーに新バージョンを適応する
ローリングアップデートと異なり、最初に一部の環境、リクエストのみ新バージョンを反映する事で、本番環境での確認を可能にしつつ、エラー発生時のリスクを減らすことも出来る
またブルーグリーンデプロイメントのようにリソースを大量に使用する必要もなくなる
ABテストとの違いとして、機能を試したい、調査したいのではなく、リリースに関わるトラブルのリスク軽減が目的

何かツールやサービスメッシュを入れて実現する例もあるが、シンプルにk8sのマニフェストファイルの作り方を工夫する形でも実現できる

  • canary用のdeploymentをアプリケーション用とは別に作る

Progressive Deliveryとは

Progressive DeliveryはContinuous Deliveryの次のステップだとされている
様々なデプロイフローや考え方があるが、Progressive Deliveryは更にもう一歩進んだデプロイメントフローだと言える(はず)

従来のContinuous Deliveryはこんな感じ
https://i.imgur.com/DCchkmz.png 引用: Leveling Up Your CD: Unlocking Progressive Delivery on Kubernetes – Daniel Thomson & Jesse Suen, Intuithttps://static.sched.com/hosted_files/kccncna19/f2/Progressive Delivery %26 Argo Rollouts.pdf

Progressive Deliveryになるとこうなる https://i.imgur.com/4ZOL5EN.png 引用: Leveling Up Your CD: Unlocking Progressive Delivery on Kubernetes – Daniel Thomson & Jesse Suen, Intuit

新しいバージョンをリリースする際には、まずカナリーデプロイを行って、一部のユーザー、リクエストに対して反映を行う。ここまでは一般的なカナリーデプロイと同様

異なる点として、全ユーザー、リクエストに反映する前にデプロイしたアプリケーションに対して、メトリクスなどを用いた分析(analyze)を行い、問題なければデプロイの継続、基準を満たさなければロールバックを実行するアクションが明示的に追加されている

今までのデプロイフローと比べると、Analyze、自動ロールバックが追加されてる点が大きな差分
Progressive Deliveryでは従来のデプロイフローの中に明示的に分析や自動ロールバックという観点を加えることで、パイプラインによるデプロイスピードと、デプロイに伴うリスクの軽減を両立することが期待出来る

Progressive Deliveryの説明として、以下のように紹介されていたり

  • Progressive delivery is the logical next step for teams who have already implemented agile development, scrums, a CI/CD pipeline, and DevOps. It includes many modern software development processes, including canary deployments, A/B testing, and observability.
  • プログレッシブデリバリは、アジャイル開発、スクラム、CI/CDパイプライン、DevOpsをすでに導入しているチームにとって、論理的な次のステップです。カナリーデプロイメント、A/Bテスト、観測性など、多くの最新のソフトウェア開発プロセスが含まれています。

どうやって実現するか

業務で触ったことのあるArgo Rolloutsについて触れる

Argo Rollouts

https://argoproj.github.io/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. Argo Rollouts (optionally) integrates with ingress controllers and service meshes,leveraging their traffic shaping abilities to gradually shift traffic to the new version during an update. Additionally, Rollouts can query and interpret metrics from various providers to verify key KPIs and drive automated promotion or rollback during an update.

Argo Projectsの中の一つであり、Argo RolloutsはBlue/Green Deployment、Canary Release、Progressive Deliveryなどのデプロイ機能をKubernetes上で提供してくれる

Argo Projects → Open source Kubernetes native workflows, events, CI and CD

istioなどのサービスメッシュの導入が不要で、KubernetesのDeploymentをArgo Rolloutsの定義するRolloutリソースに置き換えることで導入が可能なので比較的取り入れやすいのが良い点
さらに、様々なプロバイダー(Prometheusとか)からのメトリクスを用いた検証を組み込んだ上でデプロイフローを構築できるので問題発生時のリカバリも迅速に行える

  • Argo Rolloutsで出来ること
    • Blue/Green Deployment・Canary Deploymentの利用
    • デプロイ状況の分析に応じた継続の判断/自動ロールバック
      • 前述したProgressive DeliveryのAnalyzeの部分
      • Rollout中に利用するAnalysisを指定し、その結果によってデプロイ・リリースを進行したり、中断して自動的にロールバックすることが可能
      • メトリクスや成功/失敗を判定する値を指定してどのような分析をするかを定義したり
      • デプロイ状況の分析に使用できるデータとして現状あるのは以下
        • Prometheus
        • Datadog
        • NewRelic
        • Job
          • Kubernetesジョブ
        • Kayenta
          • GoogleとNetflixが開発したカナリアリリース分析ツール
        • Web
          • HTTPリクエスト
      • Argo Rolloutsは上記のデータを使用してデプロイを継続するか、中断してロールバックすることができる
      • Prometheusを例として上げると、Prometheusからデプロイ対象のingressのhttp_status _codeを用いてHTTP success rateを算出してこの値を閾値として、0.90より上だとデプロイ継続、0.90以下だと中止してロールバックなど
    • Experiments
    • トラフィックコントロール

CI/CDフロー例

  1. DevelopブランチにPR作成
    • devクラスタにPRの内容を反映
  2. Developブランチにマージ
    • dev,stg環境に反映
    • 各環境に対してintegration-testの実行
  3. MasterブランチにPR作成
    • stg環境に対してintegration-testの実行
  4. Masterブランチにマージ → カナリアパイプラインが動作
    • argo-rolloutsを利用してcanaryリリース
      • 全pod数の10%がカナリー対象
    • argo rolloutsのexperimentを用いて、サービスイン前に宣言的に動作確認
    • カナリー環境に対する動作確認は特定のヘッダーを載せることで行う
      • argo-rolloutsのtrafic managementでやる
    • argo rolloutsのanalysisを用いて特定の条件に合致した場合は自動ロールバックが行われる
      • ルール例
        • 5分間の5xxエラーの割合が1%を超えていた場合
        • レイテンシーの悪化
    • コマンドで明示的に次のステップに進めない限り、全台反映は行われない
      • 明らかに全ての障害が補足されるだけの時間でカナリーリリースを行っていると良い(引用: プロダクションレディマイクロサービス)
      • テストや外形監視が機能しているのであれば1hや2hたったら自動反映とかでも良いかも
  5. 全台反映コマンドを実行
    • 全podに修正を反映

CI充実させて、experimentにanalysisやk8sのreadinessProbとか外形監視含めた監視周りとかを充実される事でリスクの高いリリースや定義済の検証内容では担保できないリリース以外は自動でデプロイ完了することも目指せそう

導入手順(メモベースで不正確な可能性があるので別途追記)

namespaceを作成してapply

kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts -f https://raw.githubusercontent.com/argoproj/argo-rollouts/stable/manifests/install.yaml

Argo Rolloutsを操作するためにkubectl argo pluginのインストール

brew install argoproj/tap/kubectl-argo-rollouts
kubectl argo rollouts version

Rolloutリソースの定義

ほとんど通常のDeploymentと同じ構成で定義可能
Rolloutではspec.strategyにcanaryまたはblueGreenを指定することができるようになる

以下canaryの例

apiVersion: argoproj.io/v1alpha1
kind: Rollout
spec:
  strategy:
    canary:
      maxSurge: 1
      maxUnavailable: 1
      steps:
        - setWeight: 1
        - pause: {}
  template:
    # template以下は通常のtemplateを定義する

このようにsetWeightでCanaryに流すトラフィックを指定し、pauseで条件を満たすまで停止させるといった流れを記述する
pauseではdurationを指定して一定期間待機するといったことができるが、今回のように指定しない場合は先に紹介したpluginのコマンドである kubectl argo rollouts promoteを受け付けるまで停止する
setWeight、pauseは複数書けるので例えばsetWeightを指定した後promoteコマンドを受け付けるまで待機し、その後はduration指定により徐々にデプロイを拡大していくといったより細かい設定も可能

apiVersion: argoproj.io/v1alpha1
kind: Rollout
spec:
  strategy:
    canary:
      maxSurge: 1
      maxUnavailable: 1
      steps:
        - setWeight: 1
        - pause: {} # promoteを待機
        - setWeight: 10
        - pause: { duration: 1m } # 1分待機
        - setWeight: 50
        - pause: { duration: 10s } # 10秒待機
  template:
    # template以下は通常のtemplateを定義する

デプロイ

デプロイ方法の詳細は省略。ローカルで試すだけなのでシンプルにkubectl apply
kubectl pluginのgetコマンドで状態を可視化できる

$ kubectl argo rollouts get rollout sample-api --watch
Name:            samle-api
Namespace:       default
Status:          ✔ Healthy
Strategy:        Canary
  Step:          2/2
  SetWeight:     100
  ActualWeight:  100
Images:          sample-api:latest (stable)
Replicas:
  Desired:       2
  Current:       2
  Updated:       2
  Ready:         2
  Available:     2

NAME                                     KIND        STATUS     AGE    INFO
⟳ sample-api                            Rollout     ✔ Healthy  2m53s
└──# revision:1
   └──⧉ sample-api-6968f97dbf           ReplicaSet  ✔ Healthy  2m53s  stable
      ├──□ sample-api-6968f97dbf-97fs7  Pod         ✔ Running  2m53s  ready:4/4
      └──□ sample-api-6968f97dbf-zgbwt  Pod         ✔ Running  2m53s  ready:4/4

イメージタグを変更してみる 

$ kubectl argo rollouts set image sample-api sample-api=sample-api:20210121-072400
rollout "sample-api" image updated

結果

$ kubectl argo rollouts get rollout sample-api --watch                                                                                
Name:            sample-api
Namespace:       default
Status:          ॥ Paused
Message:         CanaryPauseStep
Strategy:        Canary
  Step:          1/2
  SetWeight:     50
  ActualWeight:  50
Images:          sample-api:20210121-072400 (canary)
                 sample-api:latest (stable)
Replicas:
  Desired:       2
  Current:       2
  Updated:       1
  Ready:         2
  Available:     2

NAME                                     KIND        STATUS     AGE    INFO
⟳ sample-api                            Rollout     ॥ Paused   8m35s
├──# revision:2
│  └──⧉ sample-api-56fcdff5bf           ReplicaSet  ✔ Healthy  2m8s   canary
│     └──□ sample-v2-56fcdff5bf-8mhcm  Pod         ✔ Running  2m8s   ready:4/4
└──# revision:1
   └──⧉ sample-api-6968f97dbf           ReplicaSet  ✔ Healthy  8m35s  stable
      └──□ sample-api-6968f97dbf-zgbwt  Pod         ✔ Running  8m35s  ready:4/4

100%デプロイ

kubectl argo rollouts promote sample

Kustomizeを使う場合

まず以下のconfigを設定する必要がある
https://argoproj.github.io/argo-rollouts/features/kustomize/

また加えて、kustomizeはcustom resourceの場合はうまくパッチマージできないらしい
https://github.com/argoproj/argo-rollouts/issues/538 https://github.com/jessesuen/kustomize-examples/tree/master/strategic-merge-patch-crd-fail

なのでjson形式でpatchを書く必要がある
https://github.com/kubernetes-sigs/kustomize/blob/master/examples/jsonpatch.md

patchesJson6902を使う

kind: Kustomization
namespace: sample-api
bases:
- ../../../base
patchesStrategicMerge:
- deployment.yaml
patchesJson6902:
- path: transform_to_rollout.json
  target:
    group: apps
    version: v1
    kind: Deployment
    name: sample-api
[
  {
    "op": "replace",
    "path": "/apiVersion",
    "value": "argoproj.io/v1alpha1"
  },
  {
    "op": "replace",
    "path": "/kind",
    "value": "Rollout"
  },
  {
    "op": "add",
    "path": "/spec/strategy",
    "value": {
      "canary": {
        "steps": [
          {
            "setWeight": 10
          },
          {
            "pause": {}
          }
        ]
      }
    }
  }
]

Analysisを指定してみる

追記

まとめ

目指せオール自動デプロイ

参考

Leveling Up Your CD: Unlocking Progressive Delivery on Kubernetes
Progressive Delivery
Argo Rollouts
What Is Progressive Delivery All About?
GKE + Istio + FlaggerによるProgressive Delivery
Argo Rolloutsに入門する
Argo RolloutsによるProgressive Delivery
Delivering progressive delivery with service mesh
https://devopsinstitute.com/progressive-delivery/
https://www.split.io/glossary/progressive-delivery/