前言
在云原生越来越普及的今天,GitOps 已经成为企业部署应用的标准方式。开发只需要关心代码,运维只需要维护配置,所有变更都通过 Git 管理,安全、可追溯、可回滚。
最近我从零搭建了一套 项目完整 CI/CD 流程:
- 代码提交 → Jenkins 自动构建镜像
- 自动推送到 Harbor 私有仓库
- 自动更新 Helm 的 values.yaml
- ArgoCD 自动同步到 Kubernetes
全程无人工介入 ,真正实现 代码即部署。本篇把完整步骤、踩坑、优化全部记录下来,方便自己回顾,也给大家参考。
接上文,一个完整的CI/CD流程的实现。
ArgoCD 与 GitOps:K8S 原生持续部署的实操指南_argocd 设置中文-CSDN博客
一、GitOps的设计思想

二、架构实现
操作系统均为Ubuntu24.04LTS/25.10版本。
|---------|-----------|---------|
| 服务名称 | 角色 | 版本 |
| Jenkins | CI | 2.541.2 |
| Harbor | 容器镜像持久化存储 | 2.13.1 |
| Gitee | 代码仓库 | / |
| K8S | 容器集群化管理工具 | 1.35.0 |
| ArgoCD | CD | 3.3.4 |
2.1 Gitee配置
创建存放代码与K8S资源清单仓库

2.2 Jenkins配置
2.1.1 密钥管理
Jenkins需要提前配置好Harbor、代码仓库、K8S资源清单仓库的凭证。
选择username with password 即可


2.1.2 任务创建

2.1.3 任务配置
描述一下你的这个任务
使用参数化构建,为后续镜像的自定义标签使用

源码管理,连接好git代码仓库,账号密码凭证之前创建好的,直接选择即可。

2.1.4 WebHook钩子
这个WebHook钩子目前没办法使用,因为Gitee是公有仓库,连接不到自己测试环境的内网地址,但是配置是一样的,主要触发的核心动作是push.



2.1.5 流水线或者SHELL变量定义

2.1.6 Build Steps
可以选择shell脚本,也可以shell脚本转Jenkins的pipeline。

bash
#!/bin/bash
set -e # 出错立即退出,避免无效执行
# ====================== 定义日志函数 ======================
info() {
echo -e "\033[32m[INFO] $(date +'%Y-%m-%d %H:%M:%S') $1\033[0m"
}
error() {
echo -e "\033[31m[ERROR] $(date +'%Y-%m-%d %H:%M:%S') $1\033[0m"
exit 1
}
# ====================== 1. 参数化变量 + 空值检查 =======================
IMAGE_TAG="${IMAGE_VERSION}"
ENV_TYPE="${ENV_TYPE}"
# 核心变量空值检查
[ -z "${IMAGE_TAG}" ] && error "IMAGE_VERSION参数为空,请传入镜像版本号"
[ -z "${ENV_TYPE}" ] && error "ENV_TYPE参数为空,请传入环境标识(prod/test)"
[ -z "${GIT_USER}" ] && error "GIT_USER参数为空,请配置Gitee用户名"
[ -z "${GIT_PWD}" ] && error "GIT_PWD参数为空,请配置Gitee密码/令牌"
[ -z "${HARBOR_USER}" ] && error "HARBOR_USER参数为空,请配置Harbor用户名"
[ -z "${HARBOR_PWD}" ] && error "HARBOR_PWD参数为空,请配置Harbor密码"
# Argocd repo 配置
ARGOCD_GIT_REPO="https://${GIT_USER}:${GIT_PWD}@gitee.com/cao-facan/argocd.git"
ARGOCD_GIT_BRANCH="master"
ARGOCD_TMP_DIR="/tmp/jenkins-argocd-tmp-$(date +%Y%m%d%H%M%S)"
# 生成最终镜像标签
GIT_COMMIT_MSG=$(git log -1 --pretty=format:%s | tr -d " " | cut -c1-15 || echo "nogitmsg")
TIME_STAMP=$(date +%Y%m%d%H%M)
FINAL_TAG="${TIME_STAMP}-${IMAGE_TAG}-${GIT_COMMIT_MSG}-${ENV_TYPE}"
# 镜像仓库地址
HARBOR_REPO="harbor.online.com/cicd/myweb"
# ====================== 2. 构建并推送镜像 =======================
echo "======================"
echo "开始构建镜像:${HARBOR_REPO}:${FINAL_TAG}"
echo "======================"
# 登录 Harbor 并推送镜像
echo "${HARBOR_PWD}" | docker login harbor.online.com -u "${HARBOR_USER}" --password-stdin
pwd
docker build -t ${HARBOR_REPO}:${FINAL_TAG} .
docker push ${HARBOR_REPO}:${FINAL_TAG}
docker logout harbor.online.com
echo "✅ 镜像构建推送完成!"
echo "👉 镜像标签:${FINAL_TAG}"
# ====================== 3. 修改argocd仓库values.yaml(修复Git顺序)=====================
info "===== 开始修改argocd仓库的values.yaml ====="
info "克隆argocd仓库:${ARGOCD_GIT_REPO}"
# 清理旧临时目录,创建新目录
rm -rf ${ARGOCD_TMP_DIR}
mkdir -p ${ARGOCD_TMP_DIR} || error "创建argocd临时目录失败"
# 克隆argocd仓库(带重试)
for i in {1..2}; do
if git clone -b ${ARGOCD_GIT_BRANCH} ${ARGOCD_GIT_REPO} ${ARGOCD_TMP_DIR}; then
break
fi
info "Git克隆失败,第${i}次重试..."
sleep 3
if [ ${i} -eq 2 ]; then
error "argocd仓库克隆失败,终止流程"
fi
done
# 进入argocd临时目录(git clone已自动初始化仓库,无需git init)
cd ${ARGOCD_TMP_DIR} || error "进入argocd临时目录失败"
pwd
# ====================== 关键修复1:先拉取最新代码(此时工作区干净)=====================
info "拉取argocd仓库最新代码"
git pull origin ${ARGOCD_GIT_BRANCH} || error "拉取argocd仓库最新代码失败"
# 确定要修改的values文件(兼容分环境/单环境)
VALUES_FILE="values-${ENV_TYPE}.yaml"
if [ ! -f ${VALUES_FILE} ]; then
info "未找到${VALUES_FILE},使用默认values.yaml"
VALUES_FILE="values.yaml"
fi
[ ! -f ${VALUES_FILE} ] && error "argocd仓库中未找到${VALUES_FILE}文件"
# ====================== 关键:用sed替换image.tag的值 ======================
info "修改${VALUES_FILE}的image.tag为:${FINAL_TAG}"
# 适配你的yaml格式(2个空格缩进)
sed -i -e "/^ image:$/!{n;/^ tag:/s/\".*\"/\"${FINAL_TAG}\"/}" ${VALUES_FILE}
# 验证修改是否成功
MODIFIED_TAG=$(grep -E '^ tag: ' ${VALUES_FILE} | awk -F'"' '{print $2}')
if [ "${MODIFIED_TAG}" != "${FINAL_TAG}" ]; then
error "sed修改tag失败!当前tag值:${MODIFIED_TAG},期望:${FINAL_TAG}"
else
info "✅ tag修改验证成功:${MODIFIED_TAG}"
fi
# ====================== 关键修复2:修改后再提交+推送 ======================
info "提交修改到argocd仓库"
git config --global user.name "jenkins-auto"
git config --global user.email "jenkins@auto.com"
# 暂存修改
git add ${VALUES_FILE}
# 检查是否有变更(避免无变更时提交失败)
if git diff --cached --quiet; then
info "values.yaml无变更,无需提交"
else
git commit -m "auto update ${ENV_TYPE} image tag: ${FINAL_TAG}" || error "提交修改失败"
# 推送修改(无需rebase,因为已先拉取最新代码)
git push origin ${ARGOCD_GIT_BRANCH} || error "推送argocd仓库修改失败"
fi
# 清理临时目录
rm -rf ${ARGOCD_TMP_DIR}
info "✅ 全流程执行完成!"
info "👉 argocd仓库${VALUES_FILE}的image.tag已更新为:${FINAL_TAG}"
info "👉 ArgoCD将自动同步新镜像到${ENV_TYPE}环境"
exit 0
2.1.7 Dockerfile文件模板
bash
[root@cfc /var/lib/jenkins/workspace/prod]# cat Dockerfile
FROM nginx:alpine
COPY . /usr/share/nginx/html/
EXPOSE 80
2.3 K8S配置
2.3.1 HELM资源创建
bash
[root@k8s-master ~]# helm create myweb
bash
[root@k8s-master ~/myweb]# cat values.yaml
# 副本数配置
replicaCount: 3
# 镜像配置
image:
repository: harbor.online.com/cicd/myweb
pullPolicy: IfNotPresent
tag: "202603181022--addDockerfilefi-prod"
# 服务配置
service:
type: ClusterIP
port: 80
# Gateway API 相关配置
gateway:
enabled: true
className: envoy
port: 80
httpRoute:
enabled: true
hostname: www.myweb.com
path: /
serviceName: myweb-service
servicePort: 80
[root@k8s-master ~/myweb]#
bash
[root@k8s-master ~/myweb]# rm -rf templates/*
bash
[root@k8s-master ~/myweb/templates]# cat gateway-httproute.yaml
{{- if .Values.gateway.enabled }}
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway
namespace: myweb
spec:
gatewayClassName: {{ .Values.gateway.className }}
listeners:
- name: http
port: {{ .Values.gateway.port }}
protocol: HTTP
allowedRoutes:
namespaces:
from: Same
---
{{- end }}
{{- if .Values.httpRoute.enabled }}
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: myweb
namespace: myweb
spec:
parentRefs:
- name: gateway
hostnames:
- {{ .Values.httpRoute.hostname }}
rules:
- matches:
- path:
type: PathPrefix
value: {{ .Values.httpRoute.path }}
backendRefs:
- name: {{ .Values.httpRoute.serviceName }}
namespace: myweb
port: {{ .Values.httpRoute.servicePort }}
{{- end }}
[root@k8s-master ~/myweb/templates]#
bash
[root@k8s-master ~/myweb/templates]# cat myweb-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myweb
namespace: myweb
labels:
app: myweb
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: myweb
template:
metadata:
labels:
app: myweb
spec:
containers:
- name: myweb
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "200m"
imagePullSecrets:
- name: harbor-auth
---
apiVersion: v1
kind: Service
metadata:
name: myweb-service
namespace: myweb
spec:
selector:
app: myweb
ports:
- protocol: TCP
port: {{ .Values.service.port }}
targetPort: 80
type: {{ .Values.service.type }}
然后推送到我们对应存放资源清单的的Gitee仓库即可。
2.3.2 创建Harbor连接密钥
bash
kubectl create secret docker-registry harbor-auth -n myweb --docker-server=harbor.online.com --docker-username=admin --docker-password=1 --docker-email=2XX2@qq.com
2.4 ArgoCD配置

2.4.1 SYNC
初次的话保证自己配置的ArgoCD的资源清单仓库没问题的话,可以SYNC一次,看一下资源创建是否正常,是否可以正常连接到Harbor.

访问www.myweb.com测试.

没有问题。
2.4.2 记得开启自动同步
syncPolicy:
automated:
prune: true
selfHeal: true


三、架构执行
3.1 确认环境
在最开始创建好Jenkins与git的连接之后,可以简单跑一个流程看是否可以将代码仓库下的代码给拉取下来。
bash
[root@cfc /var/lib/jenkins/workspace/prod]# cd /var/lib/jenkins/workspace/
[root@cfc /var/lib/jenkins/workspace]# ll
total 20
drwxr-xr-x 2 root root 4096 Mar 17 14:37 dockerbuild/
drwxr-xr-x 6 jenkins jenkins 4096 Mar 18 09:22 Myproject/
drwxr-xr-x 2 jenkins jenkins 4096 Mar 17 17:59 'Myproject@tmp'/
drwxr-xr-x 6 jenkins jenkins 4096 Mar 18 14:26 prod/
drwxr-xr-x 2 jenkins jenkins 4096 Mar 18 14:34 'prod@tmp'/
bash
[root@cfc /var/lib/jenkins/workspace]# ll prod
total 220
-rw-r--r-- 1 jenkins jenkins 16458 Mar 18 13:58 about.html
-rw-r--r-- 1 jenkins jenkins 20149 Mar 18 13:58 album.html
-rw-r--r-- 1 jenkins jenkins 19662 Mar 18 13:58 article_detail.html
-rw-r--r-- 1 jenkins jenkins 18767 Mar 18 13:58 article.html
-rw-r--r-- 1 jenkins jenkins 18913 Mar 18 13:58 comment.html
-rw-r--r-- 1 jenkins jenkins 16465 Mar 18 13:58 contact.html
drwxr-xr-x 2 jenkins jenkins 4096 Mar 18 13:58 css/
-rw-r--r-- 1 jenkins jenkins 60 Mar 18 13:58 Dockerfile
drwxr-xr-x 5 jenkins jenkins 4096 Mar 18 13:58 images/
-rw-r--r-- 1 jenkins jenkins 29675 Mar 18 14:26 index.html
drwxr-xr-x 2 jenkins jenkins 4096 Mar 18 13:58 js/
-rw-r--r-- 1 jenkins jenkins 24893 Mar 18 13:58 product_detail.html
-rw-r--r-- 1 jenkins jenkins 20672 Mar 18 13:58 product.html
3.2 模拟开发写代码以及推送
直接修改index.html的内容吧,这里我添加了一些内容
bash
[root@cfc /var/lib/jenkins/workspace/prod]# cat index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
......
</head>
<body>
<header>
<h1>GitOps-prod,ArgoCD自动同步!</h1>
<h2>GitOps-prod,这是我的博客测试!!!</h2>
bash
[root@cfc /var/lib/jenkins/workspace/prod]# git add index.html
[root@cfc /var/lib/jenkins/workspace/prod]# git commit -m "AddH2"
[detached HEAD b9e8ef3] AddH2
1 file changed, 1 insertion(+)
[root@cfc /var/lib/jenkins/workspace/prod]# git push origin master
Username for 'https://gitee.com': cao-facan
Password for 'https://cao-facan@gitee.com':
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 2 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 362 bytes | 362.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0), pack-reused 0
remote: Powered by GITEE.COM [1.1.23]
remote: Set trace flag 3848b698
To https://gitee.com/cao-facan/prod.git
9bd3b41..b9e8ef3 master -> master

3.3 Jenkins执行build


3.4 查看Harbor镜像

3.5 查看ArgoCD
bash
[root@k8s-master ~]# kubectl get po -n myweb
NAMESPACE NAME READY STATUS RESTARTS AGE
myweb myweb-6cbb89b958-7lf7w 1/1 Running 0 3h23m
myweb myweb-6cbb89b958-8zbf5 1/1 Running 0 3h22m
myweb myweb-6cbb89b958-rp9q7 1/1 Running 0 3h23m

同步之后,自己会创建新Pod和删除老Pod

bash
[root@k8s-master ~]# kubectl get po -n myweb
NAME READY STATUS RESTARTS AGE
myweb-7cb6b87c46-kl79s 1/1 Running 0 82s
myweb-7cb6b87c46-t2zd9 1/1 Running 0 86s
myweb-7cb6b87c46-wqr2d 1/1 Running 0 84s
3.6 访问测试

没问题!
四、常见问题解决
4.1 Harbor自建证书对接报错X509
bash
cp /etc/docker/certs.d/harbor.online.com/ca.crt /usr/local/share/ca-certificates/harbor-ca.crt
# 然后更新系统的CA库
update-ca-certificates
4.2 containerd对接Harbor
bash
mkdir -p /etc/containerd/certs.d/harbor.online.com
cp /usr/local/harbor/certs/ca.crt /etc/containerd/certs.d/harbor.online.com/ca.crt
[root@cfc /usr/local/harbor/certs]# cp -a harbor.online.com.cert harbor.online.com.key /etc/containerd/certs.d/harbor.online.com/
#
cat > /etc/containerd/certs.d/harbor.online.com/hosts.toml <<EOF
server = "https://harbor.online.com"
[host."https://harbor.online.com"]
capabilities = ["pull", "resolve", "push"]
ca = "/etc/containerd/certs.d/harbor.online.com/ca.crt"
EOF
systemctl restart containerd
[root@cfc /usr/local/harbor/certs]# ll /etc/containerd/certs.d/harbor.online.com/
total 16
-rw-r--r-- 1 root root 2061 Nov 25 14:37 ca.crt
-rw-r--r-- 1 root root 2167 Nov 25 13:44 harbor.online.com.cert
-rw------- 1 root root 3268 Nov 25 13:43 harbor.online.com.key
-rw-r--r-- 1 root root 172 Nov 25 15:32 hosts.toml
bash
[root@cfc /usr/local/harbor/certs]# containerd config dump | grep -A 5 "registry]"
[plugins.'io.containerd.cri.v1.images'.registry]
config_path = '/etc/containerd/certs.d'
[plugins.'io.containerd.cri.v1.images'.image_decryption]
key_model = 'node'
4.3 git报错 fatal: detected dubious ownership
Git 安全机制:仓库所有者(jenkins)和执行用户(root)不一致
bash
git config --global --add safe.directory 仓库路径
4.4 Jenkins 推送 Git 失败
本地分支未关联远程、凭证未配置
bash
绑定 GIT_USER/GIT_PWD 凭证 + 分支关联
4.5 ArgoCD UI 勾选自动同步后保存就取消
Application 是声明式配置,UI 修改会被 Git/YAML 覆盖
bash
在 Application YAML 中添加 syncPolicy.automated 配置
4.6 自动同步不触发
轮询间隔太长(默认 3 分钟)
bash
ArgoCD 自动同步的检测间隔,官方明确由 argocd-cm ConfigMap 中的 timeout.reconciliation 字段控制,默认值 180s(3 分钟)
[root@k8s-master ~]# kubectl edit configmap argocd-cm -n argocd
kind: ConfigMap
metadata:
creationTimestamp: "2026-03-17T03:21:17Z"
labels:
app.kubernetes.io/name: argocd-cm
app.kubernetes.io/part-of: argocd
name: argocd-cm
namespace: argocd
resourceVersion: "25751"
uid: b92b74b3-d278-4c3e-8617-de491f9eee85
# 新增这部分 data 节点,配置轮询间隔
data:
# 官方标准字段:设置自动同步检测间隔为30秒(默认180秒)
timeout.reconciliation: 30s
五、总结
本文完整记录了从 Jenkins 自动化构建、镜像推送,到 ArgoCD 实现 GitOps 自动同步的全流程落地实践。过程中解决了 Jenkins 构建时 Git 分支游离、权限异常、Webhook 内网限制,以及 ArgoCD 自动同步不触发、同步策略失效等高频问题。通过优化 Jenkins 脚本、配置 Gitee 触发机制、调整 ArgoCD 全局轮询间隔,实现了代码提交到应用发布的全链路自动化。
未来将进一步完善通知机制、构建缓存与权限管控,持续提升流水线稳定性与发布效率,为团队规模化、标准化的云原生部署打下坚实基础。
