第一部分:环境准备

1.1 集群架构说明

我的实验环境是一个 1 Master + 2 Worker 的 Kubernetes 集群:

节点

角色

说明

master-01

Control Plane

运行 API Server、Scheduler、Controller Manager 等控制面组件

worker-01

Worker Node

运行业务负载

worker-02

Worker Node

运行业务负载,并作为所有持久化数据的存储节点

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 pv

2.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 containerd

2.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.4

3.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/data

4.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_data

jenkins.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 安装所需插件

在插件列表中搜索以下插件:

插件名称

用途

搜索关键词

Kubernetes

允许 Jenkins 动态创建 Pod 作为构建 Agent

kubernetes

Gitea

与 Gitea Git 仓库集成,支持 Webhook 自动触发

gitea

Pipeline

提供 Pipeline 流水线功能

pipeline

Multibranch Pipeline Inline Definition

支持在多分支 Pipeline 中内联定义 Jenkinsfile

multibranch pipeline inline

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.tar

7.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
EOF

7.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各处查看同步信息。