基于 kubernetes 的声明式 Gitops 持续部署工具。
持续部署工具有很多,如 Jenkins 等等,我们为什么选择 ArgoCD 呢?
- 应用定义,配置和环境变量管理等等,都是声明式的,基于云原生的。
- 所有声明清单都存储在代码仓库中,受版本管理
- 应用发布和生命周期管理都是自动化的,可审计的。
最重要的,ArgoCD 操作简单,非常易用。
ArgoCD 被设计并实现为 Kubernetes 控制器,它会持续监控 ArgoCD 应用状态。ArgoCD 中的应用会对应一个 Git 仓库,ArgoCD 控制器确保应用状态始终同步。此处的 Git 仓库,并不存放项目源码,它保存的是项目在 Kubernetes 中的运行状态,也就是配置清单。Git 仓库内容的组织形式,支持 Helm, Kustomize 等;
当用户向 Git 仓库提交合并请求,合并被受理后,Git 仓库中应用状态的配置清单发生变化,此时 Git 仓库可以通过 WebHook 触发 ArgoCD 的应用同步。如果未配置 WebHook,ArgoCD 会轮询检测 Git 仓库的变更,检测周期默认为 3 分钟。当然,用户也可以通过 UI 或 CLI 的方式手动触发应用同步。
ArgoCD 的 Hook 机制,会在应用状态同步前,同步中,同步后及同步失败后,触发响应的钩子方法,用来完成一些额外操作,可以实现更加复杂的应用控制。ArgoRollouts 的蓝绿发布,就非常好的利用了 hooks 机制。
ArgoCD 不仅可以将应用发布到它所在的 Kubernates 集群,它也可以托管其他集群,实现多集群的应用部署。该功能由 ApplicationSet 实现。
推荐用 operator 的方式部署 ArgoCD。推荐理由如下:
- 使用默认配置快速安装并启动 ArgoCD
- 无缝升级 ArgoCD 所有组件
- 支持定期计划备份和恢复 ArgoCD 集群
- 聚合并发布 ArgoCD 及 Operator 本身的指标,供 Prometheus 和 Grafana 使用
- 根据需要自动缩放对应组件
curl -sL https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v0.21.2/install.sh | bash -s v0.21.2
kubectl create -f https://operatorhub.io/install/argocd-operator.yaml
kubectl get csv -n operators
!!! 注: 如果不配置环境变量,则 ArgoCD 无法创建 Namespace
默认使用 Operator 部署的 ArgoCD,没有集群资源的控制权限;找到 Subscription
类型的资源,给 Operator 添加环境变量配置;
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: argocd
namespace: argocd
spec:
channel: alpha
name: argocd-operator
source: operatorhubio-catalog
sourceNamespace: olm
config:
env:
- name: ARGOCD_CLUSTER_CONFIG_NAMESPACES
value: <argocd-namespaces(s)>
# 此处案例中 value 配置为 argocd
https://argocd-operator.readthedocs.io/en/latest/usage/ingress/
apiVersion: argoproj.io/v1alpha1
kind: ArgoCD
metadata:
name: monday-argocd
namespace: argocd
spec:
server:
insecure: true
ingress:
enabled: true
ArgoCD Operator 默认 Ingress 域名为资源名,此处即为 monday-argocd
。Operator 官方文档中说可以 overwrite
域名,但翻遍了 CRD 的描述信息,也没找到。翻源码才发现 Operator 并没有控制 host
字段。直接 kubectl edit
即可 @_@
!!! 注:官方文档言辞模糊,竟然可以直接修改
ingress 域名: argo.monday.fun
本地主机配置 hosts 如下:
# /etc/hosts
10.10.14.91 argo.monday.fun
看到下图请为自己鼓掌 !!!
默认 admin 密码存放在
<ArgoCD 实例名>-cluster
secret 里
kubectl -n argocd get secret monday-argocd-cluster -o jsonpath='{.data.admin\.password}' | base64 -d
可以通过 UI 界面或者直接 kubectl apply
下方配置清单,创建 ArgoCD 应用清单。
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook
spec:
destination:
name: ""
namespace: guestbook
server: "https://kubernetes.default.svc"
source:
path: kustomize-guestbook
repoURL: "https://github.com/argoproj/argocd-example-apps.git"
targetRevision: HEAD
project: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
常见问题: Namespace "guestbook" for Service "guestbook-helm-guestbook" is not managed
!!! 注: 这个问题就是 3.1 步骤中没有添加环境导致的
# 通过给 Namespace 打标签的方式,给 ArgoCD 授权
# 默认情况下 $NAMESPACE=argocd
kubectl label ns guestbook argocd.argoproj.io/managed-by=$NAMESPACE
上述配置清单也可以通过 UI 界面生成,具体操作见下图:
在 argocd 中,用户所管理的对象,叫做应用。应用的状态,由它对应的资源清单描述。我们可以创建一个仓库,集中管理这些应用配置清单,而这个仓库,也可以当做应用来看待。
这种模式,就是 app of apps 模式。
https://github.com/argoproj/argocd-example-apps/tree/master/apps
官方示例中,使用 Helm Chart 来实现 app of apps 模式,templates 目录下每个文件都是一个 ArgoCD Application 配置清单。
├── Chart.yaml
├── templates
│ ├── guestbook.yaml
│ ├── helm-dependency.yaml
│ ├── helm-guestbook.yaml
│ └── kustomize-guestbook.yaml
└── values.yaml
当我们删除 app of apps 应用时,对应的其他应用,也会被级联删除。
- 集群应用迁移
微服务开发模式中,一个项目往往对应多个子项目。我们可以为创建 app of apps 应用,管理项目的微服务。当项目需要迁移时,只需要在新集群或者新 Namespace 中应用即可。
- 集群初始化
生产环境中,k8s 集群部署好后,还会安装一些必要的支撑服务应用。此时可以把集群的初始化工作,使用 app of apps 应用描述。这样,新集群的初始化工作,也仅仅是创建个 argocd 应用而已。
在容器云原生的世界里,俄罗斯套娃模式很常见。例如,docker in docker,k8s in k8s。我们刚讲过的 app of apps 也是类似套娃模式。我们是否可以再套一层呢?
答案是肯定的。app of apps 应用,本质上仍然是 argocd Application,我们依旧可以将它放在更上层的 app of apps 应用中。
这里的想象空间就有点过于复杂了,这里仅仅给大家提供一种思考的方向,目前还没有听说哪个团队这么实践过。
ArgoCD 在 Web UI 界面上无法添加 Kubernetes 集群,目前仅支持通过 argocd 命令行客户端工具;
# ref: https://argo-cd.readthedocs.io/en/stable/cli_installation/
curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
chmod +x /usr/local/bin/argocd
argocd 命令行客户端,提供 cluster add 方法,可以从 kubectl 的连接上下文或者认证文件中添加集群信息。 当我们在 kubectl 的上下文中,添加 tuesday 集群的上下文后,就可以使用 argocd cluster add 命令了;
# 拷贝 Tuesday 集群的认证秘钥到
scp 10.10.15.153:~/.kube/config ~/.kube/tuesday.config
# 通过环境变量配置 kubectl
export KUBECONFIG=$HOME/.kube/config:$HOME/.kube/tuesday.config
# 查看 kubectl 上下文
kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* [email protected] cluster.monday kubernetes-admin
[email protected] cluster.tuesday kubernetes-admin
# 导出 argocd-server 443 服务
kubectl port-forward svc/monday-argocd-server -n argocd 9999:443
# 登录 argocd,输入 admin 账号密码
argocd login 127.0.0.1:9999
# 通过 kubeconfig 添加集群
argocd cluster add [email protected]
# optional: 也可以不配置 kubectl 而直接通过 Kubeconfig 文件添加集群
argocd cluster add --kubeconfig tuesday.config --name tuesday [email protected]
argocd-cli 客户端支持从 kubeconfig 文件直接添加集群信息;
$ argocd cluster add --kubeconfig tuesday.config
ERRO[0000] Choose a context name from:
CURRENT NAME CLUSTER SERVER
* [email protected] cluster.tuesday https://tuesday-api.automan.fun:6443
$ argocd cluster add --kubeconfig tuesday.config [email protected]
# 查看集群列表
argocd cluster list
SERVER NAME VERSION STATUS MESSAGE
https://tuesday-api.automan.fun:6443 tuesday Unknown Cluster has no application and not being monitored.
https://kubernetes.default.svc in-cluster Unknown Cluster has no application and not being monitored.
由于 ArgoCD 并未在集群中部署应用,所以对集群的健康检查是关闭的。
说到 ArgoCD 多集群部署应用,首先想到的方案可能在 UI 界面上配置多个应用,选择不同的集群发布即可;其次还可能想到,针对不同集群,写不同的 Application 配置清单,直接 kubectl apply
即可;我们不能说上述的两种方法不能用,但绝对不符合程序员对优雅的审美。程序员的优雅到底是有多优雅呢? ArgoCD 官方用一个子项目给出了答案。这个项目,就是我们今天要讲的 ArgoCD ApplicationSet。
当然跨集群部署应用,只是 ApplicationSet 的功能之一。我们将全面地把它讲透。
顾名思义,ApplicationSet 就是 Application 的集合,它控制器和相应的 CRD 构成。
只需在 ArgoCD CR 资源中添加 applicationSet 字段即可。
apiVersion: argoproj.io/v1alpha1
kind: ArgoCD
metadata:
name: monday-argocd
namespace: argocd
spec:
version: v2.3.4
server:
insecure: true
ingress:
enabled: true
applicationSet: {} # 增加
针对于跨集群部署应用和 monorepo,ApplicationSet 这个 CRD 可以实现自动化和更大的灵活性。
官方给出四个特性:
- 使用单个 ApplicationSet 配置清单控制多个 kubernetes 集群的能力
- 使用单个 ApplicationSet 配置清单从一个或多个 Git 存储仓库部署多个应用程序
- 在多租户集群中,提高单个集群租户使用 ArgoCD 部署应用程序的能力。无需让特权集群管理员参与启用目标集群或命名空间。假如我们有 A B 两个集群,通过 ApplicationSet,A 集群中的租户,不需要拥有 B 集群的权限,就可以在 B 集群中发布应用。这样就可以把持续发布的权限提前,放到持续集成阶段管理。
- 增强对 monorepo 的支持
monorepo: 单个 Git 仓库中定义多个 ArgoCD 应用程序资源
一个项目常常由多个微服务构成,这些微服务的部署配置清单,可以放到各自独立的仓库中,也可以化零为整,在同一个仓库中管理。独立仓库管理,对单个微服务开发团队来说,可以更方便发版和回滚,但对于整个项目来说,高灵活度会带来不稳定性。ArgoCD 则推荐使用 MonoRepo 的方式,统一管理微服务的配置。原因也很简单,统一管理能更好的控制各微服务之间的依赖关系,从而提高整个项目的稳定性。
当创建,更新或删除 ApplicationSet CRD 资源时,ApplicationSet 控制器会通过创建,更新或删除一个或多个对应的 ArgoCD Application 资源来响应。
实际上,ApplicationSet 的唯一职责就是操作 ArgoCD Application,类似 kubernetes 中 Replicaset 和 Replicas 一样。
- ApplicationSet 并不会操作 kubernetes 资源
- 除 ArgoCD 部署的集群外,ApplicationSet 不会连接其他集群
- 除 ArgoCD 部署的命名空间外,ApplicationSet 不与其他命名空间交互。
ApplicationSet 生成器负责生成参数,然后将这些参数根据模板渲染成 ArgoCD Application 资源。
ApplicationSet 控制器当前支持多个生成器:
- 列表生成器:根据列表中的元素生成参数,列表参数由用户自定义。
- 集群生成器:基于 ArgoCD 中定义的集群自动生成集群参数,可以看做列表生成器的一个特例。
- Git 生成器:根据 ApplicationSet CRD 中定义的 Git 仓库中包含的文件或文件夹生成参数。
- 包含 JSON 值的各个目录将会被解析成模板参数
- Git 仓库中的各目录路径也可以作为参数
- 矩阵生成器: 矩阵生成器结合了两个子生成器的生成参数,迭代每个生成器生成的参数的每个组合。通过组合两个升起参数,生成所有可能的组合。
这是四个常用的生成器,更多生成器可以参考官方文档。接下来将针对这四种生成器分别进行试验。
列表生成器基于一些键值对来生成参数,不过要求键对应的值必须为字符串类型。
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: guestbook
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: monday
url: https://kubernetes.default.svc
- cluster: tuesday
url: https://tuesday-api.automan.fun:6443
template:
metadata:
name: "{{cluster}}-guestbook"
namespace: guestbook
spec:
project: default
source:
repoURL: https://github.com/argoproj/argocd-example-apps.git
targetRevision: HEAD
path: kustomize-guestbook
destination:
server: "{{url}}"
namespace: guestbook
syncPolicy:
automated: {}
syncOptions:
- CreateNamespace=true
在 ArgoCD 中,托管集群存储在 ArgoCD 被部署的 Secret 中,ApplicationSet 控制器根据这些信息生成参数来识别和定位多集群。
对于每个注册到 ArgoCD 中的集群,都会提供一下参数:
1. name: 集群名
2. server: 集群服务器
3. metadata.labels.<key>
4. metadata.annotations.<key>
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: guestbook-cluster
namespace: argocd
spec:
generators:
- clusters: {} # Automatically use all clusters defined within Argo CD
template:
metadata:
name: "{{name}}-guestbook"
namespace: guestbook
spec:
project: default
source:
repoURL: https://github.com/argoproj/argocd-example-apps.git
targetRevision: HEAD
path: kustomize-guestbook
destination:
server: "{{server}}"
namespace: guestbook
syncPolicy:
automated: {}
syncOptions:
- CreateNamespace=true
标签选择器可以将目标集群的范围缩小到匹配特定标签的集群。
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: guestbook-cluster
namespace: argocd
spec:
generators:
- clusters:
selector:
matchLabels:
argocd.argoproj.io/secret-type: cluster
template:
metadata:
name: "{{name}}-guestbook"
namespace: guestbook
spec:
project: default
source:
repoURL: https://github.com/argoproj/argocd-example-apps.git
targetRevision: HEAD
path: kustomize-guestbook
destination:
server: "{{server}}"
namespace: guestbook
syncPolicy:
automated: {}
syncOptions:
- CreateNamespace=true
可以通过values
集群生成器的字段传递额外的、任意的字符串键值对。通过该values
字段添加的值添加为values.(field)
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: guestbook-cluster
namespace: argocd
spec:
generators:
- clusters:
selector:
matchLabels:
name: tuesday
values:
version: v1
template:
metadata:
name: "{{name}}-guestbook"
namespace: guestbook
spec:
project: default
source:
repoURL: https://github.com/argoproj/argocd-example-apps.git
targetRevision: HEAD
path: kustomize-guestbook
destination:
server: "{{server}}"
namespace: guestbook
syncPolicy:
automated: {}
syncOptions:
- CreateNamespace=true
info:
- name: version
value: "{{values.version}}"
Git 生成器有两个子生成器:Git 目录生成器和 Git 文件生成器。
它使用指定 Git 仓库的目录结构生成参数。
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}}"
1. {{path}}: 与 path 通配符匹配的 Git 仓库目录路径
2. {{path.basename}}: path 路径的目录名。
有时并非所有的目录都需要生成参数,可以通过如下两种方式排除目录:
- Git 目录生成器会自动排除
.
开头的文件夹; - 通过 exclude 选项排除单个目录;
需要注意,exclude 的优先级最高,即一个目录一旦被排除,其子目录也会被排除,而且即使明确写明包含目录也无效。exclude 不受 path 的先后顺序影响。
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/excludes/cluster-addons/*
- path: examples/git-generator-directory/excludes/cluster-addons/exclude-helm-guestbook
exclude: true
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}}"
在 git 仓库中找到 JSON/YAML 文件并使用其内容生成参数。
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: guestbook
namespace: argocd
spec:
generators:
- git:
repoURL: https://github.com/argoproj-labs/applicationset.git
revision: HEAD
files:
- path: "examples/git-generator-files-discovery/cluster-config/**/config.json"
template:
metadata:
name: "{{cluster.name}}-guestbook"
spec:
project: default
source:
repoURL: https://github.com/argoproj-labs/applicationset.git
targetRevision: HEAD
path: "examples/git-generator-files-discovery/apps/guestbook"
destination:
server: "{{cluster.address}}"
namespace: guestbook
上述 cluster-config 目录下找到的任何 config.json 文件,都讲会根据其内容生成参数。JSON 字段被扁平化为键值对。
除了来自配置文件的扁平化键值对之外,还提供了 Git 目录生成器的参数{{path}}
, {{path.basename}}
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: list-git
namespace: argocd
spec:
generators:
- matrix:
generators:
- clusters: {}
- list:
elements:
- color: red
size: middle
- color: green
size: large
template:
metadata:
name: "{{name}}-{{color}}-guestbook"
namespace: guestbook
spec:
project: default
source:
repoURL: https://github.com/argoproj/argocd-example-apps.git
targetRevision: HEAD
path: kustomize-guestbook
destination:
server: "{{server}}"
namespace: "{{color}}-{{size}}"
syncPolicy:
syncOptions:
- CreateNamespace=true
info:
- name: "color"
value: "{{color}}"
- name: "size"
value: "{{size}}"
非常感谢 Devops 中国社区的 @martinliu 和 极狐布道师 @小马哥,正是两位前辈不遗余力地推送此次鉴宝活动,这次分享才能如约与大家见面,于我个人而言,也才有幸认识诸位鉴宝讲师以及更多热爱开源的“技术人”。
如果在学习 ArgoCD 时有任何疑问或想法,欢迎加我的微信 - hsdtsyl ("黑色的碳酸饮料" 首字母) - 交流。
推荐: ArgoCD 相关视频教程