はじめに
こんにちは、計測プラットフォーム開発本部SREブロックの渡辺です。普段はZOZOMATやZOZOGLASSなどの計測技術に関わるシステムの開発、運用に携わっています。
先日私達のチームでは、EKS環境にArgo CDを導入し、デプロイパイプラインのリアーキテクトを行いました。
開発環境では、Argo CD Image Updater(以下、Image Updaterとする)を活用したスピーディなデプロイ設計をしました。詳しくは「EKS環境へArgo CD Image Updaterを導入し、デプロイ時間と管理コストを削減した話」を参照ください。
本記事では、Argo CD導入による本番環境のリリースフロー設計やタグ更新の仕組みなど工夫した点について紹介します。Argo CDを検討している方に向けて、少しでも参考になれば幸いです。
Argo CD導入前の課題
ここでは例としてZOZOMATにおけるArgo CD導入前のCI/CDアーキテクチャを下図に示します。
Argo CD導入前までは、以下の手順でリリースしていました。
- CIアプリケーションの変更をmainブランチへ取り込む
- アプリケーションの変更をmainブランチへ取り込む
- CircleCIのイメージビルド用のジョブが発火する
- skaffold buildを実行し、イメージをビルドしてECRにプッシュする
- ビルド時に生成されたイメージタグが記録されたjsonファイルをS3にアップロードする
- ビルド完了を待ち、CDをトリガーするスクリプトを実行する(引数でデプロイ先の環境を指定)
- CircleCIからCodePipelineのアクションプロバイダーであるS3にソースコードをアップロードする
- CodeBuildがソースとイメージタグファイルをS3から取得し、Skaffoldを使ってapplyする
旧CI/CDの問題点については、先の記事でも説明していますが、本番環境も同様に大きな課題はCD部分にありました。
開発環境ではスピーディなデプロイが求められましたが、本番環境では以下のようなデプロイ作業の不安定さがより課題感としてありました。
- CIの完了確認やデプロイする環境はリリース担当者が判断する
- CDのトリガーをリリース担当者が手作業で行う
- 各プロダクトごと作業内容が異なる(ZOZOMATとZOZOGLASSなど)
- 問題発生時の調査範囲が広くなり、ロールバック判断が遅れる
- 横展開の工数が増える(0->1でCI/CDを構築する必要がある)
- 新規にジョインしたメンバーのキャッチアップコストが大きい
Argo CD導入
上記に挙げたCDの課題を解決するため、Argo CDを導入することにしました。
Argo CDは、Kubernetes環境でのGitOpsを実現するためのCDツールです。Gitリポジトリで管理しているKubernetesマニフェストを監視して、Kubernetesクラスタに適用します。Gitを信頼できる唯一のソースとすることで、Kubernetesクラスタ内の状態を管理するものです。
GitOpsを実現するツールとして、Argo CDの他にもFluxやJenkins Xなどが挙げられます。この中でArgo CDを採用した大きな理由は、「Syncの状況が把握しやすい」「GUIの操作で特定のリソースだけをSyncできる」などGUIが優れている点になります。また、リリースはSREだけでなくバックエンドチームも担当するため、わかりやすいツールで運用することは上で重要なポイントだと判断しました。
それでは、私達がArgo CDを導入するにあたり検討した以下の内容について説明します。
- Argo CDのProject設計
- GitHubリポジトリ設計
- ブランチ戦略
- CI/CD設計
- イメージタグの更新方法
Argo CDのProject設計
Argo CDにはProjectという概念があります。ProjectはApplicationをグループ化するものです(ApplicationはGitOpsするGitリポジトリの設定)。
Projectには主に以下の機能があります。詳しくは公式ドキュメントを参照してください。
- デプロイできるソース (GitHub) を制限する機能
- デプロイする対象 (Clusterやnamespace) を制限する機能
- デプロイできるObject(Kubernetesリソース)を制限する機能
- 操作権限を制限するRBACを提供する機能
今回、私達が厳密に制御したかったのはデプロイ対象のnamespaceでした。
これは、私達が単一のEKSクラスタの上で複数のサービスを運用するシングルクラスタ・マルチテナント構成を採用していることが大きな理由になります。シングルクラスタ・マルチテナント運用については、「EKSのマルチテナント化を踏まえたZOZOGLASSのシステム設計」を参照してください。
1つのクラスタにプロダクト毎namespaceを作成しているため、あるプロダクトのリソースが別のプロダクトのnamespaceにデプロイできない制限を設ける必要があるのです。
そこで、上記のうち2番目の「デプロイする対象 (Clusterやnamespace) を制限する機能」に関して、Projectの機能を利用したnamespaceの制限について紹介します。
Argo CDは初期設定としてdefaultという名前のProjectが作成されてますが、こちらは制限なしのProjectです。全てのApplicationをdefault Projectで管理する問題として、どのApplicationからでも複数のnamespaceにデプロイできてしまうことが挙げられます。
例えば、ZOZOMATをzozomat namespace、ZOZOGLASSをzozoglass namespaceで管理している場合、ZOZOMATのApplicationからzozoglass namespaceにデプロイできる状態になってしまいます。
そこで私達は、これを防止すべく下図のように1namespace 1Projectで設計しました。
設計当初は1Application 1Projectでよりセキュアにする考えもあったのですが、この設計ではApplicationのグループ化を行えないデメリットがありました。
今後namespace内でマイクロサービス化する可能性があり、1つのnamespaceに複数のApplicationを設定することが予想されます。このため、1namespace 1Projectで管理することが望ましいと考えました。
ただ、今回はArgo CD導入初期として1namespace 1Projectで設計しましたが、今後プロダクトで共通利用するnamespaceを作成する可能性もあります。このため、変化する状況に合わせて柔軟に対応していきたいと思います。
GitHubリポジトリ設計
計測システム部では、バックエンドとSREは別のチームとして存在しています。以前はプロダクトごとにアプリケーションリポジトリ(以下、アプリリポジトリ)の中でKubernetesマニフェストを管理していました。
私達が1つのリポジトリで管理して実感した課題は以下のとおりです。
コミット履歴にKubernetesマニフェストの変更がノイズとして入る
- ロールバックする際、アプリケーションは戻したいけどKubernetesマニフェストは戻したくない場合に面倒
- Kubernetesマニフェストの変更でもCI(イメージビルド)が動く
podの数を変えたいだけなのにアプリケーションのテストが走るストレス
- リポジトリ運用をインフラとバックエンド間で調整する必要がある
ブランチ戦略を決める際に双方の要望を叶えようとするとリポジトリ設定やCI定義等が複雑化しがち
- アプリリポジトリに本番環境を変更する仕組みや権限を配置する必要がある
Argo CDのベストプラクティスでは、アプリケーションのソースコードとKubernetesマニフェストを分けて管理することが推奨されています。このため、プロダクトごと新たにKubernetesマニフェストを管理するリポジトリ(以下、Kubernetesリポジトリ)を作成しました。
チーム単位でリポジトリを分けたことで、上記のデメリットを解消できました。
ブランチ戦略
ここではKubernetesリポジトリのブランチ戦略を紹介します。
Argo CDは、Applicationの設定においてターゲットとしてリポジトリのブランチを指定できるため、環境ごとにブランチを分けることで1つのリポジトリで複数環境を構築できます。
そこで、私達はmainブランチをステージング環境、releaseブランチを本番環境として運用しています。デフォルトブランチをmainに設定し、PRの向き先をmainブランチへ指定します。
本番環境のターゲットとなるreleaseブランチはmainブランチを追従し、ステージング環境で動作確認してから本番環境に反映します。
余談ですが、アプリリポジトリと分けたことで、ブランチ戦略がバックエンドチームと競合しなくなりました。ブランチ戦略の決定権がSREチーム内にあるため、他チームとの調整が不要になり設定が楽になりました。
CI/CD設計
それでは、本記事のメインとなるArgo CD導入後の本番リリースフローについて説明します。
開発環境との大きな違いは、承認フローを設けているところです。
先の記事で説明していますが、開発環境では作業効率を優先したためImage Updaterを活用した承認フローなしのデプロイを行っています。
しかし、本番環境ではデプロイ時間の短さよりも安定性を求めているため、承認フローは必須条件です。このため開発環境とは別の仕組みでリリースフローを検討する必要がありました。
通常、アプリケーションをデプロイするためには、アプリリポジトリでビルドしたイメージをKubernetesマニフェストに反映させる必要があります。
ここでは、Push型の方法としてアプリリポジトリのCIでKubernetesリポジトリを取り込み新しいタグに書き換え、変更コミットを含むブランチを作成する仕組みを構築することが一般的かと思います。
私達も当初はそのような仕組みを考えていました。しかし、開発環境で導入したImage Updaterにイメージリポジトリの変更を検知して、イメージを変更するコミットを含むブランチを自動作成する機能があることを知りました。こちらはPull型の方法で、ECRなどをImage Updaterが監視して最新のイメージを検知した際、Push型同様Kubernetesリポジトリに変更コミットを含むブランチを作成するものです。
今回は、検証の意味も含めImage Updaterを活用した仕組みを構築しました。イメージタグ更新の仕組み下図に示します。
続きはこちら