在 DevOps 领域,自动化是主要目标之一。这包括自动化软件部署方式。与其依赖某人在部署软件的机器上进行 rsync/FTP/编写软件,不如使用 CI/CD 的概念。
CI,即持续集成,是通过代码提交创建工件的步骤。这可以是 Docker 镜像,使用 Git 仓库主分支中的提交进行部署。
CD,即持续部署/交付,是部署工件的步骤。这可以是任何操作,例如在机器上运行 docker pull 和 docker run 的部署系统,指示 AWS Lambda 将代码更新到 S3 存储桶中的最新代码,或者指示 Kubernetes 集群更新现有部署以使用新镜像。
在本文中,我将快速介绍如何设置触发 ArgoCD 的 Jenkins 流水线。ArgoCD 是一个基于 Git 仓库中的清单更新 Kubernetes 集群的 CD 工具。它可以部署标准 Kubernetes 清单,并使用 Kustomize 更新清单或 Helm 图表。
先决条件
要将 Jenkins 与 ArgoCD 结合使用,您需要执行以下操作:-
- 在 Jenkins 工作器/运行器上安装 argocd-cli
- 在 ArgoCD 中创建一个 Jenkins 部署角色,该角色有权更新应用程序
- 在基于 Git 的版本控制系统中,ArgoCD 可以访问一个或多个代码库(例如 GitHub、Gitlab、Gitea 或无前端的 Git)
- Jenkins 工作器/运行器需要能够通过 API 访问 ArgoCD,因此请确保设置了正确的防火墙规则
安装ArgoCD CLI
我们使用 Packer 为 Jenkins 工作器配置/准备镜像。因此,添加其他工具非常简单。但是,如果您运行的是静态工作器,则只需确保 argocd 二进制文件安装在 Jenkins 用户可运行的位置即可。例如:
$ pwd
/home/jenkins## Download the tool
$ curl -LO https://github.com/argoproj/argo-cd/releases/download/v1.2.0/argocd-linux-amd64 ## Move it to the /usr/local/bin
$ sudo mv argocd-linux-amd64 /usr/local/bin/argocd## Ensure it is exectutable
$ sudo chmod 755 /usr/local/bin/argocd## Check it works
$ argocd version
argocd: v1.2.0+674978cBuildDate: 2019-09-04T21:26:04ZGitCommit: 674978cd587701b39e81fce6d5c960b6d76d5882GitTreeState: cleanGoVersion: go1.12.6Compiler: gcPlatform: linux/amd64
argocd-server: v1.2.0+674978cBuildDate: 2019-09-04T21:27:17ZGitCommit: 674978cd587701b39e81fce6d5c960b6d76d5882GitTreeState: cleanGoVersion: go1.12.6Compiler: gcPlatform: linux/amd64Ksonnet Version: 0.13.1
ArgoCD Jenkins 部署角色
要在 ArgoCD 中创建部署角色,请转到 ArgoCD 仪表板,点击侧边栏上的齿轮图标(进入设置),然后转到“项目”。
在“项目中”,选择您的应用程序正在运行的项目。如果您尚未创建任何项目,则默认为“默认”。
在项目中,转到“角色”,然后点击“添加角色”。
在角色中,您可以为角色指定您想要的名称和描述,以便将来更容易理解其用途。您可以在此处应用细粒度的策略来定义 Jenkins 可以执行的操作(例如,仅创建、仅更新、仅同步现有应用程序)。或者,您可以授予其完全访问权限,但显然在生产环境中不建议这样做。)
完成上述所有操作后,您需要创建一个 JWT(JSON Web Token)。它用于对用户进行身份验证,确保只有客户端(在本例中为 Jenkins)才能承担此角色并使用其权限。这可以在 Web UI 中完成,也可以通过 CLI 完成。
要从 CLI 创建 JWT,您需要执行 argocd proj role create-token {PROJECT-NAME} {PROJECT-ROLE} 命令,示例如下:
$ argocd proj role create-token default jenkins-deploy-role
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1Njg3MjEyMjEsImlzcyI6ImFyZ29jZCIsIm5iZiI6MTU2ODcyMTIyMSwic3ViIjoicHJvajpkZWZhdWx0OmplbmtpbnMtZGVwbG95LXJvbGUifQ.UyXNZtdbDyllzGl7PbLPhMgqNMFE1oJqONaLHV8RK-k
将令牌添加到 Jenkins
要将令牌添加到 Jenkins 本身(以便在流水线中使用),首先转到您的 Jenkins 实例,然后在侧边栏上找到“凭据”,然后选择“系统”,再选择“全局凭据”。点击添加凭据,如下图所示。
允许ArgoCD 访问代码库
ArgoCD 需要访问的代码库是托管 Kubernetes 清单的代码库。您可以将清单与代码放在同一个代码库中,也可以将它们放在一个完全独立的代码库中。
您可以通过多种方式进行设置。您可以使用 SSH 或 HTTPS 连接到您的版本控制系统。我选择了 SSH。
再次转到 ArgoCD 的“设置”页面,但不要点击“项目”,而是点击“代码库”。在这里,点击“使用 SSH 连接代码库”。
ninjamac@192 ~ % ssh-keygen -o -f argocd-deploy -C "argocd@example.com"
Generating public/private ed25519 key pair.
Enter passphrase for "argocd-deploy" (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in argocd-deploy
Your public key has been saved in argocd-deploy.pub
The key fingerprint is:
SHA256:nAuYAjALq9QIGII14J1vtjZfEboOQE4+Q985bu5BjuU argocd@example.com
The key's randomart image is:
+--[ED25519 256]--+
|@+o |
|O++.. |
|++ B . |
|o.* oo..o.. |
|. .*o=.BS. |
| .* O.o.. |
| * E.. |
| . B o |
| .= |
+----[SHA256]-----+
获取私钥(在本例中为 argocd-deploy)的内容,并将其粘贴到 ArgoCD 的 SSH 私钥数据字段中。提供 Kubernetes 清单所在仓库的 URL(如果您使用 HTTPS 连接,则为 HTTPS URL;如果使用 SSH 连接,则为 SSH URL)。
您需要公钥(在本例中为 argocd-deploy.pub)来在您选择的 Git 服务器中设置部署密钥。
防火墙规则
您的 Jenkins 工作器/运行器需要能够访问 ArgoCD API。我们将其暴露在 TCP:443 端口(即标准 HTTPS)上,因此您需要确保您的防火墙允许其通过。
Kubernetes 清单
如上所述,您可以使用标准 Kubernetes 清单、Kustomize 和/或 Helm 图表来部署您的应用程序。我使用 Kustomize 来更新 Kubernetes 部署中使用的 Docker 镜像哈希值。如果没有 Kustomize,即使您的 Docker 镜像仓库(私有或公共)中有新的镜像可用,部署也不会更新。
这是因为 Kubernetes API 认为部署没有发生变化。标签与以前相同,因此它不知道需要执行任何操作。
从 Kubernetes v1.15 版本开始,它们现在支持滚动重启。与 ImagePullPolicy: Always 结合使用,这将强制部署获取新镜像。
在 Kubernetes v1.15 之前的版本中,Deployment 永远不会更新。使用 :latest 并非最佳实践,因此引用实际哈希值可能是一个好主意。
清单示例
我最基本的 Kubernetes 清单如下:-
## ------------------- Debian Deployment ------------------- #kind: Deployment
apiVersion: apps/v1
metadata:labels:k8s-app: debian-testname: debian-test
spec:replicas: 1revisionHistoryLimit: 10selector:matchLabels:k8s-app: debian-testtemplate:metadata:labels:k8s-app: debian-testspec:containers:- name: debian-testimage: docker.io/rockwang415/k8s-debian-test:latest imagePullPolicy: Alwaysresources:requests:memory: "64Mi"cpu: "250m"limits:memory: "128Mi"cpu: "500m"ports:- containerPort: 80protocol: TCPlivenessProbe:httpGet:scheme: HTTPpath: /port: 80initialDelaySeconds: 30timeoutSeconds: 30---
## ------------------- Debian Service ------------------- #kind: Service
apiVersion: v1
metadata:labels:k8s-app: debian-testname: debian-test
spec:ports:- port: 80targetPort: 80type: NodePortselector:k8s-app: debian-test
我们使用 Docker 镜像仓库来存放镜像。如您所见,上面的镜像标签为 :latest。不过,我们也使用 Kustomize 来根据部署情况进行更改。我们的 kustomization.yaml 文件非常简单:-
resources:
- debian-test.yaml
从 v1.2.0 开始,ArgoCD 可以原生地利用 Kustomize(如果您指定了正确的参数),因此无需在此 YAML 文件中添加任何其他内容。
Jenkins Pipeline
现在所有先决条件都已满足,您已将 ArgoCD 连接到您的仓库并创建了清单,接下来您可以创建一个 Jenkinsfile 来部署应用程序。
pipeline {agent {node {label 'testing'}}stages { stage('Prepare') {steps {checkout([$class: 'GitSCM',branches: [[name: "origin/master"]],doGenerateSubmoduleConfigurations: false,submoduleCfg: [],userRemoteConfigs: [[url: 'ssh://git@git.example.com/argocd-test/argocd-test.git']]])}}stage('Docker_Build') {steps {// Build the Docker imagesh '''# Login to Docker registrydocker login -u <docker-username> -p <docker-password> <docker-registry-url># Build the imagedocker build . -t k8s-debian-test'''}}stage('Deploy_K8S') {steps {withCredentials([string(credentialsId: "jenkins-argocd-deploy", variable: 'ARGOCD_AUTH_TOKEN')]) {sh '''ARGOCD_SERVER="argocd-prod.example.com"APP_NAME="debian-test-k8s"CONTAINER="k8s-debian-test"REGION="eu-west-1"# Tag the docker imagedocker tag $CONTAINER:latest <docker-registry-url>/$CONTAINER:latest# Push the image to Docker registrydocker push <docker-registry-url>/$CONTAINER:latestIMAGE_DIGEST=$(docker image inspect <docker-registry-url>/$CONTAINER:latest -f '{{join .RepoDigests ","}}')# Customize image ARGOCD_SERVER=$ARGOCD_SERVER argocd --grpc-web app set $APP_NAME --kustomize-image $IMAGE_DIGEST# Deploy to ArgoCDARGOCD_SERVER=$ARGOCD_SERVER argocd --grpc-web app sync $APP_NAME --forceARGOCD_SERVER=$ARGOCD_SERVER argocd --grpc-web app wait $APP_NAME --timeout 600'''}}}}
}
docker hub 登录
这用于登录 docker hub,检索 Docker 镜像(例如,基础 Debian 镜像),然后根据我们的 Dockerfile 进行标记和推送。如果您使用的是标准镜像或您自己的私有镜像仓库,则可以忽略这些部分。
镜像摘要
由于某种原因,Docker 镜像摘要并不总是显示在 docker images --digests 中。相反,我们会检查最新推送到 docker hub的镜像,并检索 .RepoDigests 标签。这将提供以下内容:
ninjamac@192 ~ % docker image inspect rockwang415/k8s-debian-test -f '{{join .RepoDigests ","}}'
rockwang415/k8s-debian-test@sha256:b662d828445b39ac0a659ec77b629a75d1b298a6c76afdf5296cff64806fc638
ArgoCD
由于我们的 ArgoCD API 和仪表板位于 AWS 应用负载均衡器前端,我们目前在所有 ArgoCD 命令前都添加了 --grpc-web 前缀,例如 argocd --grpc-web app sync TEST --force。这是因为 AWS 应用负载均衡器默认不支持 GRPC。如果您在 ArgoCD 前端的负载均衡器/入口支持 GRPC,请从所有命令中移除该前缀。
自定义镜像
ARGOCD_SERVER=$ARGOCD_SERVER argocd --grpc-web app set $APP_NAME --kustomize-image $IMAGE_DIGEST
上述命令将 Kubernetes 清单中的镜像设置为生成的镜像摘要变量。由于 ArgoCD 默认支持 Kustomize,它可以自行操作清单。这意味着我们不会因为镜像标签始终不变而导致 Deployment 始终不更新。
这是一个非常棒的功能,意味着我们不需要像 ArgoCD 一样在 Jenkins 工作进程中同时运行 Kustomize 二进制文件。
同步应用程序
ARGOCD_SERVER=$ARGOCD_SERVER argocd --grpc-web app sync $APP_NAME --force
ARGOCD_SERVER=$ARGOCD_SERVER argocd --grpc-web app wait $APP_NAME --timeout 600
上述代码只是要求 ArgoCD 触发 Kubernetes 部署应用程序。在 ArgoCD 中,这将使用 Git 仓库中的 Manifest 文件,该文件已通过 Kustomize 更新以使用新的镜像标签。然后,它将根据 Jenkins 生成的镜像部署应用程序的新版本。
运行Jenkins Pipeline
下面是运行后的结果
Kubernetes Output
Pre-ArgoCD Run
kubectl get pods
NAME READY STATUS RESTARTS AGE
debian-test-8648f969ff-hrsvp 1/1 Running 0 4d22h
Post-ArgoCD Run
kubectl get pods
NAME READY STATUS RESTARTS AGE
debian-test-7664c648bb-sq6h7 1/1 Running 0 22s
Jenkins Console Output - ArgoCD
+ ARGOCD_SERVER=argocd-prod.example.com argocd \ --grpc-web app set debian-test-k8s \ --kustomize-image rockwang415/k8s-debian-test@sha256:###SHA256-IMAGE-HASH###+ ARGOCD_SERVER=argocd-prod.example.com argocd --grpc-web app sync debian-test-k8s --force
TIMESTAMP GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGE
2025-05-017T13:05:27+00:00 apps Deployment default debian-test Synced Healthy
2025-05-017T13:05:27+00:00 Service default debian-test Synced Healthy
2025-05-017T13:05:27+00:00 apps Deployment default debian-test OutOfSync Healthy Name: debian-test-k8s
Project: default
Server: https://kubernetes.default.svc
Namespace: default
URL: https://argocd-prod.example.com/applications/debian-test-k8s
Repo: git@git.example.com:yeti/argocd-test.git
Target: HEAD
Path: yaml
Sync Policy: <none>
Sync Status: Synced to HEAD (f5f91ad)
Health Status: ProgressingOperation: Sync
Sync Revision: f5f91ad16296ecab90f337e5dbf3f4f927b61799
Phase: Succeeded
Start: 2025-05-01 13:05:27 +0000 UTC
Finished: 2025-05-01 13:05:29 +0000 UTC
Duration: 2s
Message: successfully synced (all tasks run)GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGEService default debian-test Synced Healthy service/debian-test unchanged
apps Deployment default debian-test Synced Progressing deployment.apps/debian-test configured
+ ARGOCD_SERVER=argocd-prod.example.com argocd --grpc-web app wait debian-test-k8s --timeout 600
TIMESTAMP GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGE
2025-05-01T13:05:29+00:00 Service default debian-test Synced Healthy service/debian-test unchanged
2025-05-01T13:05:29+00:00 apps Deployment default debian-test Synced Progressing deployment.apps/debian-test configuredName: debian-test-k8s
Project: default
Server: https://kubernetes.default.svc
Namespace: default
URL: https://argocd-prod.example.com/applications/debian-test-k8s
Repo: git@git.example.com:yetiops/argocd-test.git
Target: HEAD
Path: yaml
Sync Policy: <none>
Sync Status: Synced to HEAD (f5f91ad)
Health Status: HealthyOperation: Sync
Sync Revision: f5f91ad16296ecab90f337e5dbf3f4f927b61799
Phase: Succeeded
Start: 2025-05-01 13:05:27 +0000 UTC
Finished: 2025-05-01 13:05:29 +0000 UTC
Duration: 2s
Message: successfully synced (all tasks run)GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGEService default debian-test Synced Healthy service/debian-test unchanged
apps Deployment default debian-test Synced Healthy deployment.apps/debian-test configured
ArgoCD Dashboard
总结
我们为什么要使用 Jenkins 和 ArgoCD?Jenkins 非常擅长构建工件、整合代码提交,并接受来自 GitHub 或 GitLab 等平台的 Webhook 来启动作业。然而,ArgoCD 能够更好地控制镜像的部署方式,因为所有内容都可以在原生 Kubernetes 清单中描述。
这消除了将 Kubernetes 直接暴露给 Jenkins 的需要,也无需操作 kubectl 命令来部署(或使用额外的插件)。
ArgoCD 还为我们提供了部署进度的实时视图,以及在需要时可以回滚到的位置。它还以 Kubernetes 为中心(即部署和服务如何关联)的方式展示它们,因此开发人员和运维团队可以更轻松地了解所有内容是如何关联的。