OpenShiftでProgressive Deliveryやってみた！（Argo Rollouts基礎編-CLI）

# 背景
クラウドやコンテナが登場するよりももう何年も前から存在しているBlue/Greenデプロイメントやカナリア・リリースといった本番リリースのためのプラクティスですが、多くの阻害要因からなかなか実践できないと言ったイメージが有りました。

- 必要なときだけサーバーリソースを手に入れることが出来ないため本番と同等の環境を2つも用意できない
- 必要な機能が多岐にわたる上に候補となる製品も多く、さらには担当するチームが異なる
    - 高度なネットワーク制御（インフラチーム）
    - アプリケーションのデプロイ（アプリチーム or 運用チーム）
    - アプリケーションの機能検証（アプリチーム）
    - アプリケーションのパフォーマンス分析（運用チーム）
- 関連する製品が多いため自動化が困難で手動オペレーションに頼らざるを得ない

昨今はクラウドやコンテナが当たり前の時代に変わりつつあり、こういった問題はほぼほぼ簡単に解決できるようになりました。

- クラウドやコンテナ・プラットフォームが主流となり、誰でも必要な時に必要なだけサーバーリソースを調達できるようになる
- 必要な機能のほとんどがコンテナ・プラットフォーム（Kubernetes）で用意されるようになった上に利用方法も簡単に
    - 高度なトラフィック制御（Istio、Linkerd、Nginx Ingress）
    - アプリケーションのデプロイ（Kubernetes標準機能）
    - アプリケーションの機能検証（従来のテスト自動化製品）
    - アプリケーションのパフォーマンス分析（Prometheus）

最後の`自動化が困難`という課題についてはこれまでもJenkinsを代表とする様々なCIツールや自動化のためのサービスを利用することが出来たのですが、連携する各ツールを手動で実行するための手順を理解していないとならないという観点からリリースのオペレーションを簡単に自動化出来るとは言い難いものでした。

- Istioを利用することでBlue/Greenやカナリア・リリースに不可欠なトラフィック制御も簡単にできるとはいえ、切り替えを自動化する仕組みは作り込まなきゃいけないのか？
- Prometheusから得た様々なメトリクスを分析する事はできるが、分析結果を元にリリースプロセスに組むにはどうすればよいのか？

こういった悩みを解決するのが今回紹介をするArgo Rolloutsを始めとするProgressive Deliveryツールです。
Argo Rolloutsと共にProgressive Deliveryの双璧をなすツールであるFlaggerは共にKubernetes上で動作するツールとなっているということもあり、どちらも宣言的な方法でリリースの手順を記載することが出来るため、連携するIstioやPrometheusを操作するための方法を知る必要はありません。

この投稿ではとりあえずArgo Rolloutsの基本的な機能をマニュアルの手順をベースに試した結果をまとめています。

# 前提
- Kubernetesクラスター
    - OpenShift on IBM Cloud （v4.6）
- クライアント環境
    -  Argo Rollouts Kubectl plugin の Dockerコンテナ（Docker Desktop on Windows 10）

クライアント環境となる Argo Rollouts Kubectl pluginの実行ファイルがWindows用に提供されていないため、Windows端末から直接コマンドを叩くことが出来ません。
幸いなことにマニュアルを見るとDockerコンテナのイメージが公開されているのでこちらを利用します。
[Using the CLI with Docker](https://argoproj.github.io/argo-rollouts/installation/#using-the-cli-with-docker)

なお、このコンテナは起動時に引数で渡したコマンドを実行してくれるもので、コンテナ起動後にコンテナ内でコマンドを実行するような使い方は想定していないようです。
例にあるversionコマンドはいいけど、API Serverとの認証はどうすんの？ってところでかなり躓きましたがローカルの端末でAPI Serverと認証し、ローカルの~\.kube\configファイルをコンテナにマウントすれば良いのだと試行錯誤している中で気づきました。
また、サーバー証明書もマウントしていないとエラーになるのですが、こちらは面倒なので`--insecure-skip-tls-verify`を指定して回避しています。
具体的なコマンドは[こちら](#argo-rollouts-kubectl-plugin)を確認してください。


# 導入手順
以下の手順を参考にOpenShift on IBM Cloud上にコントローラを導入します。
https://argoproj.github.io/argo-rollouts/installation/

## Controller Installation
- OpenShiftへログイン

```
PS D:\git> oc login --token=sha256~xxxxxxxxxxxxxxxxxxxxxxxxx --server=https://c103-e.us-south.containers.cloud.ibm.com:31989
Logged into "https://c103-e.us-south.containers.cloud.ibm.com:31989" as "IAM#strada@jp.ibm.com" using the token provided.

You have access to 79 projects, the list has been suppressed. You can list all projects with ' projects'

Using project "default".
```
- 新規プロジェクトの作成

```
PS D:\git> oc new-project argo-rollouts
Now using project "argo-rollouts" on server "https://c103-e.us-south.containers.cloud.ibm.com:31989".

You can add applications to this project with the 'new-app' command. For example, try:

    oc new-app rails-postgresql-example

to build a new example application in Ruby. Or use kubectl to deploy a simple Kubernetes application:

    kubectl create deployment hello-node --image=k8s.gcr.io/serve_hostname

```
- コントローラの導入

```
PS D:\git> oc apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
customresourcedefinition.apiextensions.k8s.io/analysisruns.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/analysistemplates.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/clusteranalysistemplates.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/experiments.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/rollouts.argoproj.io created
serviceaccount/argo-rollouts created
clusterrole.rbac.authorization.k8s.io/argo-rollouts-aggregate-to-admin created
clusterrole.rbac.authorization.k8s.io/argo-rollouts-aggregate-to-edit created
clusterrole.rbac.authorization.k8s.io/argo-rollouts-aggregate-to-view created
clusterrole.rbac.authorization.k8s.io/argo-rollouts-clusterrole created
clusterrolebinding.rbac.authorization.k8s.io/argo-rollouts-clusterrolebinding created
service/argo-rollouts-metrics created
deployment.apps/argo-rollouts created

```

## Argo Rollouts Kubectl plugin
- Docker Desktopの共有設定を確認

Docker Desktopを利用している場合はDocker DesktopのSettingまたは以下の歯車アイコンから`Resources -> FILE SHARING`でkubeconfigファイルの保管先が共有されていることを確認してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/08850d26-07af-931b-a8bb-ba39f6038f11.png)
- とりあえず`version`コマンド実行

```
PS D:\git> docker run --rm -v C:\Users\YASUYUKIKUBOTA\.kube\config:/.kube/config quay.io/argoproj/kubectl-argo-rollouts:master version
kubectl-argo-rollouts: v1.1.0+unknown
  BuildDate: 2021-07-03T00:27:43Z
  GitCommit:
  GitTreeState: clean
  GoVersion: go1.16.3
  Compiler: gc
  Platform: linux/amd64
```


# 試行
マニュアルの以下のページを参考に試行します。
https://argoproj.github.io/argo-rollouts/getting-started/
マニュアルの手順にはありませんが、前提となる`Rollout`の設定を先に確認しておきます。

```rollout.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: rollouts-demo
spec:
  replicas: 5
  strategy:
    canary:
      steps:
      - setWeight: 20
      - pause: {}
      - setWeight: 40
      - pause: {duration: 10}
      - setWeight: 60
      - pause: {duration: 10}
      - setWeight: 80
      - pause: {duration: 10}
  revisionHistoryLimit: 2
  selector:
    matchLabels:
      app: rollouts-demo
  template:
    metadata:
      labels:
        app: rollouts-demo
    spec:
      containers:
      - name: rollouts-demo
        image: argoproj/rollouts-demo:blue
        ports:
        - name: http
          containerPort: 8080
          protocol: TCP
        resources:
          requests:
            memory: 32Mi
            cpu: 5m
```
- RolloutのStrategyはCanaryに設定されています。
- RolloutのStepは20%ずつCanaryに推移していく設定になっています。
- 20%から40%へのStepの移行までの間隔（pauseで指定されたオブジェクトのduration属性）が無指定なのでここで一旦停止します。
- 40%以降は移行までの間隔に10秒（単位無指定の場合は秒になる）に設定されています。

1. Deploying a Rollout

- Rolloutの作成

```
PS D:\git> oc apply -f https://raw.githubusercontent.com/argoproj/argo-rollouts/master/docs/getting-started/basic/rollout.yaml
rollout.argoproj.io/rollouts-demo created
PS D:\git> oc apply -f https://raw.githubusercontent.com/argoproj/argo-rollouts/master/docs/getting-started/basic/service.yaml
service/rollouts-demo created
```

- Rolloutの状況を確認

```
docker run --rm -v C:\Users\YASUYUKIKUBOTA\.kube\config:/.kube/config quay.io/argoproj/kubectl-argo-rollouts:master get rollout rollouts-demo --watch --insecure-skip-tls-verify
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/6d9cad08-2e54-d5d7-8bad-c75d78e70c93.png)



>Initial creations of any Rollout will immediately scale up the replicas to 100% (skipping any canary upgrade steps, analysis, etc...) since there was no upgrade that occurred.

マニュアルにも書いてある通り、`Rollout`作成時点ですぐに100%になるが、これは正しい挙動。

2. Updating a Rollout

- `Rollout`で参照しているコンテナ・イメージを変更

```
PS D:\git> docker run --rm -v C:\Users\YASUYUKIKUBOTA\.kube\config:/.kube/config quay.io/argoproj/kubectl-argo-rollouts:master set image rollouts-demo rollouts-demo=argoproj/rollouts-demo:yellow --insecure-skip-tls-verify
rollout "rollouts-demo" image updated
```

>During a rollout update, the controller will progress through the steps defined in the Rollout's update strategy. The example rollout sets a 20% traffic weight to the canary, and pauses the rollout indefinitely until user action is taken to unpause/promote the rollout. After updating the image, watch the rollout again until it reaches the paused state:

以下のように自動的に参照しているコンテナ・イメージの変更が検知されて自動的にロールアウトが開始される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/d7d2b57c-904e-9a6c-3fc6-f8a03bfd8c3d.png)


>When the demo rollout reaches the second step, we can see from the plugin that the Rollout is in a paused state, and now has 1 of 5 replicas running the new version of the pod template, and 4 of 5 replicas running the old version. This equates to the 20% canary weight as defined by the setWeight: 20 step.

20%で一旦Pauseするように設定されているため、一旦停止する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/5c775e09-81fb-52a2-9c9f-e248b98c3292.png)


3. Promoting a Rollout

- `Rollout`をPromote

```
PS D:\git> docker run --rm -v C:\Users\YASUYUKIKUBOTA\.kube\config:/.kube/config quay.io/argoproj/kubectl-argo-rollouts:master promote rollouts-demo --insecure-skip-tls-verify
rollout 'rollouts-demo' promoted
```

Promoteが実行されると`revision:1`のPodが停止され、`revision:2`のPodが起動していく
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/50bbe0b5-ec70-175b-c994-13c68038a107.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/097a04a0-6ee4-6ac4-5380-2ec9f9fa243b.png)

`revision:2`が100%になると、revision:2が`canary`から`stable`に変更される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/09cb5352-cf6f-7066-44a9-5ac344cf38fa.png)

4. Aborting a Rollout

- `Rollout`を実行

```
PS D:\git> docker run --rm -v C:\Users\YASUYUKIKUBOTA\.kube\config:/.kube/config quay.io/argoproj/kubectl-argo-rollouts:master set image rollouts-demo rollouts-demo=argoproj/rollouts-demo:red --insecure-skip-tls-verify
rollout "rollouts-demo" image updated
```
先程と同様に20%がCanaryに切り替わったところで`Pause`します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/3f4b23cd-fba0-7170-0ee9-a52b1f0f009d.png)

- `Rollout`をAbort

今度は`Promote`ではなく`Abort`を実行します。

`revision:3`が停止され、`revision:2`が起動します。
STATUSは`Degraded`になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/42f7c678-1118-eb81-7982-fc0f7bf04dbc.png)

`revision:2`が100%の状態に戻ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/981815d7-9a36-fbfc-fc42-d57895cb5cac.png)

# まとめ
この投稿ではマニュアルの`Get Started`の`Basic Usage`の手順を実施しました。
Argo Rolloutsの`Rollout`リソースを利用することで`Canary`へと推移していくステップをまとめて定義することが出来ることを体験しました。
また、`Pause`を利用することで`Canary`を`Stable`に`Promote`するかどうかの判断を挟むことが出来ることがわかりました。
`Canary`に問題があると判断した場合には`Abort`することで元のバージョンに戻すことが出来ることもわかりました。

次の投稿では`Argo Rollouts Kubectl plugin`のコマンドで実施した`Promote`や`Abort`をダッシュボードの機能を利用して実施する方法を検証したいと思います。

# Tips
## `argo rollouts get rollout`コマンド実行するたびに起動中のコンテナが増殖していく件
終了する際にCtrl+Cで抜けた場合コンテナ上のプロセスが起動したままコンテナから抜けてしまうので、`--rm`を指定していてもコンテナは起動したまま残り続ける模様。
残骸が増えていくのが嫌な場合は起動中のコンテナに`docker exec`で入って、kubectl-argo-rolloutsコマンドを直接叩くことで残骸を増やさずにすみます。

```
PS D:\git> docker ps
CONTAINER ID   IMAGE                                           COMMAND                  CREATED         STATUS         PORTS     NAMES
b0317447efc9   quay.io/argoproj/kubectl-argo-rollouts:master   "/bin/kubectl-argo-r…"   5 minutes ago   Up 5 minutes             sleepy_elbakyan
PS D:\git> docker exec -it b0317447efc9 /bin/bash
I have no name!@b0317447efc9:/home/argo-rollouts$ kubectl-argo-rollouts get rollout rollouts-demo --watch --insecure-skiptls-verify
Error: unknown flag: --insecure-skiptls-verify
I have no name!@b0317447efc9:/home/argo-rollouts$ kubectl-argo-rollouts get rollout rollouts-demo --watch --insecure-skip-tls-verify
Name:            rollouts-demo
Namespace:       argo-rollouts
Status:          ✔ Healthy
Strategy:        Canary
  Step:          8/8
  SetWeight:     100
  ActualWeight:  100
Images:          argoproj/rollouts-demo:blue (stable)
Replicas:
  Desired:       5
  Current:       5
  Updated:       5
  Ready:         5
  Available:     5
```