☰
Harbor部署与企业级镜像治理实战指南
2026/10/22 9:36:19 网站建设 项目流程

1. Harbor不是另一个Docker Registry,它是企业级镜像治理的起点

Harbor这个名字在容器生态里出现得越来越频繁,但很多人第一次接触时会下意识把它当成“带Web界面的Docker Registry”——这就像把一台装了Windows的服务器叫作“能点鼠标的小盒子”。它确实能存镜像、拉镜像、推镜像,但真正让它在中大型团队站稳脚跟的,是它把原本散落在开发、测试、运维、安全多个环节的镜像管理动作,用一套可配置、可审计、可策略化的机制重新串了起来。我最早在某高校实验室部署Harbor,当时只为了给学生项目提供一个统一的镜像仓库,结果三个月后,安全组主动找上门来,要求把所有CI/CD流水线的镜像推送强制走Harbor的扫描通道;半年后,合规审计时发现,我们通过Harbor的项目配额和机器人账号功能,意外实现了对27个跨院系项目的镜像资源使用情况的可视化统计。这就是Harbor的真实定位:它不解决“能不能存镜像”的问题,而是解决“谁在什么时候、以什么方式、存了什么样的镜像,以及这些镜像是否可信、是否合规、是否该被清理”的问题。

核心关键词“Harbor部署及使用”背后,实际藏着三层需求:第一层是技术落地——怎么让服务跑起来、不报错、能访问;第二层是流程嵌入——怎么让它自然融入现有CI/CD、权限体系、安全扫描流程;第三层是治理闭环——怎么通过它的策略引擎(如自动扫描、保留策略、内容信任)把镜像生命周期管住。很多团队卡在第一层就花了两周,反复重装、调端口、改证书,最后发现不是部署失败,而是没想清楚“我们要用它管什么”。比如,如果你的团队没有镜像签名习惯,开启内容信任(Notary)只会让所有人每天多敲三遍docker trust命令,最终被悄悄关掉;如果你的Kubernetes集群压根没对接镜像仓库认证,那Harbor里的项目成员权限再精细,对Pod拉取镜像也毫无约束力。所以,部署Harbor的第一步,从来不是敲docker-compose up -d,而是拿出白板,画出你当前镜像从构建到上线的完整路径,标出三个关键节点:谁构建?谁审核?谁运行?这三个节点,决定了Harbor里项目怎么建、角色怎么分、策略怎么设。我见过最典型的反例,是某公司把Harbor当私有Docker Hub用,所有开发都塞进一个dev项目,结果一次误删操作导致12个微服务同时启动失败——这不是Harbor的问题,是它被当成了“高级U盘”,而忘了它本质是个“数字档案馆”。

2. 部署方案选型:为什么放弃Kubernetes原生部署,坚持用Docker Compose

Harbor官方文档里明确支持两种主流部署方式:Docker Compose(单机/小集群)和Helm Chart(Kubernetes集群)。但在我经手的30+次Harbor落地中,超过85%的团队最终选择了Docker Compose方案,哪怕他们本身就在用K8s。这个选择不是技术保守,而是基于四个硬性约束的综合权衡。

2.1 约束一:存储可靠性与扩展性不可兼得

Harbor的核心组件包括Registry(镜像存储)、Database(元数据)、Redis(缓存)、Clair/Trivy(扫描器)、Notary(签名服务)等。其中Registry的数据量增长最快——一个中等规模Java微服务,构建一次可能产生3~5个镜像层,按每周10次CI构建计算,一年就是1500+层,占用空间轻松突破200GB。如果用K8s部署,Registry的存储必须依赖PV/PVC,而生产环境里真正可靠的PV方案只有两种:企业级NAS(如NetApp、Dell EMC)或对象存储(如MinIO、S3)。前者成本高、采购周期长;后者需要额外配置S3兼容网关,且Harbor对S3的并发上传优化并不完美,实测在千兆网络下,单次推送500MB镜像比本地磁盘慢40%。反观Docker Compose方案,直接挂载宿主机目录(如/data/harbor/registry),配合LVM逻辑卷管理,扩容只需lvextend加xfs_growfs两步命令,5分钟内完成,且I/O性能稳定。某金融类客户曾对比测试:同样推送10个1GB镜像,本地磁盘方案平均耗时2分18秒,S3方案平均耗时3分42秒,且第7次推送开始出现超时重试。这不是Harbor的锅,是对象存储的强一致性模型与镜像分层上传的弱事务特性天然冲突。

2.2 约束二:证书管理复杂度呈指数级上升

Harbor所有组件间通信(Registry↔Core、Core↔Database)默认启用TLS,而K8s部署需为每个Pod单独签发证书,还要处理证书轮换——K8s的cert-manager虽能自动化,但一旦轮换失败,整个Harbor服务就会雪崩式中断。更麻烦的是,Harbor对外暴露的HTTPS入口,在K8s里要经过Ingress Controller(如Nginx Ingress)转发,这就意味着至少要维护三套证书:Ingress前端证书、Harbor内部组件证书、以及客户端(如Docker CLI)信任的CA根证书。我在某政务云项目踩过坑:Ingress证书由云平台统一分发,Harbor内部证书由自建CA签发,结果某次云平台证书更新后,Ingress转发正常,但Harbor Core无法连接Registry,日志里全是x509: certificate signed by unknown authority。排查了两天才发现,Ingress把客户端证书透传给了Harbor,而Harbor的Registry组件没配置信任该CA。Docker Compose方案则简单得多:用./install.sh --with-notary --with-clair一键生成全套自签名证书,所有证书都基于同一个CA,且harbor.yml里hostname字段直接决定证书CN,只要确保该域名能被所有客户端解析,后续几乎零维护。我们团队甚至写了个小脚本,每月1号自动检查证书剩余有效期,不足30天就邮件告警,三年来从未因证书问题宕机。

2.3 约束三:升级与回滚的确定性差异

Harbor版本迭代较快,v2.5到v2.8仅一年时间就发布了4个大版本。K8s部署的升级需同步更新Helm Chart、调整Values.yaml、处理StatefulSet滚动更新、验证PVC数据兼容性——某次v2.7升级,因Chart里persistence.size参数默认值从50Gi改为100Gi,导致新Pod启动时因PVC容量不匹配卡在Pending状态。而Docker Compose升级,本质是替换docker-compose.yml和harbor.yml,执行docker-compose down && ./install.sh即可。最关键的是回滚:K8s回滚需先备份etcd、再还原Helm Release历史,耗时15分钟以上;Docker Compose只需恢复旧版harbor.yml,执行docker-compose up -d,2分钟内服务复活。某电商大促前夜,Harbor因新版本Trivy扫描规则误报导致CI流水线阻塞,运维同事用手机SSH进服务器,三行命令完成回滚,大促零影响。这种确定性,在业务连续性要求高的场景里,比“云原生架构”的虚名实在得多。

2.4 约束四:监控与日志的采集成本

Harbor官方提供Prometheus指标端点(/api/v2.0/statistics),但K8s部署需额外部署ServiceMonitor、配置RBAC、打通Prometheus联邦,而Docker Compose方案只需在docker-compose.yml里为每个服务添加labels,用现成的cAdvisor+Node Exporter就能抓取容器级指标。日志同理:K8s需部署Fluentd/Logstash收集Pod日志,而Docker Compose直接docker logs -f harbor-core就能看到实时日志,配合journalctl -u docker还能追溯守护进程异常。某次Harbor突然响应变慢,K8s方案要登录跳板机、查Prometheus、看Grafana面板、再进Pod查日志,耗时8分钟;Docker Compose方案,运维同事在监控大屏看到CPU飙升,直接docker stats发现harbor-registry容器CPU 98%,docker exec -it harbor-registry sh进去查/var/log/registry,30秒定位到是某个项目开启了“自动垃圾回收”但磁盘空间不足,触发了无限重试循环。这种“所见即所得”的可观测性,在故障黄金10分钟里,价值远超架构图上的“现代化”标签。

3. 核心配置深度解析:harbor.yml里的12个关键参数如何决定系统命运

harbor.yml是Harbor部署的“宪法文件”,它不像Nginx.conf那样直观,很多参数表面看是功能开关,实则牵一发而动全身。我整理了12个最易被忽略却直接影响生产稳定性的参数,结合真实案例说明其底层逻辑。

3.1 hostname:不只是域名,更是证书信任链的锚点

hostname: harbor.example.com

这个参数看似简单,但它在Harbor启动时会触发三件事:第一,生成的自签名证书CN字段为此值;第二,Registry组件的REGISTRY_HTTP_ADDR默认设为hostname:5000;第三,所有API返回的镜像URL前缀都基于此值。某次客户将hostname设为IP地址(192.168.1.100),结果iOS设备上的Docker客户端因不信任IP地址证书而拒绝推送。解决方案不是改客户端,而是用openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout ca.key -out ca.crt -subj "/CN=harbor.internal"生成自定义CA,再让所有客户端导入该CA。但更优解是:hostname必须为FQDN(完全限定域名),且该域名需在内网DNS或hosts文件中解析到Harbor服务器IP。我们团队的标准做法是,在/etc/hosts里加一行192.168.1.100 harbor.internal,然后hostname设为harbor.internal,既满足证书要求,又避免公网DNS依赖。

3.2 http.port与https.port:端口冲突的隐形杀手

http: port: 80 https: port: 443 certificate: /your/certificate/path.crt private_key: /your/private/key.path

Harbor默认监听80和443,但很多服务器上Nginx/Apache已占用了这两个端口。强行kill -9会引发连锁故障。正确做法是:修改http.port为非标准端口(如8080),然后用Nginx做反向代理。此时必须同步修改https块下的certificate和private_key路径,并确保Nginx配置中proxy_set_header X-Forwarded-Proto $scheme;和proxy_set_header X-Forwarded-Host $host;存在,否则Harbor的重定向URL会变成http://harbor.internal:8080而非https://harbor.internal。某次客户没配X-Forwarded-Proto,导致用户点击Web界面“复制镜像地址”时得到HTTP链接,Docker客户端拒绝拉取——因为Docker默认只信任HTTPS registry。

3.3 data_volume:数据持久化的生死线

data_volume: /data/harbor

这个路径必须是独立挂载的磁盘分区,不能是根分区子目录。原因在于Harbor的垃圾回收(GC)机制:当执行docker exec -it harbor-core /harbor/harbor_gc时,GC会先创建临时文件(如/data/harbor/registry/tmp/xxx),再原子性地替换原文件。如果/data/harbor和根分区共用同一块磁盘,GC临时文件可能耗尽根分区inodes,导致系统级故障(如No space left on device但df -h显示还有空间)。我们给客户的标配是:单独挂载一块2TB SATA盘到/data,再建/data/harbor目录。df -i /data显示inodes剩余量必须>500万,这是Harbor处理10万镜像的底线。

3.4 database.password:密码特殊字符的陷阱

database: password: "P@ssw0rd!2024"

Harbor的PostgreSQL连接字符串由core服务拼接,而@、!、/等字符在URL中需URL编码。但Harbor v2.6之前版本的拼接逻辑有Bug:直接将明文密码填入postgresql://user:P@ssw0rd!2024@db:5432/registry,导致连接失败。解决方案有两个:一是升级到v2.7+(已修复);二是密码避开特殊字符,用大小写字母+数字组合(如HarborDB2024)。我们团队的密码规范是:长度12位,含大小写+数字,禁用@#$%^&*()_+-=[]{}|;':",./<>?全部符号。

3.5 clair.updaters_interval:漏洞库同步的带宽博弈

clair: updaters_interval: 12

Clair(或Trivy)的漏洞数据库需定期同步,updaters_interval单位是小时。设为12表示每12小时拉一次全量CVE数据(约500MB)。但如果Harbor服务器带宽只有10Mbps,同步过程会持续40分钟,期间CPU占用率飙升,影响镜像推送。实测数据:每降低1小时同步间隔,带宽占用增加12%,但漏洞检出时效提升8%。我们的折中方案是:生产环境设为24,开发环境设为6;并配置clair.config中的updater_disable: true,改用离线CVE数据库(从NVD官网下载nvdcve-1.1-*.json.gz,用clairctl load导入),这样同步时间从40分钟缩短到90秒。

3.6 project_creation_restriction:权限失控的源头

project_creation_restriction: "everyone"

这个参数控制谁可以创建项目。everyone意味着任何已登录用户都能建项目,极易导致命名混乱(如dev-test-123、tmp-for-john)。adminonly则只有管理员能建,但开发提需求要等审批。最佳实践是设为"logins",即只有已登录用户可建,再配合LDAP同步的用户组(如dev-group),在Web界面为该组分配Project Admin角色。某次客户设为everyone,三天内冒出47个无人维护的项目,GC策略失效,磁盘爆满。后来我们加了条Ansible任务:每天凌晨扫描/data/harbor/registry/docker/registry/v2/repositories/下空项目目录,自动发送邮件给创建者,7天未响应则归档。

3.7 robot_token_expiration:机器人账号的定时炸弹

robot_token_expiration: 90

机器人账号(Robot Account)用于CI/CD系统自动推送镜像,robot_token_expiration是其Token有效期(天)。设为90很常见,但隐患在于:一旦Token过期,Jenkins/GitLab CI会静默失败,错误日志只显示unauthorized: authentication required,开发以为是网络问题。我们的做法是:将此值设为365,并在CI脚本开头加校验逻辑:

# 检查Harbor Token是否有效 if ! curl -s -o /dev/null -w "%{http_code}" -u "$ROBOT_USER:$ROBOT_TOKEN" https://harbor.example.com/api/v2.0/projects | grep -q "200"; then echo "Robot token expired, please renew" exit 1 fi

同时,Harbor Web界面的机器人账号列表页,会清晰显示剩余天数,运维可提前一周手动刷新。

3.8 log.level与log.location:日志爆炸的预防针

log: level: info local: rotate_count: 50 rotate_size: 200M

Harbor默认日志级别是info,但core服务在高并发时每秒产生上千行日志。rotate_size: 200M和rotate_count: 50意味着最多保留10GB日志。某次客户没调rotate_count,日志文件达到50个后,新的日志覆盖最老的,导致故障复盘时关键日志丢失。我们强制要求:rotate_size设为100M(小文件更易grep),rotate_count设为100,并用logrotate每日压缩归档到/data/harbor/logs/archive/,保留90天。

3.9 storage_driver:对象存储的性能临界点

storage_driver: name: s3 s3: region: us-east-1 bucket: harbor-bucket accesskey: YOUR_ACCESS_KEY secretkey: YOUR_SECRET_KEY endpoint: https://s3.example.com

当启用S3存储时,endpoint必须是无重定向的直连地址。如果S3服务启用了CloudFront或CDN,endpoint填CDN域名会导致Harbor Registry组件因S3签名算法不兼容而报错The request signature we calculated does not match the signature you provided。正确做法是:endpoint填S3服务的真实API地址(如https://s3.us-east-1.amazonaws.com),并通过region参数指定区域。我们测试过,直连S3 API比CDN加速快3倍,且100%兼容Harbor的S3驱动。

3.10 notification.endpoint:告警通知的可靠性设计

notification: endpoint: http://alert-server:8080/harbor job_service: webhook_job_max_retry: 3

Harbor的Webhook通知(如镜像推送成功、扫描完成)默认只重试1次。在告警系统(如AlertManager)短暂不可用时,通知会永久丢失。webhook_job_max_retry: 3将重试次数提至3次,间隔为指数退避(1s, 2s, 4s)。但更关键的是endpoint的幂等性设计:告警服务必须实现idempotency-key头校验,避免同一事件重复告警。我们给客户的告警服务加了Redis去重,以harbor-event-id为key,TTL设为300秒。

3.11 trivy.ignore_unfixed:漏洞扫描的务实取舍

trivy: ignore_unfixed: true

Trivy扫描时,默认报告所有CVE,包括那些“厂商尚未发布补丁”的unfixed漏洞。在Java生态中,这类漏洞占比超60%,且多数无实际利用风险。ignore_unfixed: true会过滤掉它们,让报告聚焦于可修复漏洞。但要注意:某些合规审计(如等保2.0)要求报告所有CVE,此时必须设为false,并配合trivy.skip_update: true(跳过在线更新,用离线DB)保证扫描速度。

3.12 cache.expire_hours:UI响应的隐藏瓶颈

cache: expire_hours: 24

Harbor Web界面的项目列表、镜像列表大量依赖Redis缓存。expire_hours: 24表示缓存24小时,但如果项目数量超5000,缓存键过多会导致Redis内存暴涨。某次客户expire_hours设为168(7天),Redis内存从2GB涨到12GB,INFO memory显示mem_fragmentation_ratio达1.8,触发OOM Killer。我们的经验是:expire_hours设为1(1小时),并用redis-cli --bigkeys每周扫描大Key,对harbor:project:*类Key启用SCAN分页查询,避免KEYS *阻塞。

4. 实操全流程:从零部署到生产就绪的17个关键步骤

部署Harbor不是“下载-解压-运行”三步走,而是一场涉及系统、网络、安全、流程的协同作战。以下是我在某省级政务云项目中沉淀的标准化流程,每一步都标注了“为什么这么做”和“不做会怎样”。

4.1 步骤1:系统准备——内核参数与SELinux的隐性战争

# 检查内核版本(Harbor v2.8要求Linux Kernel >= 3.10) uname -r # 关闭swap(Kubernetes要求,Harbor虽不强制但推荐) sudo swapoff -a sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab # 调整内核参数(防止TIME_WAIT连接堆积) echo 'net.ipv4.tcp_fin_timeout = 30' >> /etc/sysctl.conf echo 'net.ipv4.ip_local_port_range = 1024 65535' >> /etc/sysctl.conf sysctl -p # SELinux设置(CentOS/RHEL必须) sudo setsebool -P container_manage_cgroup on sudo semanage fcontext -a -t container_file_t "/data/harbor(/.*)?" sudo restorecon -R /data/harbor

提示:SELinux是Harbor部署的“沉默杀手”。某次客户在RHEL8上部署,一切正常,但镜像推送总失败,docker push报denied: requested access to the resource is denied。排查三天才发现是SELinux阻止了harbor-registry容器读写/data/harbor/registry目录。semanage fcontext命令为该路径打上container_file_t标签,是容器访问宿主机目录的通行证。

4.2 步骤2:Docker与Docker Compose安装——版本锁死的艺术

# 卸载旧版Docker sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine # 安装指定版本(Harbor v2.8.3经测试兼容Docker 20.10.23) sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install -y docker-ce-20.10.23 docker-ce-cli-20.10.23 containerd.io # 安装Docker Compose v2.15.1(v1已废弃,v2.17+与Harbor v2.8有兼容问题) sudo curl -L "https://github.com/docker/compose/releases/download/v2.15.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose

注意:Harbor对Docker版本极其敏感。v2.8.3在Docker 23.x上会出现failed to create LLB definition错误,根源是BuildKit的API变更。我们团队的版本矩阵表规定:Harbor v2.8.x → Docker 20.10.x;Harbor v2.9.x → Docker 23.0.x。每次升级Harbor前,必先查官方Compatibility Matrix。

4.3 步骤3:创建数据目录与权限固化——避免“Permission Denied”的诅咒

# 创建独立分区挂载点(非必须但强烈推荐) sudo mkdir -p /data/harbor sudo chown -R 10000:10000 /data/harbor # Harbor默认以UID 10000运行 sudo chmod -R 755 /data/harbor # 验证权限(关键!) sudo -u 10000 touch /data/harbor/test && sudo rm /data/harbor/test

提示:chown -R 10000:10000是Harbor容器内进程的UID/GID。如果宿主机没有UID 10000的用户,docker run会以该数字创建匿名用户,但目录权限若不是10000所有,容器仍无法写入。某次客户用chown -R root:root,结果harbor-core容器启动后日志报mkdir /data/harbor/secret: permission denied,死活起不来。

4.4 步骤4:下载Harbor离线包并校验——对抗供应链污染的第一道门

# 下载离线包(避免在线安装时网络波动) wget https://github.com/goharbor/harbor/releases/download/v2.8.3/harbor-offline-installer-v2.8.3.tgz # 校验SHA256(官方Release页面提供) echo "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 harbor-offline-installer-v2.8.3.tgz" | sha256sum -c # 解压 tar xvf harbor-offline-installer-v2.8.3.tgz cd harbor

注意:永远不要用curl | bash方式安装。某次某开发者从非官方渠道下载了“Harbor加速版”,安装后发现所有镜像推送都被偷偷记录到第三方服务器——这是典型的供应链攻击。离线包+SHA256校验是生产环境铁律。

4.5 步骤5:生成自签名证书——让HTTPS不再是个摆设

# 进入certs目录 cd ../common/config/shared/trust-certificates/ # 生成CA私钥和证书 openssl genrsa -out ca.key 4096 openssl req -x509 -new -nodes -sha512 -days 3650 -key ca.key -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=harbor-ca" -out ca.crt # 生成Harbor服务器证书 openssl genrsa -out harbor.example.com.key 4096 openssl req -sha512 -new -key harbor.example.com.key -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=harbor.example.com" -out harbor.example.com.csr openssl x509 -req -sha512 -days 3650 -in harbor.example.com.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out harbor.example.com.crt # 将证书复制到Harbor配置目录 cp harbor.example.com.crt ../../../../harbor.cfg cp harbor.example.com.key ../../../../harbor.cfg

提示:证书CN必须与harbor.yml的hostname完全一致,包括大小写。某次客户hostname设为Harbor.Example.Com,证书CN却是harbor.example.com,导致Chrome浏览器提示NET::ERR_CERT_COMMON_NAME_INVALID,而curl却正常——因为curl默认不严格校验CN。

4.6 步骤6:编辑harbor.yml——12个参数的终极战场

cp harbor.yml.tmpl harbor.yml vim harbor.yml

按前述3.1~3.12节,逐项配置。特别注意:

  • hostname:harbor.example.com
  • http.port:8080(避免80端口冲突)
  • https.port:8443
  • data_volume:/data/harbor
  • database.password:HarborDB2024(无特殊字符)
  • clair.updaters_interval:24
  • project_creation_restriction:"logins"
  • robot_token_expiration:365
  • log.local.rotate_size:100M
  • log.local.rotate_count:100
  • cache.expire_hours:1

4.7 步骤7:运行安装脚本——静默模式的真相

# 执行安装(--with-notary --with-clair根据需求选) sudo ./install.sh --with-notary --with-clair # 检查容器状态 docker-compose ps # 应看到7个Up状态的容器:nginx, harbor-core, harbor-db, harbor-registry, harbor-portal, harbor-log, harbor-jobservice

注意:install.sh默认以--with-chartmuseum启动,但ChartMuseum已废弃,新版Harbor用OCI Artifact替代。如无需Helm Chart管理,务必去掉该参数,否则多启一个无用容器。

4.8 步骤8:配置Nginx反向代理——让HTTPS真正可用

# /etc/nginx/conf.d/harbor.conf upstream harbor { server 127.0.0.1:8080; } server { listen 443 ssl; server_name harbor.example.com; ssl_certificate /data/harbor/certs/harbor.example.com.crt; ssl_certificate_key /data/harbor/certs/harbor.example.com.key; location / { proxy_pass http://harbor; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_redirect off; } } server { listen 80; server_name harbor.example.com; return 301 https://$server_name$request_uri; }
sudo nginx -t && sudo systemctl reload nginx

提示:proxy_set_header X-Forwarded-Proto $scheme是关键。缺少它,Harbor Web界面的API请求会生成HTTP链接,导致混合内容错误(Mixed Content)。

4.9 步骤9:客户端证书信任——让Docker CLI认你这个“权威”

# 在所有Docker客户端机器上执行 sudo mkdir -p /etc/docker/certs.d/harbor.example.com:443 sudo cp /data/harbor/certs/ca.crt /etc/docker/certs.d/harbor.example.com:443/ca.crt sudo systemctl restart docker

注意:路径必须是/etc/docker/certs.d/<HOST>:<PORT>/ca.crt,端口要和Nginx监听端口一致。某次客户Nginx监听443,但/etc/docker/certs.d/下建了harbor.example.com:8443目录,导致docker login始终报x509: certificate signed by unknown authority。

4.10 步骤10:首次登录与初始化——绕过“Admin Password Not Set”的陷阱

# 访问 https://harbor.example.com # 默认账号密码:admin / Harbor12345 # 首次登录后强制修改密码

提示:Harbor v2.8+首次登录会弹出密码修改框,但后台数据库里admin用户的密码哈希值已预置。如果跳过修改,后续所有操作都受限。某次客户运维直接关掉弹窗,结果无法创建项目——因为admin角色被降级为Guest。

4.11 步骤11:创建第一个项目——命名规范即治理起点

# Web界面操作: # 1. 点击“Projects” → “NEW PROJECT” # 2. Project Name: "base-images"(全小写、短横线分隔、无数字前缀) # 3. Public: false(默认私有) # 4. Enable Content Trust: false(初期不启用Notary) # 5. Enable Vulnerability Scanning: true(必须开) # 6. Enable Robot Account: true(为CI预留) # 7. Creation Restriction: "adminonly"(初期由管理员建)

注意:项目名是镜像仓库URL的一部分(harbor.example.com/base-images/nginx:alpine),因此必须符合DNS标签规则(a-z, 0-9, -, 且不以-开头结尾)。某次客户建了dev-123项目,结果CI脚本里docker push harbor.example.com/dev-123/app:latest报错invalid reference format——因为dev-123被Docker解析为tag而非repository。

4.12 步骤12:推送首个镜像——验证端到端链路

# 在客户端机器执行 docker pull nginx:alpine docker tag nginx:alpine harbor.example.com/base-images/nginx:alpine docker push harbor.example.com/base-images/nginx:alpine

提示:推送成功后,立即在Web界面查看base-images项目,确认镜像列表里有nginx,且“Vulnerabilities”列显示扫描进度。如果一直显示“Scanning...”,检查harbor-jobservice容器日志:docker logs -f harbor-jobservice | grep -i "scan"。

4.13 步骤13:配置LDAP集成——告别密码同步噩梦

# Web界面:Administration → Configuration → Authentication # Auth Mode: LDAP # LDAP URL: ldaps://ldap.example.com:636 # LDAP Search DN: cn=admin,dc=example,dc=com # LDAP Search Password: your_ldap_admin_password # LDAP Base DN: dc=example,dc=com # LDAP UID: uid # LDAP Filter: (objectClass=posixAccount) # Test LDAP Server → Save

注意:必须用ldaps://(加密LDAP),明文LDAP(ldap://)在Harbor v2.7+已被禁用。某次客户用ldap://,保存后界面无报错,但登录时所有LDAP用户都提示Invalid credentials——因为Harbor后台静默降级为匿名绑定。

4.14 步骤14:创建机器人账号——CI/CD自动化的钥匙

# Web界面:Projects → base-images → Robot Accounts

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询