Argo Rollouts と ALB Ingress Controller による Canary デプロイメント

# 概要
Kubernetes で GitOps を実現するツールである Argo CDと、Kubernetes で blue/grenn デプロイや canary リリースを実現するためのデプロイメントコントローラーであるArgo Rolloutsを使用して、canaryリリースの動作検証をしてみます。

前回 Argo Rollouts(以下 Rollouts とします。) と Nginx Ingress Controller(以下 Nginx とします。) の連携の記事を書きました。今回は ALB Ingress Controller(以下 ALB とします。)版となります。

説明が Nginx とかぶる部分があるので、ここでは ALB 特有の設定について説明します。

前回の記事(**Argo Rollouts と Nginx Ingress Controller による Canary デプロイメント**)
https://qiita.com/tomozo6/items/1bfc65a86a528f63d205


# 各マニフェスト
## Ingress
ALB用の Ingress です。
Nginx と Rollouts を連携させたときは、 ingress についてはあまり特殊な設定をすることは無かったのですが、 ALB の場合は ingress の設定がキモになります。

```ing.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: tomozo
  namespace: tomozo
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/security-groups: tomozo-securitygroup-arn
    alb.ingress.kubernetes.io/certificate-arn: tomozo-certificate-arn
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
    alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_302"}}'
    alb.ingress.kubernetes.io/conditions.x-canary-header: '[{"Field":"http-header","HttpHeaderConfig":{"HttpHeaderName": "X-Canary", "Values":["true"]}}]'
    alb.ingress.kubernetes.io/actions.x-canary-header: '{"Type":"forward","ForwardConfig":{"TargetGroups":[{"ServiceName":"tomozo-canary","ServicePort":"8080"}]}}'
spec:
  rules:
  - host: tomozo.tokyo
    http:
      paths:
      - path: /*
        backend:
          serviceName: ssl-redirect
          servicePort: use-annotation
      - path: /*
        backend:
          serviceName: x-canary-header
          servicePort: use-annotation
      - path: /*
        backend:
          serviceName: tomozo-stable
          servicePort: use-annotation
```

細かく解説していきます。

### ALB の基本的な設定(Rollouts関係無し)

```yaml
annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/security-groups: tomozo-securitygroup-arn
    alb.ingress.kubernetes.io/certificate-arn: tomozo-certificate-arn
```
これは Rollouts 連携が無くても必要な、ALB自体の設定となります。そのため特にここでは解説しません。

### http -> https リダイレクト設定(Rollouts関係無し)

```yaml
annotations:
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
    alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_302"}}'
~
spec:
  rules:
  - host: tomozo.tokyo
    http:
      paths:
      - path: /*
        backend:
          serviceName: ssl-redirect
          servicePort: use-annotation
```
これも Rollouts 連携とは関係ありません。 http -> https への強制リダイレクト設定となります。詳しくは ALB のドキュメントを参考にしてください。

### 特定のリクエストヘッダーを付与してアクセスすると必ず Canary サービスにルーティングされるようにする
```yaml
annotations:
    alb.ingress.kubernetes.io/conditions.x-canary-header: '[{"Field":"http-header","HttpHeaderConfig":{"HttpHeaderName": "X-Canary", "Values":["true"]}}]'
    alb.ingress.kubernetes.io/actions.x-canary-header: '{"Type":"forward","ForwardConfig":{"TargetGroups":[{"ServiceName":"tomozo-canary","ServicePort":"8080"}]}}'
~
spec:
  rules:
  - host: tomozo.tokyo
    http:
      paths:
      - path: /*
        backend:
          serviceName: x-canary-header
          servicePort: use-annotation

```
Canaryリリースは本来`stable`と`canary`に決めた割合でルーティングされますが、この仕組みを入れることで開発者側からは必ず`canary`側にルーティングされるようにできます。`canary`側へ振られる割合を 0% にすれば、ヘッダールーティングによる Blue/Green デプロイにもなります。

Nginx のときは、Rollouts の `strategy.canary.trafficRouting.nginx.additionalIngressAnnotations`にて、ヘッダールーティング(canary-by-header)の設定できたのですが、ALB用の strategy ではそのような設定が無かったのでだいぶ悩みました。まあおそらく Rollouts の開発者も「ALBのingressでできるからそっちでよしなにやってよ」ということなんだと思います。

ここでの設定は、リクエストヘッダー`X-Canary`の値が`true`の場合は、`tomozo-canary`にルーティングさるという設定になっています。

### Rollouts との連携箇所
```yaml
spec:
  rules:
  - host: tomozo.tokyo
    http:
      paths:
      - path: /*
        backend:
          serviceName: tomozo-stable
          servicePort: use-annotation
``` 
ここが今回理解が難しかったです。ここに記載している `tomozo-stable` は、Kubernetesのsvcを指しているのではなく、 annotation名(alb.ingress.kubernetes.io/actions.tomozo-stable)を指しているのです。なので servicePortが`user-annotation`になっているのです。
alb.ingress.kubernetes.io/actions.tomozo-stable は Rollouts が Kubernetes上 で追記するので、現時点では存在しないのです。だから結構混乱しました…

## Service
通常(安定版)用の Service として tomozo-stable。Canary用の Service として tomozo-canaryを定義します。名前以外は全て同じ設定値で問題ありません。

```yaml
---
kind: Service
apiVersion: v1
metadata:
  name: tomozo-stable
spec:
  type: NodePort
  selector:
    app: tomozo
  ports:
    - port: 8080
---
kind: Service
apiVersion: v1
metadata:
  name: tomozo-canary
spec:
  type: NodePort
  selector:
    app: tomozo
  ports:
    - port: 8080
```

## rollout
strategy:より上は deployment と書き方変わりません。大事なのは trafficRouting で alb を指定しているところです。

```yaml
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  # ingressに actions.tomozo-stable が作成される
      canaryService: tomozo-canary
      trafficRouting:
        alb:
           ingress: tomozo　　　　　　# ingress名を指定する
           servicePort: 8080
      steps:
        - setWeight: 10
        - pause: {}
```

このように tomozo-stable を設定することで、 ingress に以下のアノテーションが追加されます。


```yaml
    alb.ingress.kubernetes.io/actions.tomozo-stable: |
      {
        "Type":"forward",
        "ForwardConfig":{
          "TargetGroups":[
            {
                "Weight":0,
                "ServiceName":"tomozo-canary",
                "ServicePort":"8080"
            },
            {
                "Weight":100,
                "ServiceName":"tomozo-stable",
                "ServicePort":"8080"
            }
          ]
        }
      }
```

新しいコンテナイメージをリリースすると、 `tomozo-stable.steps.setWeight`に合わせて、 上記アノテーションの Weight を調整することで Canaryデプロイメントを実現しています。