Harbor 镜像仓库运维指令手册

运维指令手册 46.9 KB 原文

Harbor 镜像仓库运维指令手册

适用版本:Harbor 2.11 / 2.12 / 2.13 / 2.14 / 2.15(当前) 部署方式:Docker Compose(离线安装包)/ Helm(Kubernetes) 约定:# root 或 sudo,$ 普通用户,<xxx> 为占位符,<project> 为 Harbor 项目名。 更新时间:2026-11(版本事实核对至 2026-11-05)


目录

  1. 版本与架构
  2. 部署
  3. harbor.yml 配置详解
  4. 客户端接入
  5. 项目与 RBAC
  6. 镜像复制
  7. 漏洞扫描
  8. 标签保留与垃圾回收
  9. 备份与恢复
  10. 升级
  11. API 运维
  12. 日志与监控
  13. 故障速查
  14. 卸载
  15. 附录

1. 版本与架构

1.1 版本事实(2026-11)

版本 首发 最新补丁 状态
2.15 2026-03-19 2.15.4 / 2.15.13(社区分支) 当前,推荐
2.14 2025-09-09 2.14.6 支持中
2.13 2025-04-09 2.13.6 支持中,剩余时间最短
2.12 2024-11-05 2.12.4 已 EOL(2026-03-20)

升级规则:只支持 n-2。从 2.12 升 2.15 不行,必须 2.12 → 2.13 → 2.14 → 2.15 逐级升。

1.2 组件构成

                    ┌──────────────────────┐
   浏览器/API  ────▶│  nginx (proxy)       │ :80/:443
                    └──────────┬───────────┘
                               │
        ┌──────────────────────┼───────────────────────┐
        ▼                      ▼                       ▼
  ┌───────────┐        ┌──────────────┐        ┌─────────────┐
  │ portal    │        │ core         │        │ registry    │
  │ (前端)    │        │ (API, 核心)  │        │ (镜像存储)  │
  └───────────┘        └───────┬──────┘        └─────────────┘
                               │
        ┌──────────────┬───────┴────────┬──────────────┐
        ▼              ▼                ▼              ▼
  ┌───────────┐  ┌───────────┐   ┌───────────┐  ┌───────────┐
  │ jobservice│  │ postgresql│   │  redis    │  │ trivy     │
  │ (任务)    │  │ (元数据)  │   │ (缓存/队列)│  │ (扫描)    │
  └───────────┘  └───────────┘   └───────────┘  └───────────┘
容器 作用 挂了的影响
nginx 反向代理、TLS 终结 全部不可访问
harbor-portal Web UI 界面打不开,API 仍可用
harbor-core 核心 API、认证、策略 全部 API 失败
registry 实际存储镜像 blob push/pull 全部失败
registryctl registry 配置与 GC 执行器 无法执行 GC
harbor-jobservice 异步任务(复制、扫描、GC) 任务排队不执行
harbor-db(postgresql) 元数据(项目、用户、策略) core 起不来
redis 缓存、任务队列、会话 jobservice 异常、登录变慢
trivy-adapter 漏洞扫描 扫描任务失败

1.3 端口

端口 用途
80 HTTP(通常重定向到 443)
443 HTTPS(生产必须)
4443 可选,core 的直连端口(内部)
5432 PostgreSQL(仅内部)
6379 Redis(仅内部)
5000 registry(仅内部,不对外)
8080 jobservice(仅内部)

安全要求:只暴露 80/443。PostgreSQL/Redis/registry 端口绝不能映射到宿主机公网。

1.4 版本识别

# Web/API
curl -sk https://harbor.internal/api/v2.0/systeminfo | jq '.harbor_version'
curl -sk https://harbor.internal/api/v2.0/health

# 容器镜像 tag
cd /opt/harbor && docker compose images

# CLI
docker run -it --rm goharbor/harbor-core:v2.15.4 --version 2>/dev/null || true

# 配置文件版本
grep '_version' /opt/harbor/harbor.yml

2. 部署

2.1 部署方式选择

方式 适合 高可用 运维复杂度
Docker Compose(离线包) 中小规模、单机 需外部 PG/Redis/存储 + 多实例 低
Helm(K8s) 已有 K8s 天然支持多副本 中
Harbor Operator K8s + 自动化 支持 中高

2.2 前置条件

项 要求
CPU/内存 最小 2C4G;生产 4C8G+
磁盘 按镜像量,建议独立数据盘 ≥ 500G
Docker 20.10+
Docker Compose v2 插件(docker compose)
系统 内核 ≥ 4.0,cgroup v2 更佳
端口 80/443 可用
域名 必须有(IP 访问会导致 docker login 证书校验失败)
docker --version
docker compose version
df -h /data
ss -lntp | grep -E ':80|:443'

关键:不要用 IP 作为 hostname。docker login 10.0.0.50 会因为证书 SAN 不匹配失败(除非配 insecure-registry,但生产不建议)。准备一个域名(内网 DNS 也行)。

2.3 离线安装(推荐,生产常用)

# 1) 下载(从 GitHub Releases 取最新 2.15.x)
# harbor-offline-installer-v2.15.4.tgz
wget https://github.com/goharbor/harbor/releases/download/v2.15.4/harbor-offline-installer-v2.15.4.tgz

# 2) 解压
tar xzf harbor-offline-installer-v2.15.4.tgz -C /opt
cd /opt/harbor
ls    # harbor.v2.15.4.tar.gz  harbor.yml.tmpl  install.sh  prepare  common.sh

# 3) 准备证书
mkdir -p /opt/harbor/cert
# 自签(内网)
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
  -keyout /opt/harbor/cert/harbor.key \
  -out /opt/harbor/cert/harbor.crt \
  -subj "/CN=harbor.internal" \
  -addext "subjectAltName=DNS:harbor.internal,IP:10.0.0.50"

# 4) 配置
cp harbor.yml.tmpl harbor.yml
vim harbor.yml
hostname: harbor.internal

http:
  port: 80

https:
  port: 443
  certificate: /opt/harbor/cert/harbor.crt
  private_key: /opt/harbor/cert/harbor.key

harbor_admin_password: <强密码>

database:
  password: <强密码>
  max_idle_conns: 100
  max_open_conns: 900
  conn_max_lifetime: 5m
  conn_max_idle_time: 0

data_volume: /data/harbor

trivy:
  ignore_unfixed: false
  skip_update: false
  skip_java_db_update: false
  offline_scan: false
  security_check: vuln
  insecure: false
  timeout: 5m0s

jobservice:
  max_job_workers: 10
  job_loggers:
    - STD_OUTPUT
    - FILE
  logger_sweeper_duration: 1

notification:
  webhook_job_max_retry: 10
  webhook_job_http_client_timeout: 3

log:
  level: info
  local:
    rotate_count: 50
    rotate_size: 200M
    location: /data/harbor/logs

_version: 2.15.0

proxy:
  http_proxy:
  https_proxy:
  no_proxy:
  components:
    - core
    - jobservice
    - trivy

upload_purging:
  enabled: true
  age: 168h
  interval: 24h
  dryrun: false

cache:
  enabled: false
  expire_hours: 24
# 5) 安装(--with-trivy 必带,否则没有扫描)
./install.sh --with-trivy

# 6) 验证
docker compose ps
curl -sk https://harbor.internal/api/v2.0/health
curl -sk https://harbor.internal/api/v2.0/systeminfo | jq '.harbor_version'

2.4 在线安装

wget https://github.com/goharbor/harbor/releases/download/v2.15.4/harbor-online-installer-v2.15.4.tgz
tar xzf harbor-online-installer-v2.15.4.tgz -C /opt
cd /opt/harbor
cp harbor.yml.tmpl harbor.yml
vim harbor.yml
./install.sh --with-trivy

区别:在线包不含镜像 tar,install.sh 会从 Docker Hub 拉取。国内网络需先配镜像加速,否则极慢或失败。

2.5 外部数据库与 Redis(生产/HA 必须)

# harbor.yml
database:
  # 密码仍需保留(Harbor 用它连外部库)
  password: <pwd>
  # 指向外部 PostgreSQL
  # 注意:Harbor 不读这些键,需要改 env 或直接用 compose override

Harbor 的 harbor.yml 不直接支持外部 DB 配置,需要通过 docker-compose.yml 的环境变量覆盖。标准做法是准备 override 文件:

# /opt/harbor/docker-compose.override.yml
services:
  harbor-core:
    environment:
      POSTGRESQL_HOST: pg.internal
      POSTGRESQL_PORT: "5432"
      POSTGRESQL_DATABASE: registry
      POSTGRESQL_USERNAME: harbor
      POSTGRESQL_PASSWORD: <pwd>
      POSTGRESQL_SSLMODE: require
      _REDIS_URL_CORE: "redis://:pwd@redis.internal:6379/0"
      _REDIS_URL_REG: "redis://:pwd@redis.internal:6379/1"
  harbor-jobservice:
    environment:
      _REDIS_URL_CORE: "redis://:pwd@redis.internal:6379/0"
  registry:
    environment:
      REGISTRY_STORAGE: s3
      REGISTRY_STORAGE_S3_REGIONENDPOINT: oss-cn-hangzhou.aliyuncs.com
      REGISTRY_STORAGE_S3_BUCKET: harbor-registry
      REGISTRY_STORAGE_S3_ACCESSKEY: <ak>
      REGISTRY_STORAGE_S3_SECRETKEY: <sk>
      REGISTRY_STORAGE_S3_REGION: cn-hangzhou
      REGISTRY_STORAGE_S3_SECURE: "true"
      REGISTRY_STORAGE_S3_V4AUTH: "true"
      REGISTRY_STORAGE_DELETE_ENABLED: "true"
# 应用 override(不加 -d 先看效果)
docker compose -f docker-compose.yml -f docker-compose.override.yml up -d

注意:registry 的存储后端改了之后,已有镜像数据不会自动迁移。必须先用 registry 的 storage driver 迁移工具或 skopeo sync 搬运,再切换。切换前务必全量备份。

2.6 对象存储(S3/OSS/MinIO)

# docker-compose.override.yml 片段(MinIO 示例)
services:
  registry:
    environment:
      REGISTRY_STORAGE: s3
      REGISTRY_STORAGE_S3_REGIONENDPOINT: minio.storage:9000
      REGISTRY_STORAGE_S3_BUCKET: harbor
      REGISTRY_STORAGE_S3_ACCESSKEY: minioadmin
      REGISTRY_STORAGE_S3_SECRETKEY: minioadmin
      REGISTRY_STORAGE_S3_REGION: us-east-1
      REGISTRY_STORAGE_S3_SECURE: "false"
      REGISTRY_STORAGE_S3_V4AUTH: "true"
      REGISTRY_STORAGE_S3_FORCEPATHSTYLE: "true"
      REGISTRY_STORAGE_DELETE_ENABLED: "true"
  harbor-core:
    environment:
      REGISTRY_STORAGE_PROVIDER_NAME: s3

对象存储的好处:镜像量增长不受单机磁盘限制;GC 直接作用于对象存储;便于多实例共享。

2.7 Kubernetes(Helm)部署

helm repo add harbor https://helm.goharbor.io
helm repo update
helm search repo harbor/harbor --versions | head

cat > values.yaml <<'EOF'
expose:
  type: ingress
  tls:
    enabled: true
    certSource: secret
    secret:
      secretName: harbor-tls
  ingress:
    hosts:
      core: harbor.internal
    className: nginx
    annotations:
      nginx.ingress.kubernetes.io/proxy-body-size: "0"
      nginx.ingress.kubernetes.io/proxy-read-timeout: "600"

externalURL: https://harbor.internal

persistence:
  enabled: true
  resourcePolicy: keep
  persistentVolumeClaim:
    registry:
      size: 500Gi
      storageClass: fast-ssd
    database:
      size: 50Gi
      storageClass: fast-ssd
    redis:
      size: 10Gi
    jobservice:
      jobLog:
        size: 10Gi

# 生产建议用外部 PG/Redis
database:
  type: external
  external:
    host: pg.internal
    port: "5432"
    username: harbor
    password: <pwd>
    coreDatabase: registry
    sslmode: require

redis:
  type: external
  external:
    addr: redis.internal:6379
    password: <pwd>

trivy:
  enabled: true
  offlineScan: false

core:
  replicas: 2
portal:
  replicas: 2
jobservice:
  replicas: 2
registry:
  replicas: 2
EOF

helm install harbor harbor/harbor -n harbor --create-namespace -f values.yaml
kubectl -n harbor get pod
kubectl -n harbor get ingress

2.8 部署后验收

# 1) 容器状态(都应为 healthy)
docker compose ps

# 2) 健康检查
curl -sk https://harbor.internal/api/v2.0/health | jq
curl -sk https://harbor.internal/api/v2.0/ping

# 3) 版本
curl -sk https://harbor.internal/api/v2.0/systeminfo | jq '.harbor_version'

# 4) 登录
docker login harbor.internal -u admin

# 5) 推拉测试
docker pull nginx:1.25
docker tag nginx:1.25 harbor.internal/library/nginx:1.25
docker push harbor.internal/library/nginx:1.25
docker rmi harbor.internal/library/nginx:1.25
docker pull harbor.internal/library/nginx:1.25

# 6) 扫描(需 trivy)
# 在 UI 里点扫描,或 API:
curl -sk -u admin:<pwd> -X POST \
  "https://harbor.internal/api/v2.0/projects/library/repositories/nginx/artifacts/1.25/scan"
curl -sk -u admin:<pwd> \
  "https://harbor.internal/api/v2.0/projects/library/repositories/nginx/artifacts/1.25/additions/vulnerabilities" | jq '.total'

# 7) 磁盘
du -sh /data/harbor
du -sh /data/harbor/registry

3. harbor.yml 配置详解

3.1 核心项

配置 说明 生产建议
hostname 对外域名 必须域名,不能 IP
http.port HTTP 端口 80,配合重定向
https.port HTTPS 端口 443,必须启用
https.certificate/private_key 证书路径 用正式 CA 或内部 CA
harbor_admin_password 初始 admin 密码 首登立即改,或改用 OIDC
database.password 内置 PG 密码 强密码
data_volume 数据根目录 独立大容量盘
_version 配置版本 不要手改(升级用)

3.2 扫描(trivy)

trivy:
  ignore_unfixed: false        # true 则只显示有修复版本的 CVE
  skip_update: false           # true 则不自动更新漏洞库(离线环境设 true)
  skip_java_db_update: false
  offline_scan: false          # 离线扫描模式
  security_check: vuln         # vuln / config / secret(2.11+)
  insecure: false              # 连外部扫描器时跳过 TLS 校验
  timeout: 5m0s

3.3 日志

log:
  level: info                  # debug / info / warning / error / fatal
  local:
    rotate_count: 50           # 保留文件数
    rotate_size: 200M          # 单文件大小
    location: /data/harbor/logs
# 日志文件
ls -lh /data/harbor/logs/
tail -f /data/harbor/logs/harbor-core.log
tail -f /data/harbor/logs/jobservice/jobservice.log
tail -f /data/harbor/logs/registry/registry.log

3.4 上传清理

upload_purging:
  enabled: true
  age: 168h          # 清理 7 天前的未完成上传
  interval: 24h
  dryrun: false

重要:中断的 push 会在 /data/harbor/registry/uploads 留残片,不清会慢慢吃满磁盘。这个开关必须开。

3.5 缓存与代理

cache:
  enabled: true
  expire_hours: 24

proxy:
  http_proxy: http://proxy.internal:3128
  https_proxy: http://proxy.internal:3128
  no_proxy: 127.0.0.1,localhost,.internal,pg.internal,redis.internal
  components:
    - core
    - jobservice
    - trivy

3.6 配置生效

cd /opt/harbor

# 改了 harbor.yml 后:重新生成配置 + 重启(不重装)
./prepare
docker compose up -d

# 注意:不要用 ./install.sh 改配置(会重建部分状态)
# install.sh 只用于首次安装和版本升级

坑点:./prepare 会根据 harbor.yml 重新生成 common/config/*,直接改 common/config 下的文件会在下次 prepare 时被覆盖。


4. 客户端接入

4.1 Docker

# 1) 信任 CA(每台客户端都要做)
mkdir -p /etc/docker/certs.d/harbor.internal
cp harbor.crt /etc/docker/certs.d/harbor.internal/ca.crt
systemctl restart docker

# 或系统级信任
cp harbor.crt /etc/pki/ca-trust/source/anchors/ && update-ca-trust     # RHEL
cp harbor.crt /usr/local/share/ca-certificates/ && update-ca-certificates  # Debian

# 2) 登录
docker login harbor.internal -u admin
docker login harbor.internal -u 'robot$ci' -p '<token>'
docker login harbor.internal -u admin --password-stdin < ~/.pass

# 3) 推拉
docker tag nginx:1.25 harbor.internal/library/nginx:1.25
docker push harbor.internal/library/nginx:1.25
docker pull harbor.internal/library/nginx:1.25

# 4) 退出
docker logout harbor.internal

# 查看凭据保存位置(base64 明文,注意权限)
cat ~/.docker/config.json
chmod 600 ~/.docker/config.json

4.2 Podman

# Podman 用系统信任或 registries.d
cp harbor.crt /etc/pki/ca-trust/source/anchors/ && update-ca-trust

podman login harbor.internal -u admin
podman tag nginx:1.25 harbor.internal/library/nginx:1.25
podman push harbor.internal/library/nginx:1.25

# HTTP 仓库(不推荐,仅测试)
# /etc/containers/registries.conf
# [[registry]]
# prefix = "harbor.internal"
# location = "harbor.internal:80"
# insecure = true

4.3 containerd(k8s 节点)

mkdir -p /etc/containerd/certs.d/harbor.internal
cp harbor.crt /etc/containerd/certs.d/harbor.internal/ca.crt

cat >/etc/containerd/certs.d/harbor.internal/hosts.toml <<'EOF'
server = "https://harbor.internal"
[host."https://harbor.internal"]
  capabilities = ["pull", "resolve", "push"]
  ca = "/etc/containerd/certs.d/harbor.internal/ca.crt"
EOF

# 确认 config.toml 开启了 config_path
grep -A2 'registry]' /etc/containerd/config.toml
# config_path = "/etc/containerd/certs.d"

systemctl restart containerd
crictl pull harbor.internal/library/nginx:1.25

4.4 Kubernetes 拉取私有镜像

kubectl create secret docker-registry harbor-cred \
  --docker-server=harbor.internal \
  --docker-username='robot$ci' \
  --docker-password='<token>' \
  -n prod

# 方式一:Deployment 显式引用
# spec.template.spec.imagePullSecrets:
#   - name: harbor-cred

# 方式二:绑到 ServiceAccount(推荐,全局生效)
kubectl patch sa default -n prod -p '{"imagePullSecrets":[{"name":"harbor-cred"}]}'

# 验证
kubectl run test --image=harbor.internal/library/nginx:1.25 --restart=Never -n prod
kubectl get pod test -n prod
kubectl describe pod test -n prod | grep -A3 Events
kubectl delete pod test -n prod

4.5 node 级免密(不推荐)

# 把凭据放到节点(所有节点都要)
mkdir -p /root/.docker
cp ~/.docker/config.json /root/.docker/
# kubelet 以 root 运行,会读这个文件
# 缺点:凭据散落各节点,轮换麻烦。优先用 imagePullSecrets

5. 项目与 RBAC

5.1 项目(Project)

# API 创建项目
curl -sk -u admin:<pwd> -X POST https://harbor.internal/api/v2.0/projects \
  -H 'Content-Type: application/json' \
  -d '{
    "project_name": "web",
    "public": false,
    "storage_limit": 107374182400,
    "metadata": { "public": "false", "auto_scan": "true", "prevent_vul": "true", "severity": "high" }
  }' | jq

# 列出
curl -sk -u admin:<pwd> 'https://harbor.internal/api/v2.0/projects?page=1&page_size=50' | jq '.[].name'

# 删除(★ 会删项目下所有镜像)
curl -sk -u admin:<pwd> -X DELETE https://harbor.internal/api/v2.0/projects/web
元数据 作用
public 公开项目(匿名可 pull)
auto_scan push 后自动扫描
prevent_vul 有高危漏洞时阻止 pull
severity 阻止阈值(critical/high/medium/low)
reuse_sys_cve_allowlist 使用系统级 CVE 白名单
retention_id 绑定的保留策略

5.2 角色模型

角色 权限
Project Admin 项目内全部(成员、策略、删除)
Maintainer 推拉、扫描、复制、删镜像,不能改成员
Developer 推拉、读
Guest 只读(pull)
Limited Guest 只读,不能看日志
系统管理员 全局
# 加成员
curl -sk -u admin:<pwd> -X POST \
  https://harbor.internal/api/v2.0/projects/web/members \
  -H 'Content-Type: application/json' \
  -d '{
    "role_id": 3,
    "member_user": { "username": "alice" }
  }'

# role_id: 1=ProjectAdmin 2=Maintainer 3=Developer 4=Guest 5=LimitedGuest

# 加用户组(LDAP/OIDC)
curl -sk -u admin:<pwd> -X POST \
  https://harbor.internal/api/v2.0/projects/web/members \
  -H 'Content-Type: application/json' \
  -d '{"role_id": 3, "member_group": {"group_name": "dev-team", "group_type": 1}}'

# 列成员
curl -sk -u admin:<pwd> https://harbor.internal/api/v2.0/projects/web/members | jq

5.3 Robot 账号(CI/CD 首选)

Robot 账号是项目级的机器身份,权限可控、可设过期、可限定 action。

# 创建 robot(项目级)
curl -sk -u admin:<pwd> -X POST \
  https://harbor.internal/api/v2.0/projects/web/robots \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "ci-pusher",
    "duration": 365,
    "level": "project",
    "permissions": [
      { "kind": "project", "namespace": "web", "access": [
        { "resource": "repository", "action": "push" },
        { "resource": "repository", "action": "pull" },
        { "resource": "artifact", "action": "read" }
      ]}
    ]
  }' | jq
# 返回的 "secret" 只显示一次,务必保存

用户名格式:robot$web+ci-pusher(项目级)或 robot$ci-pusher(系统级)。

docker login harbor.internal -u 'robot$web+ci-pusher' -p '<secret>'

# 轮换:删旧建新
curl -sk -u admin:<pwd> https://harbor.internal/api/v2.0/projects/web/robots | jq '.[] | {id,name,expires_at}'
curl -sk -u admin:<pwd> -X DELETE https://harbor.internal/api/v2.0/projects/web/robots/<id>

5.4 配额

# 设置项目配额(字节)
curl -sk -u admin:<pwd> -X PUT \
  "https://harbor.internal/api/v2.0/quotas/<quota-id>" \
  -H 'Content-Type: application/json' \
  -d '{"hard": {"storage": 107374182400}}'

# 查配额
curl -sk -u admin:<pwd> 'https://harbor.internal/api/v2.0/quotas?page_size=100' | jq '.'

5.5 代理缓存(Pull-through Cache)

用于加速公网镜像、隔离外网:

# 1) 建 registry endpoint(类型 docker-registry,指向 docker.io)
curl -sk -u admin:<pwd> -X POST https://harbor.internal/api/v2.0/registries \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "dockerhub-proxy",
    "type": "docker-hub",
    "url": "https://docker.io",
    "access_key": "<user>",
    "access_secret": "<pass>"
  }'

# 2) 建项目时勾选 proxy cache 并指定该 endpoint
#    之后 docker pull harbor.internal/dockerhub-proxy/nginx:1.25 会按需缓存

代理缓存项目的镜像不占项目配额(由缓存清理策略管理),且只读——不能往代理缓存项目 push。


6. 镜像复制

6.1 使用场景

  • 多机房/多集群同步镜像
  • 从生产仓库同步到灾备
  • 从公网拉取到内网(配合代理)

6.2 创建目标(Registry Endpoint)

curl -sk -u admin:<pwd> -X POST https://harbor.internal/api/v2.0/registries \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "dr-harbor",
    "type": "harbor",
    "url": "https://harbor-dr.internal",
    "access_key": "admin",
    "access_secret": "<pwd>",
    "insecure": false,
    "description": "灾备仓库"
  }'
type 说明
harbor 另一套 Harbor
docker-hub Docker Hub
docker-registry 通用 registry v2
aws-ecr / azure-acr / gcr / quay 云厂商

6.3 创建复制规则

curl -sk -u admin:<pwd> -X POST https://harbor.internal/api/v2.0/replication/policies \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "to-dr",
    "src_registry": null,
    "dest_registry": {"id": 1},
    "dest_namespace": "mirror",
    "dest_namespace_replace_count": -1,
    "trigger": {"type": "event_based", "trigger_settings": {"cron": ""}},
    "filters": [
      {"type": "name", "value": "web/**"},
      {"type": "tag", "value": "v*"},
      {"type": "label", "value": "release"}
    ],
    "deletion": false,
    "override": true,
    "enabled": true,
    "speed_kb": 0,
    "copy_by_chunk": false
  }'
trigger 说明
event_based push 后立即触发(最常用)
scheduled 按 cron 定时(trigger_settings.cron)
manual 只手工触发
过滤类型 示例
name library/nginx(支持 ** 通配)
tag v1.*
label release
resource image / artifact

6.4 触发与查看

# 手工触发整个规则
curl -sk -u admin:<pwd> -X POST \
  "https://harbor.internal/api/v2.0/replication/executions" \
  -H 'Content-Type: application/json' \
  -d '{"policy_id": 1}'

# 查看执行记录
curl -sk -u admin:<pwd> \
  "https://harbor.internal/api/v2.0/replication/executions?policy_id=1&page_size=20" | jq '.'
# 状态:InProgress / Succeed / Failed / Stopped

# 查看任务明细(失败原因在这)
curl -sk -u admin:<pwd> \
  "https://harbor.internal/api/v2.0/replication/executions/<exec_id>/tasks" | jq '.[] | {id,status,src_resource,dst_resource}'

6.5 复制排障

现象 原因 处理
Failed 且报 401 目标仓库凭据错 更新 endpoint 的 access_key/secret
Failed 且报 x509 目标证书不受信 上传 CA 到 Harbor(配置 → 系统设置 → 证书)或 insecure: true
只同步部分 tag 过滤器不匹配 检查 filters
大镜像超时 网络慢 开 copy_by_chunk;限速 speed_kb
目标已存在同名 tag override: false 改为 true 或先清理
复制后目标无法 pull 目标项目权限 检查目标项目是否 public / robot 权限
任务一直 InProgress jobservice 卡住 查 jobservice 日志,必要时重启
docker compose logs -f --tail=100 harbor-jobservice
docker compose logs -f --tail=100 harbor-core | grep -i replicat

7. 漏洞扫描

7.1 扫描架构

harbor-core  ──创建扫描任务──▶  jobservice  ──调用──▶  trivy-adapter
                                                          │
                                                    拉取镜像层
                                                          │
                                                    比对漏洞库
                                                          │
                     ◀── 结果写回 artifact 的 addition ──────

7.2 扫描方式

方式 触发 说明
手工 UI 点「扫描」或 API 按需
推送时 项目元数据 auto_scan: true 推荐
定时 项目 → 配置 → 扫描计划 定期复扫(漏洞库更新后旧结果会过时)
# 手工扫描
curl -sk -u admin:<pwd> -X POST \
  "https://harbor.internal/api/v2.0/projects/web/repositories/app/artifacts/1.0.0/scan"

# 查扫描状态
curl -sk -u admin:<pwd> \
  "https://harbor.internal/api/v2.0/projects/web/repositories/app/artifacts/1.0.0" | jq '.scan_overview'

# 查漏洞明细
curl -sk -u admin:<pwd> \
  "https://harbor.internal/api/v2.0/projects/web/repositories/app/artifacts/1.0.0/additions/vulnerabilities" \
  | jq '.total, (.vulnerabilities[] | {id,severity,package,version,fix_version})'

# 查 SBOM(2.11+)
curl -sk -u admin:<pwd> \
  "https://harbor.internal/api/v2.0/projects/web/repositories/app/artifacts/1.0.0/additions/sbom" | jq '.sbom.mediaType'

7.3 阻止不安全镜像

项目元数据:

键 值 效果
prevent_vul true 有超过阈值漏洞时阻止 pull
severity critical / high / medium / low 阈值
auto_scan true push 即扫
curl -sk -u admin:<pwd> -X PUT \
  "https://harbor.internal/api/v2.0/projects/web" \
  -H 'Content-Type: application/json' \
  -d '{"metadata": {"auto_scan": "true", "prevent_vul": "true", "severity": "high"}}'

注意:prevent_vul 只对新扫描生效,且未扫描的镜像默认放行。所以必须同时开 auto_scan,否则策略形同虚设。

7.4 CVE 例外(白名单)

# 系统级白名单(2.10+)
curl -sk -u admin:<pwd> -X POST \
  "https://harbor.internal/api/v2.0/system/CVEAllowlist" \
  -H 'Content-Type: application/json' \
  -d '{
    "items": [
      {"cve_id": "CVE-2024-12345", "expires_at": 1790000000}
    ]
  }'

# 项目级(继承系统 + 自有)
curl -sk -u admin:<pwd> -X PUT \
  "https://harbor.internal/api/v2.0/projects/web" \
  -H 'Content-Type: application/json' \
  -d '{"metadata": {"reuse_sys_cve_allowlist": "true"}}'

原则:白名单必须带 expires_at,且要有审批记录。永久白名单等于没有安全策略。

7.5 漏洞库更新

# trivy 自动更新(默认)
docker compose logs -f trivy-adapter | grep -i update

# 离线环境:手工导入漏洞库
# 1) 在有网机器上下载
#    trivy image --download-db-only --db-repository <内部源>
# 2) 拷贝到 /data/harbor/trivy-adapter/trivy/db/
ls /data/harbor/trivy-adapter/trivy/db/

# 更新后需要重新扫描才能反映新漏洞
# 离线配置
trivy:
  skip_update: true
  offline_scan: true

7.6 扫描问题

现象 原因 处理
扫描一直 Pending jobservice 队列积压 / 挂了 查 jobservice 日志与并发数
Failed to analyze 镜像层损坏 / 不支持格式 重新 push
扫描结果为 0 漏洞库未更新 检查 skip_update、库时间
扫描很慢 镜像大 / 磁盘慢 加 max_job_workers、SSD
内存溢出 trivy 解析大镜像 提内存限制,或改用外部 scanner
prevent_vul 不生效 未开 auto_scan / 镜像未扫 同时开 auto_scan

8. 标签保留与垃圾回收

8.1 为什么需要

Harbor 删除 tag(UI 或 API)只是删元数据,底层 blob 还在。不跑 GC,磁盘空间不会释放。CI 每天推 100 个 tag,一个月就能撑爆。

8.2 保留策略(Retention)

# 创建规则:保留最近 10 个 tag,以及所有匹配 release-* 的
curl -sk -u admin:<pwd> -X POST \
  "https://harbor.internal/api/v2.0/projects/web/retentions" \
  -H 'Content-Type: application/json' \
  -d '{
    "algorithm": "or",
    "rules": [
      {
        "id": 1,
        "priority": 1,
        "disabled": false,
        "action": "retain",
        "template": "latestPushedK",
        "params": {"latestPushedK": 10},
        "tag_selectors": [{"kind": "doublestar", "decoration": "matches", "pattern": "**"}],
        "scope_selectors": {"repository": [{"kind": "doublestar", "decoration": "repoMatches", "pattern": "**"}]}
      },
      {
        "id": 2,
        "priority": 2,
        "disabled": false,
        "action": "retain",
        "template": "nDaysSinceLastPush",
        "params": {"nDaysSinceLastPush": 90},
        "tag_selectors": [{"kind": "doublestar", "decoration": "matches", "pattern": "release-*"}],
        "scope_selectors": {"repository": [{"kind": "doublestar", "decoration": "repoMatches", "pattern": "**"}]}
      }
    ],
    "trigger": {"kind": "Schedule", "settings": {"cron": "0 0 3 * * *"}},
    "scope": {"level": "project", "ref": 1}
  }'
模板 含义
latestPushedK 保留最近 push 的 K 个
latestPulledN 保留最近 pull 的 N 个
nDaysSinceLastPush 保留 N 天内 push 的
nDaysSinceLastPull 保留 N 天内 pull 的
always 永远保留

algorithm:or(满足任一即保留)/ and(全部满足才保留)。

危险点:algorithm: or + 宽泛规则 = 一个都不删;and + 严格规则 = 可能删掉正在用的。第一次上线先跑「模拟」(Harbor UI 支持 dry-run),确认清单再启用。

8.3 手工删除镜像

# 删 tag
curl -sk -u admin:<pwd> -X DELETE \
  "https://harbor.internal/api/v2.0/projects/web/repositories/app/artifacts/1.0.0"

# 按 digest 删(推荐,tag 可重复指向同一 digest)
DIGEST=$(curl -sk -u admin:<pwd> \
  "https://harbor.internal/api/v2.0/projects/web/repositories/app/artifacts/1.0.0" | jq -r '.digest')
curl -sk -u admin:<pwd> -X DELETE \
  "https://harbor.internal/api/v2.0/projects/web/repositories/app/artifacts/${DIGEST}"

# 批量删(按 tag 名正则,注意 shell 转义)
curl -sk -u admin:<pwd> \
  "https://harbor.internal/api/v2.0/projects/web/repositories/app/artifacts?page_size=100" \
  | jq -r '.tags[]?.name' | grep -E '^ci-[0-9]+$' | while read t; do
      curl -sk -u admin:<pwd> -X DELETE \
        "https://harbor.internal/api/v2.0/projects/web/repositories/app/artifacts/${t}"
    done

8.4 垃圾回收(GC)

# 方式一:API 触发(推荐,2.x)
curl -sk -u admin:<pwd> -X POST \
  "https://harbor.internal/api/v2.0/system/gc/schedule" \
  -H 'Content-Type: application/json' \
  -d '{"schedule": {"type": "Manual"}, "parameters": {"delete_untagged": true, "dry_run": false}}'

# 查 GC 历史与状态
curl -sk -u admin:<pwd> "https://harbor.internal/api/v2.0/system/gc" | jq '.[-1]'
# 状态:Pending / Running / Success / Error

# 方式二:手工跑 registry GC(必须停 registry)
cd /opt/harbor
docker compose stop registry
docker run -it --name gc --rm --volumes-from registry \
  goharbor/registry-photon:v2.15.4 garbage-collect \
  --dry-run /etc/registry/config.yml
# 确认无误后去掉 --dry-run 再跑一次
docker compose start registry

GC 必须互斥:GC 运行时 registry 应只读或停止。在线跑 GC(API 方式)Harbor 会加只读锁,期间 push 会失败。安排在业务低峰。

8.5 空间回收完整流程

# 1) 看当前占用
du -sh /data/harbor/registry
du -sh /data/harbor/database
df -h /data

# 2) 清理策略:先删无用 tag(或让 retention 跑)
# 3) 触发 GC
curl -sk -u admin:<pwd> -X POST \
  "https://harbor.internal/api/v2.0/system/gc/schedule" \
  -H 'Content-Type: application/json' \
  -d '{"schedule":{"type":"Manual"},"parameters":{"delete_untagged":true,"dry_run":false}}'

# 4) 等 GC 完成
watch -n5 'curl -sk -u admin:<pwd> https://harbor.internal/api/v2.0/system/gc | jq ".[-1] | {status, creation_time, update_time}"'

# 5) 复查占用
du -sh /data/harbor/registry
# 定时 GC(crontab,每周日凌晨 3 点)
# 0 3 * * 0 curl -sk -u admin:<pwd> -X POST https://harbor.internal/api/v2.0/system/gc/schedule \
#   -H 'Content-Type: application/json' \
#   -d '{"schedule":{"type":"Manual"},"parameters":{"delete_untagged":true}}' >/dev/null

8.6 上传残片清理

# 未完成的上传
du -sh /data/harbor/registry/uploads 2>/dev/null
find /data/harbor/registry/uploads -type f -mtime +7 | head

# 靠 harbor.yml 的 upload_purging 自动清(见 3.4)
# 手工清理(确认 registry 无活跃上传)
find /data/harbor/registry/uploads -type f -mtime +7 -delete

9. 备份与恢复

9.1 备份什么

对象 位置 丢失后果
元数据库(PostgreSQL) 容器内 /var/lib/postgresql/data 项目、用户、策略全丢
镜像数据(registry) /data/harbor/registry 镜像全丢
配置文件 /opt/harbor/harbor.yml 需重配
证书 /opt/harbor/cert HTTPS 失效
密钥 /data/harbor/secretkey 加密数据无法解密

secretkey 必须备份。丢失后数据库里的加密字段(如 registry 密码、robot secret)无法解密,等于重建。

9.2 全量备份(停服,最可靠)

#!/usr/bin/env bash
# /opt/scripts/harbor-backup.sh
set -euo pipefail
BK=/backup/harbor/$(date +%F)
mkdir -p "$BK"
cd /opt/harbor

# 1) 停服务(保证一致性)
docker compose stop

# 2) 数据(数据库 + registry + 密钥)
tar czf "$BK/harbor-data.tar.gz" -C /data harbor
tar czf "$BK/harbor-conf.tar.gz" -C /opt harbor/harbor.yml harbor/cert

# 3) 记录版本
docker compose images > "$BK/images.txt" 2>/dev/null || true
grep '_version' harbor.yml > "$BK/version.txt"

# 4) 起服务
docker compose start

echo "backup done: $BK"
ls -lh "$BK"

9.3 热备份(不停服,DB 单独 dump)

BK=/backup/harbor/$(date +%F-%H%M)
mkdir -p "$BK"

# 1) PostgreSQL dump(不锁表,一致性略弱但可用)
docker compose exec -T postgresql pg_dump -U postgres -d registry -Fc \
  > "$BK/registry.dump"

# 2) registry 数据(用 rsync 硬链接快照,减少空间)
rsync -a --link-dest=/backup/harbor/latest/registry \
  /data/harbor/registry/ "$BK/registry/"

# 3) 配置
cp /opt/harbor/harbor.yml /opt/harbor/cert/* "$BK/"

# 4) 密钥
cp /data/harbor/secretkey "$BK/"

# 5) 更新 latest 链接
ln -sfn "$BK" /backup/harbor/latest

热备份期间若有 push,registry 目录与 DB 可能不一致(镜像存在但 DB 无记录)。恢复后表现为"孤儿 blob",跑一次 GC 即可清理。

9.4 恢复

# 1) 装同版本 Harbor(版本必须一致)
cd /opt/harbor
docker compose down

# 2) 恢复配置
tar xzf /backup/harbor/2026-11-05/harbor-conf.tar.gz -C /
# 确认 harbor.yml 与备份时一致

# 3) 恢复数据
rm -rf /data/harbor
tar xzf /backup/harbor/2026-11-05/harbor-data.tar.gz -C /data

# 4) 起服务(用 prepare 重新生成配置,不重装)
./prepare
docker compose up -d

# 5) 验证
docker compose ps
curl -sk https://harbor.internal/api/v2.0/health
curl -sk -u admin:<pwd> https://harbor.internal/api/v2.0/projects | jq '.[].name'
docker pull harbor.internal/library/nginx:1.25

9.5 部分恢复(只恢复某个项目)

# 难点:Harbor 不支持项目级导入导出
# 做法:在新实例重建项目 + 用 skopeo 搬运镜像 + 重建策略

# 1) 旧实例导出镜像清单
curl -sk -u admin:<pwd> \
  "https://harbor.internal/api/v2.0/projects/web/repositories?page_size=100" \
  | jq -r '.[].name' > /tmp/repos.txt

# 2) 逐仓库搬运
while read repo; do
  curl -sk -u admin:<pwd> \
    "https://harbor.internal/api/v2.0/projects/web/repositories/${repo#web/}/artifacts?page_size=100" \
    | jq -r '.tags[]?.name' | while read tag; do
        skopeo copy \
          --src-creds admin:<pwd> --dest-creds admin:<pwd2> \
          --src-tls-verify=false --dest-tls-verify=false \
          "docker://harbor.internal/${repo}:${tag}" \
          "docker://harbor-new.internal/${repo}:${tag}"
      done
done < /tmp/repos.txt

9.6 迁移到新机器

# 旧机
/opt/scripts/harbor-backup.sh
scp -r /backup/harbor/2026-11-05 root@new-host:/backup/harbor/

# 新机
# 1) 装 docker + compose
# 2) 装同版本 harbor(只需解压,不 install)
tar xzf harbor-offline-installer-v2.15.4.tgz -C /opt
cd /opt/harbor

# 3) 恢复配置与数据(同 9.4)
# 4) 改 hostname(若域名变了)
vim harbor.yml
./prepare
docker compose up -d

# 5) DNS 切换
# 6) 客户端更新 CA(若证书变了)

10. 升级

10.1 升级前检查

# 1) 当前版本
curl -sk https://harbor.internal/api/v2.0/systeminfo | jq '.harbor_version'

# 2) 确认升级路径(只能 n-2)
# 2.12 → 2.13 → 2.14 → 2.15
# 2.13 → 2.15(可直接,因为 2.15 是 2.13 的 n+2)

# 3) 读 release notes 的 breaking changes
# https://github.com/goharbor/harbor/releases

# 4) 备份(必须)
/opt/scripts/harbor-backup.sh

# 5) 确认磁盘余量(升级需临时空间)
df -h /data /opt

10.2 升级步骤

cd /opt/harbor

# 1) 停服务
docker compose down

# 2) 备份旧目录(回滚用)
mv /opt/harbor /opt/harbor.bak-$(date +%F)

# 3) 解压新版
tar xzf harbor-offline-installer-v2.15.4.tgz -C /opt
cd /opt/harbor

# 4) 迁移配置(关键:不能直接用旧 yml,字段会变)
cp /opt/harbor.bak-$(date +%F)/harbor.yml ./harbor.yml
# 对比新版模板,补齐新增字段
diff harbor.yml.tmpl harbor.yml

# 5) 执行升级安装
./install.sh --with-trivy

# 6) 验证
docker compose ps
curl -sk https://harbor.internal/api/v2.0/systeminfo | jq '.harbor_version'
curl -sk https://harbor.internal/api/v2.0/health
docker pull harbor.internal/library/nginx:1.25

重要:install.sh 在已有数据目录上执行 = 升级(会跑数据库 migration)。不要删 /data/harbor。

10.3 升级后检查

# 1) 数据库 migration 是否成功
docker compose logs harbor-core | grep -iE 'migrat|error' | tail -20

# 2) 服务健康
docker compose ps
for c in $(docker compose ps -q); do docker inspect --format '{{.Name}} {{.State.Health.Status}}' $c; done

# 3) 登录与推拉
docker login harbor.internal -u admin
docker pull harbor.internal/library/nginx:1.25

# 4) 复制、扫描、GC 是否正常
curl -sk -u admin:<pwd> https://harbor.internal/api/v2.0/replication/policies | jq '.[].name'
curl -sk -u admin:<pwd> "https://harbor.internal/api/v2.0/projects?page_size=10" | jq '.[].name'

# 5) 审计日志
curl -sk -u admin:<pwd> "https://harbor.internal/api/v2.0/audit-logs?page_size=10" | jq '.[] | {op_time,username,operation}'

10.4 回滚

cd /opt
docker compose -f harbor/docker-compose.yml down 2>/dev/null || true

# 恢复旧目录
mv harbor harbor.failed-$(date +%F)
mv harbor.bak-2026-11-05 harbor
cd harbor

# 恢复数据(升级已改过数据库结构,必须回到备份的数据)
rm -rf /data/harbor
tar xzf /backup/harbor/2026-11-05/harbor-data.tar.gz -C /data

./prepare
docker compose up -d

curl -sk https://harbor.internal/api/v2.0/systeminfo | jq '.harbor_version'

回滚的代价:升级后到回滚之间的数据(新推的镜像、新加的用户)会全部丢失。所以升级要选在窗口期,且升级后立即验证。

10.5 Helm 方式升级

helm repo update
helm search repo harbor/harbor --versions | head -3

# 先 dry-run 看变更
helm upgrade harbor harbor/harbor -n harbor -f values.yaml --dry-run

# 执行
helm upgrade harbor harbor/harbor -n harbor -f values.yaml

# 回滚
helm history harbor -n harbor
helm rollback harbor <revision> -n harbor

11. API 运维

11.1 认证

# Basic(简单脚本用)
curl -sk -u admin:<pwd> https://harbor.internal/api/v2.0/projects

# Session(浏览器行为)
curl -sk -i -X POST https://harbor.internal/c/login \
  -d 'principal=admin&password=<pwd>' -c /tmp/harbor-cookie
curl -sk -b /tmp/harbor-cookie https://harbor.internal/api/v2.0/projects

# Robot 账号(推荐自动化用)
curl -sk -u 'robot$ci-pusher:<secret>' https://harbor.internal/api/v2.0/projects

11.2 常用端点

端点 方法 作用
/api/v2.0/health GET 健康检查
/api/v2.0/systeminfo GET 版本、存储、认证方式
/api/v2.0/ping GET 存活
/api/v2.0/projects GET/POST 项目列表/创建
/api/v2.0/projects/{name} GET/PUT/DELETE 项目详情/更新/删除
/api/v2.0/projects/{name}/repositories GET 仓库列表
/api/v2.0/projects/{p}/repositories/{r}/artifacts GET artifact 列表
.../artifacts/{ref} GET/DELETE 查/删 artifact
.../artifacts/{ref}/scan POST 触发扫描
.../artifacts/{ref}/additions/vulnerabilities GET 漏洞
.../artifacts/{ref}/additions/sbom GET SBOM
/api/v2.0/registries GET/POST 复制目标
/api/v2.0/replication/policies GET/POST 复制规则
/api/v2.0/replication/executions GET/POST 复制执行
/api/v2.0/system/gc GET GC 历史
/api/v2.0/system/gc/schedule POST 触发 GC
/api/v2.0/quotas GET/PUT 配额
/api/v2.0/users GET/POST 用户
/api/v2.0/audit-logs GET 审计日志
/api/v2.0/statistics GET 统计

11.3 实战脚本

# 统计各项目镜像数量与大小
for p in $(curl -sk -u admin:<pwd> 'https://harbor.internal/api/v2.0/projects?page_size=100' | jq -r '.[].name'); do
  cnt=$(curl -sk -u admin:<pwd> \
    "https://harbor.internal/api/v2.0/projects/$p/repositories?page_size=1000" | jq 'length')
  echo "$p: $cnt repos"
done

# 找最大的 20 个 artifact
curl -sk -u admin:<pwd> \
  "https://harbor.internal/api/v2.0/projects/web/repositories/app/artifacts?page_size=100&with_tag=true" \
  | jq -r '.[] | "\(.size) \(.tags[0].name // .digest)"' | sort -rn | head -20

# 批量给所有项目开自动扫描
for id in $(curl -sk -u admin:<pwd> 'https://harbor.internal/api/v2.0/projects?page_size=100' | jq -r '.[].project_id'); do
  curl -sk -u admin:<pwd> -X PUT "https://harbor.internal/api/v2.0/projects/$id" \
    -H 'Content-Type: application/json' \
    -d '{"metadata":{"auto_scan":"true"}}' >/dev/null && echo "project $id ok"
done

# 导出所有项目的镜像清单(盘点/审计)
for p in $(curl -sk -u admin:<pwd> 'https://harbor.internal/api/v2.0/projects?page_size=100' | jq -r '.[].name'); do
  curl -sk -u admin:<pwd> "https://harbor.internal/api/v2.0/projects/$p/repositories?page_size=1000" \
    | jq -r --arg p "$p" '.[] | "\($p)/\(.name)"'
done > /backup/harbor-repos.txt

11.4 Registry v2 API(原生,绕过 Harbor 元数据)

# 列仓库
curl -sk -u admin:<pwd> https://harbor.internal/v2/_catalog

# 列 tag
curl -sk -u admin:<pwd> https://harbor.internal/v2/library/nginx/tags/list

# 取 manifest 与 digest
curl -sk -u admin:<pwd> -I \
  -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \
  https://harbor.internal/v2/library/nginx/manifests/1.25

# 删 manifest(Harbor 侧建议用 API,原生删除不会更新元数据)
curl -sk -u admin:<pwd> -X DELETE \
  https://harbor.internal/v2/library/nginx/manifests/sha256:xxx

不要混用:用 registry 原生 API 删的 tag,Harbor 数据库里仍认为存在(UI 显示但拉不到)。删除统一走 Harbor API。


12. 日志与监控

12.1 日志位置

ls -lh /data/harbor/logs/
tail -f /data/harbor/logs/harbor-core.log
tail -f /data/harbor/logs/harbor-portal.log
tail -f /data/harbor/logs/jobservice/jobservice.log
tail -f /data/harbor/logs/registry/registry.log
tail -f /data/harbor/logs/postgresql/postgresql.log
tail -f /data/harbor/logs/redis/redis.log

# 容器 stdout
docker compose logs -f --tail=200 harbor-core
docker compose logs --since 10m harbor-jobservice | grep -i error

调整日志级别:

log:
  level: debug      # 排障用,日志量很大
./prepare && docker compose up -d

12.2 指标

# Harbor 自带 exporter(端口 9090,内部)
curl -sk https://harbor.internal/metrics 2>/dev/null | head
# 或内部直连
docker compose exec harbor-core curl -s localhost:9090/metrics | head -30

关键指标:

指标 含义
harbor_project_total 项目数
harbor_repo_total 仓库数
harbor_artifact_total artifact 数
harbor_project_quota_usage_byte 项目配额使用
harbor_artifact_pulled 拉取次数
harbor_jobservice_task_total 任务数
harbor_jobservice_task_fail_total 任务失败数
# Prometheus 抓取配置
scrape_configs:
  - job_name: harbor
    scheme: https
    tls_config:
      insecure_skip_verify: true
    static_configs:
      - targets: ['harbor.internal']
    metrics_path: /metrics
    basic_auth:
      username: admin
      password: <pwd>

12.3 审计日志

# API
curl -sk -u admin:<pwd> \
  "https://harbor.internal/api/v2.0/audit-logs?page_size=50&q=operation=delete" | jq

# 查某人操作
curl -sk -u admin:<pwd> \
  "https://harbor.internal/api/v2.0/audit-logs?q=username%3Dalice&page_size=100" | jq

# 时间范围(q 用 URL 编码)
curl -sk -u admin:<pwd> \
  "https://harbor.internal/api/v2.0/audit-logs?q=op_time%3D%5B2026-11-01~2026-11-05%5D" | jq

12.4 巡检脚本

#!/usr/bin/env bash
set -uo pipefail
H=https://harbor.internal
U=admin
P='<pwd>'

echo "===== harbor check $(date '+%F %T') ====="
echo "--- 版本"; curl -sk -u $U:$P $H/api/v2.0/systeminfo | jq -r '.harbor_version'
echo "--- 健康"; curl -sk $H/api/v2.0/health | jq -c '{status, components:[.components[]|{name,status}]}'
echo "--- 容器"; cd /opt/harbor && docker compose ps --format 'table {{.Name}}\t{{.Status}}'
echo "--- 磁盘"; df -h /data | tail -1
echo "--- 数据占用"; du -sh /data/harbor/registry /data/harbor/database
echo "--- 项目数"; curl -sk -u $U:$P "$H/api/v2.0/projects?page_size=1" -I | grep -i 'X-Total-Count'
echo "--- 失败任务"
curl -sk -u $U:$P "$H/api/v2.0/replication/executions?page_size=20" \
  | jq -r '.[] | select(.status=="Failed") | "\(.id) \(.policy_id) \(.status_text)"' | head
echo "--- 最近 GC"
curl -sk -u $U:$P "$H/api/v2.0/system/gc" | jq -r '.[-1] | "\(.status) \(.creation_time)"'
echo "--- 大日志"
find /data/harbor/logs -name '*.log' -size +200M -printf '%s\t%p\n' 2>/dev/null

13. 故障速查

13.1 部署与启动

现象 排查 处理
install.sh 报端口占用 ss -lntp | grep -E ':80|:443' 停占位服务或改端口
容器反复重启 docker compose logs <svc> 多为 DB/Redis 连不上
harbor-core 起不来 docker compose logs harbor-core 数据库密码错、migration 失败
页面 502 docker compose ps 后端容器挂了
页面白屏 浏览器控制台、portal 日志 portal 镜像版本不匹配
permission denied 写 /data/harbor 目录属主/SELinux chown -R 10000:10000 /data/harbor;chcon -Rt container_file_t

13.2 登录与证书

现象 原因 处理
x509: certificate signed by unknown authority 客户端不信任 CA 配 certs.d 或系统信任
x509: certificate is valid for X, not Y 证书 SAN 不含访问域名 重签证书,加 SAN
dial tcp ... connect: connection refused 端口不通 查防火墙/端口映射
400 Bad Request 且是 HTTP 仓库 客户端按 HTTPS 访问 配 insecure-registry 或用 HTTPS
登录成功但 push 401 robot 权限不足 检查 robot 的 action
denied: requested access to the resource is denied 项目不存在/无权限 建项目、加成员
浏览器登录正常、CLI 失败 开了 OIDC 但 CLI 未用 robot CI 用 robot 账号

13.3 推送与拉取

现象 原因 处理
push 报 413 Request Entity Too Large nginx client_max_body_size 太小 改 proxy 配置
push 中断后重试失败 uploads 残片 清 /data/harbor/registry/uploads
push 成功但 UI 看不到 core 与 registry 不一致 重启 core;查 DB
pull 报 manifest unknown tag 不存在或已删 查 artifact 列表
pull 很慢 网络/磁盘/无缓存 就近部署、加缓存层
no space left on device 磁盘满 清 tag + GC(见 8.5)
大镜像 push 超时 nginx/registry 超时 调 proxy_read_timeout、registry timeout
# docker-compose.override.yml 调 nginx(若用自带 proxy)
services:
  proxy:
    volumes:
      - ./common/config/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
# nginx.conf 关键项
client_max_body_size 0;          # 0 = 不限制
proxy_read_timeout 900s;
proxy_send_timeout 900s;
proxy_request_buffering off;

13.4 数据库与 Redis

现象 排查 处理
harbor-core 报 pq: password authentication failed DB 密码不一致 改 harbor.yml 的 database.password,./prepare 重启
too many connections 连接池/并发过大 调 max_open_conns,查慢查询
DB 磁盘满 du -sh /data/harbor/database 清审计日志(保留策略)、扩盘
Redis 连不上 docker compose logs redis 检查密码、内存上限
登录变慢/会话丢失 Redis 内存满(eviction) 提内存或设 maxmemory-policy
jobservice 任务不执行 Redis 队列异常 重启 jobservice + redis

13.5 复制与扫描

现象 排查 处理
复制一直 InProgress jobservice 卡住 重启 jobservice
复制 Failed 401/x509 目标凭据/证书 更新 endpoint
扫描一直 Pending 队列积压 加 max_job_workers
扫描 Failed 镜像层问题/漏洞库损坏 重推镜像;重建 trivy db
扫描 0 结果 漏洞库未更新 检查 skip_update
prevent_vul 不拦 未扫描 开 auto_scan

13.6 GC 与空间

现象 原因 处理
删了 tag 空间没降 未跑 GC 触发 GC(见 8.4)
GC 卡在 Running registry 有活跃连接 低峰执行;必要时重启
GC 后镜像拉不到 GC 误删(并发 push) 从备份恢复;GC 期间禁止 push
磁盘持续增长 uploads 残片 / 日志 开 upload_purging;日志轮转
du 与 UI 统计不符 UI 按 DB 算,含引用但未 GC 的 blob 正常,GC 后一致

13.7 快速定位命令

cd /opt/harbor

# 全量状态
docker compose ps
docker compose logs --tail=50 harbor-core
docker compose logs --tail=50 harbor-jobservice
docker compose logs --tail=50 registry
docker compose logs --tail=50 postgresql

# 健康
curl -sk https://harbor.internal/api/v2.0/health | jq
curl -sk https://harbor.internal/api/v2.0/ping

# 端口
ss -lntp | grep -E ':80|:443'

# 磁盘
df -h /data
du -sh /data/harbor/*

# DB 连通(容器内)
docker compose exec postgresql pg_isready -U postgres
docker compose exec postgresql psql -U postgres -d registry -c '\dt' | head

# Redis
docker compose exec redis redis-cli ping

14. 卸载

卸载前:① 所有镜像已迁移或备份;② 客户端已切到新仓库;③ 数据已全量备份(见 9.2)。

14.1 标准卸载

cd /opt/harbor

# 1) 停并删容器(保留数据)
docker compose down

# 2) 备份后再删数据(★ 不可逆)
# /opt/scripts/harbor-backup.sh     ← 先执行
rm -rf /data/harbor

# 3) 删程序目录
rm -rf /opt/harbor

# 4) 删镜像
docker images | grep -E 'goharbor|harbor' | awk '{print $1":"$2}' | xargs -r docker rmi

# 5) 删客户端信任与登录态
rm -rf /etc/docker/certs.d/harbor.internal
rm -rf /etc/containerd/certs.d/harbor.internal
docker logout harbor.internal

# 6) 删 k8s 里的 imagePullSecret(若有)
kubectl delete secret harbor-cred -n prod --ignore-not-found

14.2 Helm 卸载

helm uninstall harbor -n harbor
kubectl delete ns harbor

# PVC 通常不会自动删(resourcePolicy: keep)
kubectl get pvc -n harbor
kubectl delete pvc --all -n harbor     # ★ 数据会丢

# 删 CRD(若有)
kubectl get crd | grep harbor

14.3 只清数据(重置)

cd /opt/harbor
docker compose down
rm -rf /data/harbor
mkdir -p /data/harbor
./prepare
docker compose up -d
# 相当于全新安装,admin 密码回到 harbor.yml 中的值

14.4 卸载后验证

检查 命令 期望
容器 docker ps -a | grep -E 'harbor|goharbor' 无
端口 ss -lntp | grep -E ':80|:443' 无(或非 Harbor)
数据 ls /data/harbor 不存在
程序 ls /opt/harbor 不存在
镜像 docker images | grep goharbor 无
网络 docker network ls | grep harbor 无
# compose 创建的网络可能残留
docker network rm harbor_harbor 2>/dev/null

15. 附录

15.1 关键路径

路径 内容
/opt/harbor/harbor.yml 主配置
/opt/harbor/docker-compose.yml 编排文件(prepare 生成)
/opt/harbor/common/config/ 各组件配置(prepare 生成,勿直改)
/opt/harbor/cert/ 证书
/data/harbor/ 数据根(data_volume)
/data/harbor/registry/ 镜像 blob
/data/harbor/registry/uploads/ 未完成上传残片
/data/harbor/database/ PostgreSQL 数据
/data/harbor/redis/ Redis 数据
/data/harbor/secretkey 加密密钥(必须备份)
/data/harbor/logs/ 各组件日志
/data/harbor/trivy-adapter/trivy/db/ 漏洞库
/data/harbor/jobservice/jobs/ 任务日志

15.2 端口

端口 组件 是否对外
80 proxy(HTTP) 是(建议重定向 443)
443 proxy(HTTPS) 是
4443 core(HTTPS 直连) 否
5432 PostgreSQL 否
6379 Redis 否
5000 registry 否
8080 jobservice 否
9090 exporter/metrics 否(给 Prometheus)

15.3 默认账号

项 值
用户名 admin
初始密码 Harbor12345(或 harbor.yml 中的 harbor_admin_password)
Robot 用户名格式 robot$<project>+<name> 或 robot$<name>

首次登录必须改密码。生产建议接 OIDC/LDAP,admin 只保留应急用。

15.4 健康检查与常用 curl

# 健康
curl -sk https://harbor.internal/api/v2.0/health | jq
# {
#   "status": "healthy",
#   "components": [
#     {"name": "core", "status": "healthy"},
#     {"name": "database", "status": "healthy"},
#     {"name": "jobservice", "status": "healthy"},
#     {"name": "portal", "status": "healthy"},
#     {"name": "redis", "status": "healthy"},
#     {"name": "registry", "status": "healthy"},
#     {"name": "registryctl", "status": "healthy"}
#   ]
# }

# 存活
curl -sk https://harbor.internal/api/v2.0/ping

# 版本与配置
curl -sk https://harbor.internal/api/v2.0/systeminfo | jq '{
  harbor_version, auth_mode, registry_url, project_creation_restriction,
  has_ca_root, with_trivy, with_chartmuseum, read_only
}'

# 统计
curl -sk -u admin:<pwd> https://harbor.internal/api/v2.0/statistics | jq

15.5 容量规划参考

场景 镜像数 日 push 数 建议存储 GC 频率
小团队(<20 人) < 500 < 50 500G 每月
中型(20~100 人) 500~5000 50~500 2T 每两周
大型(>100 人) > 5000 > 500 10T+ / 对象存储 每周

经验值:单个 Java 镜像 300600MB,Node 镜像 200400MB,Go 静态镜像 20~80MB。按 tag 数量 × 平均大小 × 1.3(未 GC 的冗余)估算。

15.6 与其他手册的配合

  • Docker 客户端配置与证书:Docker-常用运维指令手册.md
  • containerd 节点信任配置:containerd-常用运维指令手册.md
  • k8s 拉取私有镜像(imagePullSecrets):Kubernetes-常用运维指令手册.md
  • Podman 登录与镜像搬运:Podman-常用运维指令手册.md(skopeo 用法)
  • 宿主磁盘与防火墙:CentOS-7-10-常规运维指令手册.md

一句话总结:Harbor 运维的核心是空间治理——保留策略(retention)决定删什么,GC 决定真正释放。两者必须配套,且 GC 期间不能 push。其次是证书链——客户端、containerd、k8s 三处都要信任 CA,且 hostname 必须是域名不是 IP。把这两件事做对,Harbor 基本不需要日常干预。