Argo CD ApplicationSet Controller: 世界為我而轉動!

smalltown
Starbugs Weekly 星巴哥技術專欄
30 min readMay 10, 2021

Background

今年一月 Flux 2 剛推出時,我寫了一篇開箱文 “利用 Flux2 為 Kubernetes 達成 Configuration Management”,那時候覺得 Flux 2 把一些基本設定,例如 Helm, Git Repository…等,都一併定義為 CRD,讓整個架構梳理起來格外的清爽,而在 Multi-Cluster 管理方面則是以相對低耦合度的方式,讓每個 Cluster 在一開始安裝好 Flux 2 的 Agent,後續就只需要以 GitOps 的流程透過 Git Repository 來管理 Multi-Cluster 即可,多組 Cluster 之間並不需要知曉彼此的存在與網路連通性

Argo CD 在 Multi-Cluster 的管理模式上則剛好相反,必須先在 Primary Cluster 安裝 Argo CD,然後再透過這個 Primary Cluster 去管理其他的 Cluster,屬於 Primary 與 Secondary 的架構,因此必須要讓 Primary Cluster 有權限去存取 Secondary Cluster, 彼此之間的網路當然就要打通

Argo CD 中有一個用來 Bootstrap Cluster 的 App of Apps Pattern 無法在 Primary & Secondary 的架構下輕易地使用,因為其中會用到 Argo CD 內才有的 Application CRD,可是 Secondary Cluster 並沒有安裝 Argo CD,這樣一來被管理的 Secondary Cluster 也想要一次安裝好所有套件要如何達成呢?!就在一個月前 Argo CD V2.0 發布的時候,推出了可以跟 Argo CD 一起搭配使用的 ApplicationSet Controller (大家都要在 2.0 拼輸贏就對了XD) 來彌補之前功能的不足,而此篇文章主要就是來開箱 Argo CD ApplicationSet Controller

ApplicationSet Controller

Bootstrapping Kubernetes with ArgoCD
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: prometheus
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
destination:
server: https://kubernetes.default.svc
namespace: default
project: default
source:
path: infrastructure/solar-system/sun/prometheus
repoURL: https://github.com/smalltown/argo-galaxy.git
targetRevision: HEAD
syncPolicy:
automated:
prune: true
selfHeal: true

在 Argo CD 中最基本的元素就是 Application CRD,它定義要如何安裝一個應用在 K8s Cluster 中,以上面的 YAML 檔案當作例子,他是一個叫做 Prometheus 的 Application CRD

  • Destination 定義要安裝 Prometheus 到哪一座 K8s Cluster 中的的哪一個 Namespace
  • Source 則是指安裝 Prometheus 的 Kubernetes Manifest 存放於何處
  • SyncPolicy 則是 Agro CD 用來監控 Prometheus 在 K8s Cluster 中的真實狀態和 Source 中定義的是否相同,不同的話就可以幫忙進行同步時的規則

新推出的 Argo CD ApplicationSet Controller 可以說是 Application CRD 的威力加強版,它讓使用者可以在單個 YAML 檔案內像是寫程式使用 For Loop 一樣,隨使用者的需求一次性地建立大量的 Application CRD,而這個 For Loop 的功能叫做 Generator,目前推出的版本中共有四種 Generator: Cluster Generator, Git Directory Generator, Git File Generator, List Generator,底下會使用實際範例來演示這四種 Generator 如何解放 Kubernetes 維運人員的雙手與時間

Prerequisite

為了增加一點趣味性,所以沒有使用 Alpha, Beta 和 Production 的字眼,這邊以大家居住的太陽系來當命名規則,太陽 (Sun) 就是安裝有 Argo CD 的 Primary K8s Cluster, 而其他的行星就是被 Argo CD 所管理的 Secondary K8s Cluster,地球上的七大洲就類比為 K8s Namespace,大家一起來當創世神!

根據底下的步驟將會在本地端創建 4 座 K8s Cluster (Sun, Mercury, Venus 和 Earth),並且將 Argo CD 和 Argo CD ApplicationSet Controller 安裝在 K8s Cluster Sun, 然後把另外三座 K8s Cluster Mercury, Venus 和 Earth 註冊到 Argo CD 中被納管

首先把需要用到的工具安裝好

# 有興趣一起手把手玩玩看的人請先把 Repository Git Clone 到本地端
~$ git clone
https://github.com/smalltown/argocd-galaxy.git
# 安裝 Docker Desktop
# 用來運行 Container
https://www.docker.com/products/docker-desktop
# 安裝 kind,其他平台安裝方式請參閱官方文件
# 用來運行輕量 K8s Cluster 於本地端
~$ brew install kind
# 安裝 kubectx,其他平台安裝方式請參閱官方文件
# 用來方便切換不同 k8s context
~$ brew install kubectx
# 安裝 helm,其他平台安裝方式請參閱官方文件
# K8s 套件管理工具
~$ brew install helm
# 安裝 kubectl, 其他平台安裝方式請參閱官方文件
# 用來與 K8s Cluster API Server 溝通
~$ brew install kubectl
# 安裝 argocd cli, 其他平台安裝方式請參閱官方文件
# 用來與 Argo CD 溝通
~$ brew install argocd

接著來建立整個太陽系 K8s Cluster!

開玩笑的,可以只建立 2~4 座 Cluster 左右,kind 用來建立 K8s Cluster 的 Configuration 有一個小地方需要修改,以 sun.yaml 為例,必須要把 apiServerAddress 的 #{IP_ADDRESS} 替換成自己目前電腦使用的 IP,可以使用指令 ifconfig (MacOS)或是 ipconfig (Windows) 來取得,並且把其他所有的 kind YAML Configuration 都做一樣的修改,如此一來,稍後建立的本地端 K8s Cluster 才能後透過內部網路互相溝通

# argocd-galaxy/misc/kind/solar-system/sun.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: sun
networking:
apiServerAddress: #{IP_ADDRESS}
apiServerPort: 61000

先建立 K8s Cluster Sun 並且安裝 Argo CD 於其中

# 創建 K8s Cluster Sun
~$ kind create cluster --config misc/kind/solar-system/sun.yaml
# 安裝 Argo CD
~$ helm repo add argo-cd https://argoproj.github.io/argo-helm
~$ helm dep update infrastructure/solar-system/sun/argo-cd
~$ helm install argo-cd infrastructure/solar-system/sun/argo-cd
--create-namespace --namespace argocd
# 安裝 Argo CD ApplicationSet
~$ helm dep update infrastructure/solar-system/sun/argocd-applicationset
~$ helm install argocd-applicationset infrastructure/solar-system/sun/argocd-applicationset
--create-namespace --namespace argocd
# 確認安裝成功
~$ kubectl get crd
NAME CREATED AT
applications.argoproj.io 2021-05-09T10:29:29Z
applicationsets.argoproj.io 2021-05-09T10:31:17Z
appprojects.argoproj.io 2021-05-09T10:29:29Z
~$ kubectl get pod -n argocd
NAME READY STATUS RESTARTS AGE
argo-cd-argocd-application-controller-74fcff6d69-mqr44 1/1 Running 0 95m
argo-cd-argocd-redis-5bf78f5b66-pnbqs 1/1 Running 0 95m
argo-cd-argocd-repo-server-5c6f7dbd7c-th4tm 1/1 Running 0 95m
argo-cd-argocd-server-786774d55c-6rc64 1/1 Running 0 95m
argocd-applicationset-58f6cb9f8f-2sss5 1/1 Running 0 93m

安裝完 Argo CD 之後有兩個方式可以存取,一個是透過 kubectl 使用 Port Forward 的方式,再使用 Browser 來存取 Argo CD 的 UI,另外一個則是透過 Argo CD CLI 直接跟 Argo CD 溝通,底下先示範 kubectl 的方式

# 先取得 Argo CD admin 帳號的初始密碼
~$ kubectl get -n argocd secret argocd-initial-admin-secret -o jsonpath='{.data}' | jq .password -r | base64 -d
# 透過 kubectl 進行 port forward
~$ kubectl port-forward svc/argo-cd-argocd-server 8080:443

接著打開瀏覽器拜訪 http://localhost:8080/ 輸入帳號 admin 與透過上面命令得到的初始密碼就可以登入成功,看到以下畫面

再建立要被 Argo CD 管理的 K8s Cluster

# 建立 K8s Cluster Mercury
~$ kind create cluster --config misc/kind/solar-system/mercury.yaml
# 建立 K8s Cluster Venus
~$ kind create cluster --config misc/kind/solar-system/venus.yaml
# 建立 K8s Cluster Earth
~$ kind create cluster --config misc/kind/solar-system/earth.yaml

將新建立的 K8s Cluster 跟前面安裝的 Argo CD 做註冊

# 先把透過 Argo CD CLI 跟 Argo CD 聯絡的路打通
~$ argocd login localhost:8080 --username admin --password #{剛剛取得的初始化密碼}
WARNING: server is not configured with TLS. Proceed (y/n)? y
'admin:login' logged in successfully
Context 'localhost:8080' updated
# 註冊 K8s Cluster Mercury
~$ kubectx kind-mercury
~$ argocd cluster add kind-mercury --name mercury
# 註冊 K8s Cluster Venus
~$ kubectx kind-venus
~$ argocd cluster add kind-venus --name venus
# 註冊 K8s Cluster Earth
~$ kubectx kind-earth
~$ argocd cluster add kind-earth --name earth
# 驗證看看是不是三個 K8s Cluster 都已經註冊成功
~$ argocd cluster list
SERVER NAME VERSION STATUS MESSAGE
https://192.168.50.28:61003 earth Unknown Cluster has no application and not being monitored.
https://192.168.50.28:61001 mercury Unknown Cluster has no application and not being monitored.
https://192.168.50.28:61002 venus Unknown Cluster has no application and not being monitored.
https://kubernetes.default.svc in-cluster Unknown Cluster has no application and not being monitored.

到這邊總算把環境設定完畢,可以開始來試玩看看 Argo CD ApplicationSet Controller 提供的四種 Generator 是不是真的可以讓維運人員事半功倍

Demonstration

Cluster Generator

  • 應用情境:HPA (Horizontal Pod Autoscaler) 是 K8s Cluster 被普遍使用的一個功能,為了要支援這個功能,需要在所有的 K8s Cluster 安裝 Metrics Server,可是一個一個安裝實在是有夠累人的,假如使用了 Argo CD ApplicationSet Controller 會變成如何呢?
  • 程式範例:
# metrics-server.yamlapiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: metrics-server
namespace: argocd
spec:
generators:
- clusters: {} # 使用 Argo CD 內所有的 K8s Cluster
template:
metadata:
name: '{{name}}-metrics-server' # name 會置換為 Cluster 名稱
spec:
project: default
syncPolicy:
automated:
prune: true
selfHeal: true
source:
repoURL: https://github.com/smalltown/argocd-galaxy
targetRevision: main
path: infrastructure/solar-system/basic/metrcis-server
destination:
server: '{{server}}' # server 會置換為 Cluster API Endpoint
namespace: kube-system

就像上面提過的,ApplicationSet 其實算是 Application 的威力加強版,可以發現主要有兩個不一樣的地方,1) 多了一個 generators 的 section 2) 在下面 template 中的定義可以使用 name 和 server 進行字串的置換,而這邊可以想像成如下的程式碼:

for cluster in argocd_clusters:
create_argocd_application(cluster["name"], cluster["server"])

所以 ApplicationSet Controller 會幫忙所有被 Argo CD 管理的 K8s Cluster 新增 Metrics Server Argo CD Application,維運人員就不再需要單獨為每一座 Cluster 新增;除了像上面的 generator 使用 {} 來代表所有被 Argo CD 所管理的 K8s Cluster 之外;也可以使用 Label Selector,讓 Application 只建立在特定的 K8s Cluster 上

  • 執行結果:

將 Metrics Server 的 ApplicationSet CRD 建立之後,打開瀏覽器查看 Argo CD UI 就可以馬上看到 Metrics Server 一瞬間被自動安裝在 4 座 Cluster 上,不再需要煩惱有沒有哪一座 Cluster 還沒有安裝到

# 建立 metrics server Argo CD ApplicationSet CRD
~$ kubectl apply -f applications/solar-system/basic/metrics-server.yaml

Git Directory Generator

  • 應用情境: 每次要建立新的 MicroService 時,一定需要先建立 NameSpace,然後在該 NameSpace 新增一些 ConfigMap 以及 Secret 給新的 MicroService 使用,這次接到的工作是要在 K8s Cluster Earth 新增 7 個 NameSpace,每次都要做一樣的事情真的是很乏味 ╮(╯_╰)╭
  • 程式範例:
# bootstrap.yamlapiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: earch-bootstrap
namespace: argocd
spec:
generators:
- git:
repoURL:
https://github.com/smalltown/argocd-galaxy
revision: main
directories:
- path: infrastructure/solar-system/earth/continents/*

template:
metadata:
name: '{{path.basename}}' # 此字串會置換成 Sub-Folder 名稱
spec:
project: default
syncPolicy:
automated:
prune: true
selfHeal: true
source:
repoURL: https://github.com/smalltown/argocd-galaxy
targetRevision: main
path: infrastructure/solar-system/basic/bootstrap
destination:
server: https://#{IP_ADDRESS}:61003 # 這邊要自己換成本地 IP
namespace: '{{path.basename}}' # 此字串會置換成 Sub-Folder 名稱

這邊使用的 Generator 為 Git Directory,定義要去哪一個 Git Repository 查看其資料夾結構,例如在這裡會去查看在 Earth 的 Continents Folder 底下存在的 Sub-Folder,根據這些 Sub-Folder 的資料夾名稱來建立 Argo CD Application,像這邊有七個 Sub-Folder 所以就會建立出七個 Argo CD Application

  • 執行結果:

建立完 Argo CD ApplicationSet Bootstrap 之後,可以看到 Argo CD UI Console 自動多了 7 個 Application 出來,而這 7 個 Application 會根據當初 Source 中定義的 K8s Manifest 將對應的 K8s Resource自動建立完成 (這邊只有簡單建立 NameSpace, 真實情況可以根據需求增加更多的 K8s Resource);所以假如今天又多一個 MicroService 時,只需要新增一個資料夾,並且提交到 Git Repository 中,Argo CD ApplicationSet Controller 就會自動多新增一個 Application

# 建立 earth bootstrap Argo CD ApplicationSet CRD
~$ kubectl apply -f applications/solar-system/earth/basic/bootstrap.yaml
# 使用 kubectl 驗證 K8s Cluster Earth
~$ kubectx kind-earth
~$ kubectl get namespaces
NAME STATUS AGE
africa Active 7m17s
antarctica Active 7m17s
asia Active 7m17s
australia Active 7m17s
default Active 11m
europe Active 7m17s
kube-node-lease Active 11m
kube-public Active 11m
kube-system Active 11m
local-path-storage Active 11m
north-america Active 7m17s
south-america Active 7m17s

Git File Generator

  • 應用情境: 今天想要將 K8s Cluster Earth 被 MicroService 使用到的 NameSpace 都加上 Resource Quota,但是每個 NameSpace 底下的設定不太一樣,例如有的是要運行 CPU-Bound 應用服務,有的則是 Memory-Bound 應用服務,該如何是好呢?
  • 程式範例:
# resourcequota.yamlapiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: resource-quota
namespace: argocd
spec:
generators:
- git:
repoURL:
https://github.com/smalltown/argocd-galaxy
revision: main
files:
- path: "infrastructure/solar-system/earth/continents/**/**/config.json"

template:
metadata:
name: '{{destination.namespace}}-resource-quota'
labels:
function: resource-quota
spec:
project: '{{project}}'
syncPolicy:
automated:
prune: true
selfHeal: true
source:
repoURL: '{{source.repoURL}}'
targetRevision: '{{source.targetRevision}}'
path: '{{source.path}}'
destination:
server: https://#{IP_ADDRESS}:61003 # 這邊要自己換成本地 IP
namespace: '{{destination.namespace}}'

上面使用的 Git Directory Generator,是利用 Sub-Folder 的名稱來建立對應的 Argo CD Application,除此之外 Application CRD 裡面的設定都是差不多的;但這邊使用的 Git File Generator 讓每個 Application Template 裡面的 Source, SyncPolicy, Project 或是 Destination 都可以使用變數置換

# config.json{
"project": "default",
"source": {
"repoURL": "https://github.com/smalltown/argocd-galaxy",
"targetRevision": "main",
"path": "infrastructure/solar-system/basic/resource-quota/cpu-bound"
},
"destination": {
"namespace": "africa"
}
}

可以看到 ApplicationSet Controller 所讀取的 infrastructure/solar-system/earth/continents/**/**/config.json 這個 JSON 檔案,它使用裡面定義的 Key/Value 來進行 Argo CD Application Template 的字串置換,用以達成在同一個 YAML 檔案裡產生出各種不同的 Application

  • 執行結果:

一樣先把 Argo CD ApplicationSet CRD 創建起來,接著就可以在 Argo CD UI Console 看到 K8s Cluster Earth 裡面的每個 NameSpace 都已經自動產生好 Resource Quota 的 K8s Resource,而且是根據 config.json 裡面定義的 Key/Value 來客製化每個 Argo CD Application

# 建立 Resource Quota Argo CD ApplicationSet CRD
~$ kubectl apply -f applications/solar-system/earth/basic/resourcequota.yaml

List Generator

  • 應用情境: 使用 Prometheus 來監控 K8s Cluster 是再正常不過的事情了,假如今天想要在 K8s Cluster Mercury, Venus 和 Earth 裡面同時安裝的話,該如何使用 Argo CD ApplicationSet Controller 來達成呢?
  • 程式範例:
# prometheus.yamlapiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: prometheus
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: mercury
url:
https://#{IP_ADDRESS}:61001 # 這邊要自己換成本地 IP
- cluster: venus
url:
https://#{IP_ADDRESS}:61002 # 這邊要自己換成本地 IP
- cluster: earth
url:
https://#{IP_ADDRESS}:61003 # 這邊要自己換成本地 IP
template:
metadata:
name: '{{cluster}}-prometheus'
spec:
project: default
syncPolicy:
automated:
prune: true
selfHeal: true
source:
repoURL: https://github.com/smalltown/argocd-galaxy
targetRevision: main
path: infrastructure/solar-system/basic/prometheus
destination:
server: '{{url}}'
namespace: kube-system

最後一個 List Generator 老實說跟 Cluster Generator 功能差不多,在最前面 Element 中定義所要安裝 Cluster 的名稱跟 URL,下面的 Application Template 中 Destination 的 Server 就可以被自動置換

  • 執行結果:

一樣先把 Argo CD ApplicationSet CRD 創建起來,接著就可以在 Argo CD UI Console 看到三座 K8s Cluster Mercury, Venus 和 Earth 都已經安裝好 Prometheus,不過我自己會比較喜歡在 Cluster 設定好 Label,然後透過 Cluster Generator 來達到一樣的效果

# 建立 Prometheus Argo CD ApplicationSet CRD
~$ kubectl apply -f applications/solar-system/basic/prometheus.yaml

Declarative Setup

上面已經把各種 ApplicationSet 的 Generator 功能演示了一次,但假如大家還有印象的話,應該還記得在一開始設定的時候,有些步驟是使用 CLI 在 Terminal 完成的,這有點違反 DevOps Everything as Code 的精神,所以在這個 Section 來為剛剛偷懶的舉動贖罪XD

Argo CD: The Chicken-and-Egg Conundrum!

The Chicken/Egg Conundrum: How Modern Stories are Best Told

我們一開始在安裝 Argo CD 和 Argo CD ApplicationSet Controller 時,是直接使用 Helm CLI,所以我們在這邊把 Argo CD 的 Argo CD ApplicationSet 給補上 (好饒口),這樣一來就連負責 Configuration Management 角色的 Argo CD 本身也將自己納入管理

# 建立 Argo CD 的 Argo CD ApplicationSet CRD
~$ kubectl apply -f applications/solar-system/sun/argo.yaml

Argo CD Register

在註冊 Argo CD Cluster 時是使用 Argo CD CLI 達成,但其實他也只是在 K8s Cluster Sun 的 NameSpace 裡面新增一個 K8s Secret 而已,大概長得像底下這樣子,Argo CD 主要透過 Label argocd.argoproj.io/secret-type: cluster 來得知這是一個 Cluster 的連線資訊,像我自己是透過 Terraform 在建立好 K8s Cluster 的時候順便把這個 K8s Secret 產生在有安裝 Argo CD 的 Primary K8s Cluster 中,這樣一來就可以確定這一個動作是 Declarative

apiVersion: v1
kind: Secret
metadata:
name: mycluster-secret
labels:
argocd.argoproj.io/secret-type: cluster
type: Opaque
stringData:
name: mycluster.com
server: https://mycluster.com
config: |
...

還有其他更多 Argo CD 設定都可以使用 YAML 檔案來撰寫跟定義,有興趣的人可以參考官方這份 Declarative Setup 文件

Repository Folder Structure

Folder Structure 沒有分好壞,只要適合自己的開發團隊即可,這邊把範例使用到的資料夾結構稍微解釋一下,我將 Argo CD CRD (Application & ApplicationSet) 與 K8s Manifest 分別置於不同的資料夾內,然後再透過行星系統來做分群 ,所以太陽系一群,克卜勒90 也自成一群,裡面就是他們的行星;對應到現實場景就是 Production 環境一群,Non-Production 環境一群,每一群裡面都會有一座 Primary K8s Cluster (安裝有 Argo CD 那座),再往下一層就是各自所管理的 Alpha-*, Beta-*, Prod-* 等 K8s Cluster,並且會希望不要將 Helm Chart 擺到這個 Repository 裡來,而是去參照在外部的 Helm Repository,用以避免團隊一起開發時的混亂局面

argocd-galaxy
├── applications
│ ├── kepler-90
│ ├── ...
│ └── solar-system
│ ├── basic
│ ├── earth
│ ├── ...
│ └── sun
└── infrastructure
├── kepler-90
├── ...
└── solar-system
├── basic
├── earth
├── mercury
├── sun
│ ...
└── venus
└── solar-system
├── basic
├── earth
├── mercury
├── sun
└── venus

Conclusion

目前 Argo CD ApplicationSet Controller 還在很早期的階段,例如 Git File Generator 只能讀取 JSON 格式檔案,Destination 的 Server 值無法像 Application CRD 內一樣直接使用 Cluster 名稱,也還沒有 If/Else 的功能 ;不過光是目前的 Generator a.k.a. For Loop 就可以幫助維運人員省下很多寶貴的時間,很期待他在未來的表現!

Reference

--

--

smalltown
Starbugs Weekly 星巴哥技術專欄

原來只是一介草 QA,但開始研究自動化維運雲端服務後,便一頭栽進 DevOps 的世界裏,熱愛鑽研各種可以提升雲端服務品質及增進團隊開發效率的開源技術