今回はArgoCDをより拡張する機能を持つ ApplicationSetを動かしてみます。
ApplicationSetとは
ApplicationSetは、Argo Projectに含まれるプロダクトの一つです。ArgoCDを利用する複数のユースケースにおいて、Application
リソースの管理を自動化しつつ、柔軟に管理する方法を提供するControllerになります。
ArgoCDはApplication
というリソースの中で、利用するGitリポジトリやマニフェストファイルの場所デプロイ先のクラスターなどを指定します。ArgoCDを利用する環境の規模が大きくなったりすると、管理をする上で面倒になるケースが出てきます。
例えば、1つのアプリケーションを提供する環境が複数のKubernetesクラスターで構成されており、それぞれのクラスターに同じアプリケーションをデプロイしようとすると、クラスターごとにApplication
リソースを作成しなければなりません。
また、monorepoのような、1つのリポジトリ中で複数のアプリケーションを管理する場合も、それぞれのアプリケーションごとに Application
リソースを用意しなくてはなりません。
ApplicationSetは、上記のような悩みを解消するため、複数のクラスターやGitリポジトリを効率的に扱うための機能を提供します。
Generator
ApplicationSetは Generator
というパラメータを含みます。 Generator
はクラスターの名称やURL、Gitリポジトリなどのパラメータを設定することで、それぞれに対応した Application
マニフェストを生成し、複数のクラスターやGitリポジトリを使用したArgoCDによるアプリケーション管理を実現します。
Generator
にはいくつか種類があり、以下の通りになります。
List generator
: クラスターの名称とアクセス先URLなどのリストを生成するCluster generator
: ArgoCDに登録されているクラスター情報を生成する。ArgoCDに登録されたクラスターは自動的に検知する。Git generator
: Gitリポジトリ、リポジトリ中のフォルダ構成などの情報を生成する。Matrix generator
: 2種類のgeneratorを組み合わせて使う。SCM Provider generator
: GitHub/GitlabのようなSCMの提供するAPIを使用し、Organization中のリポジトリを探索してパラメータを生成するCluster Decision Resource generator
: ArgoCDに登録されたクラスターのリストを生成する。Custom Resourceのインターフェイスとして利用し、事前にConfigMapに定義したCustom Resourceを作成する。
※ 参考:ApplicationSet Controller - Generators
ユースケース
ApplicationSetでは、いくつかのユースケースを想定しています。
Cluster add-on
Cluster add-onとは、Kubernetesクラスターに機能を追加・拡張するようなプロダクトのことを総称しており、例えば Prometheus Operatorや Argo workflow などを指しています。これらは各クラスターに共通して導入することが多いため、ApplicationSetを生かすことのできるユースケースの一つとなります。
Cluster add-onはクラスターレベルの権限を必要とするため、その管理はインフラ管理者が担うことが多くなります。Add-onを導入するクラスター数の増減が多くなるほど管理コストは高くなります。またクラスターごとに利用用途が異なる(テスト・本番環境など)と、add-onの設定を変更する場合も出てくるため、より複雑になります。
ApplicationSetでは複数のGeneratorを提供しており、それぞれ用途に応じて使い分けることができます。
List generator
: デプロイするadd-onごとにApplicationSetを用意し、デプロイ先のクラスターリストを定義することで管理できます。List generatorの場合クラスターの増減に応じてApplicationSetの手動更新が必要となります。Cluster generator
: デプロイするadd-onごとにApplicationSetを用意します。Cluster generatorはArgoCDに登録されたクラスターを自動で検知するため、クラスターの変更に合わせた自動更新が可能となります。Git generator
: Git generatorのfiles
というfieldを使い、クラスターのリストをJSONファイルで定義しておきます。このJSONファイルを更新することでクラスター情報を更新し、add-onをデプロイすることができます。またdirectories
というfieldを使うと、例えばクラスターごとに対応するディレクトリを用意しておき、ディレクトリ名とクラスター名を一致させておくと、ディレクトリの更新に合わせてクラスター側を更新することができます。
Monorepo
あるプロジェクトでMonorepoを採用している場合、一つのGitリポジトリにすべてのコードが格納されます。Monorepoの場合は複数チームが共通の基準に従う必要があり、それらを促進・強制するためにも、自動化が重要になってきます(参考リンク)。
Monorepoを採用する場合、Kubernetesクラスターの管理者は、1つのリポジトリからクラスター全体の状態を管理することになります。そのため、リポジトリ中のマニフェストファイルが更新されれば、その変更は即座にクラスターへと反映されるべきです。
Git generator
を利用することで、monorepoを採用するプロジェクトの管理者をサポートすることが可能となります。directories
fieldでアプリケーション個別に分かれたサブディレクトリを指定することが可能です。また files
fieldを使うと、JSONメタデータを含むファイルを参照し、そこに定義されたアプリケーションをデプロイすることも可能です。
Self-service of ArgoCD Applications on multi-tenant cluster
開発者が自らArgoCDの Applicationを開発・利用する場合、app-of-appsというパターンと組み合わせ、クラスター管理者がPRを通じてマニフェストファイルをチェックし、デプロイの許可を行う、というケースがあります。Applicationには project
cluster
など重要なパラメータがあり、この設定を誤ってしまうと別のArgoCD Projectやクラスターが更新されるため、レビュアーの重要度が上がってしまいます。そのため管理者としては、開発者の変更できるパラメータを絞り、重要なものは変更できないようにしておきたくなります。
ApplicationSetでは、Git generatorと組み合わせた代替手段を提供しています。ApplicationSetの template
filedに、 project
destination
などの情報をべた書きして固定し、 source
部分のみ Git generatorで生成するようにしておきます。開発者にはGit generator部分のみ更新してもらうことで、アプリケーションの更新先をある程度コントロールすることが可能になります。
※参考:ApplicationSet Controller - Use Cases
ApplicationSetを動かしてみる
ここから実際にApplicationSetを動かしてみます。今回はひとまず動かしてみるという目的で、いくつかのパターンで動かしました。
基本的にはドキュメント通りに動かすだけですが、1点だけ気を付けるとすると、ApplicationSet Controllerと同じNamespaceに ApplicationSetリソースを作成する必要があります。
※参考:ApplicationSet Controller - How ApplicationSet controller interacts with Argo CD
今回の検証環境は以下の通りです。
- Kubernetes version:
1.21
- Kubernetes cluster:
Amazon EKS
- ArgoCD version:
2.1.7
インストール
ApplicationSetはArgoCDと一緒に利用する必要があります。今回はArgoCDとApplicationSetを別々に導入しました。ArgoCDの導入はこちらのページに記載されているので割愛します。ArgoCD導入後は、ArgoCDと同じNamespace(今回は argocd
)にApplicationSet Controllerなどのリソースをデプロイすれば、準備は完了です。
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj-labs/applicationset/v0.2.0/manifests/install.yaml
※参考:ApplicationSet Controller - Getting Started
Local cluster
まずはArgoCDの導入されたクラスター上に、ApplicationSetを使ってリソースを作成します。
List generator
使ったマニフェストファイルはこちらです。 spec.generator.list
部分にクラスターの名称とURLを指定します。そうすると spec.template
配下にある {{cluster}}
{{url}}
部分にパラメータが設定され、それぞれのパラメータに応じた Applicationリソースが作成されます。
apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: name: guestbook namespace: argocd spec: generators: - list: elements: - cluster: in-cluster url: https://kubernetes.default.svc template: metadata: name: '{{cluster}}-guestbook' spec: project: default source: repoURL: https://github.com/argoproj/argocd-example-apps.git targetRevision: HEAD path: guestbook destination: server: '{{url}}' namespace: default
※ 参考:ApplicationSet Controller - List Generator
上記ファイルをデプロイすると、以下のようにリソースが作成されます。
$ kubectl apply -f list-applicationset.yaml applicationset.argoproj.io/guestbook created $ kubectl get applicationset -n argocd NAME AGE guestbook 16s $ kubectl get app -n argocd NAME SYNC STATUS HEALTH STATUS in-cluster-guestbook OutOfSync Missing $ argocd app list NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS REPO PATH TARGET in-cluster-guestbook https://kubernetes.default.svc argocd default OutOfSync Missing <none> <none> https://github.com/argoproj/argocd-example-apps.git guestbook HEAD
あとは argocd app sync
コマンドなどでSyncすれば、テスト用のアプリケーションがデプロイされます。もちろん template
に spec.syncPolicy: automated
を追加すればして自動Syncを有効にすれば、ApplicationSetデプロイ後にテスト用アプリのリソースは作られます。
なお、ApplicationSet controllerのログは以下の通り。
ApplicationSet作成時にControllerに出力されるログ
$ kubectl logs -n argocd argocd-applicationset-controller-5558c458d5-n52d9 -f 2021-11-23T01:58:32.411Z INFO setup ApplicationSet controller v0.2.0 using namespace 'argocd' {"namespace": "argocd", "COMMIT_ID": "2fb043581b8692c84205075494acab6f4b5aef2d"} 2021-11-23T01:58:33.118Z INFO controller-runtime.metrics metrics server is starting to listen {"addr": ":8080"} 2021-11-23T01:58:33.119Z INFO setup Starting manager 2021-11-23T01:58:33.219Z INFO controller-runtime.manager.controller.applicationset Starting EventSource {"reconciler group": "argoproj.io", "reconciler kind": "ApplicationSet", "source": "kind source: /, Kind="} 2021-11-23T01:58:33.220Z INFO controller-runtime.manager.controller.applicationset Starting EventSource {"reconciler group": "argoproj.io", "reconciler kind": "ApplicationSet", "source": "kind source: /, Kind="} 2021-11-23T01:58:33.220Z INFO controller-runtime.manager.controller.applicationset Starting EventSource {"reconciler group": "argoproj.io", "reconciler kind": "ApplicationSet", "source": "kind source: /, Kind="} 2021-11-23T01:58:33.220Z INFO controller-runtime.manager.controller.applicationset Starting Controller {"reconciler group": "argoproj.io", "reconciler kind": "ApplicationSet"} 2021-11-23T01:58:33.219Z INFO controller-runtime.manager starting metrics server {"path": "/metrics"} 2021-11-23T01:58:33.421Z INFO controller-runtime.manager.controller.applicationset Starting workers {"reconciler group": "argoproj.io", "reconciler kind": "ApplicationSet", "worker count": 1} # ApplicationSet作成時 time="2021-11-23T02:08:33Z" level=info msg="Alloc=6126 TotalAlloc=18354 Sys=73297 NumGC=9 Goroutines=49" time="2021-11-23T02:11:09Z" level=info msg="generated 1 applications" generator="{0xc0002b3080 <nil> <nil> <nil> <nil> <nil>}" time="2021-11-23T02:11:09Z" level=info msg="Starting configmap/secret informers" time="2021-11-23T02:11:09Z" level=info msg="Configmap/secret informer synced" time="2021-11-23T02:11:09Z" level=info msg="created Application" app=in-cluster-guestbook appSet=guestbook 2021-11-23T02:11:09.581Z DEBUG controller-runtime.manager.events Normal {"object": {"kind":"ApplicationSet","namespace":"argocd","name":"guestbook","uid":"35d9488c-837a-46c0-a029-014890b804f9","apiVersion":"argoproj.io/v1alpha1","resourceVersion":"1618215"}, "reason": "created", "message": "created Application \"in-cluster-guestbook\""} time="2021-11-23T02:11:09Z" level=info msg="end reconcile" requeueAfter=0s time="2021-11-23T02:11:09Z" level=info msg="generated 1 applications" generator="{0xc00092cdc0 <nil> <nil> <nil> <nil> <nil>}" time="2021-11-23T02:11:09Z" level=info msg="unchanged Application" app=in-cluster-guestbook appSet=guestbook 2021-11-23T02:11:09.607Z DEBUG controller-runtime.manager.events Normal {"object": {"kind":"ApplicationSet","namespace":"argocd","name":"guestbook","uid":"35d9488c-837a-46c0-a029-014890b804f9","apiVersion":"argoproj.io/v1alpha1","resourceVersion":"1618215"}, "reason": "unchanged", "message": "unchanged Application \"in-cluster-guestbook\""} time="2021-11-23T02:11:09Z" level=info msg="end reconcile" requeueAfter=0s time="2021-11-23T02:11:11Z" level=info msg="generated 1 applications" generator="{0xc0007f22c0 <nil> <nil> <nil> <nil> <nil>}" time="2021-11-23T02:11:11Z" level=info msg="unchanged Application" app=in-cluster-guestbook appSet=guestbook 2021-11-23T02:11:11.713Z DEBUG controller-runtime.manager.events Normal {"object": {"kind":"ApplicationSet","namespace":"argocd","name":"guestbook","uid":"35d9488c-837a-46c0-a029-014890b804f9","apiVersion":"argoproj.io/v1alpha1","resourceVersion":"1618215"}, "reason": "unchanged", "message": "unchanged Application \"in-cluster-guestbook\""} time="2021-11-23T02:11:11Z" level=info msg="end reconcile" requeueAfter=0s
Cluster generator
マニフェストファイルは以下の通りです。
Cluster generatorはArgoCDに登録されているクラスターを検知してパラメータを生成するため、デプロイ先のクラスターを指定しない場合は特に設定は必要ありません。 spec.template
の {{name}}
{{server}}
には、ArgoCDで登録されているクラスター情報がそのまま使われます(ここではそれぞれ in-cluster
https://kubernetes.default.svc
)。
apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: name: guestbook namespace: argocd spec: generators: - clusters: {} template: metadata: name: '{{name}}-guestbook' spec: project: "default" source: repoURL: https://github.com/argoproj/argocd-example-apps/ targetRevision: HEAD path: guestbook destination: server: '{{server}}' namespace: default
※ 参考:ApplicationSet Controller - Cluster Generator
こちらもデプロイすれば、先ほどと同様リソースが作成されます。
$ kubectl apply -f cluster-applicationset.yaml applicationset.argoproj.io/guestbook created $ kubectl get appset -n argocd NAME AGE guestbook 8s $ kubectl get apps -n argocd NAME SYNC STATUS HEALTH STATUS in-cluster-guestbook OutOfSync Missing
あとはSyncすればPodも作成されます。
$ argocd app sync in-cluster-guestbook $ kubectl get pods NAME READY STATUS RESTARTS AGE guestbook-ui-85985d774c-t7mxr 1/1 Running 0 13s
なお、ApplicationSetを削除すると、関連するApplication / Podなどのリソースがすべて削除されます。
$ kubectl delete -f cluster-applicationset.yaml applicationset.argoproj.io "guestbook" deleted $ kubectl get appset -n argocd No resources found in argocd namespace. $ kubectl get app -n argocd No resources found in argocd namespace. $ kubectl get pods No resources found in default namespace.
Git generator
マニフェストファイルはこちらです。
ここでは directories
fieldで使用するマニフェストファイルの場所を指定します。また template
にある path.basename
には、path
に指定したディレクトリの配下にあるサブディレクトリの名称が入ります(ここでは argo-workflows
prometheus-operator
)。
apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: name: cluster-addons namespace: argocd spec: generators: - git: repoURL: https://github.com/argoproj-labs/applicationset.git revision: HEAD directories: - path: examples/git-generator-directory/cluster-addons/* template: metadata: name: '{{path.basename}}' spec: project: default source: repoURL: https://github.com/argoproj-labs/applicationset.git targetRevision: HEAD path: '{{path}}' destination: server: https://kubernetes.default.svc namespace: '{{path.basename}}'
※ 参考:ApplicationSet Controller - Git Generator
こちらも同様にデプロイすれば、以下のようにリソースは作成されます。ここではadd-onをデプロイするためにリポジトリを変更しており、2つのApplicationリソースが作成されます。
$ kubectl apply -f git-applicationset.yaml applicationset.argoproj.io/cluster-addons created $ kubectl get appset -n argocd NAME AGE cluster-addons 25s $ kubectl get app -n argocd NAME SYNC STATUS HEALTH STATUS argo-workflows OutOfSync Missing prometheus-operator OutOfSync Missing
こちらもSyncすればリソースは作成されますが、今回はNamespaceがサブディレクトリ名と一致する形式のため、事前にNamespaceを作成してからSyncします。
$ kubectl create ns argo-workflows $ argocd app sync argo-workflows $ kubectl get pods -n argo-workflows NAME READY STATUS RESTARTS AGE argo-server-5ddd56c954-mkgkg 1/1 Running 0 31s workflow-controller-574596cc9f-2wzmk 1/1 Running 0 31s
External cluster
次は外部クラスターに向けてのデプロイです。今回は eksctl
コマンドで別のEKSクラスターを作成し、その後ArgoCDにクラスターを追加登録します。
# external clusterの作成 $ eksctl create cluster -f eks-clusterconfig-2.yaml $ eksctl get clusters 2021-11-23 15:22:29 [ℹ] eksctl version 0.69.0 2021-11-23 15:22:29 [ℹ] using region ap-northeast-1 NAME REGION EKSCTL CREATED eks-cluster-argodst ap-northeast-1 True #追加したクラスター eks-cluster-argosrc ap-northeast-1 True # 登録するクラスターの確認 $ kubectl config get-contexts # クラスターの登録 $ argocd cluster add <external cluster name>
※ 参考:ArgoCD Docs - Argocd cluster add
クラスターを登録すると以下のようにSecretリソースが作成されます。なおSecretリソース名にあるURL先のクラスターは、既に削除されています。
$ kubectl get secret -n argocd NAME TYPE DATA AGE argocd-application-controller-token-22xzv kubernetes.io/service-account-token 3 4h39m argocd-applicationset-controller-token-v84pl kubernetes.io/service-account-token 3 4h29m argocd-dex-server-token-g76wx kubernetes.io/service-account-token 3 4h39m argocd-initial-admin-secret Opaque 1 4h38m argocd-redis-token-m94v5 kubernetes.io/service-account-token 3 4h39m argocd-secret Opaque 5 4h39m argocd-server-token-rqn9r kubernetes.io/service-account-token 3 4h39m # "cluster-"で始まるSecretが作成される cluster-c68e805bffb0dd155295b27b15cbf8ac.gr7.ap-northeast-1.eks.amazonaws.com-3093402059 Opaque 3 90s default-token-qc648 kubernetes.io/service-account-token 3 4h39m
List generator
ここでは以下のマニフェストファイルを使用します。これは先ほどのマニフェストファイルに外部クラスター情報を追加したものです。
apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: name: guestbook namespace: argocd spec: generators: - list: elements: - cluster: in-cluster url: https://kubernetes.default.svc # 追加 - cluster: external-cluster url: https://C68E805BFFB0DD155295B27B15CBF8AC.gr7.ap-northeast-1.eks.amazonaws.com template: metadata: name: '{{cluster}}-guestbook' spec: project: default source: repoURL: https://github.com/argoproj/argocd-example-apps.git targetRevision: HEAD path: guestbook destination: server: '{{url}}' namespace: default
こちらをデプロイするとApplicationが作成されます。今回は2つのクラスターに対してApplication
リソースが作られ、その名称はクラスターと対応しています。
$ kubectl apply -f list-applicationset.yaml applicationset.argoproj.io/guestbook created $ kubectl get appset -n argocd NAME AGE guestbook 14s $ kubectl get app -n argocd NAME SYNC STATUS HEALTH STATUS external-cluster-guestbook OutOfSync Missing in-cluster-guestbook OutOfSync Missing
あとはSyncすればデプロイは完了です。
$ argocd app sync external-cluster-guestbook $ argocd app list NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS REPO PATH TARGET external-cluster-guestbook https://C68E805BFFB0DD155295B27B15CBF8AC.gr7.ap-northeast-1.eks.amazonaws.com default default Synced Healthy <none> <none> https://github.com/argoproj/argocd-example-apps.git guestbook HEAD in-cluster-guestbook https://kubernetes.default.svc default default OutOfSync Missing <none> <none> https://github.com/argoproj/argocd-example-apps.git guestbook HEAD # 操作対象のクラスターを切り替える $ kubectl config use-context <external cluster name> $ kubectl get pods NAME READY STATUS RESTARTS AGE guestbook-ui-85985d774c-v7rv6 1/1 Running 0 4m34s
Cluster generator
ここでは外部クラスターのみにデプロイするよう、条件を追加しています。ArgoCDにクラスターを登録した際に作られるSecretには argocd.argoproj.io/secret-type=cluster
というタグが付与されており、これを含むクラスターだけを対象とします。
apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: name: guestbook namespace: argocd spec: generators: - clusters: # 追加 selector: matchLabels: argocd.argoproj.io/secret-type: cluster template: metadata: name: 'external-guestbook' spec: project: "default" source: repoURL: https://github.com/argoproj/argocd-example-apps/ targetRevision: HEAD path: guestbook destination: server: '{{server}}' namespace: default
なお spec.template.metadata.name
の名称を {{name}}-guestbook
から external-guestbook
に変更していますが、クラスターの名称が長すぎるとSync時に以下のようなエラーが発生するため変更しています。
# エラーの例 $ argocd app sync (中略) Deployment.apps "guestbook-ui" is invalid: metadata.labels: Invalid value: "<cluster name>": must be no more than 63 characters
デプロイ後は以下の通りです。
$ kubectl apply -f cluster-applicationset.yaml $ argocd app sync external-guestbook $ kubectl config use-context <external cluster> $ kubectl get pods NAME READY STATUS RESTARTS AGE guestbook-ui-85985d774c-v4xqc 1/1 Running 0 36s
Matrix generator
Matrix generatorは2種類のgeneratorを組み合わせるgeneratorです。今回は Cluster generator と Git generator を組み合わせたパターンにしています。
apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: name: cluster-git namespace: argocd spec: generators: - matrix: generators: - git: repoURL: https://github.com/argoproj-labs/applicationset.git revision: HEAD directories: - path: examples/matrix/cluster-addons/* - clusters: selector: matchLabels: argocd.argoproj.io/secret-type: cluster template: metadata: name: '{{path.basename}}-external' spec: project: default source: repoURL: https://github.com/argoproj-labs/applicationset.git targetRevision: HEAD path: '{{path}}' destination: server: '{{server}}' namespace: default
※ 参考:ApplicationSet Controller - Matrix Generator
デプロイ後は以下の通りです。
$ kubectl apply -f matrix-applicationset.yaml applicationset.argoproj.io/cluster-git created $ kubectl get appset -n argocd NAME AGE cluster-git 11s $ kubectl get app -n argocd NAME SYNC STATUS HEALTH STATUS argo-workflows-external OutOfSync Missing prometheus-operator-external OutOfSync Missing