第一部分:镜像版本控制与自动更新流程图

第二部分:制品管理

1. 基于 Harbor 的 Docker Hub 代理缓存

1.1 任务目标

配置 Harbor 作为 Docker Hub 的代理缓存,实现:

  • 首次拉取从 Docker Hub 同步

  • 后续拉取从 Harbor 本地缓存提供

  • 避免频繁访问外网,加速镜像拉取

1.2 操作步骤

完整流量路径(拉取镜像时)

1. Harbor(192.168.1xx.100) 请求 alpine 镜像
                │
2. 发现本地无缓存,需要访问 Docker Hub
                │
3. 读取 http_proxy=http://172.16.11.1:7890
                │
4. 请求 → 代理服务器(172.16.11.1:7890)
                │
5. 代理服务器 → FW2(172.16.11.211) → FW1(172.16.11.254) → 互联网 → Docker Hub(两层防火墙架构)
                │
6. 镜像数据原路返回:Docker Hub → FW1 → FW2 → 代理服务器 → Harbor
                │
7. Harbor 缓存镜像并返回给客户端

Harbor 服务端启动

编辑 harbor.yml,添加上面的代理配置

sudo nano harbor.yml

找到proxy模块,配置参考如下:

proxy:
  http_proxy: http://172.16.11.1:7890
  https_proxy: http://172.16.11.1:7890
  no_proxy: localhost,192.168.106.102,127.0.0.1,172.16.11.211,core,jobservice,registry,harbor-db,redis,trivy,trivy-adapter
  components:
    - core
    - jobservice
    - trivy

重新生成 docker-compose.yml 并启用 Trivy,重启harbor

sudo ./prepare --with-trivy && sudo docker compose down && sudo docker compose up -d

启动成功后,进入harbor网页

为什么代理写在 harbor.yml 而非 docker-compose.yml?

Harbor 采用配置源与运行时分离的设计模式:

  • harbor.yml:管理员手动维护的唯一配置源

  • docker-compose.yml:由 ./prepare 脚本根据 harbor.yml 自动生成的运行时配置文件,禁止手动修改

每次执行 ./prepare 时,脚本会读取 harbor.yml,然后将代理配置转换为各容器的环境变量,写入 docker-compose.yml。

若手动修改 docker-compose.yml,下次执行 ./prepare 时修改将被完全覆盖。

此外,Harbor 包含 core、jobservice、registry、trivy 等多个组件,在 harbor.yml 中统一声明代理地址,./prepare 会自动将代理环境变量注入到所有需要访问外网的组件中,实现一处配置、多处生效,避免遗漏或配置不一致。

如果直接在 docker-compose.yml 中手动配置,不仅工作量大,而且极易在 Harbor 升级或重新配置时引发配置漂移问题。

为什么 no_proxy 要包含 172.16.11.211?

FW2 是同一网络架构内的防火墙管理地址,Harbor 访问它不需要经过代理服务器,写在 no_proxy 中可以:

  • 避免请求环路

  • 降低延迟

  • 防止代理服务器无法正确处理内部管理地址的请求

Harbor(192.168.1xx.100) → 访问 172.16.11.211(FW2)
                                    │
                                    ├── 如果走代理:请求 → 172.16.11.1 → FW2 环路/超时
                                    │
                                    └── 如果不走代理(no_proxy):直连 FW2 正常

Harbor 服务端配置

创建代理项目

进入 Harbor UI → 项目 → 新建项目

配置参考如下:

点击“确定”完成创建

创建 Docker Hub 代理目标

进入 Harbor UI → 系统管理 → 复制管理 → 新建目标

配置参考如下:

  • 提供者:Docker Hub

  • 目标名:docker-proxy

  • 目标 URL:https://hub.docker.com

  • 访问 ID:Docker Hub 用户名

  • 访问密码:Personal Access Token

在Docker Hub上创建Personal Access Token

点击“测试连接”,提示“测试连接成功”后保存

验证代理缓存功能

客户端拉取镜像

进入 Harbor UI → 项目 docker-proxy → 镜像仓库,应看到:

2. Harbor Trivy 扫描器离线部署与配置

2.1 任务目标

在内网离线环境下,手动导入 Trivy 漏洞数据库,并配置 Harbor 中的 Trivy 扫描器以离线模式运行,使其在不依赖外网访问的情况下,能够对 Harbor 中的容器镜像进行完整的漏洞扫描,确保镜像安全合规。

2.2 操作步骤

安装 oras 工具

oras 是一个 OCI 注册表客户端工具,用于从 GitHub Container Registry 拉取 Trivy 数据库构件。

# 下载 oras 二进制文件
curl -LO "https://github.com/oras-project/oras/releases/download/v1.2.0/oras_1.2.0_linux_amd64.tar.gz"

# 解压文件
tar -xvf oras_1.2.0_linux_amd64.tar.gz

# 移动到系统路径,便于全局调用
sudo mv oras /usr/local/bin/

# 验证安装
oras version

oras 工具在这里的作用是从 OCI 仓库拉取 Trivy 数据库构件。虽然也可以使用 docker pull,但 oras 更适合拉取非容器镜像的 OCI 构件(如数据库压缩包),且失败率更低。

拉取 Trivy 数据库

# 拉取 Trivy V2 数据库镜像(OCI 构件)
oras pull ghcr.io/aquasecurity/trivy-db:2

该命令会下载一个名为 db.tar.gz 的文件,这是 Trivy 的核心漏洞数据库,包含 CVE 漏洞信息、受影响组件及修复版本等数据。下载过程可能需要 20-30 分钟,取决于网络状况。

创建目录、解压并授权

# 创建目录 → 解压数据库 → 授权(UID 10000 为 Trivy 容器内用户)
sudo mkdir -p /data/trivy-adapter/trivy/db && \
sudo tar -zxvf db.tar.gz -C /data/trivy-adapter/trivy/db/ && \
sudo chown -R 10000:10000 /data/trivy-adapter/trivy/db/

/data 是 Harbor 默认数据目录;chown 10000 确保容器有读取权限,否则扫描失败。

修改 harbor.yml 配置并生效

trivy:
  skip_update: true      # 跳过在线更新
  offline_scan: true     # 强制离线扫描

两个参数必须同时开启。skip_update 禁止下载数据库,offline_scan 禁止所有网络请求,实现完全离线。

sudo ./prepare --with-trivy && sudo docker compose down && sudo docker compose up -d        #重启后新配置生效

查看日志

# 查看日志,确认无下载报错
sudo docker logs -f trivy-adapter
为什么会出现 "No such file or directory"?
  1. Harbor 支持自定义 TLS 证书:如果用户需要验证私有镜像仓库的证书,可以将 CA 证书放在 /etc/harbor/ssl 目录下

  2. 你没有配置自定义证书:所以该目录不存在,find 命令找不到它是预期行为

  3. 脚本的设计逻辑:Trivy 启动脚本会尝试查找该目录,找不到就跳过,继续执行后续操作

2.3 验证

审查服务可以对所有镜像进行扫描

在项目内部可以指定镜像进行扫描

查看镜像漏洞

双击漏洞名,查看漏洞详细信息

3. Harbor自动清理

3.1 任务目标

在 Harbor 中配置自动垃圾回收机制,定期清理无用的镜像层和未打 tag 的 artifacts,释放存储空间;同时配置不可变 Tag 规则,防止重要镜像被意外覆盖或删除,保障生产环境镜像的稳定性和可追溯性。

3.2 操作步骤

配置定时垃圾回收

操作路径:Harbor UI → 系统管理 → 清理服务 → 垃圾清理

什么是垃圾回收

Harbor 的垃圾回收功能用于清理不再被任何镜像引用的 Blob 层。当镜像被删除或覆盖时,底层的存储层可能仍然占用磁盘空间,垃圾回收可以释放这些空间。

不可变 Tag 规则配置

不可变 Tag 规则用于禁止对特定名称的镜像进行覆盖或删除操作,防止生产环境中正在使用的镜像被意外修改。

操作路径:项目 → 选择项目 → 策略 → 不可变 Tag

配置不可变 Tag 规则后,主要实现以下三个效果:

  • 禁止覆盖:任何人都无法向已被保护的 Tag 推送新镜像,防止生产版本被意外替换。

  • 禁止删除:无法删除带有不可变 Tag 的镜像,确保重要版本永久可追溯。

  • 无需额外权限控制:规则生效后自动拦截违规操作,不依赖用户权限设置,即使项目管理员也无法绕过。

第三部分:版本策略

1. 镜像更新器

1.1 任务目标

在 Kubernetes 集群中部署 ArgoCD Image Updater,使其能够自动检测 Harbor 私有仓库中的镜像更新,并自动同步到 ArgoCD 管理的应用中,实现镜像版本的自动化更新。

1.2 工作流程

Harbor 新镜像推送 → Image Updater 检测到变化 → 更新 ArgoCD 应用的参数 → ArgoCD 自动同步 → Pod 滚动更新

1.3 部署步骤

安装 ArgoCD Image Updater

kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj-labs/argocd-image-updater/stable/config/install.yaml

执行结果:会创建 CRD、ServiceAccount、RBAC 权限、ConfigMap、Deployment 等一系列资源。

最后 Pod 正常运行:

kubectl get pod -n argocd -l app.kubernetes.io/name=argocd-image-updater
# 状态应为 Running

创建 Harbor 仓库访问凭据

Image Updater 需要登录 Harbor 才能检查镜像。创建一个 Secret 存储用户名和密码:

kubectl create secret generic harbor-http-creds \
  --from-literal=creds=harbor用户名:密码 \
  -n argocd

#验证
kubectl get secret harbor-http-creds -n argocd

创建 Git 仓库访问凭据

Image Updater 更新应用配置后需要将变更提交回 Git 仓库,因此需要 Git 仓库的访问凭据:

# 创建 Secret 存储 Git 仓库信息
k3s kubectl create secret generic the-first-repo-credss \
  -n argocd \
  --from-literal=url="http://192.168.106.103:3000/momo/demo.git" \
  --from-literal=username="momo" \
  --from-literal=password="gitea token"

# 打上标签,标记为 ArgoCD 仓库类型的 Secret
k3s kubectl label secret the-first-repo-credss \
  -n argocd \
  "argocd.argoproj.io/secret-type=repository"

#验证标签
kubectl get secret -n argocd -l argocd.argoproj.io/secret-type=repository

Image Updater 会将检测到的新镜像版本写入 Git 仓库中的 YAML 文件,然后 ArgoCD 从 Git 拉取变更并同步到集群。

如何获取gitea的token?

配置镜像仓库(ConfigMap)

创建一个 ConfigMap,告诉 Image Updater 如何连接到 Harbor:

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: 192.168.106.102
      api_url: http://192.168.106.102
      insecure: true
      credentials: secret:argocd/harbor-http-creds#creds
EOF

#出现这个说明更改成功
configmap/argocd-image-updater-config configured

#查看配置
kubectl get cm argocd-image-updater-config -n argocd -o yaml

对应的redis.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: 192.168.106.102/nginx/redis:7.0
        ports:
        - containerPort: 30081

创建 Kustomization 配置文件

为了让 Image Updater 能够自动更新 Kustomize 管理的应用,需要在 Git 仓库中创建 kustomization.yaml 文件,并配置 Image Updater 与之协同工作。

进入 Git 仓库对应的应用目录,创建 kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - redis.yaml

Kustomization 文件告诉 ArgoCD 这是一个 Kustomize 应用,resources 中列出的 YAML 文件会被组合渲染后部署到集群。

创建 ImageUpdater 资源

基于 Image Updater v1.1.1 版本,官方已废弃旧版的注解配置方式,强制采用 CRD 进行配置。

在创建 ImageUpdater 自定义资源之前,需要先在 ArgoCD 中有一个已存在的应用。以下是创建应用的关键注意事项:

  • 不能使用 library 默认项目

Harbor 的 library 项目比较特殊,Image Updater 在处理时可能会默认剪断映射关系,导致更新失败

  • 仓库中必须有对应的 YAML 文件

Git 仓库中需要包含部署应用所需的 Kubernetes 资源文件(如 redis.yaml、deployment.yaml)

  • 镜像路径必须一致

应用 YAML 中的镜像地址必须与后续 ImageUpdater 中配置的 imageName 完全对齐

在内网环境中,由于可能存在未知的 API 解析 bug,Image Updater 在直接解析内网 IP 镜像路径时,可能会报以下错误:

could not find an image-name in application

解决方案:通过 CRD 显式映射别名(alias)和镜像路径(imageName),强制告诉 Image Updater 镜像的实际位置,绕过自动解析逻辑

创建 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: "my-redis-app"      # 匹配 ArgoCD 应用名称
      images:
        - alias: "my-redis"            # 定义别名,规避内网 IP 路径的转义歧义
          imageName: "192.168.106.102/nginx/redis"   # 必须与 redis.yaml 镜像路径完全对齐
          commonUpdateSettings:
            updateStrategy: "semver"   # 开启 SemVer 语义化版本演进算法
  writeBackConfig:
    method: "git"                      # 激活 Git Write-back 自动回写机制
    gitConfig: {}                      # 自动索引关联的仓库凭证,简化配置
EOF

配置流程图:

┌─────────────────────────────────────────────────────────────────┐
│                    ImageUpdater CRD 配置流程                     │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ① 在 ArgoCD 中创建应用(不能使用 library 项目)                   │
│                    │                                            │
│                    ▼                                            │
│  ② 确保 Git 仓库中有对应的 YAML 文件(如 redis.yaml)              │
│                    │                                            │
│                    ▼                                            │
│  ③ 创建 ImageUpdater CRD 资源                                    │
│     - 设置 alias 别名                                            │
│     - 显式指定 imageName                                         │
│     - 配置 updateStrategy                                        │
│                    │                                            │
│                    ▼                                            │
│  ④ 验证 ImageUpdater 正常运行,无报错                             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

验证 ImageUpdater 资源

# 查看 argocd 命名空间下的 ImageUpdater 资源
kubectl get imageupdater -n argocd

# 查看指定 ImageUpdater 资源的详细 YAML 配置
kubectl get imageupdater redis-repo-updater -n argocd -o yaml

切换渲染引擎

Argo CD 的默认逻辑是 “只读”。即便 Image Updater 发现了新镜像并尝试更新,如果应用处于普通的 YAML 模式,Argo CD 不会去理会那些需要动态生成的参数变更。

核心问题:

  • 纯 YAML 模式下,Argo CD 只能静态读取 Git 仓库中的文件

  • Image Updater 检测到新镜像后,需要修改应用配置并写回 Git

  • 切换为 Kustomize 引擎后,Argo CD 才能正确识别和处理这些动态更新

如果 ArgoCD 应用原本不是 Kustomize 类型,需要执行 patch 命令进行转换:

kubectl patch app my-first-gitops -n argocd --type merge -p '{"spec":{"source":{"path":".", "kustomize":{}}}}'

该命令将 ArgoCD 应用的源类型设置为 Kustomize,指定仓库根目录(.)为 Kustomization 文件所在路径。

重启 ArgoCD 核心组件

为了清理陈旧缓存并重新初始化渲染逻辑,需要重启以下组件:

# 重启 repo-server:负责拉取 Git 源码并进行模板渲染
kubectl rollout restart deployment argocd-repo-server -n argocd

# 重启控制器:负责重新计算应用状态并执行同步
kubectl rollout restart statefulset argocd-application-controller -n argocd

重启 Image Updater 控制器

# 强制删除旧 Pod,Kubernetes 会自动重建
kubectl delete pod -l app.kubernetes.io/name=argocd-image-updater -n argocd

删除 Pod 后,Deployment 控制器会立即创建新的 Pod,新 Pod 会加载最新的 ConfigMap 配置(如 Harbor 仓库凭据和镜像规则)。

1.4 验证

手动推送新版本号的镜像

等待一段时间,查看argo里的APP状态和gitea里的.argocd-source-my-redis-app.yaml