使用APISIX Ingress Controller和Flagger实现平滑的Canary发布

Hengliang Tan

January 19, 2023

Ecosystem

著者: 譚恒亮, XPENG エンジニアリングマネージャー

プロジェクト開発の過程において、サービスの更新はしばしば課題となります。最高のユーザー体験を提供するためには、サービスの利用不可リスクを可能な限り回避する必要があります。そこで、継続的デリバリーが誕生し、企業のソフトウェアプラクティスとして受け入れられ、確立された継続的インテグレーションの原則の自然な進化となりました。しかし、管理の複雑さやデプロイ失敗がシステムの可用性に影響を与える恐れから、継続的デプロイメントはまだ非常に稀です。カナリアリリースは、おそらく継続的デリバリーシステムの中で最も古典的なシナリオです。これに基づいて、不健康で問題のあるサービスを迅速に発見し、前のバージョンに簡単にロールバックすることができます。

カナリアリリース

カナリアリリースは、グレイスケールリリースとも呼ばれます。一般的に、アプリケーションの新しいバージョンを「カナリア」としてリリースし、パフォーマンスをテストします。古いバージョンは、同じ段階で通常の操作を維持します。アップグレード中、一部のユーザーは新しいバージョンを使用するように誘導され、他のユーザーは古いバージョンを引き続き使用します。システム全体の安定性を確保する前提で、早期にバグを発見し、タイムリーに調整することが可能です。

カナリアリリースは、更新を直接リリースしません。少数のユーザーに一定の割合のトラフィックをゆっくりと誘導します。エラーが検出されない場合、すべてのユーザーに昇格し、古いバージョンは段階的に廃止されます。この方法により、新しい機能を本番環境に導入するリスクが低減されます。

本記事では、Apache APISIX IngressとFlaggerを使用してスムーズなカナリアリリースを実現し、リリース効率を向上させ、リリースリスクを低減する方法を紹介します。

Apache APISIX Ingressについて

Apache APISIX Ingressは、Apache APISIXをデータプレーンプロキシとして使用するKubernetes Ingress Controllerによって実現されます。ロードバランシング、動的なアップストリーム、カナリアリリース、細かいルーティング、レートリミット、サービスデグレード、サービスサーキットブレーカー、認証、可観測性など、数百の機能を提供します。Zoom、Tencent Cloud、Jiakaobaodian、Horizon Robotics、European Copernicus Reference Systemなど、国内外の企業や組織に採用されています。

Flaggerについて

Flaggerは、CNCF(Cloud Native Computing Foundation)プロジェクトであり、FluxファミリーのGitOpsツールの一部です。最近、CNCFはFluxの正式な卒業を発表し、これはクラウドネイティブ技術の成功と将来性を示す良い指標です。プログレッシブデリバリーツールとして、FlaggerはKubernetes上で実行されるアプリケーションのリリースプロセスを自動化します。新しいソフトウェアバージョンを本番環境に導入するリスクを低減するために、トラフィックを徐々に新しいバージョンにシフトしながら、分析メトリクスを測定し、適合性テストを実行します。

Apache APISIXとFluxコミュニティの継続的な努力の結果、Flaggerは最近v1.27.0をリリースし、Apache APISIX IngressとFlaggerを使用した自動カナリアリリースをサポートしています。

featured-<Flagger and Apache APISIX Ingress>.jpg

このスムーズなカナリアリリースプロセスを一緒に体験しましょう。

環境

v1.19以降のKubernetesクラスターが必要です。kindを使用してインストールできます。

コンポーネントのインストール

Helm V3を使用してApache APISIXとApache APISIX Ingress Controllerをインストールします。

helm repo add apisix https://charts.apiseven.com
kubectl create ns apisix


helm upgrade -i apisix apisix/apisix --version=0.11.3 \
--namespace apisix \
--set apisix.podAnnotations."prometheus\.io/scrape"=true \
--set apisix.podAnnotations."prometheus\.io/port"=9091 \
--set apisix.podAnnotations."prometheus\.io/path"=/apisix/prometheus/metrics \
--set pluginAttrs.prometheus.export_addr.ip=0.0.0.0 \
--set pluginAttrs.prometheus.export_addr.port=9091 \
--set pluginAttrs.prometheus.export_uri=/apisix/prometheus/metrics \
--set pluginAttrs.prometheus.metric_prefix=apisix_ \
--set ingress-controller.enabled=true \
--set ingress-controller.config.apisix.serviceNamespace=apisix

FlaggerとPrometheusコンポーネントをapisixネームスペースにインストールします。

helm repo add flagger https://flagger.app


helm upgrade -i flagger flagger/flagger \
--namespace apisix \
--set prometheus.install=true \
--set meshProvider=apisix

注: PrometheusまたはPrometheus Operatorをカスタマイズする必要がある場合は、関連記事を検索して変更してください。

アプリケーションの初期化

Flaggerは、Kubernetesデプロイメントやその他のワークロードに適用でき、HPAと組み合わせることもできます。Kubernetesデプロイメント、ClusterIPサービス、ApisixRouteなど、一連のオブジェクトを作成します。これらのオブジェクトは、アプリケーションを外部クラスターに公開してサービスを提供し、カナリアリリースプロセスの分析に使用されます。

新しいテストネームスペースを作成します:

kubectl create ns test

新しいデプロイメントとHPAを作成します。ここでは、Flaggerの公式コードサンプルを抽出します。

kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=main

Flaggerの負荷テストサービスをデプロイし、カナリアリリース中にトラフィックを生成して分析します。

helm upgrade -i flagger-loadtester flagger/loadtester \ --namespace=test

Apache APISIXのApisixRouteを作成し、Flaggerが作成したリソースを参照し、カナリアバージョンのApache APISIX IngressのApisixRouteを生成します。(以下の例のapp.example.comを実際のドメイン名に置き換えてください)

apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
  name: podinfo
  namespace: test
spec:
  http:
    - backends:
        - serviceName: podinfo
          servicePort: 80
      match:
        hosts:
          - app.example.com
        methods:
          - GET
        paths:
          - /*
      name: method
      plugins:
        - name: prometheus
          enable: true
          config:
            disable: false
            prefer_name: true

これをpodinfo-apisixroute.yamlとして保存し、クラスターに提出します:

kubectl apply -f ./podinfo-apisixroute.yaml

FlaggerのカスタムリソースCanaryを作成します。(例のapp.example.comを実際のドメイン名に置き換えてください)

apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: podinfo
  namespace: test
spec:
  provider: apisix
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: podinfo
  # Apisix routeを参照
  routeRef:
    apiVersion: apisix.apache.org/v2
    kind: ApisixRoute
    name: podinfo
  progressDeadlineSeconds: 60
  service:
    port: 80
    targetPort: 9898
  analysis:
    interval: 10s
    # ロールバックのための最大失敗数
    threshold: 10
    # カナリアバージョンへの最大トラフィック割合
    # (0-100)
    maxWeight: 50
    # カナリア分析のステップサイズ
    # (0-100)
    stepWeight: 10
    # Prometheusを使用してAPISIXのトラフィック情報を確認
    metrics:
      - name: request-success-rate
        # 最小成功率(5xxレスポンスなし)
        # (0-100)
        thresholdRange:
          min: 99
        interval: 1m
      - name: request-duration
        # P99は最大リクエスト遅延(ms)
        thresholdRange:
          max: 500
        interval: 30s
    webhooks:
        # カナリア分析のための自動トラフィック、実際のシナリオに基づいて変更
      - name: load-test
        url: http://flagger-loadtester.test/
        timeout: 5s
        type: rollout
        metadata:
          cmd: |-
            hey -z 1m -q 10 -c 2 -h2 -host app.example.com http://apisix-gateway.apisix/api/info

これをpodinfo-canary.yamlとして保存し、クラスターに提出します:

kubectl apply -f ./podinfo-canary.yaml

Flaggerは自動的に関連リソースを生成します:

# 提出済み deployment.apps/podinfo horizontalpodautoscaler.autoscaling/podinfo apisixroute/podinfo canary.flagger.app/podinfo

# 自動生成 deployment.apps/podinfo-primary horizontalpodautoscaler.autoscaling/podinfo-primary service/podinfo service/podinfo-canary service/podinfo-primary apisixroute/podinfo-podinfo-canary

featured-<version 1>.jpg

この時点で、ドメイン名app.example.com(例のapp.example.comを実際のドメイン名に置き換えてください)を介してアプリケーションにアクセスでき、現在のバージョンのアプリケーションが表示されます。

カナリアリリースの自動化

Flaggerは、カナリアノードにトラフィックを徐々にシフトしながら、HTTPリクエストの成功率、平均リクエスト時間、ポッドの健全性などの主要なパフォーマンスメトリクスを測定する制御ループを実装します。関連する指標の分析に基づいて、リリースまたはカナリアデプロイメントを停止し、分析結果をSlack、MS Teams、Prometheus Alert Managerなどの関連プラットフォームに公開します。

Flagger Control Loop

コンテナイメージのバージョンを更新してカナリアリリースをトリガーします

kubectl -n test set image deployment/podinfo \ podinfod=stefanprodan/podinfo:6.0.1

Flaggerは、デプロイメントの新しいバージョンがあることを検出し、カナリア分析リリースの試行を開始します。

kubectl -n test describe canary/podinfo

Status:
  Canary Weight:  0
  Conditions:
    Message:               Canary analysis completed successfully, promotion finished.
    Reason:                Succeeded
    Status:                True
    Type:                  Promoted
  Failed Checks:           1
  Iterations:              0
  Phase:                   Succeeded

Events:
  Type     Reason  Age                    From     Message
  ----     ------  ----                   ----     -------
  Warning  Synced  2m59s                  flagger  podinfo-primary.test not ready: waiting for rollout to finish: observed deployment generation less than desired generation
  Warning  Synced  2m50s                  flagger  podinfo-primary.test not ready: waiting for rollout to finish: 0 of 1 (readyThreshold 100%) updated replicas are available
  Normal   Synced  2m40s (x3 over 2m59s)  flagger  all the metrics providers are available!
  Normal   Synced  2m39s                  flagger  Initialization done! podinfo.test
  Normal   Synced  2m20s                  flagger  New revision detected! Scaling up podinfo.test
  Warning  Synced  2m (x2 over 2m10s)     flagger  canary deployment podinfo.test not ready: waiting for rollout to finish: 0 of 1 (readyThreshold 100%) updated replicas are available
  Normal   Synced  110s                   flagger  Starting canary analysis for podinfo.test
  Normal   Synced  109s                   flagger  Advance podinfo.test canary weight 10
  Warning  Synced  100s                   flagger  Halt advancement no values found for apisix metric request-success-rate probably podinfo.test is not receiving traffic: running query failed: no values found
  Normal   Synced  90s                    flagger  Advance podinfo.test canary weight 20
  Normal   Synced  80s                    flagger  Advance podinfo.test canary weight 30
  Normal   Synced  69s                    flagger  Advance podinfo.test canary weight 40
  Normal   Synced  59s                    flagger  Advance podinfo.test canary weight 50
  Warning  Synced  30s (x2 over 40s)      flagger  podinfo-primary.test not ready: waiting for rollout to finish: 1 old replicas are pending termination
  Normal   Synced  9s (x3 over 50s)       flagger  (combined from similar events): Promotion completed! Scaling down podinfo.test

カナリアリリースプロセス中、ドメイン名app.example.com(実際のドメイン名に置き換えてください)を介してアプリケーションにアクセスすると、異なるレスポンスを受け取ります。

featured-<version 2>.jpg

Flaggerによって自動的に作成されたApache APISIXのApisixRouteリソースpodinfo-podinfo-canaryを表示すると、サービスpodinfo-primaryとサービスpodinfo-canaryの重みがリリースプロセスに伴って変化していることがわかります。

spec:
  http:
    - backends:
        - serviceName: podinfo-primary
          servicePort: 80
          # Flaggerによって自動調整
          weight: 80
        - serviceName: podinfo-canary
          servicePort: 80
          # Flaggerによって自動調整
          weight: 20

最終リリースが完了すると、最新の安定バージョンが表示されます。

featured-<version 3>.jpg

注: カナリアリリース中にデプロイメントを再度変更すると、Flaggerはカナリア分析を再実行します。

このコマンドで全てのカナリアリリースを観察できます:

watch kubectl get canaries --all-namespaces

NAMESPACE   NAME      STATUS      WEIGHT   LASTTRANSITIONTIME
test        podinfo-2   Progressing   10       2022-11-23T05:00:54Z
test        podinfo     Succeeded     0        2022-11-23T06:00:54Z

ロールバック

カナリアリリース分析中、HTTP 500 Bad Requestを生成してFlaggerがカナリアリリースを中断し、古いバージョンにロールバックすることをテストできます。

別のカナリアリリースをトリガーします:

kubectl -n test set image deployment/podinfo \ podinfod=stefanprodan/podinfo:6.0.2

負荷テストコンテナに入ります

kubectl -n test exec -it deploy/flagger-loadtester bash

HTTP 500エラーを生成します:

hey -z 1m -c 5 -q 5 -host app.example.com http://apisix-gateway.apisix/status/500

サーバーの遅延をシミュレートします:

watch -n 1 curl -H \"host: app.example.com\" http://apisix-gateway.apisix/delay/1

検出された失敗の数がカナリア分析の閾値に達すると、トラフィックは自動的にマスターノードに戻り、カナリアノードはゼロにスケールダウンされ、カナリアリリースプロセスは失敗としてマークされます。

kubectl -n apisix logs deploy/flagger -f | jq .msg

"New revision detected! Scaling up podinfo.test"
"canary deployment podinfo.test not ready: waiting for rollout to finish: 0 of 1 (readyThreshold 100%) updated replicas are available"
"Starting canary analysis for podinfo.test"
"Advance podinfo.test canary weight 10"
"Halt podinfo.test advancement success rate 0.00% < 99%"
"Halt podinfo.test advancement success rate 26.76% < 99%"
"Halt podinfo.test advancement success rate 34.19% < 99%"
"Halt podinfo.test advancement success rate 37.32% < 99%"
"Halt podinfo.test advancement success rate 39.04% < 99%"
"Halt podinfo.test advancement success rate 40.13% < 99%"
"Halt podinfo.test advancement success rate 48.28% < 99%"
"Halt podinfo.test advancement success rate 50.35% < 99%"
"Halt podinfo.test advancement success rate 56.92% < 99%"
"Halt podinfo.test advancement success rate 67.70% < 99%"
"Rolling back podinfo.test failed checks threshold reached 10"
"Canary failed! Scaling down podinfo.test"

カナリア分析のためのカスタムメトリクス

Prometheusメトリクスをクエリすることで、カナリア分析を拡張できます。実際のビジネスシナリオに基づいてカスタマイズします。 メトリクステンプレートを作成し、クラスターに提出します。

apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
  name: not-found-percentage
  namespace: test
spec:
  provider:
    type: prometheus
    address: http://flagger-prometheus.apisix:9090
  query: |
    sum(
      rate(
        apisix_http_status{
          route=~"{{ namespace }}_{{ route }}-{{ target }}-canary_.+",
          code!~"4.."
        }[{{ interval }}]
      )
    )
    /
    sum(
      rate(
        apisix_http_status{
          route=~"{{ namespace }}_{{ route }}-{{ target }}-canary_.+"
        }[{{ interval }}]
      )
    ) * 100
# カナリアリリースの分析を変更し、上記で作成した指標テンプレートを追加します。
  analysis:
    metrics:
      - name: "404s percentage"
        templateRef:
          name: not-found-percentage
        thresholdRange:
          max: 5
        interval: 1m

上記の設定は、HTTP 404リクエストのQPS(1秒あたりのクエリ数)が総トラフィックの5%を超えているかどうかをチェックしてカナリアを検証します。HTTP 404リクエストが5%の閾値を超えると、カナリアロールアウトは失敗します。

まとめ

上記のプロセスは、さらに多くのカスタムメトリクスチェック、Webhook、手動承認、SlackまたはMS Teams通知で拡張できます。

Apache APISIXとFlaggerの統合により、非常にスムーズなカナリアリリースが実現され、リリース効率が向上し、リリースリスクが低減されます。今後、両コミュニティはより緊密に協力し、Blue/GreenミラーリングやA/Bテストなどのより多くのリリース機能を実現します。

Tags: