KubernetesクラスターでのCanary Releaseの決定を自動化する
December 30, 2022
背景
現在、マイクロサービスは典型的で広く使用されているソフトウェアアーキテクチャパターンとなっています。サービスは疎結合で、APIを介して連携します。マイクロサービスパターンにより、各アプリケーションは独立してデプロイおよびメンテナンス可能となり、リリースがより頻繁に行われます。ご存知の通り、リリースにはリスクが伴います。新しいバージョンにバグがないかどうかは誰にもわかりません。そのため、カナリアリリースやブルーグリーンデプロイメントなどの戦略を使用して、最新バージョンを段階的に展開し、リスクを低減します。
カナリアリリースでは、トラフィックを安定グループとカナリアグループの2つのターゲットサービスに分割します。Apache APISIXのようなAPIゲートウェイは、マイクロサービスアーキテクチャをAPIとして効率的かつ安全に公開します。カナリアリリースの機能を備えています。通常、トラフィックを分割する方法には2つあります:重みベースの方法と述語式ベースの方法です。
重みベースの方法
ユーザーは、カナリアグループにヒットするトラフィックの割合を指定する必要があります。上の画像では、95%のトラフィックが安定サービスに転送され、残りの5%がカナリアサービスに転送されます。
述語式ベースの方法
リクエスト述語式ベースの方法では、指定された特性に合致するトラフィックのみがカナリアグループにヒットします。例えば、リクエストヘッダーにX-Debugとその実際の値を持つHTTPリクエストのみがカナリアサービスに到達します。
カナリアリリースの自動化
APIゲートウェイをAPIやダッシュボードを介して操作する場合、トラフィック比率(重みベースの方法)や述語(述語式ベースの方法)を調整する際に時間差が生じます。現在、多くのユーザーがKubernetesを使用してマイクロサービスをオーケストレーションしています。新しいサービスバージョンが作成されたら、カナリアリリースを開始できるでしょうか?この記事では、API7 Cloudを使用してKubernetesクラスター内でカナリアリリースを自動化する方法を紹介します。
API7 Cloudとは
API7 Cloudは、任意のクラウド、複数のロケーションでAPIを大規模にデプロイ、制御、可視化、監視するためのSaaSプラットフォームです。APIをどこで実行しても、1か所で管理できます。API7 Cloudは、Apache APISIXをAPIゲートウェイとして使用し、APIを効率的かつ安全に公開します。
API7 Cloudを使用するには、DockerやKubernetesなどのインフラストラクチャにApache APISIXをデプロイする必要があります。Cloud CLIを使用してデプロイを簡素化できます。
# API7 Cloudコンソールからアクセストークンを設定します。
cloud-cli configure --token {YOUR TOKEN}
# Apache APISIX(バージョン2.15.1)をapisixネームスペースにデプロイし、レプリカ数を1に設定します。
cloud-cli deploy kubernetes \
--name my-apisix \
--namespace apisix \
--replica-count 1 \
--apisix-image apache/apisix:2.15.1-centos
カナリアリリースはAPI7 Cloudの組み込み機能の1つです。ユーザーはコンソールを介してカナリアリリースルールを設定するか、API7 Cloud Open APIを呼び出すことができます。カナリアリリースの決定を自動化するために、後者の方法を使用します。
シナリオ
Kubernetesクラスター内に、常にエラーメッセージを返すシンプルなerror-pageアプリケーションがあるとします。バージョン2.0をリリースし、カナリアリリース戦略を使用してリリースリスクを低減したいと考えています。さらに、このプロセス全体を自動化したいと考えています。そこで、Kubernetesサービスリソースの変更を監視し、API7 Cloud Go SDKを介してAPI7 Cloud上でカナリアリリースを作成/変更するカナリアリリースコントローラーを作成します。トラフィックを分割するために重みベースの方法のみを使用します。Apache APISIX APIゲートウェイを含むすべてのコンポーネントはKubernetes上にデプロイされるため、図は次のようになります:
カナリアリリースコントローラーはサービスの変更を監視し、いくつかのアノテーションに基づいて反応します。具体的には:
- サービスに
api7.cloud/published-service
アノテーションが含まれている場合、カナリアリリースコントローラーはAPI7 Cloud上にアプリケーションを作成しようとします。 - サービスに
api7.cloud/published-canary-service
アノテーションが含まれている場合、カナリアリリースコントローラーはAPI7 Cloud上にカナリアリリースルールを設定しようとし、api7.cloud/published-service-canary-percentage
アノテーションが割合を決定します。
このコントローラーは自己完結型ではありません(サービスが削除された場合にアプリケーションを削除しません)が、自動化されたカナリアリリースプロセスを示すには十分です。
さあ、始めましょう!
まず、Apache APISIXとカナリアリリースコントローラーをデプロイします。前述の通り、Cloud CLIを使用してApache APISIXをデプロイします。また、error-pageアプリケーションとカナリアリリースコントローラーをデプロイするための2つのYAMLファイル(error-page/manifest-v1.yamlとcontroller/manifest.yaml)があります。
- 以下のコマンドを実行するために、利用可能なKubernetesクラスターを準備してください。
- カナリアリリースコントローラーはAPI7 Cloud APIを呼び出すためにアクセストークンが必要です。このドキュメントに従ってトークンを取得し、K8sシークレットに保存します。
kubectl create namespace canary-release-demo
# error-page v1バージョンをデプロイします。
kubectl apply -f https://raw.githubusercontent.com/tokers/canary-release-automation-demo/main/error-page/manifest-v1.yaml -n canary-release-demo
# API7 Cloudアクセストークンを保存するK8sシークレットを作成します。
kubectl create secret generic api7-cloud --namespace canary-release-demo --from-literal token={Your Access Token}
# カナリアリリースコントローラーをデプロイします。
kubectl apply -f https://raw.githubusercontent.com/tokers/canary-release-automation-demo/main/controller/manifest.yaml -n canary-release-demo
# すべてのワークロードが正常か確認します。
kubectl get all -n canary-release-demo
プロキシを確認する
このサービスをアノテーションして公開します。
kubectl annotate service -n canary-release-demo error-page-v1 "api7.cloud/published-service=error-page"
カナリアリリースコントローラーはこの変更を監視し、API7 Cloud上にアプリケーションを作成します。Apache APISIXにアクセスして、プロキシが正常かどうかを確認します。
kubectl port-forward -n canary-release-demo service/apisix-gateway 10080:80
curl http://127.0.0.1:10080/api/error_page -H 'Host: error-page' -s
すべてが正常であれば、{"error": "injected by error_page service", "version": "v1"}
が表示されます。
現在、カナリアリリースコントローラーはアプリケーション内に「すべてにマッチする」APIを作成し、ホストはアプリケーション名(error-page)と同じです。
V2をロールアウトする
error-pageアプリケーションのバージョン2をロールアウトしたいと考えています。まず、manifest-v2.yamlを適用してバージョン2をデプロイします。error-page-v2サービスにカナリアリリースのアノテーションを付けます。
kubectl apply -f https://raw.githubusercontent.com/tokers/canary-release-automation-demo/main/error-page/manifest-v2.yaml -n canary-release-demo
# カナリアリリースコントローラーに、error-page-v2のカナリアリリースを有効にし、割合を10%にすることを伝えます。
kubectl annotate service -n canary-release-demo error-page-v2 "api7.cloud/published-canary-service=true" "api7.cloud/published-service-canary-percentage=10"
# カナリアを開始します。
kubectl annotate service -n canary-release-demo error-page-v2 "api7.cloud/published-service=error-page"
Apache APISIXに再度100
リクエストを送信し、いくつかのリクエストがカナリアサービスerror-page-v2に転送されたかどうかを確認します。
kubectl port-forward -n canary-release-demo service/apisix-gateway 10080:80
for ((i=0; i<100; i++)); do
curl http://127.0.0.1:10080/api/error_page -H 'Host: error-page' -s
done
すべてが正常であれば、約10%のリクエストがerror-page-v2に到達します(Apache APISIXがバックエンドを選択する内部戦略により、正確に10%ではありません)。
ロールバック
バージョン2が不安定であることがわかり、ロールバックしたいと考えています。その前に、カナリアを停止するため、割合を0に変更します。その後、再度Apache APISIXにリクエストを送信します。
kubectl annotate service -n canary-release-demo error-page-v2 "api7.cloud/published-service-canary-percentage=0" --overwrite
for ((i=0; i<100; i++)); do
curl http://127.0.0.1:10080/api/error_page -H 'Host: error-page' -s
done
すべてのリクエストがerror-page-v1に転送されることがわかります。
リリース
長い間、バージョン2が十分に安定していると信じ、すべてのリクエストがバージョン2に到達するようにしたいと考えています。その後、バージョン1のerror-pageアプリケーションをオフラインにできます。そこで、割合を100%に変更します。
kubectl annotate service -n canary-release-demo error-page-v2 "api7.cloud/published-service-canary-percentage=100" --overwrite
for ((i=0; i<100; i++)); do
curl http://127.0.0.1:10080/api/error_page -H 'Host: error-page' -s
done
すべてのリクエストがerror-page-v2にプロキシされます。error-page-v1は安全にオフラインにできます。
まとめ
カナリアリリースはリリースのための有効な手段です。しかし、カナリアリリース戦略を調整するのは遅れる可能性があります。この記事では、カナリアリリースを宣言的に操作し、ある程度自動化する方法を示しました。GitOpsコンポーネントを使用して完全に自動化されたカナリアリリースを追求する人もいるかもしれません。例えば、Argo Rolloutsを使用すると、サービスメトリクスをクエリし、Ingressコントローラーと統合してCRDを変更することで、サービスを自動的にプロモートまたはロールバックできます。最終的に、APIゲートウェイはリクエストを正しい割合でカナリアバージョンに転送します。
参考
error-pageとカナリアリリースコントローラーのソースコード: https://github.com/tokers/canary-release-automation-demo