TECHSTEP

ITインフラ関連の記事を公開してます。

Flaggerに入門する

今回はProgressive Deliveryを実現するツールの一つであるFlaggerを試してみます。なお、Progressive Deliveryについては、以下の参考リンクなどを参照ください。

※参考リンク:

Flaggerとは

Flaggerは Progressive Dlivery Operator for Kubernetes と打ち出されている通り、KubernetesをベースにProgressive Deliveryの実現をサポートするプロダクトです。

Flaggerを利用する環境は、以下のような構成となります。

※画像:Flagger Docより

flagger

Flaggerの実現するのは大きく3つ紹介されており、 Progressive Deliveryによる「より安全なリリース」 サービスメッシュやIngress Controllerによる「柔軟なTraffic Routing」 Progressive Deliveryに用いるメトリックをカスタムすることも可能な「拡張性のあるValidation」 となります。

具体的な機能としては、以下のようなものが挙げられます。

サービスメッシュ・Ingress Controllerと組み合わせたトラフィックコントロール

FlaggerはKubernetes CNIと組み合わせることで、単体でもトラフィックの切り替えを行うことができますが、Istio/Linkerdといったサービスメッシュ、Nginx/ContourなどのIngress Controllerとして機能するプロダクトと組み合わせることで、CanaryリリースやA/Bテストなど、より発展的なデリバリーを実現します。

GitOpsツールとの組み合わせ

FlaggerはGitOpsプロダクトと組み合わせることで、GitOps + Progressive Deliveryというデプロイ・リリースパイプラインを実現します。以前動かしてみたFluxなどと組み合わせることが可能です

4種類のデプロイ・デリバリー戦略に対応

Flaggerは Canary というCustom Resourceの設定を変更することで、4種類のデプロイ・デリバリー戦略に対応しています。

  • Canary Release: HTTPリクエスト成功率などの重要な指標(KPI、Key Performace Indicator)を計測しつつ、異なるバージョンのアプリケーションへのトラフィック量を少しずつ変更します。
  • A/B Testing: Session Affinity (Sticky Session)を要求するようなフロントエンドのアプリケーションに対応するため、Canary ReleaseにHTTPヘッダーやCookieの条件一致を加えることで、同じユーザーが同一のバージョンにアクセスし続けるようトラフィックを制御し、A/Bテストを実現します。
  • Blue/Green: KPIなどをベースに新しいバージョンのアプリケーションに問題がないと判断できたら、一気にトラフィックを新しいバージョンのほうへ切り替えます。
  • Blue/Green with Traffic Mirroring: Canary ReleaseやBlue/Greenの前段階として利用します。インバウンドな通信をコピーして、一方は現在稼働中のServiceに、もう一方をCanary Serviceへ転送します。Canary Serviceからのレスポンスは破棄しますが、メトリックは収集し、Canaryのほうが問題ない状態かを確認することができます。

※参考リンク:Flagger Doc - Deployment Strategies

モニタリングツールへのクエリ

Flaggerはメトリックをベースにして、可用性やエラー率などのSLOを評価します。Flaggerは HTTPリクエスト成功率Duration を組み込みのメトリックとして持ちますが、カスタムすることも可能です。メトリックのターゲットにはPrometheusやDataDogを利用できます。

※参考リンク:Flagger Doc - Metrics Analysis

WebhookによるAnalysisの拡張

Flaggerでは、アプリケーション切替の前後に行うテスト(Analysis)を、Webhookによって拡張することができます。Flaggerでは8種類のHookが用意されており、そのレスポンスコードによって切り替えが発生します。またFlaggerではWebhhokの設定でコマンドを指定することで、切り替え前後に負荷テストなどを実施できます。

※参考リンク:Flagger Doc - Webhooks

通知サービスへのアラート

FlaggerはSlackやMicrosoft Teamsなどの各種チャットツールへアラートを送信することができます。送信先のプロバイダーは AlertProvider というCustom Resourceで定義します。

※参考リンク:Flagger Doc - Alerting

Flaggerを動かしてみる

ここから実際にFlaggerを動かします。Flaggerのドキュメントには、各デプロイ戦略とツールとの組み合わせごとにチュートリアルが用意されています。今回はIstioをベースに2種類のデプロイ戦略をなぞってみます。Istioについては、以前の投稿などをご参照ください。環境の用意はこちらのドキュメントをベースにしています。

※参考リンク:

前提条件

Flaggerを利用する前提条件は以下の通りです。

  • Kubernetes version: 1.16 以上
  • istio version: 1.5 以上

今回の検証環境は以下の通りです。

# eksctl version
$ eksctl version
0.57.0

# Kubernetes / kubectl version
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"20", GitVersion:"v1.20.0", GitCommit:"af46c47ce925f4c4ad5cc8d1fca46c7b77d13b38", GitTreeState:"clean", BuildDate:"2020-12-08T17:59:43Z", GoVersion:"go1.15.5", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"20+", GitVersion:"v1.20.7-eks-8be107", GitCommit:"8be107e517a77bde856aced857f3428e4f344969", GitTreeState:"clean", BuildDate:"2021-06-12T03:14:13Z", GoVersion:"go1.15.12", Compiler:"gc", Platform:"linux/amd64"}

# Istio version
$ istioctl version
no running Istio pods in "istio-system"
1.10.3

Istio / Prometheus / Flaggerのデプロイ

まずはIstioのデプロイを行います。Istioは istioctl コマンドからデプロイを行います。今回使用しているProfileはこちらから確認できます。なお、Istioインストールの際、istiodに要求されるメモリ量が多いため、nodeのスペックは十分大きいものを指定したほうが良いでしょう。今回はEC2インスタンスのsペックは t3.large を指定しています。

$ istioctl manifest install --set profile=default
This will install the Istio 1.10.3 default profile with ["Istio core" "Istiod" "Ingress gateways"] components into the cluster. Proceed? (y/N) y
✔ Istio core installed                                                                                                 
✔ Istiod installed                                                                                                     
✔ Ingress gateways installed                                                                                           
✔ Installation complete     


# デプロイ後の確認
$ kubectl get pods -n istio-system
NAME                                   READY   STATUS    RESTARTS   AGE
istio-ingressgateway-c4d8648bc-pkm72   1/1     Running   0          59s
istiod-7bd65bd549-76xgz                1/1     Running   0          71s


$ kubectl get svc -n istio-system
NAME                   TYPE           CLUSTER-IP       EXTERNAL-IP                                                                    PORT(S)                                      AGE
istio-ingressgateway   LoadBalancer   10.100.230.248   a50db11ad30db428b926a77884d22fa1-1849188677.ap-northeast-1.elb.amazonaws.com   15021:30212/TCP,80:31556/TCP,443:32089/TCP   96s
istiod                 ClusterIP      10.100.89.234    <none>                                                                         15010/TCP,15012/TCP,443/TCP,15014/TCP        108s

次にPrometheusをデプロイします。

$ kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.10/samples/addons/prometheus.yaml
serviceaccount/prometheus created
configmap/prometheus created
clusterrole.rbac.authorization.k8s.io/prometheus created
clusterrolebinding.rbac.authorization.k8s.io/prometheus created
service/prometheus created
deployment.apps/prometheus created


# デプロイ後の確認
$ kubectl get pods -n istio-system
NAME                                   READY   STATUS    RESTARTS   AGE
istio-ingressgateway-c4d8648bc-pkm72   1/1     Running   0          3m19s
istiod-7bd65bd549-76xgz                1/1     Running   0          3m31s
prometheus-69f7f4d689-bz2z5            2/2     Running   0          15s

$ kubectl get svc -n istio-system
NAME                   TYPE           CLUSTER-IP       EXTERNAL-IP                                                                    PORT(S)                                      AGE
istio-ingressgateway   LoadBalancer   10.100.230.248   a50db11ad30db428b926a77884d22fa1-1849188677.ap-northeast-1.elb.amazonaws.com   15021:30212/TCP,80:31556/TCP,443:32089/TCP   3m32s
istiod                 ClusterIP      10.100.89.234    <none>                                                                         15010/TCP,15012/TCP,443/TCP,15014/TCP        3m44s
prometheus             ClusterIP      10.100.238.59    <none>                                                                         9090/TCP                                     28s

最後にFlaggerをデプロイします。

$ kubectl apply -k github.com/fluxcd/flagger//kustomize/istio
customresourcedefinition.apiextensions.k8s.io/alertproviders.flagger.app created
customresourcedefinition.apiextensions.k8s.io/canaries.flagger.app created
customresourcedefinition.apiextensions.k8s.io/metrictemplates.flagger.app created
serviceaccount/flagger created
clusterrole.rbac.authorization.k8s.io/flagger created
clusterrolebinding.rbac.authorization.k8s.io/flagger created
deployment.apps/flagger created


# デプロイ後の確認
$ kubectl get pods -n istio-system
NAME                                   READY   STATUS    RESTARTS   AGE
flagger-5cf787b9c8-jgdtv               1/1     Running   0          44s
istio-ingressgateway-c4d8648bc-pkm72   1/1     Running   0          6m15s
istiod-7bd65bd549-76xgz                1/1     Running   0          6m27s
prometheus-69f7f4d689-bz2z5            2/2     Running   0          3m11s

Canary Deployment

まずはCanary Deploymentを試します。手順はこちらのドキュメントに記載されています。

前準備

まず使用するデモ用アプリが、サービスメッシュの外からアクセスできるよう Gateway リソースをデプロイします。


ingress-gateway.yaml

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: public-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - "*"


$ kubectl apply -f ingress-gateway.yaml
gateway.networking.istio.io/public-gateway created


# デプロイ後の確認
$ kubectl get gateway -n istio-system
NAME             AGE
public-gateway   9s

次にテスト用アプリを起動するNamespaceの作成と、IstioによるSidecar Proxyの注入を行うためのラベリングを行います。

$ kubectl create ns test
namespace/test created

$ kubectl label namespace test istio-injection=enabled
namespace/test labeled

# デプロイ後の確認
$ kubectl get ns --show-labels
NAME              STATUS   AGE   LABELS
default           Active   28m   <none>
istio-system      Active   13m   <none>
kube-node-lease   Active   28m   <none>
kube-public       Active   28m   <none>
kube-system       Active   28m   <none>
test              Active   82s   istio-injection=enabled

次にテスト用のアプリとHPAをデプロイします。なお、今回の手順ではHPAは特に利用しないため、 metrics-server のインストールは実施しません。そのためHPAのターゲットも Unknown 状態となります。

$ kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=main
deployment.apps/podinfo created
horizontalpodautoscaler.autoscaling/podinfo created


# デプロイ後の確認
$ kubectl get pods -n test
NAME                      READY   STATUS    RESTARTS   AGE
podinfo-99dc84b6f-bmtvv   2/2     Running   0          56s
podinfo-99dc84b6f-l5pk8   2/2     Running   0          71s

$ kubectl get hpa -n test
NAME      REFERENCE            TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
podinfo   Deployment/podinfo   <unknown>/99%   2         4         2          98s

また、Progressive DeliveryでのAnalysisで利用する loadtester Podも合わせてデプロイします。この loadtesterrakyll/hey / bojand/ghz という2つの負荷テストツールをベースとしています。

$ kubectl apply -k https://github.com/fluxcd/flagger//kustomize/tester?ref=main
service/flagger-loadtester created
deployment.apps/flagger-loadtester created


# デプロイ後の確認
$ kubectl get pods -n test
NAME                                  READY   STATUS    RESTARTS   AGE
flagger-loadtester-64695f854f-6mkgj   2/2     Running   0          29s
podinfo-99dc84b6f-bmtvv               2/2     Running   0          4m25s
podinfo-99dc84b6f-l5pk8               2/2     Running   0          4m40s

$ kubectl get svc -n test
NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
flagger-loadtester   ClusterIP   10.100.21.132   <none>        80/TCP    25s

Canary リソースのデプロイ

Flaggerは Canary というCustom Resourceを利用し、既存のDeploymentに対して、指定したデプロイ戦略を実行します。どのデプロイ戦略を利用するかは、 Canary 中に指定したパラメータによって変更します。

今回利用する Canaryマニフェストファイルは以下の通りです。少しだけパラメータの説明を付け加えます。

  • spec.targetRef: ターゲットとするリソース種別を指定。 Deployment DaemonSet のいずれかを指定可能
  • spec.progressDeadlineSeconds: Canary Deployment実行時、ロールバック前に進行する際の最大秒数。 kubectl get canary 実行時にCanary Deploymentの進行を表示する間隔。
  • spec.analysis: Progressive DeliveryのAnalysisを設定する部分。
    • interval: analysisの実行間隔
    • threshold: ロールバックを実行する基準となる、analysisの失敗回数
    • maxWeight: Canaryへ転送する最大のトラフィック転送割合
    • stepWeight: Canaryへの転送量を1回あたりに増やす割合。
    • metrics: メトリックの設定箇所
      • thresholdRange: request-success-rate の場合はリクエスト成功率の最低値、 request-duration の場合はリクエストのP99 duration最大ミリセカンド値を指定
    • webhooks: Webhookの設定

podinfo-canary.yaml

apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: podinfo
  namespace: test
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: podinfo
  progressDeadlineSeconds: 60
  autoscalerRef:
    apiVersion: autoscaling/v2beta2
    kind: HorizontalPodAutoscaler
    name: podinfo
  service:
    port: 9898
    targetPort: 9898
    gateways:
    - public-gateway.istio-system.svc.cluster.local
    hosts:
    - app.example.com
    trafficPolicy:
      tls:
        mode: DISABLE
    retries:
      attempts: 3
      perTryTimeout: 1s
      retryOn: "gateway-error,connect-failure,refused-stream"
  analysis:
    interval: 1m
    threshold: 5
    maxWeight: 50
    stepWeight: 10
    metrics:
    - name: request-success-rate
      thresholdRange:
        min: 99
      interval: 1m
    - name: request-duration
      thresholdRange:
        max: 500
      interval: 30s
    webhooks:
      - name: acceptance-test
        type: pre-rollout
        url: http://flagger-loadtester.test/
        timeout: 30s
        metadata:
          type: bash
          cmd: "curl -sd 'test' http://podinfo-canary:9898/token | grep token"
      - name: load-test
        url: http://flagger-loadtester.test/
        timeout: 5s
        metadata:
          cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/"


上記マニフェストファイルをデプロイすると、先ほど作成した podinfo Podを置き換える形で podinfo-primary というPodが作成されます。またPod以外にも以下のリソースが作成されます。

  • Deployment / Pod: podinfo-primary
  • HPA: podinfo-primary
  • Service: podinfo podinfo-canary podinfo-primary
  • Destination Rule: podinfo-canary podinfo-primary
  • Virtual Service: podinfo
$ kubectl apply -f podinfo-canary.yaml
canary.flagger.app/podinfo created


# デプロイ後の確認
$ kubectl get canary -n test
NAME      STATUS        WEIGHT   LASTTRANSITIONTIME
podinfo   Initialized   0        2021-07-21T10:02:46Z

$ kubectl get pods -n test
NAME                                  READY   STATUS    RESTARTS   AGE
flagger-loadtester-64695f854f-6mkgj   2/2     Running   0          30m
podinfo-primary-657f8c97dd-28lbv      2/2     Running   0          2m31s
podinfo-primary-657f8c97dd-z7sx6      2/2     Running   0          2m31s

$ kubectl get hpa -n test
NAME              REFERENCE                    TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
podinfo           Deployment/podinfo           <unknown>/99%   2         4         0          34m
podinfo-primary   Deployment/podinfo-primary   <unknown>/99%   2         4         2          70s

$ kubectl get service -n test
NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
flagger-loadtester   ClusterIP   10.100.21.132    <none>        80/TCP     29m
podinfo              ClusterIP   10.100.172.155   <none>        9898/TCP   9s
podinfo-canary       ClusterIP   10.100.249.86    <none>        9898/TCP   69s
podinfo-primary      ClusterIP   10.100.129.195   <none>        9898/TCP   69s

$ kubectl get destinationrule -n test
NAME              HOST              AGE
podinfo-canary    podinfo-canary    57s
podinfo-primary   podinfo-primary   57s

$ kubectl get virtualservice -n test
NAME      GATEWAYS                                            HOSTS                           AGE
podinfo   ["public-gateway.istio-system.svc.cluster.local"]   ["app.example.com","podinfo"]   43s

またこの時 Canary リソースのログを見ると、Initializationのプロセスが確認できます。

$ kubectl describe canary podinfo -n test

(中略)

Events:
  Type     Reason  Age                    From     Message
  ----     ------  ----                   ----     -------
  Warning  Synced  4m41s                  flagger  podinfo-primary.test not ready: waiting for rollout to finish: observed deployment generation less then desired generation
  Normal   Synced  3m41s (x2 over 4m41s)  flagger  all the metrics providers are available!
  Normal   Synced  3m41s                  flagger  Initialization done! podinfo.test

これでProgressive Deliveryを実行する準備ができました。

アプリケーションのアップデート

ここからCanary Deploymentを実行します。デプロイした podinfoのイメージを更新することでアップデートを開始すると、Analysisを実行した後にPodのアップデートを行います。

Canary Deploymentの場合は以下のような構成となります。

※画像: Flagger Docより

canary-deployment

Flaggerでは Primary Canary という2種類のPodを利用します。Canary PodはAnalysisを実行する対象のPodで、アップデート中に作成し、完了後に削除されます。 Primary Podは実際にトラフィックを流す対象のPodで、Analysisをクリアすると Primary Podをバージョンアップすることで、アップデートが完了します。

Canary Deployment実行時のFlaggerの処理は、以下のように進行します。基本的な流れはCanary DeploymentもBlue/Green Deploymentも共通です。

  • Progressing フェーズ: Analysisの実行
    • 新しいイメージバージョンのCanary Podを作成
    • Canary Podへトラフィックを転送する。この時メトリックも収集し、評価する
    • Canary Podへの転送量を maxWeight まで増加する
  • Promoting フェーズ: Analysisの完了後、Primaryのスペックを更新
    • 新しいイメージタグのPrimary Podを作成する(Secret等のリソースがある場合はそれも複製する)
    • 古いPrimary Podを削除する
  • Finalising フェーズ: トラフィックをPrimary側へ切り替える
  • Succeeded フェーズ: Analysisとアップデートの完了
    • Canary Podを削除する

※参考リンク:GitHub - flux/flagger: status.go

アップデート前のイメージタグは 3.1.0 です。

$ kubectl describe pod podinfo-primary-657f8c97dd-28lbv -n test | grep 'Image:'
    Image:         docker.io/istio/proxyv2:1.10.3
    Image:         stefanprodan/podinfo:3.1.0
    Image:         docker.io/istio/proxyv2:1.10.3

アップデート実行前に、PodとCanaryのウォッチを開始しておきます。

# watch
$ kubectl get canary -n test -w
$ kubectl get pods -n test -w

# イメージアップデート
kubectl -n test set image deployment/podinfo podinfod=stefanprodan/podinfo:3.1.1

ここからフェーズごとの推移を載せておきます。

Progressing

# Progressing
## Weightが徐々に増加する
$ kubectl get canary -n test -w
NAME      STATUS        WEIGHT   LASTTRANSITIONTIME
podinfo   Initialized   0        2021-07-21T10:02:46Z
podinfo   Progressing   0        2021-07-21T10:13:46Z
podinfo   Progressing   10       2021-07-21T10:14:46Z
podinfo   Progressing   20       2021-07-21T10:15:46Z
podinfo   Progressing   30       2021-07-21T10:16:46Z
podinfo   Progressing   40       2021-07-21T10:17:46Z
podinfo   Progressing   50       2021-07-21T10:18:46Z


## Canary Podが作成される
$ kubectl get pods -n test -w
NAME                                  READY   STATUS    RESTARTS   AGE
flagger-loadtester-64695f854f-6mkgj   2/2     Running   0          39m
podinfo-primary-657f8c97dd-28lbv      2/2     Running   0          11m
podinfo-primary-657f8c97dd-z7sx6      2/2     Running   0          11m


podinfo-5f8fd4f546-vtjvl              0/2     Pending   0          0s
podinfo-5f8fd4f546-vtjvl              0/2     Pending   0          0s
podinfo-5f8fd4f546-vtjvl              0/2     Init:0/1   0          0s
podinfo-5f8fd4f546-vtjvl              0/2     PodInitializing   0          1s
podinfo-5f8fd4f546-6mqz8              0/2     Pending           0          0s
podinfo-5f8fd4f546-6mqz8              0/2     Pending           0          0s
podinfo-5f8fd4f546-6mqz8              0/2     Init:0/1          0          0s
podinfo-5f8fd4f546-6mqz8              0/2     Init:0/1          0          1s
podinfo-5f8fd4f546-6mqz8              0/2     PodInitializing   0          2s
podinfo-5f8fd4f546-vtjvl              0/2     Running           0          8s
podinfo-5f8fd4f546-vtjvl              1/2     Running           0          9s
podinfo-5f8fd4f546-6mqz8              0/2     Running           0          7s
podinfo-5f8fd4f546-6mqz8              1/2     Running           0          9s
podinfo-5f8fd4f546-vtjvl              2/2     Running           0          16s
podinfo-5f8fd4f546-6mqz8              2/2     Running           0          14s

Promoting

# Promoting
NAME      STATUS        WEIGHT   LASTTRANSITIONTIME
podinfo   Promoting     0        2021-07-21T10:19:46Z


## Primary Podを更新
NAME                                  READY   STATUS    RESTARTS   AGE
podinfo-primary-657f8c97dd-z7sx6      2/2     Terminating       0          18m
podinfo-primary-76655b74b9-cvs9z      0/2     Pending           0          0s
podinfo-primary-76655b74b9-cvs9z      0/2     Pending           0          0s
podinfo-primary-76655b74b9-cvs9z      0/2     Init:0/1          0          0s
podinfo-primary-76655b74b9-b99lq      0/2     Pending           0          0s
podinfo-primary-76655b74b9-b99lq      0/2     Pending           0          0s
podinfo-primary-76655b74b9-b99lq      0/2     Init:0/1          0          0s
podinfo-primary-76655b74b9-cvs9z      0/2     PodInitializing   0          2s
podinfo-primary-76655b74b9-b99lq      0/2     PodInitializing   0          3s
podinfo-primary-76655b74b9-cvs9z      0/2     Running           0          3s
podinfo-primary-76655b74b9-b99lq      0/2     Running           0          4s
podinfo-primary-76655b74b9-cvs9z      1/2     Running           0          4s
podinfo-primary-76655b74b9-b99lq      1/2     Running           0          5s
podinfo-primary-657f8c97dd-z7sx6      0/2     Terminating       0          18m
podinfo-primary-657f8c97dd-z7sx6      0/2     Terminating       0          18m
podinfo-primary-657f8c97dd-z7sx6      0/2     Terminating       0          18m
podinfo-primary-76655b74b9-cvs9z      2/2     Running           0          8s
podinfo-primary-76655b74b9-b99lq      2/2     Running           0          13s
podinfo-primary-657f8c97dd-28lbv      2/2     Terminating       0          18m
podinfo-primary-657f8c97dd-28lbv      0/2     Terminating       0          18m
podinfo-primary-657f8c97dd-28lbv      0/2     Terminating       0          18m
podinfo-primary-657f8c97dd-28lbv      0/2     Terminating       0          18m

Finalising Succeeded

# Finalising
NAME      STATUS        WEIGHT   LASTTRANSITIONTIME
podinfo   Finalising    0        2021-07-21T10:20:46Z

# Succeeded
NAME      STATUS        WEIGHT   LASTTRANSITIONTIME
podinfo   Succeeded     0        2021-07-21T10:21:46Z


## Canary Podの削除
NAME                                  READY   STATUS    RESTARTS   AGE
podinfo-5f8fd4f546-6mqz8              2/2     Terminating       0          7m54s
podinfo-5f8fd4f546-vtjvl              2/2     Terminating       0          8m
podinfo-5f8fd4f546-6mqz8              0/2     Terminating       0          8m
podinfo-5f8fd4f546-vtjvl              0/2     Terminating       0          8m6s
podinfo-5f8fd4f546-vtjvl              0/2     Terminating       0          8m7s
podinfo-5f8fd4f546-vtjvl              0/2     Terminating       0          8m7s
podinfo-5f8fd4f546-6mqz8              0/2     Terminating       0          8m7s
podinfo-5f8fd4f546-6mqz8              0/2     Terminating       0          8m7s

最終的には以下のような状態になります。

$ kubectl get canary -n test
NAME      STATUS      WEIGHT   LASTTRANSITIONTIME
podinfo   Succeeded   0        2021-07-21T10:21:46Z

$ kubectl get pods -n test
NAME                                  READY   STATUS    RESTARTS   AGE
flagger-loadtester-64695f854f-6mkgj   2/2     Running   0          52m
podinfo-primary-76655b74b9-b99lq      2/2     Running   0          6m32s
podinfo-primary-76655b74b9-cvs9z      2/2     Running   0          6m32s

podinfo Canaryのログは以下の通りです。

$ kubectl describe canary -n test podinfo

(中略)

Events:
  Type     Reason  Age                From     Message
  ----     ------  ----               ----     -------
  Warning  Synced  47m                flagger  podinfo-primary.test not ready: waiting for rollout to finish: observed deployment generation less then desired generation
  Normal   Synced  46m (x2 over 47m)  flagger  all the metrics providers are available!
  Normal   Synced  46m                flagger  Initialization done! podinfo.test
  Normal   Synced  35m                flagger  New revision detected! Scaling up podinfo.test
  Normal   Synced  34m                flagger  Starting canary analysis for podinfo.test
  Normal   Synced  34m                flagger  Pre-rollout check acceptance-test passed
  Normal   Synced  34m                flagger  Advance podinfo.test canary weight 10
  Normal   Synced  33m                flagger  Advance podinfo.test canary weight 20
  Normal   Synced  32m                flagger  Advance podinfo.test canary weight 30
  Normal   Synced  31m                flagger  Advance podinfo.test canary weight 40
  Normal   Synced  30m                flagger  Advance podinfo.test canary weight 50
  Normal   Synced  29m                flagger  Copying podinfo.test template spec to podinfo-primary.test
  Normal   Synced  27m (x2 over 28m)  flagger  (combined from similar events): Promotion completed! Scaling down podinfo.test

ロールバック

次にロールバックの場合を見てみます。先ほどと同様コンテナイメージを更新後、 flagger-loadtester から特定のエンドポイント (http://podinfo-canary:9898/status/500) を叩き、エラーを発生させます。その結果、Canary Deploymentは失敗・中断され、Canary Podが削除されます。

# watch
$ kubectl get canary -n test -w
$ kubectl get pods -n test -w


# コンテナイメージの更新
$ kubectl -n test set image deployment/podinfo podinfod=stefanprodan/podinfo:3.1.2

flagger-loadtester からcurlを実行します。

$ kubectl get pods -n test
NAME                                  READY   STATUS    RESTARTS   AGE
flagger-loadtester-64695f854f-6mkgj   2/2     Running   0          85m
podinfo-75459749c9-8w9tl              2/2     Running   0          3m47s
podinfo-75459749c9-mqd7m              2/2     Running   0          3m51s
podinfo-primary-76655b74b9-b99lq      2/2     Running   0          39m
podinfo-primary-76655b74b9-cvs9z      2/2     Running   0          39m


# エラーを発生させる
$ kubectl exec -it flagger-loadtester-64695f854f-6mkgj -n test sh
/home/app $ watch curl http://podinfo-canary:9898/status/500

Deployment Failed

# Canary
NAME      STATUS      WEIGHT   LASTTRANSITIONTIME
podinfo   Succeeded   0        2021-07-21T10:21:46Z

podinfo   Progressing   0        2021-07-21T10:55:46Z

## Analysisが成功しないためにWEIGHTの値が変化しない
podinfo   Progressing   10       2021-07-21T10:56:46Z

podinfo   Progressing   10       2021-07-21T10:57:46Z
podinfo   Progressing   10       2021-07-21T10:58:46Z
podinfo   Progressing   10       2021-07-21T10:59:46Z
podinfo   Progressing   10       2021-07-21T11:00:46Z

podinfo   Progressing   10       2021-07-21T11:01:46Z

## 最終的にはFailedとなる
podinfo   Failed        0        2021-07-21T11:02:46Z


# Pod
NAME                                  READY   STATUS    RESTARTS   AGE
flagger-loadtester-64695f854f-6mkgj   2/2     Running   0          81m
podinfo-primary-76655b74b9-b99lq      2/2     Running   0          35m
podinfo-primary-76655b74b9-cvs9z      2/2     Running   0          35m

## Canary Podの作成
podinfo-75459749c9-mqd7m              0/2     Pending   0          0s
podinfo-75459749c9-mqd7m              0/2     Pending   0          0s
podinfo-75459749c9-mqd7m              0/2     Init:0/1   0          0s
podinfo-75459749c9-mqd7m              0/2     PodInitializing   0          2s
podinfo-75459749c9-8w9tl              0/2     Pending           0          0s
podinfo-75459749c9-8w9tl              0/2     Pending           0          0s
podinfo-75459749c9-8w9tl              0/2     Init:0/1          0          0s
podinfo-75459749c9-8w9tl              0/2     PodInitializing   0          2s
podinfo-75459749c9-8w9tl              0/2     Running           0          8s
podinfo-75459749c9-8w9tl              1/2     Running           0          10s
podinfo-75459749c9-mqd7m              0/2     Running           0          15s
podinfo-75459749c9-mqd7m              1/2     Running           0          16s
podinfo-75459749c9-8w9tl              2/2     Running           0          19s
podinfo-75459749c9-mqd7m              2/2     Running           0          24s



## Canary Podの削除
podinfo-75459749c9-mqd7m              2/2     Terminating       0          7m
podinfo-75459749c9-8w9tl              2/2     Terminating       0          6m56s
podinfo-75459749c9-mqd7m              0/2     Terminating       0          7m6s
podinfo-75459749c9-8w9tl              0/2     Terminating       0          7m2s
podinfo-75459749c9-mqd7m              0/2     Terminating       0          7m7s
podinfo-75459749c9-mqd7m              0/2     Terminating       0          7m7s
podinfo-75459749c9-8w9tl              0/2     Terminating       0          7m11s
podinfo-75459749c9-8w9tl              0/2     Terminating       0          7m11s

最終的には以下のような状態となります。

$ kubectl get canary -n test
NAME      STATUS   WEIGHT   LASTTRANSITIONTIME
podinfo   Failed   0        2021-07-21T11:02:46Z

$ kubectl get pods -n test
NAME                                  READY   STATUS    RESTARTS   AGE
flagger-loadtester-64695f854f-6mkgj   2/2     Running   0          91m
podinfo-primary-76655b74b9-b99lq      2/2     Running   0          45m
podinfo-primary-76655b74b9-cvs9z      2/2     Running   0          45m

podinfo Canaryのログは以下の通りです。

$ kubectl describe canary -n test podinfo

(中略)

Events:
  Type     Reason  Age                  From     Message
  ----     ------  ----                 ----     -------
  Normal   Synced  53m                  flagger  Advance podinfo.test canary weight 20
  Normal   Synced  52m                  flagger  Advance podinfo.test canary weight 30
  Normal   Synced  51m                  flagger  Advance podinfo.test canary weight 40
  Normal   Synced  50m                  flagger  Advance podinfo.test canary weight 50
  Normal   Synced  49m                  flagger  Copying podinfo.test template spec to podinfo-primary.test
  Normal   Synced  47m (x2 over 48m)    flagger  (combined from similar events): Promotion completed! Scaling down podinfo.test
  Normal   Synced  13m (x2 over 55m)    flagger  New revision detected! Scaling up podinfo.test
  Normal   Synced  12m (x2 over 54m)    flagger  Advance podinfo.test canary weight 10
  Normal   Synced  12m (x2 over 54m)    flagger  Pre-rollout check acceptance-test passed
  Normal   Synced  12m (x2 over 54m)    flagger  Starting canary analysis for podinfo.test
  Warning  Synced  10m                  flagger  Halt podinfo.test advancement success rate 97.50% < 99%
  Warning  Synced  8m25s                flagger  Halt podinfo.test advancement success rate 97.61% < 99%
  Warning  Synced  7m25s (x3 over 11m)  flagger  Halt podinfo.test advancement success rate 0.00% < 99%
  Warning  Synced  6m25s                flagger  Rolling back podinfo.test failed checks threshold reached 5
  Warning  Synced  6m25s                flagger  Canary failed! Scaling down podinfo.test

Blue/Green Deployment

続いてBlue/Green Deploymentを試します。Canary Deploymentから連続して実施する場合は、まず podinfo Canaryリソースを一度削除します。

$ kubectl delete -f podinfo-canary.yaml
canary.flagger.app "podinfo" deleted

podinfo Canaryを削除すると、作成時に一緒に作られたPodなどのリソースも合わせて削除されます。また、Canary作成時に置き換えられた podinfo Podは、Canaryを削除しても復旧されません。

# podinfo Podは再作成されない
$ kubectl get pods -n test
NAME                                  READY   STATUS    RESTARTS   AGE
flagger-loadtester-64695f854f-6mkgj   2/2     Running   0          126m

$ kubectl get deploy -n test
NAME                 READY   UP-TO-DATE   AVAILABLE   AGE
flagger-loadtester   1/1     1            1           126m
podinfo              0/0     0            0           130m

ここでは podinfo Deploymentを編集して podinfo Podを再作成します。編集したのはレプリカ数とコンテナイメージタグの2か所です。

$ kubectl edit deploy podinfo -n test
deployment.apps/podinfo edited


# Deployment編集後
$ kubectl get pods -n test
NAME                                  READY   STATUS    RESTARTS   AGE
flagger-loadtester-64695f854f-h9jdv   2/2     Running   0          33m
podinfo-99dc84b6f-svnvg               2/2     Running   0          25s
podinfo-99dc84b6f-vmv7n               2/2     Running   0          25s

Canary リソースのデプロイ

次に新しいCanaryリソースをデプロイします。Canary Deploymentの際に利用したものと比べ、Analysis部分に maxWeight stepWeight がありません。Weightの設定がないため、Canary Podへトラフィックを流すことなく、spec.analysis.iterations の回数だけAnalysisを実行、チェックを通過したら新しいバージョンのPrimary Podを作成してトラフィックを切り替えます。


podinfo-bg.yaml

apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: podinfo
  namespace: test
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: podinfo
  progressDeadlineSeconds: 60
  autoscalerRef:
    apiVersion: autoscaling/v2beta2
    kind: HorizontalPodAutoscaler
    name: podinfo
  service:
    port: 9898
    targetPort: 9898
    gateways:
    - public-gateway.istio-system.svc.cluster.local
    hosts:
    - app.example.com
    trafficPolicy:
      tls:
        mode: DISABLE
    retries:
      attempts: 3
      perTryTimeout: 1s
      retryOn: "gateway-error,connect-failure,refused-stream"
  analysis:
    interval: 1m
    threshold: 5
    iterations: 10
    metrics:
    - name: request-success-rate
      thresholdRange:
        min: 99
      interval: 1m
    - name: request-duration
      thresholdRange:
        max: 500
      interval: 30s
    webhooks:
      - name: acceptance-test
        type: pre-rollout
        url: http://flagger-loadtester.test/
        timeout: 30s
        metadata:
          type: bash
          cmd: "curl -sd 'test' http://podinfo-canary:9898/token | grep token"
      - name: load-test
        url: http://flagger-loadtester.test/
        timeout: 5s
        metadata:
          cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/"


まずは上記マニフェストファイルをデプロイします。

$ kubectl apply -f podinfo-bg.yaml
canary.flagger.app/podinfo created


# デプロイ後の確認
$ kubectl get pods -n test
NAME                                  READY   STATUS    RESTARTS   AGE
flagger-loadtester-64695f854f-h9jdv   2/2     Running   0          38m
podinfo-primary-7b67c7fbc4-4fhm6      2/2     Running   0          82s
podinfo-primary-7b67c7fbc4-w5jpt      2/2     Running   0          82s

$ kubectl get canary -n test
NAME      STATUS        WEIGHT   LASTTRANSITIONTIME
podinfo   Initialized   0        2021-07-22T00:46:56Z

アプリケーションのアップデート

次にアプリケーションを更新します。

Canary Deploymentと同様、イメージタグの更新を行います。

$ kubectl -n test set image deployment/podinfo podinfod=stefanprodan/podinfo:3.1.1
deployment.apps/podinfo image updated

PodとCanaryの経過を見てみます。Canary Deploymentと異なり、こちらのログからは経過が確認できませんが、 kubectl describe canary でのEventsにはAnalysisが進行している様子が確認できます。

Progressing

# Progressing
$ kubectl get canary -n test -w
NAME      STATUS        WEIGHT   LASTTRANSITIONTIME
podinfo   Initialized   0        2021-07-22T00:46:56Z

podinfo   Progressing   0        2021-07-22T00:47:56Z


$ kubectl get pods -n test  -w
NAME                                  READY   STATUS    RESTARTS   AGE
flagger-loadtester-64695f854f-h9jdv   2/2     Running   0          38m
podinfo-primary-7b67c7fbc4-4fhm6      2/2     Running   0          93s
podinfo-primary-7b67c7fbc4-w5jpt      2/2     Running   0          93s


## Canary Podの作成
podinfo-5f8fd4f546-5tbmv              0/2     Pending   0          0s
podinfo-5f8fd4f546-5tbmv              0/2     Pending   0          0s
podinfo-5f8fd4f546-5tbmv              0/2     Init:0/1   0          0s
podinfo-5f8fd4f546-5tbmv              0/2     PodInitializing   0          2s
podinfo-5f8fd4f546-5tbmv              0/2     Running           0          3s
podinfo-5f8fd4f546-5tbmv              1/2     Running           0          3s
podinfo-5f8fd4f546-bkxbz              0/2     Pending           0          0s
podinfo-5f8fd4f546-bkxbz              0/2     Pending           0          1s
podinfo-5f8fd4f546-bkxbz              0/2     Init:0/1          0          1s
podinfo-5f8fd4f546-bkxbz              0/2     PodInitializing   0          3s
podinfo-5f8fd4f546-bkxbz              0/2     Running           0          4s
podinfo-5f8fd4f546-bkxbz              1/2     Running           0          5s
podinfo-5f8fd4f546-bkxbz              2/2     Running           0          9s
podinfo-5f8fd4f546-5tbmv              2/2     Running           0          16s


## Iterationが進行している
$ kubectl describe canary podinfo -n test | tail -10
Events:
  Type     Reason  Age                    From     Message
  ----     ------  ----                   ----     -------
  Warning  Synced  3m24s                  flagger  podinfo-primary.test not ready: waiting for rollout to finish: observed deployment generation less then desired generation
  Normal   Synced  2m24s (x2 over 3m24s)  flagger  all the metrics providers are available!
  Normal   Synced  2m24s                  flagger  Initialization done! podinfo.test
  Normal   Synced  84s                    flagger  New revision detected! Scaling up podinfo.test
  Normal   Synced  24s                    flagger  Starting canary analysis for podinfo.test
  Normal   Synced  24s                    flagger  Pre-rollout check acceptance-test passed
  Normal   Synced  24s                    flagger  Advance podinfo.test canary iteration 1/10

$ kubectl describe canary podinfo -n test | tail -10
  Normal   Synced  11m (x2 over 12m)    flagger  all the metrics providers are available!
  Normal   Synced  11m                  flagger  Initialization done! podinfo.test
  Normal   Synced  10m                  flagger  New revision detected! Scaling up podinfo.test
  Normal   Synced  9m26s                flagger  Starting canary analysis for podinfo.test
  Normal   Synced  9m26s                flagger  Pre-rollout check acceptance-test passed
  Normal   Synced  9m26s                flagger  Advance podinfo.test canary iteration 1/10
  Normal   Synced  8m26s                flagger  Advance podinfo.test canary iteration 2/10
  Normal   Synced  7m26s                flagger  Advance podinfo.test canary iteration 3/10
  Normal   Synced  6m26s                flagger  Advance podinfo.test canary iteration 4/10
  Normal   Synced  26s (x6 over 5m26s)  flagger  (combined from similar events): Advance podinfo.test canary iteration 10/10

Promoting

# Promoting
NAME      STATUS        WEIGHT   LASTTRANSITIONTIME
podinfo   Promoting     0        2021-07-22T00:59:56Z


## Primary Podの更新
NAME                                  READY   STATUS    RESTARTS   AGE
podinfo-primary-7b67c7fbc4-w5jpt      2/2     Terminating       0          14m
podinfo-primary-666bd89c86-s8pkg      0/2     Pending           0          0s
podinfo-primary-666bd89c86-s8pkg      0/2     Pending           0          0s
podinfo-primary-666bd89c86-s8pkg      0/2     Init:0/1          0          0s
podinfo-primary-666bd89c86-vqhm5      0/2     Pending           0          0s
podinfo-primary-666bd89c86-vqhm5      0/2     Pending           0          0s
podinfo-primary-666bd89c86-vqhm5      0/2     Init:0/1          0          0s
podinfo-primary-666bd89c86-s8pkg      0/2     PodInitializing   0          2s
podinfo-primary-666bd89c86-vqhm5      0/2     PodInitializing   0          2s
podinfo-primary-666bd89c86-s8pkg      0/2     Running           0          3s
podinfo-primary-666bd89c86-vqhm5      0/2     Running           0          3s
podinfo-primary-666bd89c86-s8pkg      1/2     Running           0          4s
podinfo-primary-666bd89c86-vqhm5      1/2     Running           0          4s
podinfo-primary-7b67c7fbc4-w5jpt      0/2     Terminating       0          14m
podinfo-primary-7b67c7fbc4-w5jpt      0/2     Terminating       0          14m
podinfo-primary-7b67c7fbc4-w5jpt      0/2     Terminating       0          14m
podinfo-primary-666bd89c86-vqhm5      2/2     Running           0          16s
podinfo-primary-666bd89c86-s8pkg      2/2     Running           0          16s
podinfo-primary-7b67c7fbc4-4fhm6      2/2     Terminating       0          14m
podinfo-primary-7b67c7fbc4-4fhm6      0/2     Terminating       0          14m
podinfo-primary-7b67c7fbc4-4fhm6      0/2     Terminating       0          14m
podinfo-primary-7b67c7fbc4-4fhm6      0/2     Terminating       0          14m



$ kubectl describe canary podinfo -n test | tail -10
  Normal   Synced  13m (x2 over 14m)  flagger  all the metrics providers are available!
  Normal   Synced  13m                flagger  Initialization done! podinfo.test
  Normal   Synced  12m                flagger  New revision detected! Scaling up podinfo.test
  Normal   Synced  11m                flagger  Starting canary analysis for podinfo.test
  Normal   Synced  11m                flagger  Pre-rollout check acceptance-test passed
  Normal   Synced  11m                flagger  Advance podinfo.test canary iteration 1/10
  Normal   Synced  10m                flagger  Advance podinfo.test canary iteration 2/10
  Normal   Synced  9m5s               flagger  Advance podinfo.test canary iteration 3/10
  Normal   Synced  8m5s               flagger  Advance podinfo.test canary iteration 4/10
  Normal   Synced  5s (x8 over 7m5s)  flagger  (combined from similar events): Copying podinfo.test template spec to podinfo-primary.test

Finalising

# Finalising
NAME      STATUS        WEIGHT   LASTTRANSITIONTIME
podinfo   Finalising    0        2021-07-22T01:00:56Z


## Primary Podへトラフィックを流す
$ kubectl describe canary podinfo -n test | tail -10
  Normal   Synced  14m (x2 over 15m)    flagger  all the metrics providers are available!
  Normal   Synced  14m                  flagger  Initialization done! podinfo.test
  Normal   Synced  13m                  flagger  New revision detected! Scaling up podinfo.test
  Normal   Synced  12m                  flagger  Starting canary analysis for podinfo.test
  Normal   Synced  12m                  flagger  Pre-rollout check acceptance-test passed
  Normal   Synced  12m                  flagger  Advance podinfo.test canary iteration 1/10
  Normal   Synced  11m                  flagger  Advance podinfo.test canary iteration 2/10
  Normal   Synced  10m                  flagger  Advance podinfo.test canary iteration 3/10
  Normal   Synced  9m15s                flagger  Advance podinfo.test canary iteration 4/10
  Normal   Synced  15s (x9 over 8m15s)  flagger  (combined from similar events): Routing all traffic to primary

Succeeded

# Succeeded
NAME      STATUS        WEIGHT   LASTTRANSITIONTIME
podinfo   Succeeded     0        2021-07-22T01:01:56Z


## Canary Podの削除
NAME                                  READY   STATUS    RESTARTS   AGE
podinfo-5f8fd4f546-5tbmv              2/2     Terminating       0          14m
podinfo-5f8fd4f546-bkxbz              2/2     Terminating       0          13m
podinfo-5f8fd4f546-5tbmv              0/2     Terminating       0          14m
podinfo-5f8fd4f546-bkxbz              0/2     Terminating       0          14m
podinfo-5f8fd4f546-5tbmv              0/2     Terminating       0          14m
podinfo-5f8fd4f546-5tbmv              0/2     Terminating       0          14m
podinfo-5f8fd4f546-bkxbz              0/2     Terminating       0          14m
podinfo-5f8fd4f546-bkxbz              0/2     Terminating       0          14m


$ kubectl describe canary podinfo -n test | tail -10
  Normal   Synced  15m (x2 over 16m)  flagger  all the metrics providers are available!
  Normal   Synced  15m                flagger  Initialization done! podinfo.test
  Normal   Synced  14m                flagger  New revision detected! Scaling up podinfo.test
  Normal   Synced  13m                flagger  Starting canary analysis for podinfo.test
  Normal   Synced  13m                flagger  Pre-rollout check acceptance-test passed
  Normal   Synced  13m                flagger  Advance podinfo.test canary iteration 1/10
  Normal   Synced  12m                flagger  Advance podinfo.test canary iteration 2/10
  Normal   Synced  11m                flagger  Advance podinfo.test canary iteration 3/10
  Normal   Synced  10m                flagger  Advance podinfo.test canary iteration 4/10
  Normal   Synced  0s (x10 over 9m)   flagger  (combined from similar events): Promotion completed! Scaling down podinfo.test

ロールバック

ロールバックも試してみます。やることはCanary Deploymentとまったく同じです。

# コンテナイメージの更新
$ kubectl -n test set image deployment/podinfo podinfod=stefanprodan/podinfo:3.1.2
deployment.apps/podinfo image updated


# Canary/Podのウォッチを開始
$ kubectl get canary -n test -w
NAME      STATUS      WEIGHT   LASTTRANSITIONTIME
podinfo   Succeeded   0        2021-07-22T01:01:56Z


$ kubectl get pods -n test  -w
NAME                                  READY   STATUS    RESTARTS   AGE
flagger-loadtester-64695f854f-h9jdv   2/2     Running   0          66m
podinfo-primary-666bd89c86-s8pkg      2/2     Running   0          15m
podinfo-primary-666bd89c86-vqhm5      2/2     Running   0          15m
# flagger-loadtesterから500ステータスコードの実行
$ kubectl get pods -n test
NAME                                  READY   STATUS    RESTARTS   AGE
flagger-loadtester-64695f854f-6mkgj   2/2     Running   0          85m
podinfo-75459749c9-8w9tl              2/2     Running   0          3m47s
podinfo-75459749c9-mqd7m              2/2     Running   0          3m51s
podinfo-primary-76655b74b9-b99lq      2/2     Running   0          39m
podinfo-primary-76655b74b9-cvs9z      2/2     Running   0          39m

$ kubectl exec -it -n test flagger-loadtester-64695f854f-6mkgj sh
/home/app $ watch curl http://podinfo-canary.test:9898/status/500

Deployment Proceeding (Failed)

$ kubectl describe canary podinfo -n test | tail -10
  Normal   Synced  31m                 flagger  Advance podinfo.test canary iteration 2/10
  Normal   Synced  30m                 flagger  Advance podinfo.test canary iteration 3/10
  Normal   Synced  29m                 flagger  Advance podinfo.test canary iteration 4/10
  Normal   Synced  19m (x10 over 28m)  flagger  (combined from similar events): Promotion completed! Scaling down podinfo.test
  Normal   Synced  5m4s (x2 over 33m)  flagger  New revision detected! Scaling up podinfo.test
  Normal   Synced  4m4s (x2 over 32m)  flagger  Advance podinfo.test canary iteration 1/10
  Normal   Synced  4m4s (x2 over 32m)  flagger  Pre-rollout check acceptance-test passed
  Normal   Synced  4m4s (x2 over 32m)  flagger  Starting canary analysis for podinfo.test
  Warning  Synced  64s (x2 over 3m4s)  flagger  Halt podinfo.test advancement success rate 0.00% < 99%
  Warning  Synced  4s (x2 over 2m4s)   flagger  Halt podinfo.test advancement success rate 97.51% < 99%

Deployment Failed

NAME      STATUS      WEIGHT   LASTTRANSITIONTIME
podinfo   Failed        0        2021-07-22T01:22:56Z


NAME                                  READY   STATUS    RESTARTS   AGE
podinfo-75459749c9-w8vx8              2/2     Terminating       0          7m
podinfo-75459749c9-7qbvd              2/2     Terminating       0          7m
podinfo-75459749c9-w8vx8              0/2     Terminating       0          7m6s
podinfo-75459749c9-7qbvd              0/2     Terminating       0          7m6s
podinfo-75459749c9-7qbvd              0/2     Terminating       0          7m13s
podinfo-75459749c9-7qbvd              0/2     Terminating       0          7m13s
podinfo-75459749c9-w8vx8              0/2     Terminating       0          7m15s
podinfo-75459749c9-w8vx8              0/2     Terminating       0          7m15s


$ kubectl describe canary podinfo -n test | tail -10
  Normal   Synced  32m                    flagger  Advance podinfo.test canary iteration 4/10
  Normal   Synced  22m (x10 over 31m)     flagger  (combined from similar events): Promotion completed! Scaling down podinfo.test
  Normal   Synced  8m32s (x2 over 36m)    flagger  New revision detected! Scaling up podinfo.test
  Normal   Synced  7m32s (x2 over 35m)    flagger  Pre-rollout check acceptance-test passed
  Normal   Synced  7m32s (x2 over 35m)    flagger  Advance podinfo.test canary iteration 1/10
  Normal   Synced  7m32s (x2 over 35m)    flagger  Starting canary analysis for podinfo.test
  Warning  Synced  3m32s (x2 over 5m32s)  flagger  Halt podinfo.test advancement success rate 97.51% < 99%
  Warning  Synced  2m32s (x3 over 6m32s)  flagger  Halt podinfo.test advancement success rate 0.00% < 99%
  Warning  Synced  92s                    flagger  Rolling back podinfo.test failed checks threshold reached 5
  Warning  Synced  92s                    flagger  Canary failed! Scaling down podinfo.test

Slackへのメッセージ送信

次にSlackへメッセージを送信する場合を試してみます。Flaggerでは AlertProvider というリソースで、メッセージの送信先となるプロバイダーやWebhooknURLを設定します。

※参考リンク:Flagger Doc - Alerting

今回は以下のような AlertProvider マニフェストを利用します。前提として、SlackのIncoming Webhookを取得しておきます。

※参考リンク:SlackでのIncoming Webhookの利用


podinfo-alert.yaml

apiVersion: flagger.app/v1beta1
kind: AlertProvider
metadata:
  name: on-call
  namespace: test
spec:
  type: slack
  channel: on-call-alerts
  username: flagger
  secretRef:
    name: on-call-url
---
apiVersion: v1
kind: Secret
metadata:
  name: on-call-url
  namespace: test
data:
  address: <encoded webhook URL>


上記マニフェストを使ってリソースをデプロイします。

$ kubectl apply -f podinfo-alert.yaml
alertprovider.flagger.app/on-call created
secret/on-call-url created


# デプロイ後の確認
$ kubectl get alertprovider -n test
NAME      TYPE
on-call   slack

AlertProvider を利用するには Canary リソースのほうも修正が必要です。以下のマニフェストのように、 spec.analysis.alerts にて、利用する AlertProvider とログレベルを指定します。


podinfo-canary-alert.yaml

apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: podinfo
  namespace: test
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: podinfo
  progressDeadlineSeconds: 60
  autoscalerRef:
    apiVersion: autoscaling/v2beta2
    kind: HorizontalPodAutoscaler
    name: podinfo
  service:
    port: 9898
    targetPort: 9898
    gateways:
    - public-gateway.istio-system.svc.cluster.local
    hosts:
    - app.example.com
    trafficPolicy:
      tls:
        mode: DISABLE
    retries:
      attempts: 3
      perTryTimeout: 1s
      retryOn: "gateway-error,connect-failure,refused-stream"
  analysis:
    alerts:
      - name: "on-call Slack"
        severity: info
        providerRef:
          name: on-call
          namespace: test
    interval: 1m
    threshold: 5
    maxWeight: 50
    stepWeight: 10
    metrics:
    - name: request-success-rate
      thresholdRange:
        min: 99
      interval: 1m
    - name: request-duration
      thresholdRange:
        max: 500
      interval: 30s
    webhooks:
      - name: acceptance-test
        type: pre-rollout
        url: http://flagger-loadtester.test/
        timeout: 30s
        metadata:
          type: bash
          cmd: "curl -sd 'test' http://podinfo-canary:9898/token | grep token"
      - name: load-test
        url: http://flagger-loadtester.test/
        timeout: 5s
        metadata:
          cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/"


上記マニフェストから Canary リソースをデプロイします。

$ kubectl apply -f podinfo-canary-alert.yaml
canary.flagger.app/podinfo created

$ kubectl get pods -n test
NAME                                  READY   STATUS    RESTARTS   AGE
flagger-loadtester-64695f854f-h9jdv   2/2     Running   0          104m
podinfo-primary-df5797b7c-bj86k       2/2     Running   0          90s
podinfo-primary-df5797b7c-xgf4v       2/2     Running   0          90s

$ kubectl get canary -n test
NAME      STATUS        WEIGHT   LASTTRANSITIONTIME
podinfo   Initialized   0        2021-07-22T01:53:06Z

Slackとの連携が成功していれば、以下の画像のようなメッセージが送られます。

Canary リソース作成時

f:id:FY0323:20210722181703j:plain

※ コンテナイメージアップデート成功時

f:id:FY0323:20210722181743j:plain

※ アップデート失敗時

f:id:FY0323:20210722181807j:plain

参考ドキュメント