Argo Rolloutsを使ってKubernetesでカナリアリリースする

要約すると

Kubernetes上で動作するAPIカナリアリリースをしたかったので、Argo Rolloutsを導入してこれを実現しました。この記事ではその際の導入手順を紹介します。

概要

背景

Argo CDを利用しているので、Argo Projectのツールでカナリアリリースを実現したかった。

対象者

  • CD (Continuous Delivery) を構築・運用する人

ゴール

Argo Rolloutsでカナリアリリースができるようになること

前提

  • k8sクラスタにArgo CDがインストールされていること
  • kubectlやkubeconfig等のローカルPCの設定が完了していること

公式ドキュメント

argoproj.github.io

本記事の情報は時間が経つと古くなります。最新の情報は公式ドキュメントを参照してください。

Rolloutリソースについて

Argo Rolloutsでは Deployment リソースの代替として Rollout というリソースを使います。RolloutにはDeploymentとほとんど同じ内容を書くことができ、基本的な使い方は同じです。

異なる点として、spec.strategy にデプロイ戦略を書くことで様々な方法でデプロイすることができるようになります。カナリアリリースもここに定義できる戦略のひとつです。

以下、導入手順です。

Argo Rolloutsをクラスターにインストール

専用のnamespace argo-rollouts を作成し、そこにマニフェストをapplyします。

$ kubectl create namespace argo-rollouts
$ kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml

以上でインストールできました。簡単でした。

CLIツールをローカルPCにインストール

Argo Rolloutsの操作に使うCLIツールをインストールします。kubectlのプラグインという形で提供されています。

brewを使っている場合は以下

$ brew update
$ brew install argoproj/tap/kubectl-argo-rollouts

バイナリをダウンロードする場合は以下

# Linuxの場合は "darwin" を "linux" に読み替える
$ curl -LO https://github.com/argoproj/argo-rollouts/releases/latest/download/kubectl-argo-rollouts-darwin-amd64
$ chmod +x kubectl-argo-rollouts-darwin-amd64
# その後、名前を変更 & PATHを通す
# 例
$ sudo mv ./kubectl-argo-rollouts-darwin-amd64 /usr/local/bin/kubectl-argo-rollouts

以下のコマンドで動作確認します。

$ kubectl argo rollouts version
kubectl-argo-rollouts: v1.3.2+f780534
  BuildDate: 2022-12-15T16:06:35Z
  GitCommit: f780534ebd66a4047c813ddd7841be93b122c3e6
  GitTreeState: clean
  GoVersion: go1.18.9
  Compiler: gc
  Platform: darwin/amd64

デモアプリをインストールしてみる

ターミナル1

テスト用にデモアプリが提供されているので動かしてみます。RolloutとServiceの定義をapplyします。

$ kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-rollouts/master/docs/getting-started/basic/rollout.yaml
$ kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-rollouts/master/docs/getting-started/basic/service.yaml

完了したら rollouts-demo というRolloutリソースが作成されます。 以下のコマンドでRolloutの状態を監視します。

$ kubectl argo rollouts get rollout rollouts-demo --watch

デモ用Rolloutのイメージタグを差し替えてカナリアリリースの動作確認をする

新しいターミナルを立ち上げてください。以下のコマンドでカナリアリリースが行われます。

ターミナル2

# イメージを更新する
$ kubectl argo rollouts set image rollouts-demo rollouts-demo=argoproj/rollouts-demo:yellow
# このタイミングで全体の20%が新しいPodに入れ替わる

# カナリアリリースを進める
$ kubectl argo rollouts promote rollouts-demo
# 全てのPodが新しいイメージのものに入れ替わる

このような挙動になるのは、Rolloutに以下のような定義があるからです。 Rolloutの中から、カナリアのステップを書いた部分を抜粋

spec:
  replicas: 5
  strategy:
    canary:
      steps:
      - setWeight: 20
      - pause: {}
      - setWeight: 40
      - pause: {duration: 10}
      - setWeight: 60
      - pause: {duration: 10}
      - setWeight: 80
      - pause: {duration: 10}

意味

  1. 全体の20%を新しいpodに更新
  2. 無制限に待つ
    • promoteコマンドを受け付けたら進む
  3. 全体の40%を新しいpodに更新
  4. 10秒待つ
  5. 全体の60%を新しいpodに更新
  6. 10秒待つ
  7. 全体の80%を新しいpodに更新
  8. 10秒待つ
  9. 全て新しいpodに更新

途中でロールバックしたいときは、promoteの代わりにabortします。

# 新しいイメージに更新
$ kubectl argo rollouts set image rollouts-demo rollouts-demo=argoproj/rollouts-demo:red
# このタイミングで全体の20%が新しいpodに更新される

# puase中にabortする
$ kubectl argo rollouts abort rollouts-demo
# 先ほどまで動いていたargoproj/rollouts-demo:yellowに戻る

ここで、Rolloutを監視しているターミナル1を見てください。 abortすると、RolloutのSTATUSが Degraded になります。これはあるべき状態と現在の状態が異なるからです。 上記の例では、先ほどイメージをrollouts-demo:redに更新するコマンドを実行したのにも関わらず、abortの結果現状がrollouts-demo:yellowに戻っているためです。

Healthy に戻すためには、再度yellowを適用します。

$ kubectl argo rollouts set image rollouts-demo rollouts-demo=argoproj/rollouts-demo:yellow

デモは以上です。 このように、カナリアリリースを行うことができます。

UIで操作したい

以下のコマンドを叩くと、localhost:3100 でUIを見ることができます。

$ kubectl argo rollouts dashboard

Argo RolloutsのUI

カナリアリリース実行中は以下のような画面になります。

カナリアリリース実行中のArgo RolloutsのUI

Rolloutリソースを定義する

次に、自身の環境でRolloutを作成していきましょう。 2つの方法があります。

  • Rolloutだけを定義する
  • Deploymentを定義してから、それを参照する形でRolloutを定義しする

Migrating - Argo Rollouts - Kubernetes Progressive Delivery Controller

今回は、既存のDeploymentを参照する形式でRolloutを定義する方法を紹介します。

理由は、すでに定義済みのDeploymentが存在する状況の場合が多そうであることや、Rolloutをやめたいときに戻りやすそうであるためです。

まず、以下のようにDeploymentを spec.replicas: 0 で定義します。そして、Rolloutに spec.workloadRef を記載することで、Deploymentを参照することができます。

# 既存のDeployment
# 名前は my-api
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-api
  namespace: apps
spec:
  selector:
    matchLabels:
      app: my-api
  replicas: 0  # Deployment側のレプリカ数を0にすることで、Rollout管理のPodのみを動作させることができる
  template:
    # 略
---
# 新しく定義するRollout
# Argo Rolloutsによってカナリアリリース等を実現するための、Deploymentを代替するリソース
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: my-api
  namespace: apps
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-api
  workloadRef:  # 既存のDeploymentを参照する
    apiVersion: apps/v1
    kind: Deployment
    name: my-api
  strategy:
    canary:
      steps:
        - setWeight: 10
        - pause: {}
        - setWeight: 50
        - pause: {duration: 30s}

DeploymentからRolloutに移行するときの注意点

先述した以下のドキュメントにも書いてありますが、

Migrating - Argo Rollouts - Kubernetes Progressive Delivery Controller

いきなりDeploymentの spec.replicas を0にした定義をapplyすると、Rolloutで定義したPodが全て立ち上がる前に、既存のDeployment管理下のPodが全て削除されてしまい、ダウンタイムが発生します。そのため、はじめはDeployment側のreplicasを減らさずにリリースして、Rollout管理下のpodが全て稼働するようになってからDeploymentを縮退させる修正を反映させるとダウンタイムを抑えられます。

リリース工程

フェーズ1

  • Rolloutを作る
    • pod数は現行のDeploymentと同じ
    • この時点でDeployment管理下とRollout管理下のPodが共存する状態になる

ただし、この手順をそのまま採用すると一時的にPod数が2倍になるため、cpu、memory、storage等のリソースを考慮してオペレーションの手順を調整する必要があります。弊チームでRolloutを導入した際は、負荷が少ない時間帯を選び、Pod数が半分になることまでは許容することにしました。

フェーズ2

  • Deploymentのreplicasを0にする

サポートバージョン

最後に、サポートされているバージョンについて。

Installation - Argo Rollouts - Kubernetes Progressive Delivery Controller

公式ページには以下の記載があります。

  • Argo Rolloutsは最新版しかサポートされていない
  • 対応するKubernetesのバージョンは最新とそのひとつ前だけ

クラウドサービスプロバイダーによっては新しいKubernetesバージョンをサポートしていない場合もあり、この条件に対応するk8sクラスターを用意できない場合があると思います。

もし古いバージョンのk8s上で最新のArgo Rolloutsが動作しない場合、自己責任にはなりますが、Argo Rollouts本体、kubectl-argo-rollouts (CLI) のいずれかもしくは両方を古いバージョンにすることで動作することがあります。

# 最新をインストールするときのコマンド
$ kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml

# バージョンを指定してインストールするときのコマンド
# kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/download/v1.3.2/install.yaml

この記事を書いた人

twitter.com