从 Jenkins 到 Argo CD:搭建一条完整的 GitOps 实战链路

文章目录

    • 一、为什么要做这次实践?
    • 二、整体架构与职责划分
    • [三、第一步:为 Nginx 编写自定义镜像](#三、第一步:为 Nginx 编写自定义镜像)
    • [四、第二步:使用 Jenkins Pipeline 构建和推送镜像](#四、第二步:使用 Jenkins Pipeline 构建和推送镜像)
    • [五、第三步:排查 Docker 镜像推送失败](#五、第三步:排查 Docker 镜像推送失败)
      • [5.1 故障现象](#5.1 故障现象)
      • [5.2 定位根因](#5.2 定位根因)
      • [5.3 解决方案](#5.3 解决方案)
      • [5.4 故障排查总结](#5.4 故障排查总结)
    • [六、第四步:验证 Registry 中的镜像](#六、第四步:验证 Registry 中的镜像)
    • [七、第五步:验证 K3s 能否拉取镜像](#七、第五步:验证 K3s 能否拉取镜像)
    • [八、第六步:修改 Git 中的 Deployment 清单](#八、第六步:修改 Git 中的 Deployment 清单)
    • [九、第七步:通过 Argo CD 手动同步](#九、第七步:通过 Argo CD 手动同步)
    • 十、本次实践完成了什么?
    • 十一、当前方案的边界与后续改进
      • [1. 镜像版本更新目前需要手动修改](#1. 镜像版本更新目前需要手动修改)
      • [2. Registry 地址依赖当前单节点环境](#2. Registry 地址依赖当前单节点环境)
      • [3. 清单校验还可以继续完善](#3. 清单校验还可以继续完善)
      • [4. 需要继续实践回滚](#4. 需要继续实践回滚)
    • 十二、总结

从镜像构建、私有仓库存储到 Kubernetes 部署,亲手跑通 CI 与 GitOps 的协作流程。

一、为什么要做这次实践?

此前,我已经使用 Jenkins 和 Docker Compose 完成了「逐浪笔记」博客项目的持续集成与部署,也将博客服务迁移到了 K3s。

但在学习 DevOps 的过程中,我发现一个值得进一步探索的问题:

Jenkins 构建出一个新版本的镜像后,如何让 Kubernetes 集群按照 Git 中声明的配置部署这个版本?

如果仍然由 Jenkins 直接执行 kubectl apply,虽然能够完成自动化部署,但构建流程与集群部署逻辑仍然耦合在一起。

因此,这次我搭建了一套独立的 Nginx GitOps 实验环境,将 Jenkins CI 与 Argo CD 部署流程分开验证,逐步打通从代码仓库到 Kubernetes 集群的交付链路。

实验使用独立的 argocd-lab 命名空间,不修改现有博客服务所在的 my-blog-k8s 命名空间。

二、整体架构与职责划分

本次实践使用 Gitee、Jenkins、Docker Registry、Argo CD 和 K3s,整体流程如下:

text 复制代码
                  Gitee
                    |
                    v
             Jenkins Pipeline
                    |
          +---------+---------+
          |                   |
          v                   v
    校验 Kubernetes YAML   构建 Nginx 镜像
                              |
                              v
                     私有 Docker Registry
                              |
                              v
                      镜像推送成功
                              |
                              v
                   更新 Git 中的镜像版本
                              |
                              v
                            Gitee
                              |
                              v
                          Argo CD
                         手动 Sync
                              |
                              v
                            K3s
                              |
                              v
                    nginx-demo Deployment

各组件的职责如下:

组件 主要职责
Gitee 保存 Jenkinsfile、Dockerfile 和 Kubernetes YAML
Jenkins 拉取代码、校验清单、构建镜像并推送
Docker Registry 保存构建完成的容器镜像
Argo CD 比较 Git 中的期望状态与集群实际状态,并协调部署
K3s 运行 Kubernetes 工作负载

这里有一个重要的设计原则:Jenkins 负责 CI,Argo CD 负责 GitOps 部署。

本次实验使用手动 Sync,没有启用自动同步、Prune 或 Self-Heal,便于观察 Git 配置变更对集群的实际影响。

三、第一步:为 Nginx 编写自定义镜像

为了验证部署的确实是 Jenkins 构建的镜像,而不是直接使用官方 Nginx 镜像,我在项目中新增了 Dockerfile:

文件路径:

gitops/labs/nginx-demo/image/Dockerfile

内容如下:

dockerfile 复制代码
FROM nginx:1.27

RUN printf '<h1>逐浪笔记 GitOps Lab</h1>\n<p>Built by Jenkins CI</p>\n' \
    > /usr/share/nginx/html/index.html

这里以 nginx:1.27 为基础镜像,将默认首页替换为实验标识。

这样,部署完成后,只要访问容器的首页,就可以验证实际运行的是否为自定义镜像。

四、第二步:使用 Jenkins Pipeline 构建和推送镜像

我创建了独立的 Jenkins Pipeline 任务 nginx-gitops-lab-ci,避免影响现有的博客 CI/CD 任务。

Pipeline 包含以下主要阶段:

  1. Checkout:从 Gitee 拉取代码。
  2. Validate GitOps Manifests:检查 Kubernetes YAML 和 Dockerfile 是否存在,并校验关键配置。
  3. Build Image:构建自定义 Nginx 镜像。
  4. Check Registry Tag:查询 Registry,确认本次构建使用的 tag 尚未存在。
  5. Push Image:将镜像推送至私有 Registry。

本次成功构建的镜像为:

text 复制代码
127.0.0.1:5000/nginx-gitops-lab:build-3

其中,build-3 是本次 Jenkins 构建使用的版本标签。

需要说明的是,清单校验阶段属于基础检查,并不等同于完整的 Kubernetes YAML 语法校验或集群准入验证。

五、第三步:排查 Docker 镜像推送失败

这是本次实践中最值得记录的一次故障排查。

5.1 故障现象

最初,Jenkins Pipeline 能够正常完成代码拉取、清单检查和镜像构建,但在推送阶段失败。

关键错误信息如下:

text 复制代码
failed to do request:
Head "https://blog-registry:5000/v2/nginx-gitops-lab/blobs/sha256:..."

dial tcp: lookup blog-registry on 127.0.0.53:53:
server misbehaving

初看像是 Registry 不可用,但进一步检查发现:

  • Jenkins 容器内可以通过 blog-registry:5000 访问 Registry。
  • ECS 宿主机通过 http://127.0.0.1:5000/v2/ 访问 Registry,返回 HTTP 200。
  • Docker 镜像已经构建成功。
  • 实际失败发生在 Docker Push 阶段。

因此,不能简单地把问题归结为 Registry 服务故障。

5.2 定位根因

Jenkins 容器通过挂载 Docker Socket 使用宿主机的 Docker daemon。

也就是说,虽然 docker 命令是在 Jenkins Pipeline 中执行的,但实际处理镜像推送网络请求的是宿主机 Docker daemon。

这就产生了两个不同的网络视角:

  • Jenkins 容器可以解析 Docker 网络中的 blog-registry 名称。
  • 宿主机 Docker daemon 无法解析这个容器网络别名。

此外,最初使用的地址是 blog-registry:5000,失败日志还显示客户端尝试使用 HTTPS,而当前 Registry 实际提供的是 HTTP。

因此,需要同时解决地址可达性与 HTTP Registry 地址配置的问题。

5.3 解决方案

检查宿主机 Docker 配置后,确认 127.0.0.0/8 已经被列入 Insecure Registries 范围,因此无需为了本次问题修改 Docker daemon 配置或重启 Docker。

最终将 Registry 地址按使用场景分开:

groovy 复制代码
environment {
    REGISTRY_PUSH = '127.0.0.1:5000'
    REGISTRY_CHECK = 'blog-registry:5000'
    IMAGE_NAME = 'nginx-gitops-lab'
}

其中:

  • REGISTRY_PUSH:供 Docker 构建和推送镜像时使用,面向宿主机 Docker daemon。
  • REGISTRY_CHECK:供 Jenkins 容器中的 Registry API 检查使用,利用 Docker 网络中的服务名称。

镜像标签由 REGISTRY_PUSH 生成:

groovy 复制代码
env.FULL_IMAGE = "${env.REGISTRY_PUSH}/${env.IMAGE_NAME}:${env.IMAGE_TAG}"

检查 tag 时,则通过 Jenkins 容器能够访问的 Registry API 地址进行查询。

修改后,Pipeline 成功完成镜像推送。

5.4 故障排查总结

这次问题让我进一步明确了一个容易忽略的细节:

容器内能够访问某个服务,不代表宿主机上的 Docker daemon 也能使用同一个服务名称访问它。

排查这类问题时,应该区分命令执行环境、Docker daemon 所在位置、Registry 的监听地址、DNS 解析以及 HTTP/HTTPS 配置,而不是直接重启服务或修改网络配置。

六、第四步:验证 Registry 中的镜像

镜像推送完成后,我没有仅凭 Jenkins 的 SUCCESS 就认为交付成功,而是进一步查询 Registry API。

检查仓库列表:

bash 复制代码
curl -sS http://127.0.0.1:5000/v2/_catalog

检查指定镜像的 tag:

bash 复制代码
curl -sS \
  http://127.0.0.1:5000/v2/nginx-gitops-lab/tags/list

返回结果包含:

json 复制代码
{
  "name": "nginx-gitops-lab",
  "tags": ["build-3"]
}

这确认了镜像确实已经存入 Registry,而不仅仅是存在于本机 Docker 镜像缓存中。

七、第五步:验证 K3s 能否拉取镜像

在修改 Argo CD 管理的部署清单之前,我先创建了一个独立的临时测试 Pod:

bash 复制代码
sudo k3s kubectl run nginx-gitops-pull-test \
  -n argocd-lab \
  --image=127.0.0.1:5000/nginx-gitops-lab:build-3 \
  --image-pull-policy=Always \
  --restart=Never \
  --port=80

这里使用 imagePullPolicy=Always,要求 Kubernetes 在启动容器时向镜像仓库检查镜像,而不是只依赖节点上已有的镜像缓存。

随后进入容器访问首页:

bash 复制代码
sudo k3s kubectl exec -n argocd-lab nginx-gitops-pull-test -- \
  curl -sS --max-time 5 http://127.0.0.1/

返回:

html 复制代码
<h1>逐浪笔记 GitOps Lab</h1>
<p>Built by Jenkins CI</p>

这一步验证了镜像不仅存在于 Registry 中,而且能够被 K3s 成功拉取并运行。

八、第六步:修改 Git 中的 Deployment 清单

镜像验证成功后,我才修改 Argo CD 管理的 Kubernetes 清单。

文件路径:

gitops/labs/nginx-demo/deployment.yaml

将原来的官方镜像:

yaml 复制代码
image: nginx:1.27

修改为:

yaml 复制代码
image: 127.0.0.1:5000/nginx-gitops-lab:build-3

其他配置保持不变,包括原有的单副本设置。

随后提交并推送到 Gitee:

bash 复制代码
git add gitops/labs/nginx-demo/deployment.yaml

git commit -m "feat: deploy custom nginx image through Argo CD"

git push origin master

通过 Git 记录这次部署所使用的镜像版本,可以明确追溯当前集群期望运行的版本。

九、第七步:通过 Argo CD 手动同步

代码推送完成后,我在 Argo CD UI 中打开 nginx-gitops-lab 应用。

操作过程如下:

  1. Refresh,刷新应用状态。
  2. 查看变更预览,确认 Deployment 镜像发生了预期变更。
  3. 手动点击 Sync。
  4. 等待 Deployment 完成滚动更新。
  5. 检查 Pod 状态和实际镜像版本。

在 ECS 上执行:

bash 复制代码
sudo k3s kubectl rollout status deployment/nginx-demo \
  -n argocd-lab --timeout=120s

返回:

text 复制代码
deployment "nginx-demo" successfully rolled out

检查实际使用的镜像:

bash 复制代码
sudo k3s kubectl get deployment nginx-demo \
  -n argocd-lab \
  -o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'

结果:

text 复制代码
127.0.0.1:5000/nginx-gitops-lab:build-3

检查 Pod:

bash 复制代码
sudo k3s kubectl get pods -n argocd-lab -o wide

新 Pod 状态为 1/1 Running。

这表明 Kubernetes 已完成 Deployment 更新,新版本容器成功运行。

十、本次实践完成了什么?

本次实验完成了以下验证:

  • Jenkins 能够从 Gitee 拉取代码并执行 Pipeline。
  • Jenkins 能够校验关键文件、构建自定义 Nginx 镜像。
  • 镜像能够成功推送至私有 Docker Registry。
  • K3s 能够拉取该镜像并启动容器。
  • Git 中的 Deployment 清单能够声明实际部署的镜像版本。
  • Argo CD 能够通过手动 Sync 将 Git 中的期望状态同步到 K3s。
  • Kubernetes Deployment 成功滚动更新,新 Pod 正常运行。
  • 遇到 Registry 地址解析失败时,能够从网络视角和 Docker daemon 的执行机制入手定位问题。

更重要的是,这次实践将几个容易混淆的概念分开了:

CI 解决如何构建和交付制品,Git 记录期望部署状态,Argo CD 负责协调集群状态,Kubernetes 负责运行工作负载。

十一、当前方案的边界与后续改进

这套实验链路已经打通,但距离更完整的自动化 GitOps 流程仍有改进空间。

1. 镜像版本更新目前需要手动修改

当前 Jenkins 已经负责构建和推送镜像,但构建完成后,仍然需要人工更新 Deployment YAML 中的 tag,再提交到 Git。

下一步可以设计一个独立的版本更新流程,让 Jenkins 在镜像构建成功后生成部署变更提交或 Pull Request,再由 Argo CD 根据 Git 中的新版本进行部署。

这样可以保留清晰的版本记录与审批边界。

2. Registry 地址依赖当前单节点环境

本次使用 127.0.0.1:5000,是因为实验环境中的 Registry 与 K3s 节点位于同一台 ECS 主机。

在多节点 Kubernetes 集群中,每个节点的 127.0.0.1 都指向各自本机,不能直接照搬这一方案。届时应使用各节点都可访问的 Registry 地址,并正确配置节点的镜像仓库访问方式、认证和网络策略。

3. 清单校验还可以继续完善

目前 Pipeline 进行了基础文件和关键配置检查。后续可以引入 YAML 语法检查、Kubernetes schema 校验以及部署前的策略检查,尽早发现清单错误。

4. 需要继续实践回滚

本次完成的是新版本发布验证,还没有验证通过 Git 回退提交恢复旧版本的完整流程。

后续可以在独立实验环境中,将 Deployment 镜像恢复到先前的已知版本,观察 Argo CD 如何重新协调集群状态,并记录回滚耗时与验证结果。

十二、总结

从 Jenkins 构建镜像,到 Registry 保存制品,再到 Argo CD 根据 Git 中的部署清单更新 K3s,这次实践让我把 CI/CD 和 GitOps 的关系从概念落实到了实际操作。

过程中最有收获的并不只是看到 Pod 进入 Running,而是通过真实的推送失败,理解了 Jenkins 容器与宿主机 Docker daemon 之间的网络边界,并采用最小改动解决了问题。

对我而言,逐浪笔记不仅是一个 Spring Boot + Vue 的个人博客项目,也逐渐成为验证 Linux 运维、容器化、持续集成、Kubernetes 和 GitOps 技术的实践环境。

接下来,我会继续围绕版本管理、自动化交付、故障排查和安全回滚完善这条链路,让每一次技术学习都能沉淀为可以复现、可以解释、也可以用于面试交流的工程实践。


文章分类建议: DevOps 实战

标签建议: Jenkins、Argo CD、GitOps、K3s、Docker Registry、CI/CD

摘要: 基于个人博客项目的独立实验环境,搭建 Jenkins、私有 Docker Registry、Argo CD 与 K3s 的 GitOps 交付链路,完成自定义镜像构建、推送、集群拉取和手动同步,并记录一次 Registry 地址解析故障的排查过程。

相关推荐
张文君1 小时前
vmware克隆后ubuntu26.04 ip怎么更改
linux·运维·服务器
迪康coolmu2 小时前
基于迪康端点安全一体化管理系统的 AI 工具使用审计实战
运维·网络·人工智能·安全·电脑
Brilliantwxx2 小时前
【Linux】基础IO(下):输出重定向、dup2 与 minishell 重定向实战
linux·运维·服务器·github·bash
小HANN2 小时前
(架构 + InnoDB 事务 MVCC):《MySQL 底层探秘①:拆解 InnoDB,读懂事务、MVCC 与日志的底层逻辑》
linux·运维·服务器·经验分享
kyle~3 小时前
Linux --- 虚拟内存 1.fork、2.COW、3.mmap、4.page fault、5.TLB、6.页表
linux·运维·服务器
你看我名字3 小时前
LAMP 架构演练:从云主机准备到 Apache、PHP、MySQL 上线验证
运维·ci/cd·架构·kubernetes·jenkins
小HANN3 小时前
Ceph分布式存储深度解析-下篇
linux·运维·服务器·经验分享
张文君3 小时前
ubuntu iscsi客户端 关闭磁盘
linux·运维·ubuntu
凯基迪科技3 小时前
硬件做授权保护,有哪些常见方案
运维·服务器