はじめに
前回、Local環境にSpinnakerとKayentaを導入しました。
https://qiita.com/sugimount/items/0627f99b1bdabf511927
今回は、Spinnaker上にCanaryReleaseを行うための、Pipelineを設定します。
全体像
Kubernetesで何らかのサービスを提供している時の構成を、以下の構成と仮定します。
画像の左側に記載しているClientが、Kubernetes上のServiceを経由して、Deployment Prodで作成されるPodへアクセスする構成です。
ProdはVersion 1で稼働しています。
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のように柔軟にトラフィックコントロールが出来るものを取り入れると、数ではなくより柔軟にコントロールが出来るのではないかと予想しています。
Canary分析に問題がなければ、Prod環境を全てVersion2に移行し、BaselineとCanaryは削除することにより、安全にリリースすることが出来ます。
PIPELINE構築
Application作成
SpinnakerでPIPELINEを定義するには、Applicationと呼ばれる枠組みを作成する必要があります。
Applicationメニューから、Create Application をクリック。Spinnaker上で、アプリケーションという枠組みを作成します。 (Kubernetesクラスタは何も変わりません)
Applicationが作成されました。初期状態では、Canaryは有効になっていないので、有効にするためにCONFIGを選択します
FEATURES から Canary を選択して、Save Changes
Recently viewedから、作成したApplication 「testcanary2」 を選択します
元々、PIPELINE というメニューが、DELIVERY に変化しています。
CanaryConfig作成
DELIVERY - CANARY CONFIG メニューを開きます
Add configurationを選択
Canary分析の時に、何のMetricをどのように分析するかを設定します
今回はtest用にCPUのMetricを使用してカナリー分析を行います。
実際の本番環境では、アプリケーションの挙動に関するMetricをカナリー分析で利用すると良いです。
Webアプリケーションの場合は、HTTPステータスコード、応答時間、例外数、負荷平均などを確認すると良いとおもいます。
適当に Configuration Nameを入れます。
Name: cpu-canary
まず、FILTER TEMPLATES で Add Templateを押します。
Canary分析のために、今回はPrometheusを使用しますが、Prometheusで収集しているMetricから、今回のアプリケーションで該当しているMetricのみ取得するようにFilterする設定となります。
以下のパラメータを入力します。
Name : cpu_filtertemplate
Template : pod_name=~"spinnaker-${scope}.*"
上記のパラメータは、Canary分析で利用するMetricDatastoreに依存します。今回は、Prometheusを利用するため、Prometheusで「何のMetric」を「どのようにFilterする」というのを考えながら設定する必要があります。
今回は、testとしてPrometheusの「container_cpu_usage_seconds_total」のMetricを利用します。
下記の画像は、Prometheus上で「container_cpu_usage_seconds_total」のMetric表示させています
上記のMetricには、Labelに pod_name が付与されていて、該当のPod名前でFilterを行う事が出来ます。
Prometheus上でFilterを行うと、以下の式を指定すると、Podの名前が[spinnaker-prod]で始まるもののみ取得するようにFilterを掛けることが出来ます。
container_cpu_usage_seconds_total{pod_name=~"spinnaker-prod.*"}
Spinnaker上のFilterTemplateには、この式の {} 内に記載する文字列を指定します。
SpinnakerのFilter Templateには「pod_name=~"spinnaker-${scope}.*"」と指定していますが、scopeは、Pipeline上の変数を指定する形式のなります。
scopeは後述しますが、baselineという文字と、canaryという文字が入ります。これにより、Prometheus上のMetricを正しくFilterして評価することが出来ます。
次に、Canary分析時に、どのMetricを使用するかを指定します。METRICSメニューで該当のMetricをグループ分けを行うことが出来ます。グループ分けを行うことで、グループごとにMetric評価の重みを付けることが出来ます。
DefaultでGROUP1が存在するので、これの名前を変更します。
以下のパラメータを入力します。
- 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の名前を指定
最後に、各グループの評価の重みづけを行います。
合計で100になるように指定します。今回は1グループしか存在しないので、100を指定します。
Canary分析のJudgeアルゴリズムは「NetflixACAJudge-v1.0」となっており、これは、指定した全てのMetricが成功した割合を基に、判定を行います。
例えばPassを75%と指定している場合は、100個のMetricのうち、75個が成功(pass)している場合は、Canary分析を成功と判断します。
Pipeline設定 初期設定
PIPELINESメニューを選択し、Configure a new pipeline を選びます。
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 を選択
DockerHubのRegistoryの場所を指定します。DockerHubのRegistory自体は、Halyardで設定しています。
DockerImageに、Trigger元に含まれているであろう情報を指定し、Save Changes を押します。
index.docker.io/sugimount/spin-kub-v2-demo
index.docker.io はDockerHubです。
Pipeline設定 Baseline, CanaryのDeploy
次に、Step 1 として、Kubernetesクラスタ上に、CanaryとBaselineのDeploymentを作成します。
クラスタ内には既に本番環境として Service や Deploymentが存在しています。
PIPELINE で Add stage を選択します。stage は、pipelineの中で、実行するタスクの単位となります。
Stageが追加されます。Deploy (Manifest) を選択します。注意点としてはManifestを直接扱う「Deploy (Manifest)」は、現在開発中のため仕様が変わる可能性があります。
Manifestを指定する方法は、直接Textで指定する方法と、Artifactとして外部参照する方法があります。Artifactで参照する方法は、制限事項がおおく扱いにくいため、Textで扱う方式とします。
TextareaにManifestファイルを直接指定します。
張り付ける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
次に、BaselineをDeployするStageを設定します。
まず、現行環境のprodで使用しているイメージのタグ情報を取得するstageを追加します。
Add stage を選択し、Typeを選びます。
Stage Name と Depends On を選択します。
KubernetesクラスタからImage情報を取得するための、パラメータを指定します。
指定した条件を基に、下位のStageで使用するArtifact情報を生成します。
Produces Artifacts から、 Add Artifact を選択
Dockerの指定と、Docker image を指定します
index.docker.io/sugimount/spin-kub-v2-demo
これで、Prod環境で使用しているImage情報を、下位のStageへArtifactとしてお知らせする準備が出来たため、Baseline Deploymentを作成するStageを作ります。
Add stage
Manifestファイルを書き換える情報をbindします。どちらが find で生成した Artifact か判断できませんが、基本的には下側になっていると思います。下側を選びます
後程Pipelineの動作確認を行います。
Pipelineの動作確認を行います。
Pipelineのメニューを開き、Start Manual Excution を選択します。
※ Pipeline のTriggerをDockerHub (SpinnakerがDockerHubをPullする形)を指定しているので、手動でPipelineを実行することが出来ますが、GitHubからWebHookでPushされる形にすると、基本的には手動実行は出来ません。Pushされる形で手動実行すると、Artifactが生成されないため、手動実行に失敗します。
正しく生成されています。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設定 CanaryAnalysis
Canary分析を行います。
PipelineのConfigureを押します。
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時間といった長い期間が推奨されている。)
Marginal : 50 (50%以上のScoreの場合は、人の目による判断を行う設定方法が出来る)
Pass : 75 (75%以上のScoreが出ると成功とする)
前回は、0.0.28を使用したため、今回はtestで0.0.27 を使用
正常に実行出来た場合は、CANARY REPORTS のページで、カナリー分析の詳細を確認できます。
Pipeline設定 Prod環境へDeploy
Step3. Canary分析が成功した場合は、Prod環境をCanaryで使用したImageに置き換えます。
Add stage
ArtifactのBindを指定。上側の物が、Canary用の新しいバージョンだとおもいます
Pipeline設定 Baseline, Canaryの削除
Step 4. として、BaselineとCanaryで作成したDeploymentを削除します。
Add stage
削除対象のDeploymentのパラメータを指定し、保存します。
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
まとめ
SpinnakerとKayentaを利用して、CanaryReleaseを行うPipelineを確認出来ました。ただ、まだ以下の課題があると考えています。
- Pipelineのコード化
- CanaryAnalysisで利用するべきMetricの選択
- MetricをPassかFailか判断するときのアルゴリズムが不明。(どれくらいの差であればPassとなるのか?Marginは設定可能なのか?)
Pipelineのコード化は、spincliで構成する方法があるようですので、これを確認したいと考えています。
CanaryAnalysisで利用するべきMetricについては、アプリケーションへの依存している部分が大きいため、アプリケーション専用のExporterを準備するやり方が良いのではないかと考えています。こちらもどういうやり方が良いかふくめて、引き続き検討したいと思います。