第一部分:环境准备
1.1 集群架构说明
我的实验环境是一个 1 Master + 2 Worker 的 Kubernetes 集群:
1.2 Helm 安装
Helm 是 Kubernetes 的包管理器,类似于 Ubuntu 的 apt 或 CentOS 的 yum。它可以将复杂的 Kubernetes 应用打包成 Chart,通过一条命令完成部署。
# 给安装脚本添加执行权限并运行
chmod 700 get_helm.yaml
./get_helm.yaml
# 验证安装是否成功
helm version
第二部分:Harbor 镜像仓库搭建
2.1 为什么需要私有镜像仓库?
在生产环境中,我们通常不会直接从 Docker Hub 拉取镜像,原因有:
网络问题:Docker Hub 在国内访问不稳定
安全合规:企业内部镜像不应公开
性能优化:私有仓库可以作为代理缓存,加速镜像拉取
2.2 持久化存储配置
这一步的核心思想是:将容器内的数据存储到宿主机磁盘上。
Kubernetes 中的 Pod 是临时性的,如果 Pod 重启,容器内的数据会丢失。对于 Harbor 这种有状态应用(数据库、镜像文件等),必须将数据持久化到外部存储。
在 worker-02 上创建挂载目录:
# 创建 Harbor 各组件的数据目录
sudo mkdir -p /mnt/harbor/{registry,database,redis,chartmuseum,jobservice,trivy}
# 开放所有权限(实验环境)
sudo chmod -R 777 /mnt/harbor因为PV需要绑定到具体的宿主机路径。我们将所有有状态服务都调度到同一节点,简化存储管理。
2.3 导入 Harbor 镜像
由于 Harbor 安装需要大量镜像,从 Docker Hub 拉取耗时长且不稳定。我们提前下载好离线包,手动导入到 containerd 中。
镜像压缩包需要分别在每个 Worker 节点上解压和导入,因为每个节点都需要运行 Harbor 的 Pod。
在 worker-01 和 worker-02 上执行:
# 解压离线安装包
tar -zxvf harbor-offline-installer-v2.14.3.tgz && cd harbor
# 将 tar 包中的镜像导入到 containerd 的 k8s.io 命名空间
sudo ctr -n k8s.io images import harbor.v2.14.3.tar.gz
# 验证导入成功
sudo crictl images | grep harbor
为什么 Master 节点不需要导入?
因为 Master 节点默认被添加了污点,普通 Pod 不会调度到 Master 上运行。Harbor 的所有组件通过 nodeSelector 指定运行在 worker-02,所以只需要在 Worker 节点准备镜像即可。
2.4 创建持久化卷(PV)
在部署 Harbor 之前,需要先创建 PV,让 Harbor 的 PVC 能够正确绑定到我们预先创建的宿主机目录。
在 master 节点执行:
# 应用 PV 定义文件
kubectl apply -f harbor.pv.yaml
# 验证 PV 状态应该是 Available
kubectl get pv2.5 Helm 安装 Harbor
这是整个部署中最复杂的步骤,我们来逐条解释每个参数的意义:
helm install harbor harbor/harbor \
--version 1.18.3 \
--namespace harbor --create-namespace \ # 创建名为 harbor 的命名空间
--set expose.type=nodePort \ # 使用 NodePort 方式暴露服务
--set expose.tls.enabled=false \ # 暂不启用 HTTPS(简化配置)
--set expose.nodePort.http.nodePort=30002 \ # 指定 HTTP 访问端口
--set externalURL="http://172.16.11.109:30002" \ # Harbor 对外访问地址
--set image.tag=v2.14.3 \ # 指定镜像版本
--set persistence.enabled=true \ # 开启持久化存储
--set persistence.persistentVolumeClaim.registry.storageClass=harbor-registryl-storage \
--set persistence.persistentVolumeClaim.database.storageClass=harbor-database-storage \
--set persistence.persistentVolumeClaim.redis.storageClass=harbor-redis-storage \
--set persistence.persistentVolumeClaim.chartmuseum.storageClass=harbor-chartmuseum-storage \
--set persistence.persistentVolumeClaim.jobservice.storageClass=harbor-jobservice-storage \
--set persistence.persistentVolumeClaim.trivy.storageClass=harbor-trivy-storage \
--set nodeSelector."kubernetes\.io/hostname"=worker-02 \ # 强制调度到 worker-02
--set proxy.httpProxy="http://10.10.1.76:7890" \ # 配置 HTTP 代理
--set proxy.httpsProxy="http://10.10.1.76:7890" \
--set proxy.noProxy="127.0.0.1\,localhost\,.local\,.internal\,harbor-core\,harbor-jobservice\,harbor-database\,harbor-registry\,harbor-portal\,harbor-trivy\,harbor-exporter\,10.96.0.0/12\,10.244.0.0/16\,172.16.0.0/12"如果安装后发现某个 Pod 处于 Pending 状态,通常是 PVC 绑定问题。
kubectl patch pvc harbor-jobservice -n harbor -p '{"spec":{"storageClassName":"harbor-jobservice-storage"}}'这个命令手动修正了 PVC 的存储类名称,让它可以正确绑定到我们预先创建的 PV。
#验证pod状态是否为running
kubectl get pods -n harbor
2.6 配置容器运行时信任私有仓库
Docker 和 containerd 默认只信任 HTTPS 的镜像仓库。我们使用的是 HTTP 协议,所以需要手动配置信任。
在 worker-01 和 worker-02 上配置:
# 为私有仓库Harbor创建专用配置目录
sudo mkdir -p /etc/containerd/certs.d/172.16.11.109:30002
# 编写配置文件
sudo tee /etc/containerd/certs.d/172.16.11.109:30002/hosts.toml <<EOF
server = "http://172.16.11.109:30002"
[host."http://172.16.11.109:30002"]
capabilities = ["pull", "resolve"]
skip_verify = true # 跳过证书验证
EOF什么是 hosts.toml?
这是 containerd 的 registry 配置文件,用于定义如何连接到特定的镜像仓库。上面的配置告诉 containerd:
仓库地址是 http://172.16.11.109:30002
支持 pull 和 resolve 操作
跳过 TLS 证书验证(因为我们是 HTTP)
修改 containerd 配置:
sudo nano /etc/containerd/config.toml
# 找到 [plugins.'io.containerd.cri.v1.images'.registry] 部分
# 将 config_path 修改为 /etc/containerd/certs.d
# 重启 containerd 使配置生效
sudo systemctl restart containerd2.7 Harbor 配置镜像代理
登录 Harbor Web 界面(http://172.16.11.109:30002),我们需要配置镜像代理功能。这是一个非常有用的功能:
为什么需要镜像代理?
拉取 Docker Hub 镜像时,如果不配置代理,每次都要从外网拉取,速度慢且可能被限流
Harbor 作为代理后,第一次拉取会缓存到本地,后续拉取直接走内网,速度极快
配置步骤如下:
A.创建代理目标(仓库管理 → 新建目标):
提供者:DockerHub
目标名:proxy
目标 URL:https://hub.docker.com
访问 ID:你的 Docker Hub 用户名
访问密码:Docker Hub 的 Personal Access Token(不是登录密码)

B.创建代理项目(新建项目):
项目名称:proxy
访问级别:公开
镜像代理:选择上一步创建的 proxy

Personal Access Token 如何获取?
登录 Docker Hub → Settings → Personal access tokens → Generate new token,选择 Only Read 权限即可。

验证代理功能:
# 在 worker 节点测试拉取 Redis 镜像
sudo crictl pull 172.16.11.109:30002/proxy/library/redis:latest
# 输出: Image is up to date for sha256:56937f91b9b70e...第三部分:Nginx Proxy Manager 部署
3.1 NPM 是什么?为什么需要NPM?
Nginx Proxy Manager 是一个带 Web 界面的反向代理工具。它解决了以下问题:
统一入口:所有服务都通过同一个域名/IP 访问,不需要记住一堆端口号
SSL 自动化:内置 Let's Encrypt 支持,自动申请和续期 SSL 证书
简单易用:不需要手写 Nginx 配置文件
在我们的架构中,NPM 扮演的是流量网关的角色。
3.2 导入 NPM 镜像
同样,镜像压缩包需要在每个 Worker 节点上解压和导入。
在 worker-01 和 worker-02 上执行:
# 导入 NPM 相关的镜像包
sudo ctr -n k8s.io images import npm.tar
# 该 tar 包包含以下镜像:
# - docker.m.daocloud.io/jc21/nginx-proxy-manager:latest
# - docker.m.daocloud.io/library/busybox:latest
# - docker.m.daocloud.io/library/mariadb:10.43.3 创建持久化目录
在 worker-02 上创建:
sudo mkdir -p /mnt/npm/{db,data,letsencrypt}
sudo chmod -R 777 /mnt/npm/3.4 修改 YAML 文件中的镜像地址
在应用 YAML 文件之前,需要先修改其中的镜像地址。原始 YAML 文件中使用的镜像地址是 harbor.pigeon.show/proxy/...,这是一个外部私有仓库,我们需要替换成可访问的镜像地址。
在 master 节点执行:
# 查看原始镜像地址
grep "image:" npm.yaml
# 输出:
# image: harbor.pigeon.show/proxy/library/mariadb:10.4
# image: harbor.pigeon.show/proxy/library/busybox:latest
# image: harbor.pigeon.show/proxy/jc21/nginx-proxy-manager:latest
# 替换为在worker节点上存在的镜像名
sed -i 's|harbor.pigeon.show/proxy/library/mariadb:10.4|docker.m.daocloud.io/library/mariadb:10.4|g' npm.yaml
sed -i 's|harbor.pigeon.show/proxy/library/busybox:latest|docker.m.daocloud.io/library/busybox:latest|g' npm.yaml
sed -i 's|harbor.pigeon.show/proxy/jc21/nginx-proxy-manager:latest|docker.m.daocloud.io/jc21/nginx-proxy-manager:latest|g' npm.yaml
# 再次验证替换结果
grep "image:" npm.yaml 
3.5 配置 Service 的外部 IP
为了让 NPM 能够通过 Master 节点的 IP 直接访问,需要在 Service 中配置 externalIPs:
sudo nano npm.yaml
#在 Service 定义中添加
apiVersion: v1
kind: Service
metadata:
name: npm-service
namespace: npm
spec:
externalIPs:
- 172.16.11.109 # 绑定到 Master 节点的 IP设置 externalIPs 后,可以直接通过 172.16.11.109:81 访问 NPM,而不需要 NodePort。这需要集群的负载均衡或路由配置支持。
3.6 部署 NPM
# 创建命名空间
kubectl create namespace npm
# 应用 YAML 配置(使用服务端应用模式)
kubectl apply -f npm.yaml -n npm --server-side
# 验证 Pod 运行状态
kubectl get pods -n npm
3.7 NPM 配置详解
访问 http://172.16.11.109:81 进入 NPM 管理界面。
申请 SSL 证书(证书列表 → 添加证书):

这个 Token 需要具备 Zone:Read 和 DNS:Edit 权限,NPM 会自动调用 API 添加 TXT 记录完成域名验证。
创建代理服务:
以代理 Gitea 为例


Kubernetes 内部 DNS 解析:
gitea-service.gitea.svc.cluster.local 是 Kubernetes 标准的服务发现格式:
gitea-service:Service 名称
gitea:命名空间
svc.cluster.local:集群内部域名后缀
第四部分:Gitea Git 仓库部署
4.1 Gitea定义
Gitea 是一个轻量级的 Git 服务,类似于 GitHub 或 GitLab,但资源占用更低。在我们的架构中,Gitea 负责:
存储 YAML 配置文件:ArgoCD 会从这里拉取配置
管理代码:Jenkins 从这里拉取源码进行构建
4.2 导入 Gitea 镜像
在 worker-01 和 worker-02 上执行:
# 导入 Gitea 镜像(已经在 argocd-images.tar 中包含)
sudo ctr -n k8s.io images import argocd-images.tar
# 验证 Gitea 镜像已导入
sudo crictl images | grep gitea
4.3 创建持久化目录
在 worker-02 上创建:
sudo mkdir -p /mnt/gitea/data
sudo chmod -R 777 /mnt/gitea/data4.4 修改 Gitea YAML 文件中的镜像地址
在 master 节点执行:
# 查看原始镜像地址
grep "image:" gitea.yaml
# 输出: image: harbor.pigeon.show/proxy/gitea/gitea:latest
# 替换为 Docker Hub 官方地址
sed -i 's|harbor.pigeon.show/proxy/gitea/gitea:latest|docker.io/gitea/gitea:latest|g' gitea.yaml
# 验证替换结果
grep "image:" gitea.yaml 
4.5 部署 Gitea
# 创建命名空间
kubectl create namespace gitea
# 部署 Gitea
kubectl apply -f gitea.yaml -n gitea --server-side
# 验证 Pod 运行状态
kubectl get pods -n gitea
# 修改 Service 类型为 NodePort,方便外部访问(也可通过NPM配置的域名进行访问)
kubectl patch svc gitea-service -n gitea -p '{"spec": {"ports": [{"port": 80, "targetPort": 3000, "nodePort": 30080}]}}'
# 查看 Service
kubectl get svc -n gitea

4.6 初始化配置
通过 NPM 配置的域名 https://waldns.hetao.one 访问 Gitea 初始化页面。
首次访问需要配置:
数据库类型:SQLite(简单场景))
管理员账号设置
SSH 服务配置(默认端口 22)
4.7 创建 Jenkins 部署配置
在 Gitea 中创建 jenkins 仓库,包含两个文件:
jenkins-pv.yaml:定义物理存储卷
apiVersion: v1
kind: PersistentVolume
metadata:
name: jenkins-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: ""
hostPath:
path: /mnt/jenkins_datajenkins.yaml:完整的部署定义
# --- 1. 命名空间与权限配置 (RBAC) ---
apiVersion: v1
kind: Namespace
metadata:
name: jenkins
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins-admin
namespace: jenkins
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: jenkins-agent-role
namespace: jenkins
rules:
- apiGroups: [""]
resources: ["pods", "pods/exec", "pods/log", "persistentvolumeclaims", "secrets"]
verbs: ["get", "list", "watch", "create", "delete", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: jenkins-agent-binding
namespace: jenkins
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: jenkins-agent-role
subjects:
- kind: ServiceAccount
name: jenkins-admin
namespace: jenkins
---
# --- 2. 持久化存储 (PVC) ---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins-pvc
namespace: jenkins
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
# --- 3. Jenkins Master 部署 ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins
namespace: jenkins
labels:
app: jenkins
spec:
replicas: 1
selector:
matchLabels:
app: jenkins
template:
metadata:
labels:
app: jenkins
spec:
serviceAccountName: jenkins-admin
nodeSelector:
kubernetes.io/hostname: worker-02
containers:
- name: jenkins
image: 'docker.m.daocloud.io/jenkins/jenkins:lts' #此处镜像名和worker节点上已存在的镜像名保持一致
imagePullPolicy: IfNotPresent
securityContext:
runAsUser: 0
ports:
- containerPort: 8080
name: http
- containerPort: 50000
name: jnlp
resources:
limits:
cpu: '2'
memory: 2Gi
requests:
cpu: '1'
memory: 1Gi
env:
- name: JAVA_OPTS
# 1. 内存优化:留出25%内存给非堆空间,防止容器OOM
# 2. 调度优化:让 Jenkins 发现任务排队时立即申请 K8s 资源
# 3. 时区对齐:确保日志和构建记录显示北京时间
value: >-
-Xmx1536m -XshowSettings:vm
-Dhudson.slaves.NodeProvisioner.initialDelay=0
-Dhudson.slaves.NodeProvisioner.MARGIN=50
-Dhudson.slaves.NodeProvisioner.MARGIN0=0.85
-Duser.timezone=Asia/Shanghai
-Dorg.apache.commons.jelly.tags.fmt.timeZone=Asia/Shanghai
volumeMounts:
- mountPath: /var/jenkins_home
name: data
volumes:
- name: data
persistentVolumeClaim:
claimName: jenkins-pvc
---
# --- 4. 服务暴露 ---
apiVersion: v1
kind: Service
metadata:
name: jenkins
namespace: jenkins
labels:
app: jenkins
spec:
type: NodePort
selector:
app: jenkins
ports:
- name: http
port: 8080
targetPort: 8080
nodePort: 32001 # 浏览器访问端口
- name: jnlp
port: 50000
targetPort: 50000
nodePort: 32002 # Agent 通信端口提交修改到 Gitea 仓库。
在 worker-02 创建物理挂载点:
sudo mkdir -p /mnt/jenkins_data
sudo chmod 777 /mnt/jenkins_data第五部分:ArgoCD GitOps 工具部署
5.1 什么是 GitOps?为什么用 ArgoCD?
GitOps 是一种持续交付模式,核心理念是:声明式的应用配置应该存储在 Git 仓库中,自动同步到 Kubernetes 集群。
5.2 导入 ArgoCD 镜像
在 master 节点执行:
# 导入 ArgoCD 镜像包
sudo ctr -n k8s.io images import argocd-images.tar
# 该 tar 包包含以下镜像:
# - quay.io/argoproj/argocd:v2.13.3
# - ghcr.io/dexidp/dex:v2.41.1
# - docker.io/library/redis:7-alpine
# - docker.io/gitea/gitea:latest
# - docker.io/library/nginx:alpine
# - docker.io/library/alpine:latest注意:ArgoCD 镜像只需要在 Master 节点导入,因为 Master 节点需要运行 kubectl apply 来部署 ArgoCD 的 Pod。
5.3 修改 ArgoCD YAML 文件中的镜像地址
在 master 节点执行:
# 查看原始 Redis 镜像地址
grep "image:" argocd-install.yaml
# 输出中有: image: redis:7-alpine
# Redis 镜像地址不完整(缺少 registry 前缀),需要补全为完整地址
sed -i 's|image: redis|image: docker.io/library/redis|g' argocd-install.yaml
# 验证修改结果
grep "image:" argocd-install.yaml
# 输出: image: docker.io/library/redis:7-alpine
5.4 部署 ArgoCD
# 创建命名空间
kubectl create namespace argocd
# 部署 ArgoCD(使用服务端应用模式)
kubectl apply -f argocd-install.yaml -n argocd --server-side验证部署状态:
kubectl get pods -n argocd
5.5 暴露 ArgoCD UI
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "NodePort", "ports": [{"port": 443, "targetPort": 8080, "nodePort": 30000}]}}'ArgoCD Server 默认监听 443 端口(HTTPS),但内部实际服务运行在 8080 端口。我们将 443 映射到 NodePort 30000,通过 http://172.16.11.109:30000 访问。
获取初始密码:
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d密码存储在 Secret 中,需要 base64 解码。初始用户名是 admin。
5.6 创建 ArgoCD Application
登录 ArgoCD后,创建 new APP 的配置要点:


成功创建后,如图所示:


jenkins服务创建成功。
第六部分:Jenkins 初始化与系统配置
6.1 Jenkins 初始化
6.1.1 通过域名访问 Jenkins
根据前面NPM配置部分,同样为Jenkins配置代理服务。通过 NPM 代理后,可以通过域名访问 Jenkins。

如果尚未配置 NPM 代理,也可以通过 NodePort 直接访问:http://172.16.11.109:32001。
6.1.2 获取 Jenkins 初始密码
Jenkins 首次启动时会生成一个随机密码,存储在容器内的 /var/jenkins_home/secrets/initialAdminPassword 文件中。
# 查看 Jenkins Pod 名称
kubectl get pods -n jenkins
# 获取初始密码
kubectl exec -it jenkins-65fb846cf4-2wtjv -n jenkins -- cat /var/jenkins_home/secrets/initialAdminPassword
6.1.3 解锁 Jenkins
访问 Jenkins 页面后:
在解锁界面输入上述获取的密码
点击「继续」按钮
6.1.4 安装推荐插件
Jenkins 提供了两种插件安装方式:
安装推荐的插件:安装一组常用插件,适合大多数场景
选择插件来安装:自定义选择需要安装的插件
选择「安装推荐的插件」,Jenkins 会自动开始下载和安装以下常用插件。
安装过程需要几分钟,请耐心等待。
6.1.5 创建管理员账户
插件安装完成后,进入管理员账户创建页面。
6.1.6 设置 Jenkins URL
进入实例配置页面,Jenkins URL填写通过 NPM 代理后的访问地址,如https://www.hetao.one/jenkins。
点击「保存并完成」,然后点击「开始使用 Jenkins」进入 Jenkins 主页。
6.2 插件管理
6.2.1 进入插件管理页面
进入 Jenkins 主页后:
点击左侧菜单「系统管理」
选择「插件管理」
切换到「Available Plugins」标签页

6.2.2 安装所需插件
在插件列表中搜索以下插件:
6.3 系统配置
6.3.1 配置 Kubernetes Cloud(动态 Agent)
这是 Jenkins 与 Kubernetes 集成的核心步骤。配置完成后,Jenkins 可以在需要构建时动态创建 Pod 作为构建 Agent。
进入配置页面:系统管理 → 节点和云管理 → Clouds → 新增云 → Kubernetes。
填写 Kubernetes 云配置:

配置完成后点击「Save」保存。
6.3.2 配置 Jenkins URL
进入配置页面:系统管理 → 系统配置
找到「Jenkins Location」部分:
Jenkins URL 填写Jenkins 对外访问地址。
6.3.3 配置 Gitea Server
Jenkins 需要与 Gitea 集成,以便从 Gitea 拉取代码、接收 Webhook 通知。
步骤一:在 Gitea 中生成 Access Token
登录 Gitea(https://waldns.hetao.one):
点击右上角头像 → 设置
左侧菜单选择「应用」
在「管理个人访问令牌」区域点击「生成新令牌」
填写令牌名称:jenkins
选择权限:
repo:仓库读写权限(拉取代码)
点击「生成令牌」
保存令牌:生成后会显示一个字符串,请立即保存,关闭页面后将无法再次查看。

步骤二:在 Jenkins 中添加 Gitea Server
进入配置页面:系统管理 → 系统配置
找到「Gitea Server」部分:

6.3.4 配置全局凭据(Gitea Token)
为了让 Jenkins 在 Pipeline 中拉取 Gitea 仓库代码,需要配置一个全局的 Git 凭据。
进入配置页面:系统管理 → 凭据 → 系统 → 全局凭据 → Add Credentials

6.3.5 验证配置
配置完成后,可以进行验证:
验证 Gitea 连接:
进入「系统管理」→「系统配置」
找到「Gitea Server」部分
点击「Test Connection」
应显示 Success 和 Gitea 版本号
验证 Kubernetes 连接:
进入「系统管理」→「节点和云管理」→「Clouds」
点击「Kubernetes」的「Check Connection」
应显示成功连接的信息
6.4 创建示例应用与 Pipeline
6.4.1 在 Gitea 中创建仓库
登录 Gitea(https://waldns.hetao.one):
点击右上角「+」→「新建仓库」
仓库名称:nginx(任取)
可见性:公开或私有
点击「创建仓库」
6.4.2 准备仓库文件
在redis仓库创建三个文件:
文件一:redis.yaml(Kubernetes 部署配置)
#redis.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-deployment
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: 172.16.11.109:30002/nginx/redis:7.0 #对应Harbor里的镜像名
ports:
- containerPort: 30081文件二:Dockerfile(镜像构建定义)
FROM 172.16.11.109:30002/proxy/library/redis:7-alpine
RUN echo "build by jenkins ci" > /build_info.txt文件三:Jenkinsfile(CI/CD 流水线定义)
pipeline {
agent {
kubernetes {
yaml """
apiVersion: v1
kind: Pod
spec:
serviceAccountName: jenkins-admin
containers:
- name: kaniko
image: 172.16.11.109:30002/nginx/executor:debug #注意修改镜像名
command: ['sleep']
args: ['99d']
volumeMounts:
- name: harbor-auth
mountPath: /kaniko/.docker
- name: jnlp
image: 172.16.11.109:30002/nginx/inbound-agent:latest
volumes:
- name: harbor-auth
secret:
secretName: harbor-secret
items:
- key: .dockerconfigjson
path: config.json
"""
}
}
// 每 2 分钟扫描一次代码
triggers {
pollSCM('H/2 * * * *')
}
stages {
stage('生产镜像') {
// 【核心防死循环逻辑】
// 如果Git提交消息中包含 "automatic update",说明是镜像更新器,直接跳过此阶段
when {
not {
changelog '.*automatic update of.*'
}
}
steps {
// 拉取代码
checkout scm
container('kaniko') {
// 产出 7.x 格式镜像
sh """
/kaniko/executor --context ${WORKSPACE} \
--dockerfile Dockerfile \
--destination 172.16.11.109:30002/nginx/redis:7.${BUILD_NUMBER} \
--skip-tls-verify
"""
}
}
}
}
post {
success {
echo "流程处理完成"
}
aborted {
echo "检测到镜像更新器提交,已跳过构建以防止死循环"
}
}
}6.5 创建 Harbor 认证凭据
Jenkins 需要登录 Harbor 才能推送镜像。
kubectl create secret docker-registry harbor-secret \
--docker-server=Harbor域名或IP \
--docker-username='用户名' \
--docker-password='密码' \
-n jenkins这条命令用于在 Kubernetes 中创建一个 docker-registry 类型的 Secret,用于存储 Harbor 私有镜像仓库的登录认证信息。
第七部分:ArgoCD 镜像自动更新器
7.1 什么是 ArgoCD Image Updater?
ArgoCD 本身只能同步 Git 仓库中的配置。但镜像 tag 的变化通常是由 CI 流水线触发的(如 Jenkins 构建后推送到 Harbor)。ArgoCD Image Updater 是一个扩展组件,它可以:
监控镜像仓库:定期检查 Harbor 中是否有新的镜像 tag
自动更新 Git 仓库:当发现新 tag 时,自动修改 Git 仓库中的 YAML 文件
触发 ArgoCD 同步:Git 更新后,ArgoCD 自动同步到集群
7.2 部署 ArgoCD Image Updater
7.2.1 导入镜像
# 导入 Image Updater 镜像
sudo ctr -n k8s.io images import argocd-updater.tar7.2.2 部署 Image Updater
# 应用部署 YAML
kubectl apply -n argocd -f update-install.yaml部署过程中会创建以下资源:
CRD: imageupdaters.argocd-image-updater.argoproj.io
ServiceAccount、Role、ClusterRole
ConfigMap(配置镜像仓库信息)
Deployment: argocd-image-updater-controller
7.2.3 验证部署状态
kubectl get pod -n argocd | grep image-updater
7.3 配置 Image Updater
7.3.1 创建 Harbor 认证 Secret
Image Updater 需要登录 Harbor 才能检查镜像:
# 创建包含用户名和密码的 Secret
kubectl create secret generic harbor-http-creds \
--from-literal=creds=Harbor用户名:密码 \
-n argocd
# 验证 Secret 创建成功
kubectl get secret harbor-http-creds -n argocd
7.3.2 配置 Image Updater ConfigMap
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-image-updater-config
namespace: argocd
data:
argocd.insecure: "true"
registries.conf: |
registries:
- name: Internal Harbor
prefix: 172.16.11.109:30002
api_url: http://172.16.11.109:30002
insecure: true
credentials: secret:argocd/harbor-http-creds#creds
EOF7.3.3 创建 Gitea 仓库访问 Secret
Image Updater 需要写权限来更新 Git 仓库中的 YAML 文件。
# 创建仓库访问 Secret
kubectl create secret generic the-first-repo-credss \
-n argocd \
--from-literal=url="gitea访问IP或者域名" \
--from-literal=username="gitea用户名" \
--from-literal=password="gitea token"
# 给 Secret 打上标签,ArgoCD 才能识别为仓库凭证
kubectl label secret the-first-repo-credss \
-n argocd \
"argocd.argoproj.io/secret-type=repository"
# 验证 Secret 已正确创建
kubectl get secret -n argocd -l argocd.argoproj.io/secret-type=repository
7.4 创建 ImageUpdater CRD
ImageUpdater 资源定义了要监控的镜像参数:
cat <<EOF | kubectl apply -f -
apiVersion: argocd-image-updater.argoproj.io/v1alpha1
kind: ImageUpdater
metadata:
name: redis-repo-updater
namespace: argocd
spec:
applicationRefs:
- namePattern: "redis" # 匹配 ArgoCD 应用名称
images:
- alias: "redis" # 定义别名,规避内网 IP 路径的转义歧义
imageName: "172.16.11.109:30002/nginx/redis" # 必须与 redis.yaml 镜像路径完全对齐
commonUpdateSettings:
updateStrategy: "semver" # 开启 SemVer 语义化版本演进算法
writeBackConfig:
method: "git" # 激活 Git Write-back 自动回写机制
gitConfig: {} # 自动索引关联的仓库凭证,简化配置
EOF
# 验证 ImageUpdater 创建成功
kubectl get imageupdater -n argocd
7.5 重启相关组件使配置生效
# 重启 ArgoCD 相关组件
kubectl rollout restart deployment argocd-repo-server -n argocd
kubectl rollout restart statefulset argocd-application-controller -n argocd
# 删除 Image Updater Pod 让其重建加载新配置
kubectl delete pod -l app.kubernetes.io/name=argocd-image-updater -n argocd
# 验证 Image Updater 正常运行
kubectl get pods -n argocd | grep image-updater
7.6 测试镜像自动更新
为了验证 Image Updater 是否正常工作,手动推送一个新版本的 Redis 镜像到 Harbor:
# 标记旧镜像为新版本 7.1
sudo docker tag 172.16.11.109:30002/nginx/redis:7.0 172.16.11.109:30002/nginx/redis:7.1
# 推送新镜像
sudo docker push 172.16.11.109:30002/nginx/redis:7.1推送完成后,Image Updater 会检测到新镜像,自动执行以下操作:
修改 Gitea 仓库中的 redis.yaml,将 image 字段更新为新版本
提交并推送更改
ArgoCD 检测到 Git 变化,自动同步到集群
# 查看 Image Updater 日志,观察更新过程
kubectl logs -f deployment/argocd-image-updater-controller -n argocd日志关键输出:
time="2026-05-12T08:57:36Z" level=info msg="Setting new image to 172.16.11.109:30002/nginx/redis:7.1"
time="2026-05-12T08:57:37Z" level=info msg="Successfully updated the live application spec"
time="2026-05-12T08:57:37Z" level=info msg="git push origin main"
time="2026-05-12T08:57:38Z" level=info msg="Successfully updated image '172.16.11.109:30002/nginx/redis:7.0' to '172.16.11.109:30002/nginx/redis:7.1'"在gitea仓库 发现.argocd-source-redis.yaml自动更新镜像,且不污染原redis.yaml文件。


7.7 验证同步状态
在gitea处修改redis.yaml副本数量,提交后可在CI/CD各处查看同步信息。



