DevOps全链路实战 | 第 7 天:Harbor 镜像版本管理与 Tag 策略配置

第 7/18 天

引言

在前面六天的实战中,我们完成了全链路架构设计、K8s 集群部署、Harbor 私有镜像仓库搭建、GitLab CE 自托管部署、GitLab Runner 配置以及 GitLab CI 流水线搭建。流水线已经能通过 Kaniko 将构建好的容器镜像推送到 Harbor 仓库。然而,随着团队规模扩大和发布频率提升,一个关键问题浮出水面:镜像 Tag 该怎么管?

如果每次构建都用 latest,线上回滚时无从追溯;如果每个开发分支都随意打 Tag,仓库很快会变成垃圾场。今天我们将深入 Harbor 镜像版本管理,设计一套科学的 Tag 策略,配置 Harbor 的 Tag 保留规则(Tag Retention)与垃圾回收机制,确保镜像仓库在长期运行中保持整洁、可追溯、可回滚。

K8s

核心概念:镜像 Tag 的本质与挑战

Docker 镜像 Tag 不是版本号

很多团队误把 Docker Tag 当作语义化版本号(SemVer)使用,但实际上 Tag 只是一个指向镜像 manifest 的可变标签。同一个 Tag(如 v1.2.0)可以被覆盖推送多次,底层 digest(SHA256)才是镜像的真正唯一标识。

💻 代码示例

# 查看镜像的 digest(不可变唯一标识)

docker inspect –format='{{index .RepoDigests 0}}' harbor.stellardata.top/app/frontend:v1.2.0

# 输出示例: harbor.stellardata.top/app/frontend@sha256:a1b2c3d4e5…

 

# 同一个 Tag 推送两次后 digest 不同

docker pull harbor.stellardata.top/app/frontend:v1.2.0

docker image inspect –format='{{.Id}}' harbor.stellardata.top/app/frontend:v1.2.0

# 每次重新构建后 .Id 会变化

常见 Tag 策略对比

策略 示例 优点 缺点
latest app:latest 简单 不可追溯,无法回滚
语义版本 app:1.2.0 语义清晰 需手动维护版本号
Git Commit app:a1b2c3d 自动化,唯一 不可读
构建号 app:build-42 自动递增 跨分支可能冲突
混合策略 app:1.2.0-a1b2c3d 兼具语义与唯一性 Tag 较长

在 CI/CD 流水线中,最佳实践是采用混合策略:主版本号 + Git Commit 短哈希 + 构建时间戳,既保证可读性又保证唯一性,同时配合 Harbor 的不可变 Tag 功能防止覆盖。

实战步骤:Tag 策略设计与 Harbor 配置

第一步:设计生产级 Tag 命名规范

我们定义一套适用于 GitLab CI 的 Tag 生成方案。在 .gitlab-ci.yml 中动态生成镜像 Tag:

💻 代码示例

# .gitlab-ci.yml 片段 — 构建阶段的 Tag 生成逻辑

variables:

HARBOR_REGISTRY: "harbor.stellardata.top"

IMAGE_NAME: "app/frontend"

# 短 commit hash (前 8 位)

SHORT_SHA: "${CI_COMMIT_SHORT_SHA}"

# 构建日期 (用于时间排序)

BUILD_DATE: "${CI_PIPELINE_CREATED_AT}"

 

build_image:

stage: build

image:

name: gcr.io/kaniko-project/executor:v1.23.0-debug

entrypoint: [""]

script:

# 生成多维度 Tag: 语义版本-commit hash

– TAG_VERSION="${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHORT_SHA}"

– /kaniko/executor

–context="${CI_PROJECT_DIR}"

–dockerfile="${CI_PROJECT_DIR}/Dockerfile"

–destination="${HARBOR_REGISTRY}/${IMAGE_NAME}:${TAG_VERSION}"

–destination="${HARBOR_REGISTRY}/${IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}"

–destination="${HARBOR_REGISTRY}/${IMAGE_NAME}:latest"

–build-arg="BUILD_DATE=${BUILD_DATE}"

–build-arg="GIT_COMMIT=${CI_COMMIT_SHA}"

–cache=true

–cache-repo="${HARBOR_REGISTRY}/${IMAGE_NAME}:cache"

rules:

– if: $CI_COMMIT_BRANCH == "main"

variables:

TAG_VERSION: "prod-${CI_COMMIT_SHORT_SHA}"

– if: $CI_COMMIT_BRANCH == "develop"

variables:

TAG_VERSION: "dev-${CI_COMMIT_SHORT_SHA}"

上面的配置同时推送三个 Tag:完整的 分支-commit Tag 用于精确引用,纯 commit hash Tag 用于快速查找,latest 标记最新构建。这样无论是 Argo CD 做 GitOps 部署还是人工排查问题,都能快速定位到具体镜像。

第二步:在 Harbor 中启用 Tag 不可变(Immutable Tag)

Harbor 企业版(以及开源版 v2.4+)支持将特定 Tag 标记为不可变,防止构建流水线覆盖已发布的镜像版本。这在生产环境中至关重要。

💻 代码示例

# 通过 Harbor REST API 设置 Tag 不可变规则

# 规则:匹配 prod- 前缀的 Tag 设为不可变

curl -X POST

"https://harbor.stellardata.top/api/v2.0/projects/app/repositories/frontend/immutablerules"

-H "Authorization: Basic $(echo -n 'admin:Harbor12345' | base64)"

-H "Content-Type: application/json"

-d '{

"tag_matching": "prod-*",

"repo_matching": "**",

"tag_excluding": "",

"repo_excluding": "",

"disabled": false

}'

 

# 验证不可变规则已生效

curl -s "https://harbor.stellardata.top/api/v2.0/projects/app/repositories/frontend/immutablerules"

-H "Authorization: Basic $(echo -n 'admin:Harbor12345' | base64)" | python3 -m json.tool

设置后,任何尝试用相同 Tag 覆盖推送 prod-* 前缀镜像的操作都会被 Harbor 拒绝,返回 HTTP 409 Conflict。这从仓库层面杜绝了”Tag 漂移”问题。

第三步:配置 Tag 保留策略(Tag Retention)

随着持续构建,镜像数量会无限增长。如果不做清理,磁盘空间很快耗尽。Harbor 的 Tag Retention 功能可以按规则自动保留若干版本的 Tag,其余的自动删除。

💻 代码示例

#!/usr/bin/env python3

"""

Harbor Tag Retention 策略配置脚本

为指定项目创建保留规则:

– prod-* Tag: 保留最近 30 个

– dev-* Tag: 保留最近 10 个

– 其他: 保留最近 5 个

"""

import requests

import base64

import json

 

HARBOR_URL = "https://harbor.stellardata.top"

USERNAME = "admin"

PASSWORD = "Harbor12345"

PROJECT_ID = 3 # app 项目 ID

 

auth = base64.b64encode(f"{USERNAME}:{PASSWORD}".encode()).decode()

headers = {"Authorization": f"Basic {auth}", "Content-Type": "application/json"}

 

# 创建保留策略

retention_policy = {

"algorithm": "or",

"rules": [

{

"action": "retain",

"scope_selectors": {

"repositories": [

{"kind": "doublestar", "decoration": "repoMatches", "pattern": "**"}

]

},

"tag_selectors": [

{"kind": "doublestar", "decoration": "matches", "pattern": "prod-*"}

],

"params": {"latest": 30}

},

{

"action": "retain",

"tag_selectors": [

{"kind": "doublestar", "decoration": "matches", "pattern": "dev-*"}

],

"params": {"latest": 10}

},

{

"action": "retain",

"tag_selectors": [

{"kind": "doublestar", "decoration": "matches", "pattern": "**"}

],

"params": {"latest": 5}

}

],

"trigger": {"kind": "Schedule", "settings": {"cron": "0 0 3 * * *"}, "references": []}

}

 

resp = requests.post(

f"{HARBOR_URL}/api/v2.0/retentions",

headers=headers,

json={"scope_selectors": {}, "rules": retention_policy["rules"],

"trigger": retention_policy["trigger"]}

)

print(f"Retention policy created: {resp.status_code}")

print(json.dumps(resp.json(), indent=2) if resp.status_code in (200, 201) else resp.text)

上面的 Python 脚本通过 Harbor v2.0 REST API 创建了三条保留规则:生产镜像保留 30 个版本、开发镜像保留 10 个版本、其余保留 5 个版本。定时任务每天凌晨 3 点执行清理。算法使用 or 逻辑,意味着只要满足任一规则 Tag 就会被保留,不会被误删。

第四步:配置垃圾回收(Garbage Collection)

Tag Retention 删除的只是 Tag 引用,底层的 Blob 数据并不会立即释放。需要配合 Harbor 的垃圾回收机制才能真正回收磁盘空间。

💻 代码示例

# 在 Harbor 部署目录执行垃圾回收(需先停止 Harbor)

cd /opt/harbor

docker-compose stop

 

# 执行垃圾回收(不带 –dry-run 为真实执行)

docker run -it –name gc –rm

-v /data/harbor/registry:/storage

-e "REGISTRY_REDIS_ADDR=redis:6379"

goharbor/registry-photon:v2.12.0 garbage-collect

–dry-run /etc/registry/config.yml

 

# 确认无误后执行真实回收

docker run -it –name gc –rm

-v /data/harbor/registry:/storage

-e "REGISTRY_REDIS_ADDR=redis:6379"

goharbor/registry-photon:v2.12.0 garbage-collect

/etc/registry/config.yml

 

# 重启 Harbor

docker-compose up -d

对于在线垃圾回收(Harbor v2.0+ 支持),可以在 Web 界面”配置管理 → 垃圾回收”中设置定时任务,无需停机:

💻 代码示例

# Harbor values.yaml 中的 GC 配置(Helm 部署方式)

harbor:

gc:

enabled: true

schedule: "0 0 4 * * *" # 每天凌晨 4 点执行

forceSecureGc: false # 不强制停止 Harbor

persistence:

persistentVolumeClaim:

registry:

size: 200Gi

storageClass: "nfs-client"

第五步:验证 Tag 策略与镜像溯源

部署完成后,我们需要验证整套 Tag 策略是否按预期工作。模拟一次完整构建并检查 Harbor 中的镜像版本记录:

💻 代码示例

# 1. 模拟 GitLab CI 构建推送(main 分支)

docker tag app/frontend:local harbor.stellardata.top/app/frontend:prod-a1b2c3d4

docker push harbor.stellardata.top/app/frontend:prod-a1b2c3d4

 

# 2. 查看 Harbor 仓库中的所有 Tag

curl -s "https://harbor.stellardata.top/api/v2.0/projects/app/repositories/frontend/artifacts?with_tag=true"

-H "Authorization: Basic $(echo -n 'admin:Harbor12345' | base64)"

| python3 -c "

import sys, json

data = json.load(sys.stdin)

for artifact in data:

digest = artifact['digest'][:19]

tags = [t['name'] for t in artifact.get('tags', [])]

push_time = artifact.get('push_time', 'N/A')

print(f' Digest: {digest}… Tags: {tags} Pushed: {push_time}')

"

 

# 3. 验证不可变 Tag 规则 — 尝试覆盖推送应被拒绝

docker tag nginx:latest harbor.stellardata.top/app/frontend:prod-a1b2c3d4

docker push harbor.stellardata.top/app/frontend:prod-a1b2c3d4

# 预期输出: error: tag prod-a1b2c3d4 is immutable

 

# 4. 查看保留策略执行日志

curl -s "https://harbor.stellardata.top/api/v2.0/retentions/1/tasks"

-H "Authorization: Basic $(echo -n 'admin:Harbor12345' | base64)"

| python3 -m json.tool | head -50

通过以上验证,我们可以确认:每次构建都会生成包含 commit hash 的唯一 Tag,生产 Tag 不可被覆盖,超出保留数量的旧 Tag 会被自动清理,垃圾回收会定期释放磁盘空间。这构成了一个闭环的镜像版本管理体系。

常见问题

Q1:Tag 保留策略执行后磁盘空间没有释放?

Tag Retention 只删除 Tag 引用,不删除底层 Blob。必须执行垃圾回收才能释放空间。如果使用在线 GC,请确认 Harbor 版本 ≥ v2.0 且 gc.forceSecureGc 设置为 false。离线 GC 需要先停止 Harbor 再执行,否则可能导致数据不一致。

Q2:不可变 Tag 规则误拦截了正常推送?

检查 Tag 匹配模式。prod-* 会匹配所有以 prod- 开头的 Tag,如果 CI 流水线在开发阶段也使用该前缀就会被拦截。建议区分前缀:prod-*(生产)、staging-*(预发布)、dev-*(开发),并为每种环境设置不同的不可变规则和保留数量。

Q3:Argo CD GitOps 如何配合 Tag 策略做回滚?

Argo CD 通过追踪 Git 仓库中的镜像 Tag 来决定部署版本。当需要回滚时,只需在 Git 中将部署清单的镜像 Tag 改回之前的 commit hash Tag(如 prod-a1b2c3d4),Argo CD 会自动检测到变更并执行回滚。由于我们保留了 30 个生产版本,90 天内的任意版本都可以快速回滚。也可以配合 Argo CD Image Updater 自动选择最新通过扫描的镜像 Tag。

Q4:多架构镜像(multi-arch)的 Tag 如何管理?

使用 docker buildx 构建多架构镜像时,同一个 Tag 会指向一个 manifest list,其中包含不同架构(amd64/arm64)的子镜像。Harbor v2.0+ 完整支持 manifest list,保留策略和不可变规则同样适用于多架构镜像的 Tag。建议在 CI 中统一使用 buildx 构建,确保同一个 Tag 在所有架构下行为一致。

总结

今天我们围绕 Harbor 镜像版本管理这个核心主题,完成了一套生产可用的 Tag 策略设计:

  1. Tag 命名规范:采用 分支名-commit hash 的混合策略,兼顾可读性与唯一性,在 GitLab CI 中自动生成
  2. 不可变 Tag:通过 Harbor REST API 将 prod-* 前缀的 Tag 设为不可变,从仓库层面防止 Tag 覆盖
  3. Tag 保留策略:使用 Python 脚本通过 Harbor API 配置自动清理规则,生产保留 30 版、开发保留 10 版、其余保留 5 版
  4. 垃圾回收:配置在线/离线 GC 定时任务,真正释放磁盘空间
  5. 验证溯源:通过 REST API 查询镜像版本记录,验证不可变规则与保留策略的执行效果

这套方案让镜像仓库在持续高频构建的压力下依然保持整洁有序,每个镜像版本都可追溯、可回滚、可审计,为后续 Argo CD GitOps 部署提供了可靠的镜像版本基础。

下期预告

明天我们将进入 GitOps 的核心环节——第 8 天:Argo CD 部署与 GitOps 配置仓库创建。将安装 Argo CD 到 K8s 集群,创建 GitOps 配置仓库结构,打通从 Git 到集群的自动化部署通道。敬请期待!

系列大纲

  1. 全链路架构总览:从代码提交到灰度发布的完整管道设计
  2. K8s 集群与网络基础:kubeadm 离线部署与 Cilium 网络插件
  3. Harbor 私有镜像仓库部署与验证:Helm 安装与镜像推送
  4. GitLab CE 自托管部署:代码仓库与项目管理平台
  5. GitLab Runner 配置:K8s Executor 与 RBAC 权限设置
  6. GitLab CI 流水线搭建:.gitlab-ci.yml 与 Kaniko 无守护进程构建
  7. Harbor 镜像版本管理与 Tag 策略配置(今天)
  8. Argo CD 部署与 GitOps 配置仓库创建
  9. CI 与 CD 联动:从代码提交到部署的全自动闭环
  10. Prometheus 部署:kube-prometheus-stack 与 ServiceMonitor 指标采集
  11. Grafana 看板搭建:K8s 标准面板与自定义业务看板
  12. 告警规则与 Alertmanager:钉钉/邮件通知渠道配置
  13. Argo Rollouts 安装与基础概念:CRD 与 Rollout 资源定义
  14. 金丝雀发布实战:setWeight 流量分割与 pause 步骤
  15. AnalysisTemplate 指标分析与自动回滚机制
  16. 全链路端到端演示:代码提交→构建→灰度→监控→告警完整流程
  17. 生产化加固要点:Harbor/GitLab/Argo CD 高可用方案
  18. 供应链安全闭环:Trivy 扫描 + cosign 签名 + Kyverno 验签
微信二维码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像 - 恒星
欢迎您留下宝贵的见解!
提交
头像 - 恒星

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容