
简介针对国产化ARM架构aarch64环境中容器镜像仓库的离线部署需求这份资源提供了Harbor v2.10.2的完整软件包。包内共6个文件主要包括两个Shell脚本install.sh与common.sh负责安装流程与公共函数、Harbor镜像离线压缩包harbor.v2.10.2.tar.gz承载镜像内容的主体压缩包、LICENSE许可文件、prepare初始化组件以及harbor.yml.tmpl配置模板整体约650.11MB结构清晰为内网和信创环境而设计。目前已有98人学习下载适合运维工程师、DevOps人员以及负责国产化迁移的技术团队使用。利用该离线包用户只需按模板调整配置并执行安装脚本即可完成Harbor的部署与基础配置无需在线拉取镜像有效规避网络隔离或不稳定因素同时可在此基础上开启Harbor的RBAC权限、镜像复制、存储管理等能力形成企业内部可复用的生产级镜像仓库基线。对于需要快速交付、稳定运行的ARM服务器环境这份资源能显著降低部署门槛缩短落地周期。1. 拿到harbor-offline-installer-aarch64-v2.10.2.tgz先弄清楚它解决什么问题一个 2GB 出头的离线安装包文件名里把架构写死在aarch64上这基本就说明了它的使用场景一台没有外网、或者外网极不稳定的 ARM64 服务器鲲鹏、飞腾、或者 Apple Silicon 上的 Linux 虚拟机需要在内网搭一套 Harbor 镜像仓库。如果你正被docker pull超时、yum 源连不上、内网机器上连个基础镜像都得靠 U 盘拷贝这些问题折磨这个包就是用来绕开所有网络依赖的——它把 Harbor 的容器镜像、安装脚本、配置模板全部打在一个 tgz 里拷进去就能装。我见过不少团队第一次用这个包时以为它和在线安装器一样解压后跑install.sh就万事大吉。结果卡在docker load的镜像导入时间上或者因为没提前装好 docker-compose 而翻车。这篇文章从选型逻辑讲起逐步拆解从解压到配置再到排错的全部过程重点放在 aarch64 平台上那些和 x86 不一样的坑。适合谁看两类人。一类是要在 ARM64 内网环境交付的运维和平台工程师另一类是打算把 Harbor 作为团队镜像仓库、但不想在依赖管理上浪费时间的后端开发者。看完你至少能回答三个问题这个离线包和在线安装器到底差在哪、在 aarch64 上安装有哪些必须留意的参数、以及推送和拉取失败时日志里的关键信息长什么样。2. 为什么选离线包 aarch64 专用包架构差异比你想的更影响落地2.1 离线安装器与在线安装器的本质区别Harbor 官方提供的安装器分两种在线版是一个很小的脚本执行时会动态从 Docker Hub 拉取 Harbor 自身的组件镜像离线版则把goharbor/下的所有镜像portal、core、jobservice、registry、redis、postgresql、nginx 等预先导出成 tar 包和安装脚本一起封装。harbor-offline-installer-aarch64-v2.10.2.tgz里的.tgz后缀意味着你需要先解压再执行内部脚本。选择离线包的核心原因只有一个环境隔离。内网机器通常连不上 Docker Hub甚至连接外网的链路本身就受限。在线安装器在docker pull这一步就会卡死而离线包把网络依赖压缩到了安装前的文件拷贝阶段。代价是包体巨大且必须和你的 CPU 架构严格匹配——x86 的离线包在 ARM64 机器上无法直接运行反之亦然。这里要特别注意一个常被忽略的点离线包内的镜像在docker load之后会被打上goharbor/...的标签但安装脚本会再用docker tag把镜像重新标记为本地仓库地址。如果你之前手动docker load过某个组件镜像并打了自定义 tag脚本执行时可能因为 tag 冲突报错。所以规范的流程是让脚本自己管理镜像的导入和标记不要提前干预。2.2 aarch64 与 x86 在 Harbor 部署上的差异点aarch64对应 ARM 64 位指令集常见于鲲鹏 920、飞腾 FT-2000 这些国产芯片以及 AWS Graviton、Ampere Altra 这类云实例。Harbor 的组件镜像对 ARM 的支持差距很大redis、postgresql、nginx这些基础镜像官方一直有 multi-arch 构建但 Harbor 自身的一些组件比如harbor-core、harbor-jobservice在某些版本上只有 amd64 的官方镜像。这也是为什么你会看到-aarch64-这个后缀——它表明这套镜像已经被重新构建过能跑在 ARM 平台上。安装时的直观差异体现在三个方面。第一docker load的时间明显比 x86 长因为 ARM 镜像层通常更多我在鲲鹏机器上导入 2GB 离线包大约需要 8-15 分钟取决于磁盘是 HDD 还是 SSD。第二Harbor 依赖的docker-compose在 ARM 上的安装方式不同Ubuntu 的 apt 源里那个docker-compose版本可能太老需要单独处理。第三日志里的错误信息往往带有架构相关特征比如exec format error这不是 Harbor 的问题而是镜像架构不匹配导致的。如果你打算把 x86 的离线包直接拷到 ARM 机器上省掉重新下载的功夫实践下来这条路走不通。docker load不会报错但容器一启动就exec format error最后还得老老实实找 aarch64 专用包。这个教训我们当时花了一个下午才定位到。2.3 部署前的基础环境检查清单在解压离线包之前有一组环境检查值得做能把后面一半的排错时间省掉。至少需要确认Linux 内核版本不低于 3.10这是 Docker 的硬性要求docker和docker-compose已安装磁盘剩余空间大于离线包体积的两倍镜像导入会临时占用额外空间80 或 443 端口没有被占用。# 确认架构必须是 aarch64 或 arm64 uname -m # 确认 Docker 版本Harbor 官方对 20.10 以下版本兼容性不好 docker version --format {{.Server.Version}} # 确认 docker-compose 独立版本 docker-compose --version # 检查磁盘剩余空间建议至少 20GB df -h /data逻辑说明第一行uname -m是判断架构的起点如果输出是x86_64说明你拿错包了。第二行和第三行检查 Docker 与 Compose 的存在性缺一个都不行——Harbor 的install.sh不会主动帮你装 Docker。第四行看数据盘剩余Harbor 的镜像存储目录默认为/data这个路径如果没空间安装到后面docker load就不会报错但推镜像时会突然失败。参数拓展/data是 Harbor 默认的数据卷目录可以通过 harbor.yml 里的data_volume改成任意路径比如/opt/harbor-data。但改路径要趁早一旦install.sh执行过目录结构已经生成再改配置需要先执行docker-compose down再重新 prepare。提示如果你的机器内存小于 4GBHarbor 安装后启动会非常吃力尤其是同时跑起 postgresql 和 registry 两个容器。建议至少 8GB 内存否则docker ps能看到容器一直在 restarting。3. 从解压到装完完整安装步骤与核心参数解析3.1 解压离线包并检查目录结构拿到harbor-offline-installer-aarch64-v2.10.2.tgz后第一步不是急着解压而是先校验文件的完整性。tgz 在 U 盘和网盘之间来回拷贝损坏的概率不低而镜像包一旦有字节损坏docker load会在某个镜像层上报archive/tar: invalid tar header这个错基本等于白跑一趟。# 计算 SHA256 校验值和下载页提供的值对比 sha256sum harbor-offline-installer-aarch64-v2.10.2.tgz # 解压到 /opt 目录 tar -zxvf harbor-offline-installer-aarch64-v2.10.2.tgz -C /opt # 进入解压后的目录 cd /opt/harbor # 查看目录结构 ls -lh解压后你会看到install.sh、harbor.yml.tmpl、prepare、common.sh这几个关键文件以及一个harbor.v2.10.2.tar.gz镜像包。这个内部 tar 包就是刚才说的所有组件镜像的合集后续install.sh会调用docker load导入它。参数说明-C /opt指定解压目标目录如果/opt/harbor已经存在需要先删掉旧目录再解压否则会混杂旧版本文件。common.sh被安装脚本引用里面定义了日志级别、镜像仓库地址等公共变量建议不要手动修改。3.2 配置 harbor.yml决定安装成败的必改项进入目录后第一步操作是把配置模板复制成真正的配置文件cp harbor.yml.tmpl harbor.yml。Harbor 的安装机制是prepare脚本读取harbor.yml生成 docker-compose 所需的 env 文件和证书然后install.sh才真正拉起容器。所以配置文件的错误会在prepare阶段就暴露。# harbor.yml 关键配置节选 hostname: 192.168.1.100 # 必改本机 IP 或域名不能留 localhost http: port: 80 # HTTP 端口和已有服务冲突时改 8080 https: port: 443 certificate: /data/cert/harbor.crt # 自签名证书路径 private_key: /data/cert/harbor.key harbor_admin_password: Harbor12345 # 必改管理员初始密码 data_volume: /data/harbor # 镜像和数据存储位置 log: level: info rotate_count: 20 rotate_size: 100M database: password: root123 # postgres 密码修改后需和 core 配置一致这里最容易犯的错是把hostname写成localhost或者不写。Harbor 会把hostname拼进镜像仓库地址比如192.168.1.100/library/nginx。如果写了localhost你在另一台机器上 push 时会得到一个192.168.1.100无法访问的怪异地址。参数说明http.port和https.port是两套独立监听端口你可以只启用 HTTP、只启用 HTTPS、或者两者都启用。生产环境建议只开 HTTPS但内网环境很多机器没有证书信任HTTP 反而更方便——注意把http段打开https段注释掉即可。log.rotate_count和rotate_size控制日志轮转策略默认值够用但高频推拉环境下建议把rotate_size调大到 200M。提示harbor_admin_password如果保持默认为Harbor12345安装脚本不会强制你改但所有知道 Harbor 默认密码的人都能进管理后台。内网环境也不能省这一步至少换成一个 12 位以上的随机串。3.3 执行安装prepare 阶段、加载镜像、启动容器配置改完后从./install.sh开始后面的事情就交给脚本了。但脚本的执行过程值得拆开看因为它分成三个阶段每个阶段失败的错误都不一样。# 执行安装脚本 sudo ./install.sh脚本内部会依次做三件事调用prepare生成配置文件与证书、执行docker load导入镜像包、最后docker-compose up -d启动所有服务。整个流程耗时和机器性能强相关我在 16 核鲲鹏 SSD 的机器上跑完约 12 分钟在 4 核虚拟机上则要半小时以上。安装完成后docker-compose ps会看到 9 个容器在运行状态。如果某个容器反复重启比如harbor-core起来又退出最可能的原因有两类一是harbor.yml里数据库密码改了一半core 连不上 postgres二是磁盘权限不对postgres 容器无法写数据目录。# 查看容器运行状态 docker-compose ps # 查看具体容器日志 docker-compose logs -f harbor-core # 查看全部服务的启动日志 docker-compose logs --tail200逻辑说明docker-compose ps比docker ps更适合排查因为它显示的是 Harbor 编排内的服务名一眼能看出哪个服务状态异常。logs -f harbor-core是定位 core 起不来的首选命令日志里如果出现dial tcp ...:5432: connect: connection refused基本就是数据库容器还没就绪等几秒再看如果出现password authentication failed则一定是数据库密码配置不一致。3.4 验证安装从 Web 到 docker login 的全链路检查安装完成先别急着用按顺序验证三件事Web 页面是否可访问、docker login 是否成功、push 一个测试镜像是否正常。前两步验证管理面和认证链路第三步验证存储链路。# 浏览器或 curl 验证 Web 服务 curl -I http://192.168.1.100/ # 在另一台机器上测试登录 docker login 192.168.1.100 -u admin -p 你的密码 # 打一个测试镜像并推送 docker tag nginx:latest 192.168.1.100/library/nginx-test docker push 192.168.1.100/library/nginx-test参数说明登录时如果报Error response from daemon: Get https://192.168.209.133/v2/: http: server gave HTTP response to HTTPS client说明 docker 默认用 HTTPS 访问你的 Harbor但你的 Harbor 只开了 HTTP。解决方法是往 docker daemon 配置里加insecure-registries: [192.168.1.100]重启 docker 后重试。curl -I返回200 OK是 Web 层面的验证但不代表容器调度正常。推送成功则证明 registry 容器读写磁盘正常这一步通过了Harbor 的安装才算真正完成。4. aarch64 平台上的关键落地配置存储、证书与镜像同步4.1 数据目录选型不要把镜像存进系统盘Harbor 默认的数据目录是/data但实际落地上我见过太多人直接把系统盘塞满导致后面的镜像 GC 都无法执行。Harbor 的镜像存储是docker-registry的 filesystem 驱动一个镜像的多个层散落在/data/database/registry/docker/registry/v2/repositories下占用空间是镜像实际大小的 1.5 到 2 倍。如果你有独立的数据盘比如挂载在/mnt/harbor-data建议安装前就把data_volume指过去。这里有个额外好处后续如果需要备份或迁移直接对data_volume目录做快照即可不需要逐个容器导出。# harbor.yml 中数据目录配置 data_volume: /mnt/harbor-data配置好之后重启服务生效docker-compose down后重新./install.sh。注意down不会删除数据卷重启后原有镜像数据还在这个操作是可逆的。但如果你把data_volume指向一个全新空目录重启后仓库里的镜像列表会短暂为空——数据其实还在旧目录里别急着删。参数说明data_volume路径的用户属主会影响容器写入权限建议统一chown -R 10000:10000这是 Harbor 容器内运行用户 IDpostgres 和 registry 都用 UID 10000 运行。权限不对时registry 容器会报permission denied但写入错误往往延迟到你 push 镜像时才暴露。4.2 自签名证书生成与分发免掉 HTTPS 告警内网环境没有公共 CA最常用的做法是生成自签名 CA再用它签发 Harbor 的服务器证书。这样做的好处是只要把 CA 证书加到客户端的信任库所有docker login和docker pull都不需要走insecure-registries更接近生产环境的使用方式。# 生成 CA 私钥和证书 openssl req -newkey rsa:4096 -nodes -sha256 -keyout ca.key -x509 -days 3650 -out ca.crt -subj /CNHarbor-CA # 生成 Harbor 服务器私钥 openssl genrsa -out harbor.key 2048 # 创建证书签名请求 openssl req -new -key harbor.key -out harbor.csr -subj /CN192.168.1.100 # 使用 CA 签发服务器证书 echo subjectAltNameIP:192.168.1.100 extfile.cnf openssl x509 -req -in harbor.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out harbor.crt -days 825 -extfile extfile.cnf逻辑说明这里的关键是第三步和第四步的CN必须等于 Harbor 的hostname同时subjectAltName里必须包含 IP 或域名。docker 校验证书时会检查这二者缺任何一个都会在docker login时报x509: certificate relies on legacy Common Name field或cannot validate certificate。生成完成后把harbor.crt和harbor.key放到harbor.yml中指定的路径然后重新执行./install.sh。客户端的配置需要手工同步把ca.crt拷贝到每台客户机的/etc/docker/certs.d/192.168.1.100/ca.crt重启 docker 后即可免告警访问。参数说明证书有效期 825 天是 Chrome 和 docker 都接受的上限超过这个天数现代客户端会直接拒绝。到期前需要重新签发并替换替换方式和首次生成一样但要注意先docker-compose down再替换证书否则 nginx 容器还持有旧证书的内存副本。4.3 离线环境下的镜像同步从上游拖镜像的替代方案离线环境建好 Harbor 之后下一步就是往里灌镜像。这里有一个天然的鸡生蛋问题内网机器没有外网初始镜像从哪来常见做法是在一台有外网的机器上docker pull再docker save成 tar拷进内网后docker load并 push。# 在有外网的机器上拉取并保存镜像 docker pull nginx:1.25 docker save nginx:1.25 -o nginx-1.25.tar # 拷贝 tar 到内网机器后加载并推送 docker load -i nginx-1.25.tar docker tag nginx:1.25 192.168.1.100/library/nginx:1.25 docker push 192.168.1.100/library/nginx:1.25这个方案手动操作效率太低镜像一多就烦。更效率的做法是用 Harbor 自带的harbor-registryctl或直接调用其 API 做项目间复制Replication。该功能支持从另一个 Harbor、docker registry、甚至阿里云 ACR 同步镜像并且支持定时触发——在离线环境下如果有一条虽然慢但可用的网络链路这个方法比完全靠人工搬运优雅得多。逻辑说明复制策略在 Harbor 的 Web 界面里配核心选项是「源注册表类型」和「源地址」以及需要同步的项目。首次同步会全量拷贝之后按标签变化做增量。这个功能对 aarch64 环境尤其重要因为 ARM 镜像的获取渠道比 x86 少一旦在一个节点上找到了某镜像的 ARM 版本尽快复制到其他 Harbor 节点能省大量重复搜索的时间。4.4 单机 Docker 配置insecure-registries 的正确写法很多内网 Harbor 用的是 HTTP 协议这就必须在 docker daemon 侧声明insecure-registries。这个配置的坑在于改完要重启 docker 才生效而重启 docker 会短暂中断所有运行中容器。// /etc/docker/daemon.json { insecure-registries: [192.168.1.100, my-harbor.internal:8080] }修改后重启方式建议用systemctl daemon-reload systemctl restart docker。重启后docker info能看到Insecure Registries列表里出现了你配置的地址才算真正生效。这里有一处容易踩的坑如果你同时配置了registry-mirrors和insecure-registries部分版本 docker 在拉取镜像时会先去 mirror 找找不到才走 insecure registry。由于 mirror 通常在外网内网环境里每次docker pull都会因为超时白白多等十几秒严重时直接导致 login 超时。建议离线环境把registry-mirrors整个删掉只保留insecure-registries。参数说明地址可以带端口比如192.168.1.100:8080可以写网段比如192.168.10.0/24但网段匹配的优先级低于精确匹配。配置了网段后该网段内所有地址都会被当作 insecure 处理安全性上要自己权衡。5. 避坑指南aarch64 离线安装的 5 个高频故障与排查思路5.1 镜像导入时报invalid tar header文件损坏或校验缺失现象执行install.sh时docker load阶段报错指出harbor.v2.10.2.tar.gz内某一层archive/tar: invalid tar header安装进程中断。原因tgz 在拷贝过程中发生字节损坏甚至有些是下载工具提前掐断连接导致文件不完整。另一个常见源头是 U 盘格式不兼容FAT32 不支持大于 4GB 的单文件而 Harbor 离线包的内部 tar 常常超过这个限制拷贝时会静默截断。解决第一步永远是用sha256sum核对下载时的校验值。如果源站没有提供就把包重新拷贝一次用rsync -c比对文件内容。拷贝目标盘要选 ext4 或 xfs不要用 FAT32。如果文件确实损坏只能重新下载——不要尝试用tar --ignore-zeros去抢救Harbor 的镜像包内部结构复杂跳过坏块后导入的镜像大概率在启动时报错这种玄学修复不如重下稳定。5.2exec format error拿错架构的离线包现象容器启动瞬间退出docker logs显示exec format error或者docker inspect里镜像的Architecture字段是amd64。原因拿 x86 的离线包装到了 ARM64 机器上。docker load不会校验宿主机架构镜像能导入成功但运行时内核无法执行 x86 指令于是容器直接崩溃。解决从文件名即可判断aarch64后缀的包才能在 ARM64 机器上用。如果你同时管理两种架构的机器建议在下载后就重命名把架构写进目录名比如harbor-offline/arm64/和harbor-offline/amd64/。另外在解压后执行docker load之前可以先tar -xOf内部某个镜像的 manifest 文件看架构但这操作太麻烦不如直接看文件名。5.3dial tcp 192.168.209.133: ... connect: connection refused端口没监听或防火墙拦截现象docker login时报Get https://192.168.209.133/v2/: dial tcp 192.168.209.133:443: connect: connection refused但 Harbor Web 页面却可以正常访问。原因Web 页面通过浏览器访问走的是你输入的端口而 docker login 默认走 443。如果 Harbor 只开了 HTTP 80 端口但你用docker login 192.168.209.133不带端口docker 会自己加https://前缀并连 443连接被拒绝。另一种情况是 80 端口被云安全组或 iptables 挡了curl 从本机访问正常但跨机器访问不通。解决先确认 Harbor 开了哪些端口netstat -tlnp | grep -E 80|443。如果只有 80那就在 docker 的 daemon.json 里加insecure-registries并且登录时显式写端口docker login 192.168.209.133:80。如果端口明确放开还连不上检查 iptablesiptables -L -n | grep 80必要时临时放行测试iptables -I INPUT -p tcp --dport 80 -j ACCEPT。5.4 磁盘空间不足推镜像时报no space left on device现象docker push到一半失败registry 容器日志里有no space left on device检查df -h却发现data_volume所在分区还有几十个 G。原因Harbor 的 registry 容器默认使用docker volume或本地目录存储如果你配置的是data_volume: /data但/data和/不在同一分区df -h /data的空间可能不够。更隐蔽的是如果 docker 的storage-driver是overlay2docker load导入的镜像层会占用/var/lib/docker所在分区而这个分区往往和/data不是同一个。解决检查两个分区的剩余空间df -h /data和df -h /var/lib/docker。如果/var/lib/docker紧张可以迁移 docker 根目录到数据盘修改 daemon.json 里的>{ log-driver: json-file, log-opts: { max-size: 100m, max-file: 3 } }max-size: 100m让单个容器日志文件超过 100MB 就轮转max-file: 3保留最近 3 份。这两个参数只对重启后新建的容器生效所以要systemctl restart docker再docker-compose down docker-compose up -d让 Harbor 全部容器应用新配置。镜像 GC 更值得养成习惯。Harbor 的 Web 界面删除镜像时实际删除的只是元数据磁盘上的分层数据还在。只有执行 garbage collection 后才会物理删除。使用方式是进入 Harbor 的 registry 容器内部执行docker-compose exec registry registry garbage-collect /etc/registry/config.yml执行时建议先停掉写入docker-compose stop registry再执行否则 GC 过程中新 push 的镜像可能被当作未被引用的层误删。GC 完成后docker-compose start registry。另一个习惯是定期做数据目录快照备份。data_volume目录包含所有镜像数据和数据库文件直接用tar或rsync备份都比逐容器导出快。我一般用rsync -avz --delete同步到异地每周一次配合前端配置的复制策略基本能保证 Harbor 节点挂了以后半天内重建。如果你正在规划内网镜像仓库aarch64 离线包这条路是走得通的前提是提前想清楚三件事数据目录放在哪、docker 的 insecure-registries 怎么配、以及日志和 GC 的兜底策略。把这三个问题在安装前定好后面能少踩很多坑。希望这篇能帮到你。本文还有配套的精品资源点击获取