Help us understand the problem. What is going on with this article?

Spinnaker+Kayentaでカナリアリリース Pipeline編 (local Kubernetes)

More than 1 year has passed since last update.

はじめに

前回、Local環境にSpinnakerとKayentaを導入しました。
https://qiita.com/sugimount/items/0627f99b1bdabf511927

今回は、Spinnaker上にCanaryReleaseを行うための、Pipelineを設定します。

全体像

Kubernetesで何らかのサービスを提供している時の構成を、以下の構成と仮定します。

画像の左側に記載しているClientが、Kubernetes上のServiceを経由して、Deployment Prodで作成されるPodへアクセスする構成です。
ProdはVersion 1で稼働しています。
1104_4301.png

SpinnakerでCanaryReleaseを行う際には、新しいバージョン(Version 2)と現行バージョン(Version 1)のMetricを比較することにより、新しいバージョンに問題が無い事を確認する手法になります。Spinnakerを開発したNetflixやGoogleは、BaselineとCanaryを新たにDeployする方法を推奨しています。
もちろん、本番で稼働している環境を使用してMetric比較することは出来ますが、長時間稼働しているProdと、新たに作成するCanary間では、Metricの信頼度が異なる場合があるため、新たにBaselineを作成する構成を推奨しています。

DeploymentのProd, Baseline, Canary を同じServiceにぶら下げることにより、Podの数によってトラフィックを分散させる方法です。なお、Istioのように柔軟にトラフィックコントロールが出来るものを取り入れると、数ではなくより柔軟にコントロールが出来るのではないかと予想しています。
1180_4341.png

Canary分析に問題がなければ、Prod環境を全てVersion2に移行し、BaselineとCanaryは削除することにより、安全にリリースすることが出来ます。
1050_4131.png

PIPELINE構築

Application作成

SpinnakerでPIPELINEを定義するには、Applicationと呼ばれる枠組みを作成する必要があります。
Applicationメニューから、Create Application をクリック。Spinnaker上で、アプリケーションという枠組みを作成します。 (Kubernetesクラスタは何も変わりません)
1428_7581.png

適当に、Nameと Email を入力
615_5051.png

Applicationが作成されました。初期状態では、Canaryは有効になっていないので、有効にするためにCONFIGを選択します
1423_7571.png

FEATURES から Canary を選択して、Save Changes
1421_7581.png

画面を更新するために、一度Searchメニューを選択
1423_7611.png

Recently viewedから、作成したApplication 「testcanary2」 を選択します
1418_7521.png

元々、PIPELINE というメニューが、DELIVERY に変化しています。
1423_7211.png

CanaryConfig作成

DELIVERY - CANARY CONFIG メニューを開きます
1425_7581.png

Add configurationを選択
Canary分析の時に、何のMetricをどのように分析するかを設定します
1422_6901.png

今回はtest用にCPUのMetricを使用してカナリー分析を行います。
実際の本番環境では、アプリケーションの挙動に関するMetricをカナリー分析で利用すると良いです。
Webアプリケーションの場合は、HTTPステータスコード、応答時間、例外数、負荷平均などを確認すると良いとおもいます。

適当に Configuration Nameを入れます。 
Name: cpu-canary
1423_7521.png

まず、FILTER TEMPLATES で Add Templateを押します。
Canary分析のために、今回はPrometheusを使用しますが、Prometheusで収集しているMetricから、今回のアプリケーションで該当しているMetricのみ取得するようにFilterする設定となります。
1412_6741.png

以下のパラメータを入力します。
Name : cpu_filtertemplate
Template : pod_name=~"spinnaker-${scope}.*"
607_2571.png

上記のパラメータは、Canary分析で利用するMetricDatastoreに依存します。今回は、Prometheusを利用するため、Prometheusで「何のMetric」を「どのようにFilterする」というのを考えながら設定する必要があります。
今回は、testとしてPrometheusの「container_cpu_usage_seconds_total」のMetricを利用します。
下記の画像は、Prometheus上で「container_cpu_usage_seconds_total」のMetric表示させています
1587_7371.png

上記のMetricには、Labelに pod_name が付与されていて、該当のPod名前でFilterを行う事が出来ます。
Prometheus上でFilterを行うと、以下の式を指定すると、Podの名前が[spinnaker-prod]で始まるもののみ取得するようにFilterを掛けることが出来ます。
container_cpu_usage_seconds_total{pod_name=~"spinnaker-prod.*"}
Spinnaker上のFilterTemplateには、この式の {} 内に記載する文字列を指定します。
1587_5551.png

SpinnakerのFilter Templateには「pod_name=~"spinnaker-${scope}.*"」と指定していますが、scopeは、Pipeline上の変数を指定する形式のなります。
scopeは後述しますが、baselineという文字と、canaryという文字が入ります。これにより、Prometheus上のMetricを正しくFilterして評価することが出来ます。
609_2441.png

次に、Canary分析時に、どのMetricを使用するかを指定します。METRICSメニューで該当のMetricをグループ分けを行うことが出来ます。グループ分けを行うことで、グループごとにMetric評価の重みを付けることが出来ます。
DefaultでGROUP1が存在するので、これの名前を変更します。
1423_7581.png

CPUと名前を変更します
1424_7541.png

CPUグループ内で、Add Metricを押します
1417_7041.png

以下のパラメータを入力します。

  • Name : cpu_utils 任意の名前を指定します。
  • Fail on : CanaryとBaselineを比較した時に、Failとする条件を指定します。以下の3パターンあります。
    • Increase : 比較して増加している場合はFail
    • Decrease : 比較して減少している場合はFail
    • Either : 増加・減少の両方ともFail
  • Criticality : このMetricをFailと判断した場合は、即座にCanary分析を失敗とします。最重要なMetricが失敗したときの挙動の設定の用途です。
  • NaN Storategy : Metricが存在しない場合の処理内容を指定。Default(remove) は削除して計算を行う。
  • Metric Name : Prometheus上のMetricの名前を指定
    1581_7281.png

追加されます。
1578_6861.png

最後に、各グループの評価の重みづけを行います。
合計で100になるように指定します。今回は1グループしか存在しないので、100を指定します。
Canary分析のJudgeアルゴリズムは「NetflixACAJudge-v1.0」となっており、これは、指定した全てのMetricが成功した割合を基に、判定を行います。
例えばPassを75%と指定している場合は、100個のMetricのうち、75個が成功(pass)している場合は、Canary分析を成功と判断します。
1443_7631.png

CanaryConfigが作成されます。
1434_7571.png

Pipeline設定 初期設定

PIPELINESメニューを選択し、Configure a new pipeline を選びます。
1427_7531.png

名前を入れます。
623_2401.png

Pipelineの全体像で書いたように、以下のStepを作成していきます。
Step 1. Baseline(現行バージョン)とCanary(新バージョン)のDeploymentをDeploy
Step 2. Canary分析
Step 3. Canary分析をPassした場合は、Prodを新バージョンに入れ替え
Step 4. BaselineとCanaryを削除

まずは、Pipelineの初期設定(Configuration)を設定します。
Pipelineのトリガーは、DockerHubへ新たなImageがBuildされたことをトリガーにします。
Add Trigger を選択
1422_7591.png

Triggerに、DockerRegistoryを選択
1420_7291.png

DockerHubのRegistoryの場所を指定します。DockerHubのRegistory自体は、Halyardで設定しています。
1429_7511.png

Add Artifactを選択します。
1419_7141.png

Dockerを選択
1420_7221.png

DockerImageに、Trigger元に含まれているであろう情報を指定し、Save Changes を押します。
index.docker.io/sugimount/spin-kub-v2-demo
index.docker.io はDockerHubです。
1434_7651.png

Pipeline設定 Baseline, CanaryのDeploy

次に、Step 1 として、Kubernetesクラスタ上に、CanaryとBaselineのDeploymentを作成します。
クラスタ内には既に本番環境として Service や Deploymentが存在しています。

PIPELINE で Add stage を選択します。stage は、pipelineの中で、実行するタスクの単位となります。
1432_7531.png

Stageが追加されます。Deploy (Manifest) を選択します。注意点としてはManifestを直接扱う「Deploy (Manifest)」は、現在開発中のため仕様が変わる可能性があります。
1425_7611.png

Stage Name を入力
1430_7601.png

Manifestを指定する方法は、直接Textで指定する方法と、Artifactとして外部参照する方法があります。Artifactで参照する方法は、制限事項がおおく扱いにくいため、Textで扱う方式とします。
TextareaにManifestファイルを直接指定します。
1419_7621.png

張り付けるManifestファイルの例

- apiVersion: apps/v1beta2
  kind: Deployment
  metadata:
    labels:
      app: spinnaker
      source: demo
      stack: frontend
    name: spinnaker-canary
    namespace: default
  spec:
    replicas: 1
    selector:
      matchLabels:
        app: spinnaker
        source: demo
    template:
      metadata:
        labels:
          app: spinnaker
          source: demo
      spec:
        containers:
          - envFrom:
              - configMapRef:
                  name: spinnaker-demo-config
            image: index.docker.io/sugimount/spin-kub-v2-demo
            name: primary
            ports:
              - containerPort: 80
            readinessProbe:
              httpGet:
                path: /
                port: 80

Req. Artifacts To Bindを選択します。これにより、Textで指定したManifestファイルのimageの指定が、Trigger元のDockerHubのイメージtagに書き換えられます。
※ 書き換えには、Textで指定したイメージ名と、Trigger元のイメージ名が一致している必要があります。
Text : index.docker.io/sugimount/spin-kub-v2-demo
Trigger元 : index.docker.io/sugimount/spin-kub-v2-demo:0.0.28
書き換え : index.docker.io/sugimount/spin-kub-v2-demo:0.0.28
1425_7941.png

次に、BaselineをDeployするStageを設定します。
まず、現行環境のprodで使用しているイメージのタグ情報を取得するstageを追加します。
Add stage を選択し、Typeを選びます。
1421_7781.png

Stage Name と Depends On を選択します。
1415_7941.png

KubernetesクラスタからImage情報を取得するための、パラメータを指定します。
1426_8431.png

指定した条件を基に、下位のStageで使用するArtifact情報を生成します。
Produces Artifacts から、 Add Artifact を選択
1420_8261.png

Dockerの指定と、Docker image を指定します
index.docker.io/sugimount/spin-kub-v2-demo
1425_8351.png

これで、Prod環境で使用しているImage情報を、下位のStageへArtifactとしてお知らせする準備が出来たため、Baseline Deploymentを作成するStageを作ります。
Add stage
1437_7491.png

Typeなどを指定します
1431_8441.png

Manifestの内容をTextとして指定します
1411_8471.png

Manifestファイルを書き換える情報をbindします。どちらが find で生成した Artifact か判断できませんが、基本的には下側になっていると思います。下側を選びます
後程Pipelineの動作確認を行います。
1431_8301.png

保存します。
1441_7971.png

Pipelineの動作確認を行います。
Pipelineのメニューを開き、Start Manual Excution を選択します。
※ Pipeline のTriggerをDockerHub (SpinnakerがDockerHubをPullする形)を指定しているので、手動でPipelineを実行することが出来ますが、GitHubからWebHookでPushされる形にすると、基本的には手動実行は出来ません。Pushされる形で手動実行すると、Artifactが生成されないため、手動実行に失敗します。
1414_8231.png

対象のImageIDを指定します。
621_3391.png

実行されています。
1442_7741.png

正しく生成されています。IMAGES の列で、baselineとcanaryで正しいtagが使用されていることを確認します。

root@qicoo-k8s-master01(default kubernetes-admin):~# kubectl get deployments -o wide
NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE       CONTAINERS    IMAGES                                              SELECTOR
spinnaker-baseline   1         1         1            1           50s       primary       index.docker.io/sugimount/spin-kub-v2-demo:0.0.27   app=spinnaker,source=demo
spinnaker-canary     1         1         1            1           50s       primary       index.docker.io/sugimount/spin-kub-v2-demo:0.0.28   app=spinnaker,source=demo
spinnaker-prod       1         1         1            1           12h       primary       index.docker.io/sugimount/spin-kub-v2-demo:0.0.27   app=spinnaker,source=demo

Pipelineの実行が終了しています。
1440_7641.png

Pipeline設定 CanaryAnalysis

Canary分析を行います。
PipelineのConfigureを押します。
1432_7901.png

Add stage
1425_8491.png

Stageのパラメータを入力します。
1429_8561.png

Canary Config を入力します。

  • Acalysis Type : Real Time
  • Config Name : 作成済みのCanary Configを指定 (cpu-canary)
  • Interval : 1 minutes
  • Lookback Type : Growing
  • Baseline : baseline (ここで指定する文字列が、PrometheusからMetricを取得する際の、変数scope の値となる。CanaryConfigの中にあるFilterTemplateに使用される)
  • Baseline Location : default (これも同様に、FilterTemplate内で使用できるが、現在は使用していない。変数locationの値)
  • Canary : canary (ここで指定する文字列が、PrometheusからMetricを取得する際の、変数scope の値となる。CanaryConfigの中にあるFilterTemplateに使用される)
  • CanaryLocation : default (これも同様に、FilterTemplate内で使用できるが、現在は使用していない。変数locationの値)
  • Lifetime : 0 hours 1minutes (Canary分析で分析を行う期間を指定。推奨としては3時間といった長い期間が推奨されている。) 1418_8111.png

Marginal : 50 (50%以上のScoreの場合は、人の目による判断を行う設定方法が出来る)
Pass : 75 (75%以上のScoreが出ると成功とする)
1437_7981.png

CanaryPipelineの動作確認
1431_8431.png

前回は、0.0.28を使用したため、今回はtestで0.0.27 を使用
609_3321.png

実行されています。
1428_7021.png

正常に実行出来た場合は、CANARY REPORTS のページで、カナリー分析の詳細を確認できます。
1425_8451.png

Pipeline設定 Prod環境へDeploy

Step3. Canary分析が成功した場合は、Prod環境をCanaryで使用したImageに置き換えます。
Add stage
1427_8261.png

stageのパラメータ入力
1417_8181.png

Manifestファイルを指定
1427_8551.png

ArtifactのBindを指定。上側の物が、Canary用の新しいバージョンだとおもいます
1435_8731.png

Pipeline設定 Baseline, Canaryの削除

Step 4. として、BaselineとCanaryで作成したDeploymentを削除します。
Add stage
1429_8351.png

Stageのパラメータ入力
1441_8481.png

削除対象のDeploymentのパラメータを指定し、保存します。
1441_8101.png

同様にCanaryも削除します。
Add stage
1428_8231.png

Stageのパラメータを指定します。
1434_7751.png

削除対象の情報を指定し、保存します。
1433_8071.png

動作確認のために、手動でPipelineを実行します
1435_8341.png

0.0.28を指定
615_3371.png

Baseline, CanaryのDeploy後のKubernetesの状態↓

root@qicoo-k8s-master01(default kubernetes-admin):~# kubectl get deployments -o wide
NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE       CONTAINERS    IMAGES                                              SELECTOR
spinnaker-baseline   1         1         1            1           49m       primary       index.docker.io/sugimount/spin-kub-v2-demo:0.0.27   app=spinnaker,source=demo
spinnaker-canary     1         1         1            1           49m       primary       index.docker.io/sugimount/spin-kub-v2-demo:0.0.28   app=spinnaker,source=demo
spinnaker-prod       1         1         1            1           13h       primary       index.docker.io/sugimount/spin-kub-v2-demo:0.0.27   app=spinnaker,source=demo

ProdをCanary用イメージに入れ替え後↓

root@qicoo-k8s-master01(default kubernetes-admin):~# kubectl get deployments -o wide
NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE       CONTAINERS    IMAGES                                              SELECTOR
spinnaker-baseline   1         1         1            1           50m       primary       index.docker.io/sugimount/spin-kub-v2-demo:0.0.27   app=spinnaker,source=demo
spinnaker-canary     1         1         1            1           50m       primary       index.docker.io/sugimount/spin-kub-v2-demo:0.0.28   app=spinnaker,source=demo
spinnaker-prod       1         2         1            1           13h       primary       index.docker.io/sugimount/spin-kub-v2-demo:0.0.28   app=spinnaker,source=demo

Baseline, Canaryを削除後↓

root@qicoo-k8s-master01(default kubernetes-admin):~# kubectl get deployments -o wide
NAME             DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE       CONTAINERS    IMAGES                                              SELECTOR
spinnaker-prod   1         1         1            1           13h       primary       index.docker.io/sugimount/spin-kub-v2-demo:0.0.28   app=spinnaker,source=demo

正常に実行されたことを確認できます
1417_7081.png

まとめ

SpinnakerとKayentaを利用して、CanaryReleaseを行うPipelineを確認出来ました。ただ、まだ以下の課題があると考えています。

  • Pipelineのコード化
  • CanaryAnalysisで利用するべきMetricの選択
  • MetricをPassかFailか判断するときのアルゴリズムが不明。(どれくらいの差であればPassとなるのか?Marginは設定可能なのか?)

Pipelineのコード化は、spincliで構成する方法があるようですので、これを確認したいと考えています。
CanaryAnalysisで利用するべきMetricについては、アプリケーションへの依存している部分が大きいため、アプリケーション専用のExporterを準備するやり方が良いのではないかと考えています。こちらもどういうやり方が良いかふくめて、引き続き検討したいと思います。

sugimount
CloudNativeな色々をやっています / 投稿している内容は個人的な見解なので、所属組織とは関係ありません https://twitter.com/sugimount
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away