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


はじめに

前回、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を準備するやり方が良いのではないかと考えています。こちらもどういうやり方が良いかふくめて、引き続き検討したいと思います。