チュートリアル: Kubernetes のデプロイにカナリア デプロイ戦略を使用する
Azure DevOps Services | Azure DevOps Server 2022
"カナリア" デプロイ戦略とは、安定した運用バージョンの隣に新しいバージョンのアプリケーションをデプロイすることを意味します。 その後、デプロイを昇格させるか拒否する前に、カナリア バージョンとベースラインを比較して確認できます。
このステップ バイ ステップ ガイドでは、Kubernetes マニフェスト タスクのカナリア戦略を使用する方法について説明します。 具体的には、Kubernetes のカナリア デプロイと、コードを評価するための関連ワークフローを設定する方法について説明します。 その後、そのコードを使ってベースラインとカナリア アプリのデプロイを比較し、カナリア デプロイを昇格させるか拒否するかを決定できます。
Azure Kubernetes Service を使っている場合、プライベート クラスターまたはローカル アカウントが無効になっているクラスターに接続する最適な方法は、Azure Resource Manager サービス接続の種類です。
前提条件
- アクティブなサブスクリプションが含まれる Azure アカウント。 無料でアカウントを作成できます。
- GitHub アカウント。 GitHub アカウントをまだお持ちでない場合は、無料の GitHub アカウントを作成できます。
- プッシュ特権を持つ Azure Container Registry。 まだ持っていない場合は、Azure コンテナー レジストリを作成します。
- Kubernetes クラスター。 Azure Kubernetes Service (AKS) クラスターをデプロイします。
サンプル コード
GitHub で次のリポジトリをフォークします。
https://github.com/MicrosoftDocs/azure-pipelines-canary-k8s
このガイドで使うリポジトリ内のファイルの概要を次に示します。
- ./app:
- app.py - Python アプリケーション用の Prometheus インストルメンテーション ライブラリを使ってインストルメント化されたシンプルな Flask ベースの Web サーバー。 カスタム カウンターは、
success_rate
変数の値に基づいて、指定された良い応答と悪い応答の数に設定されます。 - Dockerfile - app.py に行われた変更ごとにイメージをビルドするために使われます。 変更されるたびに、ビルド パイプラインがトリガーされてイメージがビルドされ、コンテナー レジストリにプッシュされます。
- app.py - Python アプリケーション用の Prometheus インストルメンテーション ライブラリを使ってインストルメント化されたシンプルな Flask ベースの Web サーバー。 カスタム カウンターは、
- ./manifests:
- deployment.yml - 前に公開されたイメージに対応する
sampleapp
デプロイ ワークロードの仕様が格納されています。 このマニフェスト ファイルは、デプロイ オブジェクト安定バージョン用だけでなく、ワークロードのベースラインとカナリアのバリエーションを派生するためにも使います。 - service.yml -
sampleapp
サービスを作成します。 このサービスは、前に説明したデプロイ (安定、ベースライン、カナリア) によってスピンアップされたポッドに要求をルーティングします。
- deployment.yml - 前に公開されたイメージに対応する
- ./misc
- service-monitor.yml - ServiceMonitor オブジェクトを設定するために使われます。 このオブジェクトは、Prometheus メトリック スクレイピングを設定します。
- fortio-deploy.yml - fortio デプロイを設定するために使われます。 このデプロイは、後でロード テスト ツールとして使われ、前にデプロイされた
sampleapp
サービスに要求のストリームを送信します。sampleapp
に送信される要求のストリームは、3 つのデプロイ (安定、ベースライン、カナリア) すべてでポッドにルーティングされます。
注意
このガイドでは、コードのインストルメンテーションと監視に Prometheus を使います。 Azure Application Insights などの同等のソリューションを代わりに使用できます。
prometheus-operator をインストールする
クラスターに Prometheus をインストールするには、開発用コンピューターから次のコマンドを使います。 kubectl と Helm がインストールされている必要があり、コンテキストをデプロイ対象のクラスターに設定する必要があります。 後でベースラインとカナリアのメトリックをダッシュボードで視覚化するために使う Grafana は、この Helm チャートの一部としてインストールされます。
最初に、Prometheus Community Kubernetes Helm Charts リポジトリを Helm インストールに追加します。 次に、kube-prometheus スタック、Kubernetes マニフェスト、Grafana ダッシュボード、および Prometheus ルールのコレクションをインストールします。
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update # update local cache
helm install --name sampleapp prometheus-community/kube-prometheus-stack
サービス接続を作成する
- Azure DevOps メニューで [プロジェクトの設定]>[パイプライン]>[サービス接続] に移動します。
- コンテナー レジストリに関連付けられた Docker レジストリ サービス接続を作成します。 その名前を azure-pipelines-canary-k8s にします。
- デプロイ先の Kubernetes クラスターと名前空間に対する Kubernetes サービス接続を作成します。 その名前を azure-pipelines-canary-k8s にします。
注意
Azure Kubernetes Service を使っている場合、プライベート クラスターまたはローカル アカウントが無効になっているクラスターに接続する最適な方法は、Azure Resource Manager サービス接続の種類です。
継続的インテグレーションのセットアップ
[パイプライン]>[パイプラインの作成] に移動して、リポジトリを選びます。
[構成] タブで、[スタート パイプライン] を選びます。
[レビュー] タブで、パイプライン YAML を次のコードに置き換えます。
trigger: - main pool: vmImage: ubuntu-latest variables: imageName: azure-pipelines-canary-k8s steps: - task: Docker@2 displayName: Build and push image inputs: containerRegistry: azure-pipelines-canary-k8s #replace with name of your Docker registry service connection repository: $(imageName) command: buildAndPush Dockerfile: app/Dockerfile tags: | $(Build.BuildId)
作成した Docker レジストリ サービス接続が
example.azurecr.io
に関連付けられている場合、上記の構成に基づいて、イメージはexample.azurecr.io/azure-pipelines-canary-k8s:$(Build.BuildId)
に対するものになります。
マニフェスト ファイルを編集する
manifests/deployment.yml で、<example>
を自分のコンテナー レジストリの URL に置き換えます。 たとえば、置き換えた後のイメージ フィールドは contosodemo.azurecr.io/azure-pipelines-canary-k8s
のようになります。
Azure App Service での GIT による継続的なデプロイ
次のセクションでは、カナリア ステージのデプロイ方法や、手動介入によってカナリアを昇格させるか拒否する方法など、継続的デプロイを設定する手順について説明します。
Deploy canary ステージ
YAML またはクラシックを使ってデプロイできます。
[パイプライン]>[環境]>[環境の作成] に移動します。
環境の新規作成
- 名前: akscanary
- リソース: Kubernetes を選びます。
[次へ] を選んで、次のように Kubernetes リソースを構成します。
- プロバイダー: Azure Kubernetes Service
- Azure サブスクリプション: Kubernetes クラスターを保持するサブスクリプションを選びます。
- クラスター: 自分のクラスターを選びます。
- 名前空間: canarydemo という名前で新しい名前空間を作成します。
[検証と作成] を選びます。
[パイプライン] に移動します。 作成したパイプラインを選んで、[編集] を選びます。
前に作成したステップを、ステージを使うように変更します。 連続するステージで使うための成果物として manifests と misc ディレクトリをコピーするため、さらに 2 つのステップを追加します。 後のパイプラインで簡単に使用できるように、いくつかの値を変数に移動することもできます。 完成した YAML は次のようになります。
trigger: - main pool: vmImage: ubuntu-latest variables: imageName: azure-pipelines-canary-k8s dockerRegistryServiceConnection: dockerRegistryServiceConnectionName #replace with name of your Docker registry service connection imageRepository: 'azure-pipelines-canary-k8s' containerRegistry: example.azurecr.io #replace with the name of your container registry, Should be in the format example.azurecr.io tag: '$(Build.BuildId)' stages: - stage: Build displayName: Build stage jobs: - job: Build displayName: Build pool: vmImage: ubuntu-latest steps: - task: Docker@2 displayName: Build and push image inputs: containerRegistry: $(dockerRegistryServiceConnection) repository: $(imageName) command: buildAndPush Dockerfile: app/Dockerfile tags: | $(tag) - publish: manifests artifact: manifests - publish: misc artifact: misc
カナリア バージョンをデプロイするためのステージを、YAML ファイルの最後に追加します。
- stage: DeployCanary displayName: Deploy canary dependsOn: Build condition: succeeded() jobs: - deployment: Deploycanary displayName: Deploy canary pool: vmImage: ubuntu-latest environment: 'akscanary.canarydemo' strategy: runOnce: deploy: steps: - task: KubernetesManifest@0 displayName: Create imagePullSecret inputs: action: createSecret secretName: azure-pipelines-canary-k8s dockerRegistryEndpoint: azure-pipelines-canary-k8s - task: KubernetesManifest@0 displayName: Deploy to Kubernetes cluster inputs: action: 'deploy' strategy: 'canary' percentage: '25' manifests: | $(Pipeline.Workspace)/manifests/deployment.yml $(Pipeline.Workspace)/manifests/service.yml containers: '$(containerRegistry)/$(imageRepository):$(tag)' imagePullSecrets: azure-pipelines-canary-k8s - task: KubernetesManifest@0 displayName: Deploy Forbio and ServiceMonitor inputs: action: 'deploy' manifests: | $(Pipeline.Workspace)/misc/*
メイン ブランチに直接コミットして、パイプラインを保存します。 このコミットで、パイプラインは既に正常に実行しているはずです。
カナリアを昇格させるか拒否するための手動介入
YAML またはクラシックを使って手動で介入できます。
[パイプライン]>[環境]>[新しい環境] に移動します。
新しい環境を構成します。
- 名前: akspromote
- リソース: Kubernetes を選びます。
[次へ] を選んで、次のように Kubernetes リソースを構成します。
- プロバイダー: Azure Kubernetes Service
- Azure サブスクリプション: Kubernetes クラスターを保持するサブスクリプションを選びます。
- クラスター: 自分のクラスターを選びます。
- 名前空間: 前に作成した名前空間 canarydemo を選びます。
[検証と作成] を選びます。
環境の一覧から新しい
akspromote
環境を選びます。[承認とチェック]>[承認] を選びます。 次に、省略記号アイコン (3 つのドット) を選びます。
次のように承認を構成します。
- 承認者: 自分のユーザー アカウントを追加します。
- 詳細設定: [承認者が自分の実行を承認できるようにします] ボックスがオンになっていることを確認します。
[作成] を選択します
[パイプライン] に移動し、作成したパイプラインを選びます。 次に、 [編集] を選択します。
変更を昇格させるための別のステージ
PromoteRejectCanary
を、YAML ファイルの最後に追加します。- stage: PromoteRejectCanary displayName: Promote or Reject canary dependsOn: DeployCanary condition: succeeded() jobs: - deployment: PromoteCanary displayName: Promote Canary pool: vmImage: ubuntu-latest environment: 'akspromote.canarydemo' strategy: runOnce: deploy: steps: - task: KubernetesManifest@0 displayName: promote canary inputs: action: 'promote' strategy: 'canary' manifests: '$(Pipeline.Workspace)/manifests/*' containers: '$(containerRegistry)/$(imageRepository):$(tag)' imagePullSecrets: '$(imagePullSecret)'
変更をロールバックするための別のステージ
RejectCanary
を、YAML ファイルの最後に追加します。- stage: RejectCanary displayName: Reject canary dependsOn: PromoteRejectCanary condition: failed() jobs: - deployment: RejectCanary displayName: Reject Canary pool: vmImage: ubuntu-latest environment: 'akscanary.canarydemo' strategy: runOnce: deploy: steps: - task: KubernetesManifest@0 displayName: reject canary inputs: action: 'reject' strategy: 'canary' manifests: '$(Pipeline.Workspace)/manifests/*'
[保存] を選んで YAML パイプラインを保存してから、メイン ブランチに直接コミットします。
安定バージョンをデプロイする
YAML またはクラシックを使って安定バージョンをデプロイできます。
パイプラインの最初の実行では、ワークロードの安定バージョンと、そのベースラインまたはカナリア バージョンは、クラスターに存在しません。 安定バージョンをデプロイするには:
- app/app.py で、
success_rate = 5
をsuccess_rate = 10
に変更します。 この変更によってパイプラインがトリガーされ、イメージがビルドされて、コンテナー レジストリにプッシュされます。 また、DeployCanary
ステージもトリガーされます。 akspromote
環境で承認を構成したため、リリースはそのステージを実行する前に待機します。- 実行の概要で、[レビュー]>[承認] を選びます。 これにより、ワークロードの安定バージョン (manifests/deployment.yml 内の
sampleapp
のデプロイ) が名前空間にデプロイされます。
カナリア ワークフローを開始する
ワークロード sampleapp
の安定バージョンがクラスターに存在するようになりました。 次に、シミュレーション アプリケーションに対して次の変更を行います。
app/app.py で、success_rate = 10
を success_rate = 20
に変更します。
この変更によりビルド パイプラインがトリガーされ、イメージがビルドされてコンテナー レジストリにプッシュされます。 このプロセスによってリリース パイプラインがトリガーされて、Deploy canary ステージが開始されます。
要求をシミュレートする
開発用コンピューターで次のコマンドを実行し、それを実行したままにして、sampleapp
サービスで一定の要求ストリームを送信します。 sampleapp
によって、要求は、安定デプロイ sampleapp
によってスピンアップされたポッドと、デプロイ sampleapp-baseline
と sampleapp-canary
によってスピンアップされたポッドにルーティングされます。 sampleapp
に対して指定したセレクターを、これらのすべてのポッドに適用できます。
FORTIO_POD=$(kubectl get pod | grep fortio | awk '{ print $1 }')
kubectl exec -it $FORTIO_POD -c fortio /usr/bin/fortio -- load -allow-initial-errors -t 0 http://sampleapp:8080/
Grafana ダッシュボードを設定する
Grafana にアクセスできるように、ローカル開発用コンピューターで次のポート転送コマンドを実行します。
kubectl port-forward svc/sampleapp-grafana 3000:80
ブラウザーで、次の URL を開きます。
http://localhost:3000/login
資格情報の入力を求められたら、
prometheus-operator
Helm チャートのインストール中にadminPassword
の値がオーバーライドされていない限り、次の値を使用できます。- ユーザー名: admin
- パスワード: prom-operator
左側のメニューから、[+]>[ダッシュボード]>[グラフ] を選びます。
新しく追加されたパネルの任意の場所を選び、「
e
」と入力してパネルを編集します。[メトリック] タブで、次のクエリを入力します。
rate(requests_total{pod=~"sampleapp-.*", custom_status="good"}[1m])
[全般] タブで、このパネルの名前を「すべての sampleapp ポッド」に変更します。
ページの上部にある概要バーで、期間の範囲を [過去 5 分] または [過去 15 分] に変更します。
このパネルを保存するには、概要バーの保存アイコンを選びます。
前のパネルに、すべてのバリエーションの成功率メトリックが表示されます。 これには、安定 (
sampleapp
デプロイから)、ベースライン (sampleapp-baseline
デプロイから)、カナリア (sampleapp-canary
デプロイから) が含まれます。 次の構成で別のパネルを追加することで、ベースラインとカナリアのメトリックのみを表示できます。- [全般] タブで、[タイトル] を「sampleapp のベースラインとカナリア」にします。
- [メトリック] タブで、次のクエリを使います。
rate(requests_total{pod=~"sampleapp-baseline-.*|sampleapp-canary-.*", custom_status="good"}[1m])
注意
ベースラインとカナリアのメトリックのパネルには、特定の条件下での比較に使用できるメトリックのみが表示されます。 これらの条件は、Deploy canary ステージが正常に完了し、Promote/reject canary ステージが手動介入を待機している場合です。
ヒント
Deploy canary と Promote/reject canary のステージ完了イベントを視覚的に示すため、Grafana ダッシュボードの注釈を設定します。 これは、それぞれ、ベースラインとカナリアの比較を開始するタイミングと、カナリアの昇格または拒否が完了したタイミングを把握するのに役立ちます。
ベースラインとカナリアを比較する
この時点で、Deploy canary ステージは正常に完了しています (
success_rate
が10
から20
に変化したので)。 Promote/reject canary ステージは、手動介入を待機しています。 Grafana ダッシュボードで、ベースラインとカナリアのバリエーションの (custom_status=good
によって決定される) 成功率を比較できます。 次のようになっているはずです。カナリアの成功率が高いという観察に基づいて、カナリアを昇格させます。 手動介入タスクで [再開] を選びます。