こんにちは😸 Necoの@dulltzです。
皆さんはマルチテナントでGitOpsするためにどのような構成をとっていますか? 我々はArgoCDを利用しています。
以前、@zoetroからArgoCDについての紹介がありました。
上の記事でもテナント*1に対しArgoCDを提供する方法に触れているのですが、 最近そこからもう一歩踏み込んで、テナントがApplicationを任意のタイミングで安全に作れるようにしました。これについて説明します。
なおNecoでは実装をOSSにしているので、記事内にソースコードへのリンクを適宜貼っておきます。気になる方はそちらも御覧ください。
前提知識
- ArgoCD
- KubernetesでGitOpsを行うためのミドルウェアです。*2
- この記事ではArgoCD v1.3.6を対象とします。
Application- ArgoCDのカスタムリソースです。GitOpsするアプリケーションを宣言します。指定したGitリポジトリパスに置いてあるマニフェストを、ArgoCDはKubernetesに適用してくれます。
AppProject- ArgoCDのカスタムリソースです。
Applicationの論理グループを表現します。AppProjectを使うとApplicationの適用範囲や、Applicationにアクセス可能なユーザを制御できます。
- ArgoCDのカスタムリソースです。
- App of Apps
Application群を管理するためのApplicationを用意することで、Application自体もGitOpsするというパターンです。*3
現在の構成
先に現在の構成を書いておきます。
Adminチームのマニフェストリポジトリneco-appsにテナントのリポジトリを参照するApp of Apps用Applicationを配置することで、
テナントのApplicationをテナント管理のマニフェストリポジトリへ配置できるようにしました。
こうすると何が良いのかというと、テナントチームがApplicationを作るときにKubernetes adminチームへレビューを依頼する手間がなくなります。
詳しい話は後述します。
以前のやり方
以前はテナントがArgoCDを扱えるように、次のような2つのAppProjectを用意していました。
default: admin向けのprojectです。ArgoCDで可能なすべての権限が許されています。tenant: テナント向けのprojectです。テナント用のNamespace内にのみアプリケーションをデプロイできます。
これらのうち、テナント用のApplication.spec.projectにはtenantを指定することで、テナントの権限を制御していました。
テナントのApplicationはすべてNecoチーム(=admin)のマニフェスト用リポジトリneco-appsで管理し、
テナントチームが新たにApplicationを追加したい際には、neco-appsにPRを出してもらうことで対応していました。
この仕組みでは、テナントがApplicationを追加するたびにNecoチームはレビューする必要があり、
テナントはそのレビューを通過するのを待つ必要があります。
どうしてこうなった
テナントがadminチームに毎回PRを投げなければならない仕組みはなんかちょっと面倒そうです。
ここでより良さそうなやり方として思いつくのは、
「AppProjectによる権限分離を利用して、
neco-appsにはテナントのApplication群を管理するApp of Apps用Applicationだけを配置しておけば、
テナント用Applicationをテナントのリポジトリの中に配置できるのでは?」という方式です。
もしそれができれば、テナントがApplicationを追加しようとするたびNecoチームへPRを出す手間がなくなります。
ですが最初にNecoチームがマルチテナントArgoCDを設計したときは、その方式は採用しませんでした。
なぜかというとApplicationのspec.project、つまりそのApplicationが所属するAppProjectを指定するフィールドに、任意の値をセットできてしまうからです。
言い換えると、Applicationを作る権限を渡すことが、ArgoCDでできるすべてのデプロイを許可することになってしまうということです。
これを防ぐために、neco-appsの中でテナントのApplicationも保持しておき、テナントにはApplicationの作成権限を渡さなかったのでした。
今のやり方
前述の通り、最近やり方を見直してテナントがadminに毎回PRを投げなくても良くなるようにしました。 なぜそうしたのかというと、インフラ管理のための手作業コストの低減がNecoチームの目的の1つだからです。
新しいやり方ではさきほど触れた「テナント用Applicationをテナントのリポジトリの中に配置しておいて、neco-appsにはテナントのApplication群を管理するApp of Apps用Applicationだけを配置する」という仕組みになっています。
次のような3つのAppProjectを用意しました。
default: admin向けのAppProjectです。ArgoCDで可能なすべての権限が許されています。tenant: テナント向けのAppProjectです。テナント用のNamespace内にのみアプリケーションをデプロイできます。tenant-apps: テナントのApplicationを作るためのAppProjectです。ArgoCDのApplication ControllerがウォッチするNamespaceにApplicationを作る権限だけを持っています。
テナントのリポジトリを参照するApp of Apps用Applicationはtenant-appsに所属させています。
前述の図を再掲します。
Validating Admission Webhookによる解決
さきほど触れた「テナントがApplication.spec.projectにdefaultを指定できてしまう」問題を解決するために、Validating Admission Webhookを実装しました。
Applicationの参照しているリポジトリのURLをもとにApplication.spec.projectとして指定可能なプロジェクト名を判定し、テナントの権限を超えたApplicationの作成を禁止しています。
閑話休題: Admission Webhookの実装
巷ではGatekeeperを使いRegoでAdmission Webhookのルールを書く方式が流行っていますが、 NecoではAdmission Webhookをcontroller-runtimeで実装しています。 つまりGoで書いています。
なぜGoで実装しているのかというと、Admission Webhookはカスタムコントローラとほぼ同じやり方で実装できるので、 チームメンバーのスキルセットと相性が良かったからです。
また、現在はneco-containers/admissionにすべてのwebhookを実装しワンプロセスで動かしていますが、 これはそのうち分割するかもしれません。
まとめ
今回紹介した新しいやり方によって、テナントチームがApplicationを作るたびにadminチームへレビューを依頼する手間がなくなりました。
ただしテナントチームが新しいマニフェスト用リポジトリを追加したいときには、adminチームがApp of Apps用Applicationを新規追加したり、AppProjectを更新したりする手間が残っています。
このような定形作業は、テナント管理のためのカスタムコントローラを作ることで自動化していく予定です。
今回の内容についてもっと良いやり方があったらぜひ教えてください。
サイボウズではKubernetesが好きなインフラ〜ミドルウェア領域のエンジニアを募集中です。
*1:Kubernetesの管理者権限を持たないユーザとそのグループ
*2:https://github.com/argoproj/argo-cd
*3:https://argoproj.github.io/argo-cd/operator-manual/cluster-bootstrapping/#app-of-apps-pattern