Gunosy Tech Blog

Gunosy Tech Blogは株式会社Gunosyのエンジニアが知見を共有する技術ブログです。

Argo RolloutsによるKubernetesでのCanary Deploy

この記事は Gunosy Advent Calendar 2020 10日目の記事です。 昨日はjohnmanjiroさんの TerraformでLaunch Templateを使ったManaged Node Groupを導入する でした。

こんにちは、広告技術部のhidappleです。普段は広告配信サーバや管理画面の開発をしています。 この記事では、以前EKSでのアプリケーションのデプロイにArgo Rolloutsを導入したので、それについて紹介したいと思います。

Argo Rolloutsとは

Argo RolloutsArgo Projectsの1プロダクトであり、KubernetesにBlue-GreenやCanary等のより高度なデプロイ機能を提供してくれるものです。類似のツールとしてはflagger等が候補に上がると思いますが、今回はサービスメッシュなどのツールを必要とせず単独で入れられ、後述しますがKubernetesのDeploymentをArgo Rolloutsの定義するRolloutリソースに置き換えることで比較的容易に導入が可能といった理由から、Argo Rolloutsを採用しました。

今回はCanaryデプロイのためにArgo Rolloutsを導入し、デプロイ時にまず一部のPodのみ新しいアプリケーションに入れ替え、問題がなければ全体にデプロイを適用するといったフローを実現しました。

Argo Rolloutsの導入

Argo Rolloutsをクラスタにインストールするには、namespaceを作って、公開されているmanifestをapplyしてやります。

kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts -f https://raw.githubusercontent.com/argoproj/argo-rollouts/stable/manifests/install.yaml

Helmでも提供されており、広告技術部ではEKSのクラスタをTerraformで管理しているので、今回は terraform-helm-providerを使ってHelm Chartをデプロイする方法をとりました。Terraformに以下のようなresourceを追加してやればいいです。

resource "helm_release" "argo_rollouts" {
  name      = "argo-rollouts"
  chart     = "https://argoproj.github.io/argo-helm/argo-rollouts-0.3.7.tgz"
  namespace = "argo-rollouts"
  # values等は必要に応じて適宜追加
}

またArgo Rolloutsを操作するためにkubectl argo pluginが必要なのでInstall Guideに従って事前に入れておくといいでしょう。

Rolloutsリソースの定義

次にRolloutリソースを定義していきます。基本的にほとんど通常のDeploymentと同じ構成ですがRolloutではspec.strategycanaryまたはblueGreenを指定することができるので、ここにデプロイのステップを記述していくことになります。 簡略化したものですが以下にRolloutのyamlの例を示します。

apiVersion: argoproj.io/v1alpha1
kind: Rollout
spec:
  strategy:
    canary:
      maxSurge: 1
      maxUnavailable: 1
      steps:
        - setWeight: 1
        - pause: {}
  template:
    # template以下は通常のtemplateを定義する

このようにsetWeightでCanaryに流すトラフィックを指定*1し、pauseで条件を満たすまで停止させるといった流れを記述していきます。pauseではdurationを指定して一定期間待機するといったことができますが、今回のように指定しない場合は先に紹介したpluginのコマンドである kubectl argo rollouts promote *2 を受け付けるまで停止します。もちろんsetWeightpauseを複数書けるので例えばsetWeightを指定した後promoteコマンドを受け付けるまで待機し、その後はduration指定により徐々にデプロイを拡大していくといったより細かい設定もできます。その場合は例えば以下のようになるでしょう。

apiVersion: argoproj.io/v1alpha1
kind: Rollout
spec:
  strategy:
    canary:
      maxSurge: 1
      maxUnavailable: 1
      steps:
        - setWeight: 1
        - pause: {} # promoteを待機
        - setWeight: 10
        - pause: { duration: 1m } # 1分待機
        - setWeight: 50
        - pause: { duration: 10s } # 10秒待機
  template:
    # template以下は通常のtemplateを定義する

その他の詳細な設定やBlue-Greenでの書き方を知りたい方は以下のリンクを参考にしてみてください。

デプロイの流れ

さてArgo RolloutsのインストールとRolloutリソースの定義ができたので実際にデプロイをするための仕組みを作っていきます。方法としては同じArgo ProjectsのArgo CDに組み込む等が考えられますが、今回はこちらの記事と同様にCircleCIからskaffoldを使ってデプロイフローを構築することにしました。

先の記事でも紹介しているように弊社ではskaffoldのCI-imageを公開しており、CircleCIの記述も通常のDeploymentを使っていたときとほとんど同様です。一点、現状skaffoldではbuildしたimageのタグをRolloutのようなカスタムリソースに対してうまく当てられない*3という問題がありましたが、今回はYAMLのパッケージングにkustomizeを使っていたので、kustomize edit set imageを使って事前にYAMLに埋め込むことで解消しました。

docker:
  - image: gunosy/ci-skaffold:1.0.1
steps:
  - checkout
  - setup_remote_docker
  - run:
      name: docker login
      command: $(aws ecr get-login --no-include-email)
  - run: aws eks update-kubeconfig --name << parameters.env_short >>-cluster
  - run:
      name: set docker image tag
      command: |
        cd k8s/overlays/<< parameters.env_long >> && \
        kustomize edit set image front-api=front-api:new-tag \ # Tag名は実際は動的に生成しています
  - run: skaffold run --profile << parameters.env_long >> --label skaffold.dev/run-id="static",skaffold.dev/docker-api-version="static",app.kubernetes.io/managed-by="skaffold",skaffold.dev/tag-policy="static"

さてここまでの手順で概ねArgo Rolloutsを使ったCanaryデプロイが実現できるようになりました。上記のフローを実行するとArgo RolloutsによるCanaryデプロイがおこなわれるので、問題なくデプロイを拡大できると判断した場合は先述したpromoteコマンドを送ってやります。(実際はCircleCIからpromoteもおこなっています。)

Rolloutの状態はkubectl argo rollouts getコマンドで可視化できるので異変があった場合等にはすぐに状態を確認できます。

> kubectl argo rollouts get rollout front-api --watch

Name:            front-api
Namespace:       default
Status:          ॥ Paused
Strategy:        Canary
  Step:          1/2
  SetWeight:     1
  ActualWeight:  50
Images:          front-api:new-tag (canary)
                 front-api:old-tag (stable)
Replicas:
  Desired:       2
  Current:       2
  Updated:       1
  Ready:         2
  Available:     2

NAME                                             KIND        STATUS     AGE    INFO
⟳ front-api                            Rollout     ॥ Paused   17m
├──# revision:2
│  └──⧉ front-api-68f7d86788           ReplicaSet  ✔ Healthy  8m15s  canary
│     └──□ front-api-68f7d86788-g742f  Pod         ✔ Running  8m15s  ready:1/1
└──# revision:1
   └──⧉ front-api-57bd7bbbdb           ReplicaSet  ✔ Healthy  17m    stable
      └──□ front-api-57bd7bbbdb-v4bwb  Pod         ✔ Running  17m    ready:1/1

まとめ

ここまでArgo Rolloutsを使ったCanaryデプロイについて紹介しました。普段書いているDeploymentの定義を少し変更してやるだけで比較的容易に導入することができたという印象なので、Kubernetes上でBlue-GreenやCanaryデプロイをしたい場合には1つの候補として検討してみてはいかがでしょうか。また今回は最低限の機能しか使っていないのですがAnalysisExperimentsの機能を使ってさらに高度なデプロイフローを実現することもできるのでこちらもそのうち試してみたいと思います。

明日は山本さんが記事を書いてくれます。お楽しみに!

*1:Taraffic Managementによる、よりきめ細やかな制御も可能です

*2:またはkubectl argo rollouts abortでabortも可能です

*3:議論はされているのでそのうち解決されるかもしれません