GKEパイプラインの旅 〜CircleCIからGitHub Actions、そしてArgoCDへ〜

こんにちは。
各位お世話になっております。 [Intimate Merger](https://corp.intimatemerger.com/) のitayaです。

弊社ではGKEのCI/CDツールとして[ArgoCD](https://github.com/argoproj/argo-cd)を採用しています。

https://github.com/argoproj/argo-cd

導入後はCI/CDツールとして期待通りに動いてくれていますし、GKEの運用も楽になりました。
弊社におけるGKEへのCI/CDの流れを追いながら、ArgoCD導入に至った流れをまとめていきます。

# 【初期】CircleCI

弊社でのGKEをdeploy先とするCI/CDは[CircleCI](https://circleci.com/ja/)で始まりました。
2020年初頭の話となります。

https://circleci.com/ja/

GKE自体もテスト的な導入であったので、組織としてのスタンダードなものに合わせた形となります。
いくつかのアプリについて `kubectl apply` ができさえすればそれで十分でした。

# 【中期】Github Actions

[Github Actions](https://docs.github.com/ja/actions)が勢いを増し、弊社でもいくつかのrepositoryで実験的に採用が始まります。

https://docs.github.com/ja/actions

GKEを利用するrepositoryではGithub Actionsへの移行はスムーズに進みました。
CircleCIで用意していたCI/CDの内容が前述の通り `kubectl apply` する程度だったので、移行のハードルが低かったためです。

また、ある時から[self-hosted runner](https://docs.github.com/ja/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners)がセキュリティ的に求められるようになってきたことも移行の追い風となりました。
Service Accountのkeyの発行をできるだけ減らしてkeyの流出リスクを低減しようという流れによるものです。

https://docs.github.com/ja/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners

GKE自体のセキュリティも向上させたい流れもありました。
[限定公開クラスタ](https://cloud.google.com/kubernetes-engine/docs/concepts/private-cluster-concept?hl=ja)を利用して、信頼されたネットワーク以外からはコントロールプレーンにアクセスできない状態にする形がベターであるとわかってきたためです。

https://cloud.google.com/kubernetes-engine/docs/concepts/private-cluster-concept?hl=ja

`kubectl apply` によるmanifestの反映にはコントロールプレーンへのアクセスが必要で、self-hosted runnerはこの問題もクリアすることができました。
GKEにアクセスできるGCEインスタンスを用意し、それをself-hosted runnerのマシンとして使うことで `限定公開クラスタにアクセスしたい` `Service Account keyを減らしたい` 2つの要望を満たせました。

# 【現在】ArgoCD

弊社では22年から23年にかけてAWSからGCPへクラウド環境の移行を行いました。
その結果としてGKEのサービスが一気に増えることになります。

GCPの複数のプロジェクトでGKE clusterが動く状態になってくると、これまでの構成を見直す必要が出てきました。
以下のような問題点が出てきたためです。

- 複数の限定公開クラスタに対してdeployできるCI/CDが欲しい
- self-hosted runnerは便利だけど設定が大変
- 複数のclusterの状態をまとめて見れるようにしたい

これらを解決するものとして白羽の矢が立ったのが[ArgoCD](https://github.com/argoproj/argo-cd)です。

https://github.com/argoproj/argo-cd

ArgoCDはPull型と呼ばれるCI/CDです。
GitHub等にあるrepositoryを監視し、manifestの変更を検知してKubernetesへ反映させてくれます。

限定公開クラスタにアクセスできるネットワーク内にArgoCDがいれば、self-hosted runnerのためのGCEインスタンスも必要なくなる上、 docker imageのpushとmanifestの更新だけで `kubectl apply` をする必要すらないというのは魅力的でした。

複数の限定公開クラスタにGKEのネットワーク内からアクセスするにあたってはConnect Gatewayに注目しました。
Google Cloud blogに投稿された記事 「[Connect Gateway と ArgoCD: 分散 Kubernetes へのデプロイ](https://cloud.google.com/blog/products/containers-kubernetes/connect-gateway-with-argocd?hl=en)」 を参考に実装を進めることになります。

https://cloud.google.com/blog/products/containers-kubernetes/connect-gateway-with-argocd?hl=en

Connect GatewayはAnthos Service MeshをはじめとしたMulti Cluster GKEの流れにあるサービスの認識があったので、過去に調べた認識ではそこそこお金がかかるイメージでいましたが、Connect Gatewayのみを用いることで追加料金がかからずに導入できたのは嬉しい誤算でした。

導入時はSelf Managed ArgoCD(ArgoCDの管理をArgoCD自身で行う形)として運用することで、ArgoCDのメンテナンスコストを低減させました。
新しくArgoCDで管理する対象を増やす場合は `Application` や `ApplicationSet` の定義を追加する形になるので、Self Managed ArgoCDにはとても満足しています。

また、[External Secrets](https://github.com/external-secrets/external-secrets)などのGKEの運用上必要なアプリケーションもApplicationSetとして利用しています。
導入したいGKE Cluster容易に追加することができるのがありがたい点です。

https://github.com/external-secrets/external-secrets

以前は必要なGKE Clusterごとにこの手のmanifestを用意していたため、複数の箇所にExternal Secretsの定義が存在していました。versionの指定もまばらになりがちだったので、そこを統一し、管理しやすくなったのは良い体験でした。

# ArgoCD運用時の悩み

ArgoCDを運用する上で悩んだのは、manifestファイルとソースコードのrepositoryを分離するか否かでした。
ArgoCD以前はコードと同じrepositoryに統一していました。

1. Docker imageをbuild → push
1. kustomizeでmanifestにimage tagを反映
1. `kubectl apply`

の順でCIが動いており、同じrepositoryある方が構成として簡単だったためです。
ArgoCDでは指定した箇所のmanifestを見ることとなり、image tagの反映を必ずどこかで行う必要があります。
それを解決するために、ArgoCDで運用するにおいてはいくつかのパターンが考えられました。

1. manifestとソースコードを一つのrepositoryで管理する
  docker pushしたのち、manifestを更新しcommit → pushを行うCIを構築する
1. manifestとソースコードを一つのrepositoryで管理する
  ArgoCD Image Updaterを導入する
  docker pushしたことをArgoCD Image Updaterが検知し、該当のtagになるようcommitを行う
1. manifestとソースコードのrepositoryを分離する
  ArgoCD Image Updaterを導入する
  docker pushしたことをArgoCD Image Updaterが検知し、該当のtagになるようcommitを行う

それぞれのパターンを考えてみましょう。

`1` のパターンはcommit → pushまでを自分で構築することになり、できなくはないですがあまりやりたくないと感じます。
CIのフローが複雑になり、メンテナンス性も下がりそうです。

`2` `3` のパターンは[ArgoCD Image Updater](https://github.com/argoproj-labs/argocd-image-updater)を利用します。
ArgoCD Image Updaterとは、docker registryを監視し、指定したimageの更新があった場合にmanifestを更新しcommitを行うツールです。

https://github.com/argoproj-labs/argocd-image-updater

`1` のパターンと同じような問題は抱えていますが、自分で構築する箇所が少なくなるので `1` よりはメンテナンス性が保てそうです。
ですが、ソースコードのコミットログとArgoCD Image Updaterのコミットログが混ざり、`1` 同様の問題は依然として残ります。

`2` `3` のパターンの違いはmanifest用にrepositoryを用意するかどうかです。
image tagの自動コミットをソースコードも含まれているrepositoryに対して行うか、専用に分けたrepositoryで行うかという問題です。

結果として、弊社は `3` を選択して今に至ります。

ソースコードのコミットとimage tag更新だけのコミット履歴は分けた方がスッキリしそうだという判断でした。
判断として間違ってはいないと感じていますが、もっとより良い手段があるのではないかとも感じます。この辺りは運用しながら最適解を探していきたいです。

# ArgoCDを導入してみて

GKEを管理する上で多くのメリットがありました。

- deploy方法の統一
- 複数のGCPプロジェクトに存在したGKE Clusterの状態の把握が便利に
- RBACを利用したアクセスコントロール
- self-hosted runner用GCEの削除
- External SecretsなどのKubernetes便利機能の導入が簡易に

考えることはそれなりにあったものの、導入して良かったと思います。
限定公開クラスタとの相性がいいのが素晴らしいです。

2023/12/07(執筆時点)でうまくいっていないところもいくつかあります。

- ArgoCDのexecがConnect Gatewayに対応しておらずexec機能が使えない
- ArgoCD Image Updaterの更新が1年近く止まっている

execについても将来的に対応されそうな空気もありますし、[ArgoCD Image UpdaterはArgoCD v3.0から公式の機能となる](https://github.com/argoproj-labs/argocd-image-updater/issues/491)そうなので、それまでの辛抱かなと。

総じてとてもハッピーです！ArgoCDありがとう！

# あとがき

次回の記事は **Airflowのスケジューリング** の話だそうです！お楽しみに！

https://qiita.com/advent-calendar/2023/intimatemerger