资讯动态

Docker Harbor 安装配置全解析:从部署到运维的镜像治理实践

发布时间:2026/10/17 12:53:08 来源:尧图企业网站定制
做容器化落地的人迟早会碰到一个问题镜像越来越多靠 Docker 自带的 Registry 管不住权限也管不住审计和复制。Docker Harbor安装配置就成了绕不开的一环——把 Harbor 部署起来给团队一个靠谱的镜像中心。尤其是当团队规模超过十个人或者开始有测试、预发、生产多套环境的时候你会发现没有一整个镜像治理方案线上出问题只是时间问题。这篇文章不讲废话直接记录我从规划到落地的完整过程、踩过的坑和最终能直接用的配置。我从裸 Registry 迁移到 Harbor 的契机很简单某次某开发者在测试环境把镜像 tag 打了latest结果生产环境的拉取脚本刚好没有固定 tag一个晚上把生产服务全带上了未验证的包。查日志发现谁也说不清谁推的、什么时候推的更可怕的是镜像被覆盖后原始内容无法恢复。从那之后我就明确了镜像仓库不能只解决“能不能存”还得解决“谁在动、能不能动、怎么同步”。Harbor 正是冲着这些治理问题来的它适合正在做容器化改造的运维、SRE、后端开发以及需要一套自建镜像仓库的团队参考。下面按我的实际操作顺序把每一步都拆开讲清楚。1. Harbor 到底解决什么问题为什么 Docker 裸仓库不够用网上很多教程直接告诉你怎么装但我建议先理解它存在的意义。Harbor 并不是把 Docker Registry 换了而是在 Registry 外围包了一层完整的治理能力。没有这层东西你只是得到了一个能 push 能 pull 的“网盘”而不是一个“仓库”。1.1 裸 Registry 的三个硬伤第一个硬伤是权限模型。Docker 官方 Registry 镜像本身几乎不做细粒度权限默认情况下所有人都是超级管理员或者所有人共用一个账号。多团队共用时没有“项目空间”这个概念A 团队可以随手看、改、删 B 团队的镜像。我见过不少团队一开始就一两个人用觉得没什么问题等扩展后开始互相踩镜像才意识到权限隔离必须提前做。第二个硬伤是审计和追溯。裸 Registry 没有操作记录谁删了镜像、谁覆盖了 tag、哪个客户端在哪台机器上拉过镜像统统没有。出了问题只能看应用日志猜效率极低。Harbor 会记录操作日志尤其是镜像删除、配置修改、复制任务这些关键操作在出事故时能帮你快速定位责任人。第三个硬伤是跨环境复制。做多机房部署或者测试环境与生产环境分隔的团队会希望把镜像从测试仓库同步到生产仓库。裸 Registry 完全没有复制能力只能自己写脚本打 tag 再 push脚本写得再好也经不住网络抖动和镜像数量增大。Harbor 内置了复制规则基于事件、定时、手动都可以是容灾和多环境分发的基础能力。1.2 Harbor 的核心组件与工作逻辑理解 Harbor 的组件对你后续排错特别有帮助。Harbor 2.x 安装完成后会启动一组容器其中核心角色包括nginx对外统一入口负责 SSL 终止、反向代理到 Portal/Core/Registry。harbor-core处理 API、认证、RBAC、项目管理等业务逻辑。harbor-registry真正存储镜像 blob 数据的 Docker Registry。registryctl负责管理 Registry 的配置和 GC 任务。harbor-dbPostgreSQL保存元数据、用户、项目、权限、审计日志注意它不存镜像层文件。harbor-jobservice执行复制任务、GC 上报、回收任务等异步操作。harbor-portalWeb 管理界面。trivy可选漏洞扫描器对镜像做安全扫描。这里要重点区分“元数据”和“镜像数据”。镜像层的实际二进制内容保存在data_volume下的registry目录或者你配置的对象存储而项目、用户、权限、tag 列表、审计记录这些结构化的信息保存在 PostgreSQL 里。明白这个区分你才知道备份时不能只 dump 数据库也不能只拷 registry 目录两部分要一起备份。Harbor 通过nginx做统一入口因此你在页面上操作项目、在看审计日志、在跑复制都是先走到harbor-core的 API再由它协调其他组件完成。这种前后端分离的结构也解释了为什么页面能打开但 push 报错时问题通常不在 nginx 而在 core 或 registry 容器。2. 安装前的规划版本选型、硬件与存储方案访问 Harbor 官网的 release 页面时你会发现最新版本一直在更新。但我的建议是先别追新按稳定业务来选择。Harbor 每个大版本的配置模板、数据库结构、容器编排方式都有差异如果你随手拿了个最新版却参考旧版教程很容易踩到参数对不上的坑。2.1 版本选型与配套设施以 Harbor 2.x 为例它要求 Docker Engine 20.10 以上Docker Compose 建议用 V2即docker compose命令旧环境如果还在用 Python 版docker-compose也至少保证 1.29.2 以上。我在一个老机器上踩过一次坑服务器还是 Docker 18.09直接跑 Harbor 离线包install.sh脚本本身没问题但启动后的容器健康检查全部失败升级 Docker 后一切正常。选择版本时我一般会确认三件事当前生产服务器上的 Docker 版本是否满足要求。Harbor 所选版本的官方文档对存储后端、漏洞扫描器、数据库迁移的具体说明。是否需要与现有 CI/CD 系统中的 API 版本兼容避免升级后脚本接口报 404/410。不建议用“最新版 旧教程”的组合。Harbor 的离线安装包内部自带一套依赖组件理论上和在线包没区别但离线包更适合内网环境因为不需要在安装时从外部拉取镜像。下载后得到一个harbor-offline-installer-vX.Y.Z.tgz压缩包后续所有操作都在它解压出的目录里进行。2.2 存储后端选型Harbor 默认把镜像层数据存储在本地数据卷即harbor.yml中的data_volume。你需要确保这个目录落在独立的大容量分区上尽量不要和系统根分区共用。因为镜像仓库的数据增长很快一个五六人团队跑半年几十 GB 到上百 GB 很正常如果根分区和系统日志混在一起磁盘占满后整个服务器都会受影响。存储后端我按三种场景来选单机或测试环境本地磁盘足够简单、可靠、延迟最低。磁盘格式建议 ext4/xfs不要用玩票性质的网络文件系统直接挂到 registry 目录。多机房或高可用场景优先考虑 S3 兼容对象存储。因为镜像层是不可变对象特别适合对象存储的语义Harbor 原生支持配置 S3 endpoint、bucket、access key/secret key。如果必须用 NFS也可以但要注意 NFS 的挂载参数和网络延迟。Registry 对文件锁和原子重命名比较敏感低质量 NFS 服务会导致 push 镜像时出现filesystem: failed to rename类错误。我的经验是存储规模比较大时优先对象存储NFS 只承担小规模共享场景。还有一个容易被忽略的点Harbor 数据库目录也在data_volume下如果数据盘是挂载在慢速存储上的PostgreSQL 的写延迟会直接影响页面的响应速度和复制任务的性能。有条件的话数据库目录可以考虑单独放到性能更好的磁盘上但这样会增加备份的复杂度需要你在部署前做好权衡。2.3 域名与证书规划Harbor 的hostname参数是它对外服务的地址也是客户端连接时用来识别仓库的地址。这个地址强烈建议用域名而不是 IP。原因有两个一是 Docker 和 containerd 在 TLS 校验时依赖主机名匹配域名方式更清晰二是后续如果更换服务器 IP只要 DNS 改解析即可客户端不需要改动。我的习惯是先在内部 DNS 里规划一个专用域名比如registry.example.com然后提前生成好证书。如果你所在的网络没有企业级 CA也可以用自建的内部 CA 给这个域名签发证书。要注意的是证书必须具备subjectAltNameSAN而且要包含 Docker 客户端解析该域名所得到的所有 IP。很多人只在证书里写了域名结果客户端通过 IP 访问时报x509: certificate is valid for registry.example.com, not ip这就是 SAN 缺失导致的。HTTPS 不是可选项。生产环境我强烈建议直接关掉 HTTP 访问只暴露 HTTPS。不要觉得“反正内网没关系”镜像仓库中经常会有生产环境敏感信息和密钥一旦在传输层被人监听后果远比多花十分钟配置证书严重。3. 完整安装过程离线包部署 Harbor 实例现在进入实操环节。我用的是 Harbor 2.x 离线包在 CentOS 7 兼容环境下的安装过程Debian/Ubuntu 操作也几乎一致只是包管理器不同。整个过程分三步环境准备、配置harbor.yml、执行安装并验证。3.1 环境准备先确认服务器有足够的资源。Harbor 全套容器启动后空闲状态下会占用约 2GB 内存如果你启用了漏洞扫描器内存占用会再多 1GB 左右。最低配建议 2 核 4GB线上环境至少 4 核 8GB。磁盘除了给镜像数据留空间还要预留日志容量Harbor 的日志组件默认也会写不少数据到data_volume下的/var/log。端口方面Harbor 默认占用 80 和 443。如果你的服务器上已有 Nginx 或其他 Web 服务占用这两个端口后面就要在harbor.yml里修改http.port或https.port。我用旧机器部署时就遇到过端口冲突install.sh提示成功但容器起不来排查半天才发现是系统自带的 Nginx 占着端口这是新手最容易踩的坑之一。SELinux 的处理策略要提前想清楚。如果测试环境图省事可以临时关闭生产环境不建议直接 disable而是给对应端口放行规则或者使用chcon给 Harbor 数据目录设置正确的上下文。我不想在这里展开 SELinux 的安全策略细节只想提醒一句很多 Harbor 容器启动后报权限错误、挂载目录无法读写背后都是 SELinux 在拦截面对这种情况先查ausearch -m avc再决定怎么办。Docker Compose 也要提前装好。现在大多数 Linux 发行版通过 Docker 官方仓库安装 Docker 后自带 compose plugin直接执行docker compose version能看到版本号。如果看不到说明缺少 compose需要单独安装不要想着省略这一步Harbor 的所有组件依赖 compose 编排。3.2 解压与初始化配置harbor.yml 关键参数逐项讲解把离线包上传到服务器后解压tar -zxvf harbor-offline-installer-v2.9.0.tgz cd harbor cp harbor.yml.tmpl harbor.yml接下来是重中之重编辑harbor.yml。这个文件里的参数会在安装时被prepare阶段写入到各容器配置中。官方模板里有完整注释但有几个参数必须根据你的环境修改我逐一说明。hostname这是最重要的参数。写上你的对外访问域名比如registry.example.com。如果写错页面能打开但 API 请求会失败因为浏览器和 API 请求都用这个地址回调客户端登录也会因为地址不匹配而报证书或 404 错误。http.port和https.port默认分别是 80 和 443。如果你只走 HTTPS可以把http整段注释掉如果你在内网图省事只走 HTTP就把https整段注释掉。千万不要两个都开着还配了不同端口的证书这样容易让 Docker 客户端产生混淆。我的建议是只保留 HTTPS。data_volume改成你要存储镜像数据的目录路径例如/data/harbor。这个路径要提前创建好并且给足够的磁盘空间。注意权限Harbor 的容器在启动时会以特定的 UID 运行如果目录权限不对registry 容器会一直重启。我习惯在执行安装前直接把目录 owner 设为1000:1000避免后续权限问题mkdir -p /data/harbor chown -R 1000:1000 /data/harborharbor_admin_password初始管理员密码必须先改掉长度至少 12 位。不要用默认的 Harbor12345 上生产环境。安装后如果忘记密码需要在数据库里重置非常麻烦而且容易把用户表搞坏所以这一步宁可停下来多想几秒。database#password这是 Harbor 内部 PostgreSQL 的 root 用户密码不是系统 root。默认也是弱密码公网环境如果不改数据库很容易被别人连上。即使你只在内网用我也建议改因为 Harbor 的数据库端口在容器网络内部就可以访问一旦容器网络暴露风险极大。certificate和private_keyHTTPS 证书和私钥的路径。改成你实际存放证书的路径比如/data/harbor/certs/server.crt。证书可以先放到这个路径也可以等后面生成好再放但安装时一定要保证文件存在且路径正确。其他参数像max_job_workers默认 10用于限制并行后台任务数量普通场景不用动trivy相关参数如果不需要扫描可以先注释掉这样资源占用会更低。3.3 执行安装脚本与验证安装结果配置完成后执行安装./install.sh脚本会帮我们做以下事情加载镜像、准备配置、检查 Docker Compose、启动所有 Harbor 组件。如果一切顺利最后会输出Harbor has been deployed and started successfully.。使用docker compose ps查看容器状态所有服务都应该是Up且harbor-core、harbor-registry、harbor-db等关键组件后面的状态是healthy而不是简单的Up。验证可以从三个层面来Web 页面浏览器访问https://registry.example.com能正常显示登录页。健康检查命令行执行curl -u admin:密码 https://registry.example.com/api/v2.0/health返回{status:healthy}表示 API 正常。Docker 登录在一台 Docker 客户端机器上执行docker login registry.example.com输入管理员账号密码能显示Login Succeeded。在这一步最容易遇到的问题是install.sh报错harbor-db无法启动。我遇到过的典型原因是data_volume目录没有写权限或者 SELinux 拦截了容器目录访问。另一种情况是服务器上已有内部 DNS 把harbor这个主机名解析到了其他地址导致容器在启动时自检失败这种问题可以通过暂时去掉/etc/hosts里的无关条目来定位。4. 配置 HTTPS 访问与 Docker 客户端认证Harbor 本身只是配置 HTTPS 很简单麻烦的是让所有 Docker 客户端信任你这个仓库。这一节内容虽然不起眼却是影响实际使用体验的关键。4.1 生成自签名证书或内部 CA 证书如果你是自建 CA需要先创建一个 CA 根证书再用这个根证书给 Harbor 域名签发服务器证书。步骤看起来多但每一行都建议手动跑一遍不要想当然。先创建 CA 私钥并生成自签名 CA 证书openssl genrsa -out ca.key 4096 openssl req -x509 -new -nodes -sha256 -days 3650 \ -subj /CNRegistry Root CA \ -key ca.key -out ca.crt然后生成 Harbor 服务器的私钥和证书签名请求openssl genrsa -out server.key 4096 openssl req -new -sha256 \ -subj /CNregistry.example.com \ -key server.key -out server.csr这里最关键的一步是创建扩展文件把 SAN 写进去。新建一个ext.cnfsubjectAltNameDNS:registry.example.com,IP:192.168.1.100然后基于这个扩展文件签发服务器证书openssl x509 -req -sha256 -days 3650 \ -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \ -extfile ext.cnf -out server.crt很多人省略-extfile这一步结果证书就只能匹配CN字段Docker 客户端用 IP 访问时直接报证书校验失败。我在内部环境吃过这个亏而且是在一批服务器上同时发现的最后只能重新签发并批量替换非常浪费时间。因此这里宁可多写两行命令也别偷懒。4.2 配置 Docker 客户端信任与登录服务器证书生成后把server.crt和server.key放到你之前指定的 Harbor 证书路径然后在 Harbor 服务器上重启相关容器docker compose down ./prepare --with-trivy docker compose up -d在 Docker 客户端上需要把 CA 根证书放到 Docker 的信任目录。目录名必须和 Harbor 的对外仓库地址完全一致不能带协议也不能额外加路径。假设你的 Harbor 域名是registry.example.commkdir -p /etc/docker/certs.d/registry.example.com cp ca.crt /etc/docker/certs.d/registry.example.com/ca.crt systemctl restart docker注意这里拷贝的是 CA 根证书ca.crt而不是 Harbor 的服务器证书。Docker 在 TLS 握手时遇到发来的服务器证书需要用根证书去验证它的签名链。如果你把两者搞混登录时照样会报未知颁发机构。重启 Docker 后测试docker login registry.example.com -u admin -p 密码如果客户端用的是 Docker DesktopWindows/macOS路径管理方式不同需要把证书导入到系统信任链里再重启 Docker Desktop。Linux 服务器上就按上面的方式操作。4.3 常见证书错误排查我在帮其他团队排查时经常遇到三种情况下面用表格整理出来方便你对照处理报错信息原因分析处理方式x509: certificate signed by unknown authority客户端没有信任签发 Harbor 证书的 CA把 CA 根证书放到/etc/docker/certs.d/域名/ca.crt并重启 Dockerx509: certificate is valid for registry.example.com, not IP证书 SAN 中未包含客户端访问所用的 IP 或主机名用ext.cnf补上对应的 DNS 和 IP重新签发证书替换 Harbor 的证书后重启 nginx 容器http: server gave HTTP response to HTTPS clientDocker 客户端尝试用 HTTPS 访问一个只开启了 HTTP 的 Harbor 端口检查harbor.yml是否只配置了http而不含https或者端口号是否对应错误tls: first record does not look like a TLS handshake客户端连到了一个非 TLS 服务端口检查 Harbor 是否在 nginx 上正确开启了 HTTPS以及端口是否被其他服务占用另外提醒一点在harbor.yml里修改证书路径后不是简单的docker compose restart就能生效因为 nginx 容器内的证书是prepare阶段拷贝进去的。改完配置和证书后要重新执行./prepare再重启容器。这个步骤忘了你会看到证书还是旧的浪费大量时间在无效排查上。5. 项目空间、用户权限与镜像复制策略安装好 Harbor 只是开始真正让团队用得顺手的是权限模型和复制策略。很多团队装完了就丢在那里结果所有人共用 admin 账号跟裸 Registry 没有任何区别。这一节希望你能认真看完。5.1 项目空间与仓库权限模型Harbor 的核心隔离单位是项目Project。每个项目下有镜像仓库、成员、标签、扫描配置等资源。形象地理解项目就是大房子的一个房间团队每个成员只能进属于自己的房间。Harbor 内置角色从高到低分别是系统管理员、项目管理员、开发者、访客、有限访客系统管理员管理整个 Harbor 实例包括用户、配置、复制任务、GC、系统设置。项目管理员管理某个项目下的成员、配额、扫描规则可以设置镜像保留策略。开发者可拉取镜像也可推送镜像到该项目。访客只能拉取镜像不能推送。访客和有限访客的区别在于普通访客可以拉取公开项目的镜像有限访客在公开项目中只能访问被显式授权的仓库。小团队可以不用这么细但推荐一开始把“公开项目”的默认访问权限设为受限访问防止外部用户无意间拉取所有公开镜像。镜像仓库控制体的权限还包括“是否允许删除镜像、是否允许复制到外部项目”等配置。我看过不少团队因为懒把项目全部设为公开然后等到有人误拉取了还在开发中的镜像时开始后悔。建议默认项目全部私有只在必要时开放个别公共基础镜像项目。5.2 创建项目与配置用户在 Web 界面里点击“新建项目”输入项目名称如backend、>docker exec harbor-db pg_dump -U postgres -c -f /tmp/harbor_backup.sql harbor docker cp harbor-db:/tmp/harbor_backup.sql ./然后把data_volume下的registry目录做快照或压缩备份。如果是对象存储后端镜像数据本身已经在对象存储上你只需要备份数据库和harbor.yml。升级流程我走过多轮备份现有数据和配置。下载新版本离线包解压。停掉旧版 Harbordocker compose down。将新版harbor.yml.tmpl复制为harbor.yml把旧版harbor.yml中的参数逐项对照填进去。执行./prepare和./install.sh或直接./upgrade.sh脚本会自动迁移数据库。这里要特别提醒不要直接让新旧版本使用同一个data_volume目录而不做任何检查。Harbor 升级脚本一般会处理数据库迁移但如果你的旧版本跨度太大比如从 1.x 直接跳到 2.x数据库结构和 API 兼容性都可能出问题。稳妥做法是先升级到相邻大版本再逐级升到目标版本。恢复时装好同版本 Harbor 后停掉容器把备份的registry目录复制回data_volume用备份的 SQL 文件导入数据库然后启动容器。恢复完成后要测试一遍登录、拉取、推送和复制确保都正常后再切换流量。6.2 磁盘空间管理与 GC镜像仓库最明显的故障就是磁盘写满。很多团队删了一个镜像 tag 后以为空间释放了其实没有。Docker Registry v2 采用的内容寻址存储机制决定了删除 tag 后被引用层并不会立刻被清除只有所有指向某个 blob 的 manifest 都删除后blob 才会变成“孤儿”需要通过 GC 任务清理。Harbor 在“管理”-“垃圾回收”里提供了 GC 功能。你可以手动执行也可以配置定时策略。GC 执行前务必选择“Dry Run”模式先跑一遍看会释放多少空间确认没有意外后再正常执行。GC 期间如果有人在 push 或 delete 镜像可能出现不可预期的状态所以正式 GC 建议在维护窗口执行。另外很多人忽略镜像保留策略。Harbor 支持基于 tag 规则自动清理例如只保留最近 5 个镜像。给项目配置保留策略比天天手动清理省心得多。我的经验是测试项目可以配置每天清理旧版本生产项目配置按版本标签保留重要镜像并关闭自动清理防止某天需要回滚但发现版本已经没了。6.3 常见故障速查表把我在实际工作中排查过的典型问题汇总成表方便你快速定位故障现象可能原因处理建议页面显示 502 Bad Gatewayharbor-core容器未启动或健康检查不过nginx 无法连接到 core 服务查看harbor-core日志docker logs harbor-core看具体报错常见是数据库连接失败或数据卷权限错误harbor-registry容器反复重启data_volume目录权限不合适存储后端配置错误确认目录属主为 1000:1000检查registryctl日志数据库容器启动失败磁盘满、PostgreSQL 数据目录权限不对、数据库文件损坏df -h检查磁盘查看harbor-db日志必要时恢复备份push 镜像报unauthorized登录凭证过期、机器人账号权限不足重新登录检查机器人账号的项目权限镜像 push 到一半失败网络抖动、存储写入延迟高检查目标存储 IO、带宽确认数据盘没有满再重试 push复制任务一直失败目标 Harbor 地址不可达、目标机器人账号密码错误、目标项目不存在先手工从源机器curl目标 API 健康检查确认目标端凭据有效页面能打开但docker pull超时防火墙未放行 443 端口DNS 解析到旧地址在客户端用curl -v https://registry.example.com和ping/dig检查连通性Docker 登录后无法列出镜像项目是私有的当前用户未被授权将用户加入相应项目并分配访客或开发者角色或在客户端重新登录后确认账号权限这里还想单独展开一个容易误判的场景页面能打开但 docker login 却报证书问题。出现这种情况不要急着改 Harbor 配置先看客户端/etc/docker/certs.d下的目录名是不是完全匹配仓库域名。Docker 对证书目录名称非常敏感如果你写成了带http://前缀或者在路径里加了冒号它不会按预期加载证书。我之前就遇到过服务器配置全部正确只是客户端多了个https://前缀导致 docker login 永远报unknown authority最后删掉目录重建才解决。在日常运维中我还有一个习惯每次对 Harbor 做较大配置变更前都先快照整个数据目录尤其是harbor.yml和数据库。这看起来多花几分钟时间但在出问题时能让你从“焦头烂额”变成“从容回滚”。最后再说一个我自己的操作纪律不管用离线包还是在线包我都会留好当初的harbor.yml和证书副本归档到单独目录。因为 Harbor 的配置不像普通容器环境变量那样改了就能生效很多参数在初始化后不能直接修改一旦要改端口、动数据库密码往往得重新跑prepare和重启服务群组。没有当初的原始配置靠记忆排错非常痛苦。希望这篇 Docker Harbor安装配置记录能帮你少走一些弯路把镜像仓库这一环稳稳落地。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑