概要
Kubernetes で GitOps を実現するツールである Argo CD
と、Kubernetes で blue/grenn デプロイや canary リリースを実現するためのデプロイメントコントローラーであるArgo Rollouts
を使用して、canaryリリースの動作検証をしてみます。
Argo Rollouts と Nginx Ingress Controller の連携の必要性
Kubernetes は標準でトラフィックミラーリング
やヘッダーによるルーティング
やパーセンテージ指定による負荷分散
などの細かいトラフィック管理を実現できるような機能はありません。
Kubernetesでアプリケーションの異なるバージョンへのトラフィックの割合を制御する唯一の方法は、svc のセレクターに基づいて pod のグループにトラフィックをルーティングするエンドポイントを提供することです。つまりバージョンのレプリカ数を操作することで実現できる単純な負荷分散です。
この負荷分散方式だと Pod 単位 での割合でしか分散することができません。Argo Rollouts
も Kubernetesの標準機能を使用しているので、実現できる Canaryデプロイメントもこの程度です。
しかしArgo Rollouts
は、サービスメッシュリソースを操作してロールアウトの目的に一致させることにより、きめ細かいトラフィック管理を実現することが可能です。Argo Rollouts は現在次のサービスメッシュをサポートしています。
- Istio
- Nginx Ingress Controller
- AWS ALB Ingress Controller
- Service Mesh Interface(SMI)
私の環境ではNginx Ingress Controller
にて各サービスへのトラフィック管理をしているため、Nginx Ingress Controller と Argo Rollouts を連携させることで、きめ細かいトラフィック管理ができる canaryデプロイメントを実現します。
Nginx Ingress Controller はそれ単体でトラフィックミラーリング
やヘッダーによるルーティング
やパーセンテージ指定による負荷分散
を使用したcanary の仕組みが備わっており、アプリケーションの異なるバージョン間のトラフィック管理のために、いくつかの特別なアノテーションを備えた2つ目のIngressオブジェクト(Canary Ingress)を導入することにより、トラフィックを分割する機能を提供します。
Argo Rollouts Controller は、動的に Canary Ingress を生成することで、Nginx Ingress Controller と連携します。
構成図
基本の構成はこのようになります。このうちtomozo-canary
Ingressは、Argo Rollouts Controller に自動生成されることになります。
各マニフェスト
Ingress
Nignx Ingress Controller用 の Ingress です。argo rollouts
を使用することで追加で必要な設定は特にありません。
くどいようですが、canary用のIngressは自動生成されるため、ここで定義する必要はありません。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: tomozo
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
rules:
- host: tomozo.tokyo
http:
paths:
- backend:
serviceName: tomozo-stable
servicePort: 80
path: /v1/tomozo/.*
Service
通常(安定版)用の Service として tomozo-stable
。Canary用の Service として tomozo-canary
を定義します。名前以外は全て同じ設定値で問題ありません。
---
kind: Service
apiVersion: v1
metadata:
name: tomozo-stable
spec:
selector:
app: tomozo
ports:
- port: 80
---
kind: Service
apiVersion: v1
metadata:
name: tomozo-canary
spec:
selector:
app: tomozo
ports:
- port: 80
rollout
strategy:
より上は deployment と書き方変わりません。大事なのは trafficRouting で nginx を指定しているところです。
stableIngress
でしていた Ingress をベースに、 Canary-Ingress を自動生成します。
またannotationPrefix
,additionalIngressAnnotations
で指定した内容が、自動生成される Canary-Ingressに設定されます。
私の場合は、 Ingress で正規表現を使用しているのでuse-regex: 'true'
を追加しました。
また Nginx の canary 用として、以下の4つの annotation があります。
Name | Description |
---|---|
canary-weigh | パーセンテージを指定し、特定の割合でルーティング先を変えることができます |
canary-by-cookie | 特定のCookieの名前を設定しておくことで、そのCookieの値がリクエストに含まれている際に、アクセスを新バージョン側に振ることができる |
canary-by-header | 特定のHeaderの名前を設定しておくことで、そのHeaderの値がリクエストに含まれている際に、アクセスを新バージョン側に振ることができる |
canary-by-header-value | canary-by-header で指定したHeaderの値が、ここで指定した値の場合に、アクセスを新バージョン側に振ることができます |
carry-weigh
については、strategy.canary.steps.setWeight
で指定した内容が反映されるため、ここであえて指定する必要はありません。
今回は canary-by-header
とcanary-by-header-value
を設定してみました。
---
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: tomozo
labels:
app: tomozo
spec:
selector:
matchLabels:
app: tomozo
template:
metadata:
labels:
app: tomozo
spec:
serviceAccountName: tomozo
containers:
- name: tomozo
image: tomozo-repo/tomozo:0.0.1
# -----この行から上は元のDeploymentと一緒------
# -----この行から下はRolloutの拡張部分------
strategy:
canary:
stableService: tomozo-stable
canaryService: tomozo-canary
trafficRouting:
nginx:
stableIngress: tomozo
annotationPrefix: nginx.ingress.kubernetes.io
additionalIngressAnnotations:
use-regex: 'true'
canary-by-header: 'X-Canary'
canary-by-header-value: 'true'
steps:
- setWeight: 10
- pause: {}
Canary デプロイメントの流れ
steps:
- setWeight: 10
- pause: {}
rollout のマニフェストで設定したstrategy.canary.steps
の場合、どのような流れになるかを簡単な図で追っていきます。
通常時
通常時はこのようになっており、 アクセスは Service: tomozo-stable
を通って ver.1の pod にルーティングされます。
Service: tomozo-canary
もver.1 の pod にルーティングされていますが、Nginxからはアクセスを振っていません。
アプリケーションのリリース時
コンテナのタグを ver.2 にしたマニフェストを反映させると、ver.2用の ReplicaSet が作成され、 Service:tomozo-canary
からルーティングされます。また wetWeight
の値が Nginx に反映され、 10%のアクセスが ver.2 のアプリケーションにルーティングされます。
アプリケーションのリリース完了
strategy.canary.steps
で設定している pause を解除すると、ver.2のアプリケーションが Service:tomozo-stable
からもルーティングされるようになり、 ver.1のアプリケーションは削除されます。これでリリース完了となります。
なお、pauseの解除は コマンドでもできますし、 ArgoCD のGUI上からも操作可能です。
終わりに
Argo Rollouts を Nginx Ingress Controller と連携させることにより、お手軽かつきめ細かいCannaryデプロイメントを実現することができました。