面向 106.75.30.92 上已有的 Jenkins + Kubernetes(若依微服务已上线运行)环境,在不影响现网的前提下引入 GitOps 持续交付。
本文档中的“现状”部分全部来自对 106.75.30.92 的只读读取(kubectl get / describe / exec cat 配置文件、读取 Jenkins 配置与 workspace 文件、curl 探测网络)。未执行任何 apply / patch / create / delete / restart / 镜像拉取等写操作,集群与 Jenkins 状态与改造前一致。下面第四章之后的所有命令都由你在确认后自行执行。
先把“当前真实跑着什么”钉死。下面每一条都是现场读出来的,不是推测。
4 个节点全部通过 Tailscale 组网,kubectl 的 API Server 挂在 Jenkins 那台机器上,它同时兼任控制平面:
| 节点名 | 角色 | Tailscale IP | CPU / 内存(allocatable) | 当前内存占用 | 状态 |
|---|---|---|---|---|---|
jenkins | control-plane(etcd / apiserver / controller-manager / scheduler 全在这) | 100.68.117.72 | 2C / 7.4Gi | 71% | Ready(带 control-plane 污点) |
k8s-node1 | worker | 100.125.80.98 | 4C / 3.5Gi | 76% | Ready |
k8s-node2 | worker | 100.97.166.90 | 4C / 3.5Gi | 93% | Ready(内存最紧张) |
k8s-node3 | worker | 100.78.249.106 | 2C / 7.4Gi | 70% | Ready(内存余量最大,≈2.2Gi) |
| 组件 | 现状 |
|---|---|
| Kubernetes | v1.28.14(kubeadm 部署,kubernetes-admin@kubernetes 上下文) |
| 容器运行时 | containerd 2.3.3;/etc/containerd/certs.d 已按 registry 维度配置了 hosts.toml 加速 |
| CNI / 代理 | Calico(VXLAN,vxlan.calico),kube-proxy 为 IPVS 模式 |
| Ingress | ingress-nginx(IngressClass 名 nginx,控制器实际 Release v1.0.0),Service 为 NodePort 30080(HTTP) / 30443(HTTPS) |
| 存储 | 无 StorageClass、无 PV、无 PVC —— 全集群无持久化卷(Argo CD 安装本身不需要,但后续做有状态服务要留意) |
| 可观测 | metrics-server 已装;kube-system 有 filebeat DaemonSet 对接 k8s-elk 节点;Kuboard v4 以 docker 容器方式跑在宿主机 |
| 已有 CRD | 仅 Calico + Kuboard,没有 Argo CD / Istio / Prometheus Operator 等 |
应用全部落在 ruoyi 命名空间,形态是「7 个 Deployment + 7 个 NodePort Service + 1 个 Ingress」:
| Deployment | 容器端口 | Service NodePort | 当前镜像 tag |
|---|---|---|---|
ruoyi-gateway-deploy | 8080 | 31080 | 20260827145338 |
ruoyi-auth-deploy | 9200 | 31081 | 20260827145033 |
ruoyi-modules-system-deploy | 9201 | 31093 | 20260827145603 |
ruoyi-modules-gen-deploy | 9202 | 30091 | 20260827161313 |
ruoyi-modules-job-deploy | 9203 | 30090 | 20260827162653 |
ruoyi-modules-file-deploy | 9300 | 30092 | 20260827152043 |
ruoyi-visual-monitor-deploy | 9100 | 30095 | 20260827191643 |
另外几个关键事实:
106.75.29.47:10086,项目 ruoyi-cloud,tag 一律是「秒级时间戳」(yyyyMMddHHmmss)。ruoyi 命名空间里有 harbor-secret / harbor-cred 两个 dockerconfigjson,Deployment 通过 imagePullSecrets 引用 harbor-secret。ruoyi-gateway-config(以 subPath 挂到 /home/ruoyi/application.yml);其余服务的配置是打进镜像里的。Redis 指向 106.75.29.47:6379。ruoyi-gateway-ingress,ingressClassName: nginx,无域名(host 为 *),全部 / 转发到 gateway。ruoyi-ui 是宿主机 Nginx 直接托管 /opt/ruoyi/dist(/etc/nginx/conf.d/ruoyi.conf,server_name 106.75.30.92)。仓库里虽然有 ruoyi-ui 的 k8s 清单,但没有真的部署到集群。106.75.29.47 上开放了 3306(MySQL)、6379(Redis)、8848/9848(Nacos)、10086(Harbor)、80/8080。| 项 | 现状 |
|---|---|
| Jenkins Home | /var/lib/jenkins(rpm 安装、systemd 托管,服务 active) |
| 执行器 | numExecutors=2 |
| Job 形态 | 全部是 Multibranch Pipeline,Jenkinsfile 在各仓库根目录 |
| 代码来源 | Gitee:https://gitee.com/pang_le/ruoyi-{gateway,auth,modules-system,modules-gen,modules-job,modules-file,visual-monitor,ui,dependencies,common}.git,分支过滤 prod*(实际分支 prod-k8s),未配置凭据 → 公开仓库 |
| 构建工具 | Maven-3.8.9 + JDK-17(Java 侧);前端用 npm + registry.npmmirror.com |
| 凭据清单 | harbor-credentials(Harbor账号密码)、gitee-enterprise-token、deploy-ssh-key、nexus-admin |
| 宿主机角色 | 既是 Jenkins,又是 k8s 控制平面;nginx 占 80(Jenkins 反代 + 若依前端),java 占 8080/50000 |
stage('代码编译打包') → mvn clean package -Dmaven.test.skip=true
stage('构建 Docker 镜像') → docker build -t ${HARBOR_URL}:latest . && docker tag ... :$VERSION
stage('推送镜像到 Harbor') → docker push ...:latest && docker push ...:$VERSION
stage('部署到 K8s') → sed -i 's/:latest/:$VERSION/g' k8s/deployment.yaml
kubectl apply -f k8s/deployment.yaml -n ruoyi
post.success → docker rmi ...(清理本地镜像)
其中 VERSION = date '+%Y%m%d%H%M%S',k8s/deployment.yaml 随代码仓库一起维护。
kubectl apply,构建与发布耦合,失败无法自动回滚。sed -i 污染工作区:Jenkinsfile 在构建时直接改写仓库里的 k8s/deployment.yaml,导致工作区 git 树长期处于 dirty 状态,清单文件在本地被“改坏”,下次全量构建会带着上次的 tag 继续滚。docker push 还同时推 latest 这种可变标签。ruoyi-gateway-deploy 实际挂载了 ruoyi-gateway-config(有 volumeMounts),但仓库里的 k8s/deployment.yaml 这段是被注释掉的。也就是说漏跑一次 CI 或重装一次集群,网关的配置文件挂载就会丢。这正是 GitOps 要解决的问题。改造后的职责划分用一句话概括:Jenkins 只负责“把代码变成镜像并把镜像题写进 Git”,Argo CD 负责“把 Git 里的期望状态搬到集群”。Jenkins 不再需要集群的写权限。
| 维度 | 改造前 | 改造后 |
|---|---|---|
| 部署指令来源 | Jenkins 执行 kubectl apply | Argo CD 读取 Git,自动同步 |
| 集群写权限 | Jenkins 持有 admin kubeconfig | 只有 Argo CD 的 ServiceAccount 持有 |
| 期望状态存放 | 散落在各业务仓库的 k8s/ 目录 | 集中在一个 GitOps 仓库,可审计、可 Review |
| 镜像 tag | 时间戳 + latest | Git 短 SHA(不可变、可追溯) |
| 漂移处理 | 无人感知(手动 kubectl edit 无人知道) | Argo CD 标记 OutOfSync,selfHeal 自动纠偏 |
| 回滚 | 重新跑一次旧版本构建 | Git revert 或 Argo CD 一键回滚到任一历史版本 |
| 发布可见性 | 只有构建日志 | UI 上能看到每个资源的同步状态、健康度、diff |
ruoyi-ui 前端的一个明确取舍现状前端是宿主机 Nginx 托管静态目录,不在集群内。本次改造建议不动它,理由:它的发布物是静态文件而非镜像,纳入 GitOps 需要先把 ruoyi-ui 容器化后进集群,属于独立的架构调整,与本轮「引入 Argo CD」目标不同。文档里会给出一条可选路径(见 §10.6),但不作为本轮必须项。
这一步是整份方案里最容易踩坑的地方,必须先定死。
当前集群是 Kubernetes v1.28.14,而 Argo CD 官方「已测试版本矩阵」显示:3.3 / 3.4 / 3.5 只测试了 v1.32~v1.36,3.x 完全不覆盖 v1.28。硬装 3.x 会出现 CRD 版本与控制器行为不匹配的风险,且官方不背书。
官方矩阵中仍覆盖 v1.28 的最新分支是 2.14(测试 v1.31/v1.30/v1.29/v1.28),2.13 覆盖 v1.30/v1.29/v1.28/v1.27。
| Argo CD 分支 | 官方测试过的 Kubernetes 版本 | 是否覆盖 v1.28 | 说明 |
|---|---|---|---|
| 3.5 / 3.4 / 3.3 | v1.36~v1.32 | 否 | 最新特性最全,但要求集群较新 |
| 2.14 | v1.31, v1.30, v1.29, v1.28 | 是 | 本次选型,2.x 线最后一个覆盖 1.28 的分支 |
| 2.13 | v1.30, v1.29, v1.28, v1.27 | 是 | 可作备选(若 2.14 出问题) |
| 2.12 及更早 | v1.29 及以下 | 是 | 过旧,不推荐新装 |
因此本方案锁定 Argo CD v2.14.21(2.14 分支末版)。
按 Argo CD 的支持策略,只维护最近 3 个 minor。2.14 目前已经不在补丁窗口内,意味着不会有安全补丁。两条路线:
kubeadm upgrade 逐个小版本升级即可,1.28→1.33 需要跨 5 次,建议排在 GitOps 稳定运行之后做,并单独出升级方案。集群内存偏紧(node1 76%、node2 93%),而 jenkins 节点带 control-plane 污点、Argo CD 的 Pod 不会调度上去。因此选 k8s-node3(7.4Gi 分配,当前 70%,余量约 2.2Gi,2 核)作为 Argo CD 的落脚点,并用 nodeSelector 固定,避免被调度到只剩几百 Mi 的 node1/node2 上导致 OOM。
| 组件 | 类型 | 建议 requests | 建议 limits |
|---|---|---|---|
| argocd-application-controller | StatefulSet | 200m / 384Mi | 1000m / 768Mi |
| argocd-repo-server | Deployment | 100m / 192Mi | 500m / 512Mi |
| argocd-server | Deployment | 50m / 128Mi | 300m / 256Mi |
| argocd-redis | Deployment | 30m / 64Mi | 200m / 128Mi |
| argocd-applicationset-controller | Deployment | 30m / 64Mi | 200m / 128Mi |
| argocd-notifications-controller | Deployment | 20m / 64Mi | 100m / 128Mi |
| argocd-dex-server | Deployment | —(可选,见 §4.5) | — |
| 合计(不含 dex) | ≈430m / 900Mi | ≈2.3C / 1.9Gi | |
limits 之和超过 node3 的 2 核,但这是 limit 不是预留,实际稳态占用通常在 300m / 600Mi 量级。requests 的 900Mi 是真正把 node3 余量吃掉的部分,仍在安全范围内。
安装分三步:准备清单 → 把 3 个镜像搬进自己的 Harbor → apply 并打补丁。全程不依赖 quay.io 的裸网络。
v2.14.21 的 install.yaml(1425427 字节)一共 59 个资源段,需要的外部镜像只有 3 个:
| 清单中的镜像 | 用途 | 国内镜像源(已实测可用) |
|---|---|---|
quay.io/argoproj/argocd:v2.14.21 | server / controller / repo-server / applicationset / notifications 共 8 处引用 | quay.m.daocloud.io/argoproj/argocd:v2.14.21 |
ghcr.io/dexidp/dex:v2.41.1 | SSO(Dex) | ghcr.m.daocloud.io/dexidp/dex:v2.41.1 |
redis:7.2.11-alpine | 缓存 | m.daocloud.io/docker.io/library/redis:7.2.11-alpine |
安装会产生:3 个 CRD、7 个 ServiceAccount、若干 Role/Binding、7 个 ConfigMap、2 个 Secret、8 个 Service、6 个 Deployment、1 个 StatefulSet(application-controller)、7 个 NetworkPolicy。
106.75.30.92 上实测通过
测试方法:按 Docker Registry v2 协议做匿名 token 换取后请求 manifest,返回 200 且能取到 amd64 架构即为可用。
| 用途 | 地址 | 实测结果 | 备注 |
|---|---|---|---|
| Argo CD 主镜像 | quay.m.daocloud.io/argoproj/argocd:v2.14.21 | manifest 200 · amd64/arm64 | DaoCloud quay 代理,首选 |
| Argo CD 主镜像(备) | quay.nju.edu.cn/argoproj/argocd:v2.14.21 | manifest 200 | 南京大学镜像站 |
| Dex | ghcr.m.daocloud.io/dexidp/dex:v2.41.1 | manifest 200 | ghcr 代理 |
| Redis | m.daocloud.io/docker.io/library/redis:7.2.11-alpine | manifest 200 | Docker Hub 代理 |
| 安装清单 install.yaml | https://gh-proxy.com/https://raw.githubusercontent.com/argoproj/argo-cd/v2.14.21/manifests/install.yaml | 200 · 1425427 B | 与官方 raw 内容字节一致 |
| 安装清单(备) | https://ghfast.top/https://raw.githubusercontent.com/... | 200 · 1425427 B | 备选加速 |
| 安装清单(备) | https://cdn.jsdelivr.net/gh/argoproj/argo-cd@v2.14.21/manifests/install.yaml | 200 · 1425427 B | jsDelivr |
| argocd CLI 二进制 | https://files.m.daocloud.io/github.com/argoproj/argo-cd/releases/download/v2.14.21/argocd-linux-amd64 | 200 · 14868892 B | DaoCloud 文件代理,走这个 |
| Helm Chart 仓库 | https://argoproj.github.io/argo-helm | index.yaml 200 | 本环境可直连;备选 https://ben-wangz.github.io/helm-chart-mirror/charts |
swr.cn-north-4.myhuaweicloud.com/ddn-k8s/quay.io/argoproj/argocd → 404(该路径下无此镜像)registry.cn-hangzhou.aliyuncs.com/argoproj/argocd → 401(该命名空间下无此镜像)docker.m.daocloud.io/argoproj/argocd → 403(该域只代理 Docker Hub,不代理 quay)registry-1.docker.io → 不可达,所以 redis 这类官方镜像必须走 m.daocloud.io/docker.io/ 前缀# 在 106.75.30.92 上执行
mkdir -p /root/argocd/{manifests,offline} && cd /root/argocd
# 下载 v2.14.21 官方安装清单(走国内加速)
curl -fL --retry 3 -o manifests/install-v2.14.21.yaml \
https://gh-proxy.com/https://raw.githubusercontent.com/argoproj/argo-cd/v2.14.21/manifests/install.yaml
# 校验:应为 1425427 字节
wc -c manifests/install-v2.14.21.yaml
# 下载 argocd CLI(可选,但强烈建议装,后面验收和触发同步都要用)
curl -fL --retry 3 -o /usr/local/bin/argocd \
https://files.m.daocloud.io/github.com/argoproj/argo-cd/releases/download/v2.14.21/argocd-linux-amd64
chmod +x /usr/local/bin/argocd && argocd version --client
虽然 quay.io 在本环境能直连,但用它拉取有两个问题:速度不稳定、集群对公网有外部依赖。既然已经有 Harbor,把它当作集群的唯一镜像入口是更干净的架构——顺带也让 Argo CD 自身的升级走同一套流程。
全部收敛到一个 Harbor 项目 infra,便于管理和设置保留策略:
quay.io/argoproj/argocd:v2.14.21 → 106.75.29.47:10086/infra/argocd:v2.14.21ghcr.io/dexidp/dex:v2.41.1 → 106.75.29.47:10086/infra/dex:v2.41.1redis:7.2.11-alpine → 106.75.29.47:10086/infra/redis:7.2.11-alpine# 0) Harbor 里创建 infra 项目(若不存在)
# 也可以直接在执行 docker push 时由 Harbor 自动创建(若开启了自动创建项目)
# 手动创建:Harbor UI → 项目 → 新建项目 → 名称 infra → 私有
# 1) 登录 Harbor
docker login 106.75.29.47:10086 -u admin -p '@Pl000000'
# 2) 从国内镜像源拉取,重新打标签,推入 Harbor
set -e
HARBOR=106.75.29.47:10086
docker pull quay.m.daocloud.io/argoproj/argocd:v2.14.21
docker tag quay.m.daocloud.io/argoproj/argocd:v2.14.21 ${HARBOR}/infra/argocd:v2.14.21
docker push ${HARBOR}/infra/argocd:v2.14.21
docker pull ghcr.m.daocloud.io/dexidp/dex:v2.41.1
docker tag ghcr.m.daocloud.io/dexidp/dex:v2.41.1 ${HARBOR}/infra/dex:v2.41.1
docker push ${HARBOR}/infra/dex:v2.41.1
docker pull m.daocloud.io/docker.io/library/redis:7.2.11-alpine
docker tag m.daocloud.io/docker.io/library/redis:7.2.11-alpine ${HARBOR}/infra/redis:7.2.11-alpine
docker push ${HARBOR}/infra/redis:7.2.11-alpine
# 3) 清理本地临时镜像,避免占满 /(当前 99G 盘已用 58%)
docker rmi quay.m.daocloud.io/argoproj/argocd:v2.14.21 \
ghcr.m.daocloud.io/dexidp/dex:v2.41.1 \
m.daocloud.io/docker.io/library/redis:7.2.11-alpine || true
这套环境已有 /etc/containerd/certs.d/<registry>/hosts.toml 机制(docker.io、registry.k8s.io、106.75.29.47:10086 三个目录已经存在)。加一个 quay.io 的即可,所有节点(jenkins / node1 / node2 / node3)都要配:
mkdir -p /etc/containerd/certs.d/quay.io
cat > /etc/containerd/certs.d/quay.io/hosts.toml <<'EOF'
server = 'https://quay.io'
[host.'https://quay.m.daocloud.io']
capabilities = ['pull', 'resolve']
override_path = true
EOF
systemctl restart containerd # 注意:重启 containerd 会短暂影响该节点上的容器运行时
两种方式二选一。推荐用 Harbor 方案(§4.4 主线):集群不依赖外网、镜像有留存、后续升级路径统一;containerd 方案适合“先快速验证”的场景。
cd /root/argocd
HARBOR=106.75.29.47:10086
cp manifests/install-v2.14.21.yaml manifests/install-harbor.yaml
# 把 3 个外部镜像全部替换成自己的 Harbor 地址
sed -i \
-e "s#quay.io/argoproj/argocd:#${HARBOR}/infra/argocd:#g" \
-e "s#ghcr.io/dexidp/dex:#${HARBOR}/infra/dex:#g" \
-e "s#image: redis:7.2.11-alpine#image: ${HARBOR}/infra/redis:7.2.11-alpine#g" \
manifests/install-harbor.yaml
# 确认替换干净:这里应该只剩下 infra/ 开头的 3 类镜像,没有 quay.io / ghcr.io / 裸 redis
grep -n 'image:' manifests/install-harbor.yaml | sort -u
# 创建命名空间并安装(CRD 体积大,必须 --server-side)
kubectl create namespace argocd
kubectl apply -n argocd --server-side --force-conflicts -f manifests/install-harbor.yaml
随后给所有工作负载打上「节点固定 + 镜像拉取凭据 + 资源限额」的补丁。把下面这段存成 patch-argocd.sh:
#!/usr/bin/env bash
set -euo pipefail
NS=argocd
NODE=k8s-node3 # 内存余量最大的 worker
SECRET=harbor-secret # 与 ruoyi 命名空间同源,在 argocd 命名空间里重建一份
# 先在 argocd 命名空间创建拉取凭据(从 ruoyi 命名空间复制,避免明文再写一遍)
kubectl get secret harbor-secret -n ruoyi -o yaml \
| sed -e 's/namespace: ruoyi/namespace: argocd/' \
-e '/resourceVersion:/d' -e '/uid:/d' -e '/creationTimestamp:/d' \
-e '/kubectl.kubernetes.io\/last-applied-configuration/d' \
| kubectl apply -f -
# 统一补丁:节点选择 + 拉取凭据
patch_one() {
local kind=$1 name=$2
kubectl -n $NS patch $kind/$name --type=strategic -p "{
\"spec\":{\"template\":{\"spec\":{
\"nodeSelector\":{\"kubernetes.io/hostname\":\"$NODE\"},
\"imagePullSecrets\":[{\"name\":\"$SECRET\"}]
}}}}" || echo "!! $kind/$name 打补丁失败"
}
patch_one statefulset argocd-application-controller
for d in argocd-repo-server argocd-server argocd-redis \
argocd-applicationset-controller argocd-notifications-controller argocd-dex-server; do
patch_one deployment $d
done
# 资源限额(贴近 §3.1 的预算)
kubectl -n $NS set resources statefulset/argocd-application-controller \
--requests=cpu=200m,memory=384Mi --limits=cpu=1000m,memory=768Mi --containers='*'
kubectl -n $NS set resources deployment/argocd-repo-server \
--requests=cpu=100m,memory=192Mi --limits=cpu=500m,memory=512Mi --containers='*'
kubectl -n $NS set resources deployment/argocd-server \
--requests=cpu=50m,memory=128Mi --limits=cpu=300m,memory=256Mi --containers='*'
kubectl -n $NS set resources deployment/argocd-redis \
--requests=cpu=30m,memory=64Mi --limits=cpu=200m,memory=128Mi --containers='*'
kubectl -n $NS set resources deployment/argocd-applicationset-controller \
--requests=cpu=30m,memory=64Mi --limits=cpu=200m,memory=128Mi --containers='*'
kubectl -n $NS set resources deployment/argocd-notifications-controller \
--requests=cpu=20m,memory=64Mi --limits=cpu=100m,memory=128Mi --containers='*'
bash patch-argocd.sh
# 等待就绪
kubectl -n argocd rollout status statefulset/argocd-application-controller --timeout=300s
kubectl -n argocd rollout status deployment/argocd-server --timeout=300s
kubectl -n argocd get pods -o wide
预期 7 个 Pod(server / repo-server / application-controller / redis / applicationset-controller / notifications-controller / dex-server)全部 Running,且都落在 k8s-node3 上。
你现在没有接 SSO,Dex 只用不到就属于纯开销。如果暂时只用 admin 单用户,可以把它去掉以省资源:
kubectl -n argocd scale deployment/argocd-dex-server --replicas=0
kubectl -n argocd patch cm argocd-cm --type=merge -p '{"data":{"dex.config":""}}'
反过来,你这台机器上已经跑着 Casdoor(docker ps 可见),它本身就是 OIDC Provider,后面接 SSO 时可以直接用 Casdoor 替代 Dex(见 §10.5)。
集群已有 nginx IngressClass(NodePort 30080/30443),复用它是成本最低的做法。为了走纯 HTTP 便于起步,先把 Argo CD 自身的 TLS 关掉(由 Nginx 侧终结或直接明文内网访问):
# 让 argocd-server 以 HTTP 提供服务(Ingress 后端用 HTTP)
kubectl -n argocd patch cm argocd-cmd-params-cm --type=merge \
-p '{"data":{"server.insecure":"true"}}'
kubectl -n argocd rollout restart deployment/argocd-server
kubectl -n argocd rollout status deployment/argocd-server
# 顺带把资源跟踪方式改成注解,避免 Argo CD 往你现有的资源上塞 label
kubectl -n argocd patch cm argocd-cm --type=merge \
-p '{"data":{"application.resourceTrackingMethod":"annotation"}}'
# 存为 /root/argocd/manifests/argocd-server-ingress.yaml
cat <<'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server-ingress
namespace: argocd
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTP" # 已设 server.insecure=true
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/proxy-body-size: "0"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
spec:
ingressClassName: nginx
rules:
- host: argocd.wanfeng.com # 参考你现有的 jenkins.wanfeng.com 命名习惯
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 80
EOF
argocd.wanfeng.com 解析到某个节点 IP,并且从集群外访问时流量要走 NodePort 30080。如果只是本机学习,最简单的做法是用 kubectl port-forward 或直接改 /etc/hosts 指到 100.78.249.106(Tailscale 里就能通)。argocd CLI 走的是 gRPC,通过 Ingress 时需要 HTTP/2。学习阶段建议 CLI 用 port-forward 直连,最省事:
kubectl -n argocd port-forward svc/argocd-server 8080:80 --address 0.0.0.0
argocd login 127.0.0.1:8080 --insecure --username admin \
--password "$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d)"
首次登录后立刻改掉初始密码:
argocd account update-password # 或 kubectl -n argocd delete secret argocd-initial-admin-secret
| 检查项 | 命令 | 期望 |
|---|---|---|
| Pod 就绪 | kubectl -n argocd get pods -o wide | 全部 Running,NODE 列都是 k8s-node3 |
| 镜像来源正确 | kubectl -n argocd get pods -o jsonpath='{..image}' | tr ' ' '\n' | sort -u | 只出现 106.75.29.47:10086/infra/ |
| 无外部依赖 | kubectl -n argocd get events --field-selector reason=Failed | 无 ImagePullBackOff / ErrImagePull |
| 若依未受影响 | kubectl get pods -n ruoyi | 7 个 Pod 仍然 Running,重启次数不增加 |
| 节点无压力 | kubectl top nodes | k8s-node3 内存未超过 85% |
| UI 可访问 | 浏览器打开入口 | 能登录并看到 Applications 页面 |
k8s/deployment.yaml
勘察已经证实:线上 ruoyi-gateway-deploy 额外挂了 ruoyi-gateway-config 这个 ConfigMap,而仓库里的清单是注释掉的。如果拿仓库清单建 GitOps 仓库,Argo CD 第一次同步就会把网关的配置挂载删掉,网关会因缺少 application.yml 而异常。
正确做法:从集群导出当前真实状态,作为 GitOps 仓库的第一版,这样首次同步的 diff 为空,切换零风险。
mkdir -p /root/argocd/gitops-bootstrap && cd /root/argocd/gitops-bootstrap
# 导出 ruoyi 命名空间的应用类资源(含 Deployment / Service / Ingress / ConfigMap)
kubectl get deploy,svc,ingress,cm -n ruoyi -o yaml > ruoyi-live-raw.yaml
# 检查:确认 gateway 的 volumeMounts 在导出的内容里(这是关键校验)
grep -n -A3 'volumeMounts' ruoyi-live-raw.yaml | head -20
grep -n 'ruoyi-gateway-config' ruoyi-live-raw.yaml
导出结果里会带一批必须剥掉的运行时字段,否则每次同步都会显示漂移。用下面这个清洗脚本处理(kubectl neat 或手工 jq 都可以,这里给纯 jq 版本,不引入额外插件):
# 存为 clean.py,在 bootstrap 目录执行
cat > clean.py <<'PYEOF'
import subprocess, yaml, sys, re
DROP_META = ["creationTimestamp","resourceVersion","uid","generation",
"selfLink","managedFields","annotations"]
KEEP_ANNOT = {"nginx.ingress.kubernetes.io/proxy-body-size",
"nginx.ingress.kubernetes.io/proxy-read-timeout",
"nginx.ingress.kubernetes.io/backend-protocol"}
docs = list(yaml.safe_load_all(open(sys.argv[1], encoding="utf-8")))
out = []
for d in docs:
if not d or d.get("kind") == "List":
for it in (d or {}).get("items", []): out.append(it)
continue
out.append(d)
def scrub(obj):
if isinstance(obj, dict):
for k in list(obj.keys()):
if k in ("creationTimestamp","resourceVersion","uid","generation",
"selfLink","managedFields"):
obj.pop(k, None)
elif k == "annotations":
a = obj.pop("annotations") or {}
keep = {kk: vv for kk, vv in a.items()
if kk in KEEP_ANNOT or not kk.startswith(("kubectl.kubernetes.io",
"deployment.kubernetes.io",
"argocd.argoproj.io"))}
if keep: obj["annotations"] = keep
else:
scrub(obj[k])
elif isinstance(obj, list):
for i in obj: scrub(i)
return obj
res = [scrub(d) for d in out]
print(yaml.safe_dump_all(res, allow_unicode=True, sort_keys=False, default_flow_style=False))
PYEOF
python3 clean.py ruoyi-live-raw.yaml > ruoyi-live-clean.yaml
grep -n 'ruoyi-gateway-config' ruoyi-live-clean.yaml # 仍然要在
grep -c 'last-applied-configuration' ruoyi-live-clean.yaml # 应为 0
新建一个独立仓库,例如 https://gitee.com/pang_le/ruoyi-gitops.git。结构如下(按服务拆分,便于 Argo CD 用不同 Application 独立同步、独立回滚):
ruoyi-gitops/
├── README.md
├── argocd/ # Argo CD 自身资源(AppProject + Application)
│ ├── project-ruoyi.yaml
│ ├── app-ruoyi-gateway.yaml
│ ├── app-ruoyi-auth.yaml
│ ├── app-ruoyi-modules-system.yaml
│ ├── app-ruoyi-modules-gen.yaml
│ ├── app-ruoyi-modules-job.yaml
│ ├── app-ruoyi-modules-file.yaml
│ ├── app-ruoyi-visual-monitor.yaml
│ └── app-ruoyi-ingress.yaml
└── environments/
└── prod/
├── kustomization.yaml
├── namespace.yaml # 只在首次创建时用,之后 CreateNamespace=false
├── ingress.yaml
├── config/
│ └── ruoyi-gateway-config.yaml
└── services/
├── gateway/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ └── service.yaml
├── auth/…
├── modules-system/…
├── modules-gen/…
├── modules-job/…
├── modules-file/…
└── visual-monitor/…
每个服务的 kustomization.yaml 长这样,Jenkins 后续只改 newTag 这一行:
# environments/prod/services/gateway/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: ruoyi
resources:
- deployment.yaml
- service.yaml
images:
- name: 106.75.29.47:10086/ruoyi-cloud/ruoyi-gateway
newTag: prod-k8s-a3e752d # ← Jenkins 每次只改这一行
deployment.yaml 里镜像就写“不含 tag”的地址,由 Kustomize 注入:
spec:
containers:
- name: ruoyi-gateway
image: 106.75.29.47:10086/ruoyi-cloud/ruoyi-gateway
imagePullPolicy: IfNotPresent # 原来 Always,改成 IfNotPresent 更省拉取
volumeMounts: # ← 从线上导出的,务必保留
- name: gateway-config
mountPath: /home/ruoyi/application.yml
subPath: application.yml
volumes:
- name: gateway-config
configMap:
name: ruoyi-gateway-config
harbor-secret:不要提交进 Git
harbor-secret / harbor-cred 是明文的 dockerconfigjson,进 Git 等于把 Harbor 密码公开。
cd /root/argocd/gitops-bootstrap
# 按 §5.3 的目录把 ruoyi-live-clean.yaml 拆分到各服务目录(拆分是一次性工作)
git init -b main
git add -A && git commit -m "chore: bootstrap GitOps manifests from live cluster (2026-09-23)"
git remote add origin https://gitee.com/pang_le/ruoyi-gitops.git
git push -u origin main
AppProject 把「这个项目能用哪些 Git 仓库、能往哪些命名空间、能创建哪些资源类型」约束住。这是多团队环境下最重要的安全边界:
# argocd/project-ruoyi.yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: ruoyi
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
description: 若依微服务(RuoYi-Cloud)生产环境
sourceRepos:
- https://gitee.com/pang_le/ruoyi-gitops.git
destinations:
- namespace: ruoyi
server: https://kubernetes.default.svc
# 只允许部署这些类型,防止误建 ClusterRole 之类的高危资源
clusterResourceWhitelist: []
namespaceResourceWhitelist:
- group: apps
kind: Deployment
- group: ""
kind: Service
- group: ""
kind: ConfigMap
- group: networking.k8s.io
kind: Ingress
orphanedResources:
warn: true
kubectl apply -f argocd/project-ruoyi.yaml
argocd proj get ruoyi
# argocd/app-ruoyi-gateway.yaml(其余 6 个服务同构,只改名称与路径)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: ruoyi-gateway
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "10" # 网关后于后端服务同步
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: ruoyi
source:
repoURL: https://gitee.com/pang_le/ruoyi-gitops.git
targetRevision: main
path: environments/prod/services/gateway
destination:
server: https://kubernetes.default.svc
namespace: ruoyi
syncPolicy:
automated:
prune: true # Git 里删掉的资源,集群里也删掉
selfHeal: true # 有人手动改集群,自动纠偏回来
allowEmpty: false
syncOptions:
- CreateNamespace=false # ruoyi 已存在
- ApplyOutOfSyncOnly=true # 只 apply 有差异的资源,减少 API 压力
- PruneLast=true
- ServerSideApply=true
retry:
limit: 5
backoff:
duration: 10s
factor: 2
maxDuration: 3m
revisionHistoryLimit: 20 # 保留 20 个历史版本,方便回滚
prune: true 是把双刃剑,第一次上线务必先关
prune 会把「集群里有、Git 里没有」的资源删掉。第一次接管的正确姿势:先 prune: false + 手动同步,确认 diff 只有预期的内容之后,再打开 prune 和 selfHeal。具体节奏见 §8。
若依的服务之间存在依赖(gateway 依赖后端服务注册到 Nacos,auth 依赖 system)。Argo CD 的 sync-wave 建议:
0:ConfigMap(ruoyi-gateway-config)5:auth、modules-system、modules-gen、modules-job、modules-file、visual-monitor10:gateway20:Ingress注意每个服务的 readinessProbe 是保证顺序的必要前提——目前 Deployment 没有配置探针(勘察确认 readinessProbe/livenessProbe 均为空)。没有探针时 Pod 一启动就被视为 Ready,滚动更新会出现“新 Pod 还没起来就被切断流量”的窗口。强烈建议这次一起补上,否则 Argo CD 的同步状态无法反映真实可用性。
readinessProbe:
tcpSocket: { port: 8080 } # 或 /actuator/health
initialDelaySeconds: 40
periodSeconds: 10
failureThreshold: 3
livenessProbe:
tcpSocket: { port: 8080 }
initialDelaySeconds: 90
periodSeconds: 20
Argo CD 默认每 3 分钟轮询一次 Git。要“提交即同步”,两种方式:
| 方式 | 做法 | 适用 |
|---|---|---|
| Jenkins 主动触发(本轮采用) | CI 末尾调用 argocd app sync 并 --wait,把部署结果带回构建日志 | 希望「构建日志里能看到部署成功/失败」,闭环清晰 |
| Gitee Webhook → Argo CD | Argo CD 提供 /api/webhook,在 Gitee 仓库配 Webhook 指向它 | 有人直接改 GitOps 仓库时也能秒级生效 |
| 什么都不配 | 等默认 3 分钟轮询 | 学习阶段最省事 |
读取 webhook 的 secret 并配置(可选):
kubectl -n argocd get secret argocd-secret -o jsonpath='{.data.webhook\.gitee\.secret}' | base64 -d; echo
# Gitee 侧:仓库 → 管理 → WebHooks → URL 填
# http://<argocd入口>/api/webhook
# 密钥:用 argocd-cm 里 webhook.gitee.secret 配置的那个值
Argo CD 靠“清单里的内容变了”来决定是否同步。如果 tag 是 latest 或者复用同一个 tag 反复覆盖,清单内容不变 → Argo CD 认为没有变化 → 不会触发同步;即便强制同步,回滚也无从下手(同一个 tag 指不同的东西)。
换成不可变的 Git 短 SHA 后:tag ↔ 提交一一对应,diff 天然可见,回滚只是把这个 tag 改回上一个值。
// 在 pipeline 顶层 environment 里
VERSION = "${BRANCH_NAME}-${GIT_COMMIT.take(7)}" // 例:prod-k8s-a3e752d
pipeline {
agent any
tools {
maven 'Maven-3.8.9'
jdk 'JDK-17'
}
environment {
APP_NAME = 'ruoyi-gateway'
NAMESPACE = 'ruoyi'
HARBOR = '106.75.29.47:10086'
IMAGE_REPO = "${HARBOR}/ruoyi-cloud/${APP_NAME}"
HARBOR_CRED_ID = 'harbor-credentials'
GITOPS_REPO = 'https://gitee.com/pang_le/ruoyi-gitops.git'
GITOPS_DIR = 'environments/prod/services/gateway'
GITOPS_CRED_ID = 'gitee-gitops-credentials' // 新增凭据,见 §7.3
ARGOCD_APP = 'ruoyi-gateway'
ARGOCD_SERVER = 'argocd-server.argocd.svc.cluster.local:80'
VERSION = "${env.BRANCH_NAME}-${env.GIT_COMMIT.take(7)}"
MAVEN_OPTS = '-Dmaven.compiler.source=17 -Dmaven.compiler.target=17 -Dproject.build.sourceEncoding=UTF-8'
}
options { disableConcurrentBuilds(); timestamps() }
stages {
stage('1. 编译打包 (CI)') {
steps { sh 'mvn clean package -Dmaven.test.skip=true' }
}
stage('2. 构建并推送镜像 (CI)') {
steps {
withCredentials([usernamePassword(credentialsId: env.HARBOR_CRED_ID,
usernameVariable: 'HU', passwordVariable: 'HP')]) {
sh """
echo "\$HP" | docker login ${HARBOR} -u "\$HU" --password-stdin
docker build --build-arg JAR_FILE=target/${APP_NAME}.jar \
-t ${IMAGE_REPO}:${VERSION} .
docker push ${IMAGE_REPO}:${VERSION}
docker rmi ${IMAGE_REPO}:${VERSION} || true
"""
}
}
}
stage('3. 更新 GitOps 仓库镜像版本 (CD 触发点)') {
steps {
withCredentials([usernamePassword(credentialsId: env.GITOPS_CRED_ID,
usernameVariable: 'GU', passwordVariable: 'GP')]) {
sh """
rm -rf .gitops-tmp
git clone -q \
https://\${GU}:\${GP}@gitee.com/pang_le/ruoyi-gitops.git .gitops-tmp
cd .gitops-tmp
git config user.email "jenkins@wanfeng.com"
git config user.name "Jenkins CI"
cd ${GITOPS_DIR}
# 只改 kustomization.yaml 里的 newTag 一行,幂等且无副作用
sed -i -E "s#(newTag:).*#\\1 ${VERSION}#" kustomization.yaml
cd ../..
git add -A
git diff --cached --quiet && echo "镜像版本未变化,跳过提交" || {
git commit -m "ci(${APP_NAME}): bump image to ${VERSION} [skip ci]"
git push origin HEAD:main
}
"""
}
}
}
stage('4. 等待 Argo CD 完成同步 (CD)') {
steps {
withCredentials([string(credentialsId: 'argocd-api-token', variable: 'ARGOCD_TOKEN')]) {
sh """
argocd app sync ${ARGOCD_APP} --grpc-web --insecure \
--server ${ARGOCD_SERVER} --auth-token \$ARGOCD_TOKEN
argocd app wait ${ARGOCD_APP} --grpc-web --insecure \
--server ${ARGOCD_SERVER} --auth-token \$ARGOCD_TOKEN \
--health --timeout 300
"""
}
}
}
}
post {
success { echo "✅ 构建并发布成功:${IMAGE_REPO}:${VERSION}" }
failure {
echo "❌ 构建或发布失败,镜像 ${IMAGE_REPO}:${VERSION}"
// 可选:自动回滚 —— 把 GitOps 仓库回退到上一个提交
// sh "cd .gitops-tmp && git revert --no-edit HEAD && git push origin HEAD:main"
}
}
}
kubectl apply —— Jenkins 不再需要集群写权限。sed -i ... k8s/deployment.yaml —— 不再污染构建工作区,清单文件保持干净。latest —— 只推不可变 SHA tag,避免「谁在跑什么版本」说不清。| 类型 | ID | 内容 | 用途 |
|---|---|---|---|
| Username/Password 凭据 | gitee-gitops-credentials | Gitee 账号 + 私人令牌(或仓库访问令牌) | CI 推送 GitOps 仓库 |
| Secret text | argocd-api-token | Argo CD 生成的 API Token | CI 触发并等待同步 |
Argo CD Token 的生成方式(在装好 CLI 之后):
# 建议为 CI 单独建一个账号,而不是用 admin
argocd account create ci-bot
argocd account generate-token --account ci-bot --expires-in 8760h
同时给它收权限(避免 CI 用超管令牌):
kubectl -n argocd patch cm argocd-rbac-cm --type=merge -p '{"data":{
"policy.csv":"p, role:ci, applications, sync, ruoyi/*, allow\np, role:ci, applications, get, ruoyi/*, allow\ng, ci-bot, role:ci\n"
}}'
你现在的模式是「一个服务一个 Multibranch Job、监听 prod* 分支」。这个模式本身没问题,改造时可以保留,只需注意:
ruoyi-dependencies、ruoyi-common 这类只被依赖的父 POM 仓库,可以继续用现有方式(只需 install 到 Nexus / 本地仓库),不产生镜像也就不参与 Argo CD。ruoyi-modules-gen 或 ruoyi-visual-monitor——它们在最边上,出错影响面最小。核心原则:新旧两套机制并存一段时间,逐个服务切换,任何一步都能立刻退回。
| 步骤 | 动作 | 验证点 | 回退方式 |
|---|---|---|---|
| 0 | 备份:kubectl get all,cm,secret,ingress -n ruoyi -o yaml > backup-$(date +%F).yaml;导出 Jenkins JENKINS_HOME 关键配置 | 备份文件可读、非空 | — |
| 1 | 安装 Argo CD(§4),不动任何业务负载 | 若依 7 个 Pod 无变化 | kubectl delete ns argocd |
| 2 | 建 GitOps 仓库(§5),只做仓库,先不建 Application | 清单里含 gateway 的 volumeMounts | 删仓库 |
| 3 | 建 AppProject + 只建 1 个 Application(选影响最小的服务),automated 先关掉 | UI 上显示 Synced 或 diff 仅为你预期的内容 | kubectl delete app <name> -n argocd(资源不会被删) |
| 4 | 手动 Sync 一次,观察 diff 与结果 | Pod 未重启(或正常滚动一次);接口可用 | argocd app rollback <name> |
| 5 | 打开 selfHeal: true,先不开 prune | 手动改集群里一个 label,看是否被自动纠正 | 改回 selfHeal: false |
| 6 | 改造该服务的 Jenkinsfile(删掉 kubectl 阶段),跑一次完整流水线 | 镜像 tag 变化 → GitOps 仓库出现 commit → Argo CD 自动同步 → Pod 更新 | Jenkins Job 回滚到旧 Jenkinsfile(git revert) |
| 7 | 确认稳定后,打开 prune: true | Git 里删一个无用资源,集群里同步消失 | 关闭 prune |
| 8 | 按同样节奏推广到其余 6 个服务 + Ingress | 逐个服务独立验证 | 逐个回退 |
# A. Argo CD 视角:哪些应用不同步 / 不健康
argocd app list -o wide
argocd app get ruoyi-gateway
argocd app diff ruoyi-gateway # 看具体差异在哪
# B. 集群视角:期望副本 vs 实际副本
kubectl -n ruoyi get deploy -o custom-columns=\
'NAME:.metadata.name,DESIRED:.spec.replicas,READY:.status.readyReplicas,IMAGE:.spec.template.spec.containers[0].image'
# C. 端到端可用性(沿用你已有的测试方式,补一条命令版)
curl -s -o /dev/null -w 'gateway HTTP %{http_code}\n' http://100.78.249.106:31080/
curl -s -o /dev/null -w 'ingress HTTP %{http_code}\n' -H 'Host: 106.75.30.92' http://100.78.249.106:30080/
# D. 漂移检测演练(验证 selfHeal 确实在工作)
kubectl -n ruoyi label deploy ruoyi-gateway-deploy chaos-test=yes
sleep 60
kubectl -n ruoyi get deploy ruoyi-gateway-deploy -o jsonpath='{.metadata.labels}'; echo
# 期望:chaos-test 标签被 Argo CD 自动清除
# E. 回滚演练
argocd app history ruoyi-gateway
argocd app rollback ruoyi-gateway <REVISION_ID>
Synced 且 Healthy。kubectl 介入。| 层级 | 手段 | 耗时 | 适用 |
|---|---|---|---|
| 一:应用级 | argocd app rollback <app> <revision> | 秒级 | 发错版本、需要立刻回退。Argo CD 用 revisionHistoryLimit 保留历史 |
| 二:Git 级 | 在 GitOps 仓库 git revert <commit> 并推送 | 1~2 分钟 | 需要留审计记录、正规回滚 |
| 三:K8s 原生 | kubectl -n ruoyi rollout undo deploy/<name> | 秒级 | Argo CD 不可用时的应急通道(注意:selfHeal 开着时会被纠回,需先暂停自动同步) |
argocd app set <app> --sync-policy none(或 UI 上 Disable Auto-Sync)。否则你手工操作会被 selfHeal 反复覆盖。| 现象 | 可能原因 | 处理 |
|---|---|---|
Application 一直 OutOfSync,diff 里有 creationTimestamp / resourceVersion | 导出清单时没清洗运行时字段 | 用 §5.2 的脚本重新清洗 |
diff 只有 status 或 metadata.annotations 差异 | 被其他控制器(如 kubectl edit)写入 | 用 ignoreDifferences 忽略,或接受 Argo CD 纠偏 |
| repo-server 报克隆失败 | Gitee 网络波动 / 凭据过期 | kubectl -n argocd logs deploy/argocd-repo-server;公开仓库不需要凭据 |
Pod 卡在 ImagePullBackOff | Harbor 不可达 / harbor-secret 过期 | kubectl -n ruoyi describe pod ...;确认 106.75.29.47:10086 连通 |
| Argo CD 自身被 OOMKilled | repo-server 处理大仓库时内存飙升 | 调大 repo-server limits;给 repo-server 加 --parallelismlimit |
| 同步成功但服务不可用 | 缺少 readinessProbe,Pod 未真正就绪就被判 Healthy | 补探针(§6.3) |
| NetworkPolicy 导致 UI 打不开 | Calico 拦截 | kubectl -n argocd get netpol;Argo CD 默认策略是宽松的,若被改过需检查 |
argocd 命名空间,定期 kubectl -n argocd get cm,secret -o yaml > argocd-backup-$(date +%F).yaml。Application/AppProject 本身也建议纳入 Git(argocd/ 目录即可)。argocd-metrics / argocd-server-metrics Service 已存在)。可以接到你已有的 ELK 或后续上 Prometheus。核心告警指标:argocd_app_info{sync_status!="Synced"}、argocd_app_info{health_status!="Healthy"}。ruoyi-cloud 项目配保留策略(比如保留最近 30 个 tag),否则 SHA tag 会快速堆积。当前 / 已用 58%(57G/99G)。| # | 风险 / 事项 | 影响 | 应对 |
|---|---|---|---|
| 1 | Argo CD 2.14 已不在补丁窗口 | 中 | 因集群 v1.28 无法上 3.x。方案:本轮用 2.14.21,内网使用 + RBAC 收敛;后续安排集群升级到 v1.33+ 后切 3.5.x |
| 2 | k8s-node2 内存已用 93% | 高 | Argo CD 必须用 nodeSelector 钉在 k8s-node3,并设 requests 上限。建议同时排查 node2 上是什么占了内存(6 个 Pod,含 4 个若依服务) |
| 3 | 首次同步可能删掉 gateway 的 ConfigMap 挂载 | 高 | 必须用「从线上导出」的第一版清单(§5.1/§5.2),并核对 ruoyi-gateway-config 在导出内容中 |
| 4 | prune: true 误删资源 |
高 | 切换期先关闭(§8.1 步骤 5→7),确认应用清单完整后再打开 |
| 5 | 无 readinessProbe,健康状态失真 | 中 | 本次一并补齐探针,否则 Argo CD 的 Healthy 判断和滚动更新都不可靠 |
| 6 | Jenkins 与控制平面同机,且磁盘已用 58% | 中 | Argo CD 装好后 Jenkins 负载会下降;但仍要给 / 留余量,Harbor 保留策略要配 |
| 7 | 集群无 StorageClass / PV | 中 | Argo CD 本身不需要,但若后续要 Argo CD Image Updater、或把 Nacos/MySQL 搬进集群,必须先解决存储 |
| 8 | Gitee 作为唯一 GitOps 源 | 中 | Gitee 抖动会阻塞发布。已确认 gitee.com 可达、仓库为公开可读;建议给 GitOps 仓库开保护分支 + 单独令牌,并留意 85.137.247.103:8765 的 GitLab 当前不可达,不要作为依赖 |
| 9 | NetworkPolicy 与 Calico | 低 | install.yaml 自带 7 条 NetworkPolicy,默认宽松;若后续收紧网络策略需回归验证 UI/CLI 可用性 |
| 10 | ingress-nginx 控制器为 v1.0.0 | 低 | 较老,但已稳定运行 28 天且能承载现有 Ingress。给 Argo CD 加 Ingress 前先在测试路径验证;如遇 HTTP/2 问题改用 CLI port-forward |
| 11 | Harbor 明文密码散布 | 中 | Jenkinsfile 注释里、containerd hosts.toml 里都存在 admin/@Pl000000。建议改用 Harbor robot account 专用凭据,并撤销 admin 的长期使用 |
imagePullPolicy现在的 Deployment 是 Always。换成不可变 SHA tag 后,Always 只会带来额外的一次 registry 查询而不会有正确性收益,建议改 IfNotPresent,能明显减少 Harbor 压力和 Pod 启动时间。
7 个 Service 全是 NodePort,其中 gateway 同时被 NodePort 31080 和 Ingress 暴露。这属于历史遗留。本次改造不建议顺手改(会变更访问入口),但可以在 GitOps 稳定后单独排期收敛为 ClusterIP + Ingress。
Jenkins numExecutors=2,而每个 Job 都设了 disableConcurrentBuilds()。7 个服务同时提交时会有排队。改造后每条流水线多了一个「等 Argo CD 同步」的阶段(最长 5 分钟),需要评估构建队列是否会变长。如果出现堆积,可以把 --timeout 调小到 180s,或把「等待同步」改成只提交不等待(由 Argo CD 轮询/webhook 兜底)。
ruoyi-ui 的三种后续路径Dockerfile 构建 ruoyi-ui 镜像,Deployment + ConfigMap(ruoyi-ui-nginx-config 仓库里已有)+ Ingress,然后交给 Argo CD。你机器上已经跑着 casbin/casdoor:latest,它本身就是 OIDC Provider。这样可以不装 Dex,直接在 argocd-cm 里配 oidc.config,用 Casdoor 做统一登录,和 /opt/maxkey、/opt/CASdoor 的现有 IAM 体系统一。属于本轮之后的增量优化。
如果你希望「CI 只管推镜像,GitOps 仓库的 tag 由 Argo CD 自动改写」,可以引入 Image Updater。但它需要写回 Git,并且要额外访问 Harbor 的 registry API。本轮不推荐——由 Jenkins 写 tag 更直观、可控性更强,也更符合「CI 管构建、CD 管部署」这个你想学的心智模型。
# 1 准备清单与 CLI
mkdir -p /root/argocd/manifests && cd /root/argocd
curl -fL --retry 3 -o manifests/install-v2.14.21.yaml \
https://gh-proxy.com/https://raw.githubusercontent.com/argoproj/argo-cd/v2.14.21/manifests/install.yaml
curl -fL --retry 3 -o /usr/local/bin/argocd \
https://files.m.daocloud.io/github.com/argoproj/argo-cd/releases/download/v2.14.21/argocd-linux-amd64
chmod +x /usr/local/bin/argocd
# 2 镜像入库(Harbor 项目 infra)
HARBOR=106.75.29.47:10086
docker login ${HARBOR} -u admin -p '@Pl000000'
docker pull quay.m.daocloud.io/argoproj/argocd:v2.14.21 && \
docker tag quay.m.daocloud.io/argoproj/argocd:v2.14.21 ${HARBOR}/infra/argocd:v2.14.21 && \
docker push ${HARBOR}/infra/argocd:v2.14.21
docker pull ghcr.m.daocloud.io/dexidp/dex:v2.41.1 && \
docker tag ghcr.m.daocloud.io/dexidp/dex:v2.41.1 ${HARBOR}/infra/dex:v2.41.1 && \
docker push ${HARBOR}/infra/dex:v2.41.1
docker pull m.daocloud.io/docker.io/library/redis:7.2.11-alpine && \
docker tag m.daocloud.io/docker.io/library/redis:7.2.11-alpine ${HARBOR}/infra/redis:7.2.11-alpine && \
docker push ${HARBOR}/infra/redis:7.2.11-alpine
# 3 改镜像并安装
sed -i -e "s#quay.io/argoproj/argocd:#${HARBOR}/infra/argocd:#g" \
-e "s#ghcr.io/dexidp/dex:#${HARBOR}/infra/dex:#g" \
-e "s#image: redis:7.2.11-alpine#image: ${HARBOR}/infra/redis:7.2.11-alpine#g" \
manifests/install-v2.14.21.yaml
kubectl create namespace argocd
kubectl apply -n argocd --server-side --force-conflicts -f manifests/install-v2.14.21.yaml
# 4 打补丁(节点/凭据/资源/HTTP)
bash patch-argocd.sh
kubectl -n argocd patch cm argocd-cmd-params-cm --type=merge -p '{"data":{"server.insecure":"true"}}'
kubectl -n argocd patch cm argocd-cm --type=merge -p '{"data":{"application.resourceTrackingMethod":"annotation"}}'
kubectl -n argocd rollout restart deployment/argocd-server
# 5 登录
kubectl -n argocd port-forward svc/argocd-server 8080:80 --address 0.0.0.0 &
argocd login 127.0.0.1:8080 --insecure --username admin \
--password "$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d)"
argocd app list -o wide # 所有应用状态一览
argocd app get <app> # 单应用详情(含资源树)
argocd app diff <app> # 差异明细
argocd app sync <app> --prune # 手动同步
argocd app history <app> # 历史版本
argocd app rollback <app> <rev> # 回滚
argocd app set <app> --sync-policy none # 关自动同步(应急处置前必做)
argocd app set <app> --sync-policy automated --self-heal --auto-prune
kubectl -n argocd logs -f statefulset/argocd-application-controller # 控制器日志
kubectl -n argocd logs -f deploy/argocd-repo-server # 渲染/拉取 Git 的问题看这里
kubectl -n argocd get events --sort-by=.lastTimestamp | tail -30
| 用途 | 地址 | 结果 |
|---|---|---|
| Argo CD 镜像 | quay.m.daocloud.io/argoproj/argocd:v2.14.21 | 200 |
| Argo CD 镜像(备) | quay.nju.edu.cn/argoproj/argocd:v2.14.21 | 200 |
| Dex 镜像 | ghcr.m.daocloud.io/dexidp/dex:v2.41.1 | 200 |
| Redis 镜像 | m.daocloud.io/docker.io/library/redis:7.2.11-alpine | 200 |
| install.yaml | https://gh-proxy.com/https://raw.githubusercontent.com/argoproj/argo-cd/v2.14.21/manifests/install.yaml | 200 / 1425427 B |
| install.yaml(备) | https://ghfast.top/https://raw.githubusercontent.com/… | 200 / 1425427 B |
| install.yaml(备) | https://cdn.jsdelivr.net/gh/argoproj/argo-cd@v2.14.21/manifests/install.yaml | 200 / 1425427 B |
| argocd CLI | https://files.m.daocloud.io/github.com/argoproj/argo-cd/releases/download/v2.14.21/argocd-linux-amd64 | 200 / 14868892 B |
| Helm 仓库 | https://argoproj.github.io/argo-helm | 200 |
| Helm 仓库(备) | https://ben-wangz.github.io/helm-chart-mirror/charts | 200 |