|
| 1 | +--- |
| 2 | +title: Harbor 多架构镜像管理完整指南 |
| 3 | +date: 2025-08-13 10:40:00 |
| 4 | +tags: |
| 5 | + - Docker |
| 6 | + - Harbor |
| 7 | + - Manifest |
| 8 | + - Multi-Architecture |
| 9 | +# comments: true |
| 10 | +category: Docker |
| 11 | +--- |
| 12 | + |
| 13 | + 在现代容器化环境中,多架构支持变得越来越重要。随着 ARM64 架构在服务器和边缘设备中的普及,我们需要构建和管理支持多种 CPU 架构的容器镜像。本文将详细介绍如何在 Harbor 私有镜像仓库中管理多架构镜像,包括配置、推送和创建 manifest 列表的完整流程。 |
| 14 | + |
| 15 | +<!-- more --> |
| 16 | + |
| 17 | +## 环境说明 |
| 18 | + |
| 19 | +- **Harbor 版本**: 自建 Harbor 私有仓库 |
| 20 | +- **部署方式**: HTTPS 模式,使用自签名证书,443 端口 |
| 21 | +- **Docker 版本**: 支持 manifest 功能的版本 |
| 22 | +- **目标架构**: AMD64 和 ARM64 |
| 23 | + |
| 24 | +## 问题背景 |
| 25 | + |
| 26 | +在实际项目中,我们遇到了以下挑战: |
| 27 | + |
| 28 | +1. 需要将单架构镜像转换为多架构镜像 |
| 29 | +2. Harbor 使用自签名 HTTPS 证书,需要正确配置 Docker 客户端 |
| 30 | +3. Docker manifest 功能需要特定的配置和操作流程 |
| 31 | +4. 需要批量处理多个版本的镜像 |
| 32 | + |
| 33 | +## 解决方案 |
| 34 | + |
| 35 | +### 1. Harbor 证书配置 |
| 36 | + |
| 37 | +#### 问题现象 |
| 38 | +```bash |
| 39 | +Get "http://110.1.20.3/v2/": dial tcp 110.1.20.3:80: connect: connection refused |
| 40 | +``` |
| 41 | + |
| 42 | +这个错误表明 Docker 客户端尝试使用 HTTP 协议连接 Harbor,但 Harbor 实际运行在 HTTPS 模式。 |
| 43 | + |
| 44 | +#### 解决步骤 |
| 45 | + |
| 46 | +**Step 1: 配置客户端证书** |
| 47 | +```bash |
| 48 | +# 创建证书目录 |
| 49 | +mkdir -p /etc/docker/certs.d/110.1.20.3/ |
| 50 | + |
| 51 | +# 复制 Harbor CA 证书和服务器证书 |
| 52 | +\cp -rvf /data/opt/installharbor/certs/{ca.crt,harbor.crt} /etc/docker/certs.d/110.1.20.3/ |
| 53 | + |
| 54 | +# 设置正确的文件权限 |
| 55 | +cd /etc/docker/certs.d/110.1.20.3/ |
| 56 | +chmod 644 ca.crt harbor.crt |
| 57 | +``` |
| 58 | + |
| 59 | +**Step 2: 配置 Docker daemon** |
| 60 | + |
| 61 | +编辑 `/etc/docker/daemon.json`: |
| 62 | +```json |
| 63 | +{ |
| 64 | + "registry-mirrors": [ |
| 65 | + "https://docker.m.daocloud.io", |
| 66 | + "http://docker.1panel.live", |
| 67 | + "https://docker.agsv.top", |
| 68 | + "https://docker.agsvpt.work", |
| 69 | + "https://dockerpull.com", |
| 70 | + "https://dockerproxy.cn" |
| 71 | + ], |
| 72 | + "debug": false, |
| 73 | + "insecure-registries": [ |
| 74 | + "110.1.20.3" |
| 75 | + ], |
| 76 | + "ip-forward": true, |
| 77 | + "ipv6": false, |
| 78 | + "live-restore": true, |
| 79 | + "log-driver": "json-file", |
| 80 | + "log-level": "warn", |
| 81 | + "log-opts": { |
| 82 | + "max-size": "100m", |
| 83 | + "max-file": "2" |
| 84 | + }, |
| 85 | + "selinux-enabled": false, |
| 86 | + "experimental": true, |
| 87 | + "storage-driver": "overlay2", |
| 88 | + "data-root": "/data/docker_dir", |
| 89 | + "default-ulimits": { |
| 90 | + "nofile": { |
| 91 | + "Name": "nofile", |
| 92 | + "Hard": 65536, |
| 93 | + "Soft": 65536 |
| 94 | + } |
| 95 | + }, |
| 96 | + "exec-opts": ["native.cgroupdriver=systemd"] |
| 97 | +} |
| 98 | +``` |
| 99 | + |
| 100 | +**关键配置说明:** |
| 101 | +- `"experimental": true` - 启用 manifest 功能 |
| 102 | +- `"insecure-registries": ["110.1.20.3"]` - 信任自签名证书 |
| 103 | + |
| 104 | +**Step 3: 重启服务** |
| 105 | +```bash |
| 106 | +# 重启 Docker |
| 107 | +systemctl daemon-reload |
| 108 | +systemctl restart docker |
| 109 | + |
| 110 | +# 重启 Harbor(同台机器部署需要) |
| 111 | +cd /data/opt/installharbor/ |
| 112 | +bash install.sh |
| 113 | +``` |
| 114 | + |
| 115 | +### 2. 常见证书问题解决 |
| 116 | + |
| 117 | +#### 问题1:证书文件扩展名错误 |
| 118 | +``` |
| 119 | +missing key ca.key for client certificate ca.cert. CA certificates must use the extension .crt |
| 120 | +``` |
| 121 | + |
| 122 | +**解决方法:** |
| 123 | +- Docker 要求 CA 证书必须使用 `.crt` 扩展名 |
| 124 | +- 客户端证书目录不应包含私钥文件(`.key` 文件) |
| 125 | + |
| 126 | +**正确的证书目录结构:** |
| 127 | +``` |
| 128 | +/etc/docker/certs.d/110.1.20.3/ |
| 129 | +├── ca.crt # CA 证书(必需) |
| 130 | +└── harbor.crt # Harbor 服务器证书(可选) |
| 131 | +``` |
| 132 | + |
| 133 | +#### 问题2:Docker daemon 配置错误 |
| 134 | +``` |
| 135 | +json: cannot unmarshal string into Go struct field Config.experimental of type bool |
| 136 | +``` |
| 137 | + |
| 138 | +**解决方法:** |
| 139 | +```json |
| 140 | +// 错误写法 |
| 141 | +"experimental": "enabled" |
| 142 | + |
| 143 | +// 正确写法 |
| 144 | +"experimental": true |
| 145 | +``` |
| 146 | + |
| 147 | +### 3. 多架构镜像管理脚本 |
| 148 | + |
| 149 | +创建自动化脚本 `pushImages.sh` 来批量处理镜像: |
| 150 | + |
| 151 | +```bash |
| 152 | +#!/bin/bash |
| 153 | +# =================================== |
| 154 | +# 批量处理:打 tag + 推送 + 创建多架构 manifest |
| 155 | +# 支持 HTTPS Harbor 自签名证书 |
| 156 | +# =================================== |
| 157 | +set -e |
| 158 | + |
| 159 | +# Harbor 配置 |
| 160 | +HARBOR_HOST="110.1.20.3" |
| 161 | +REPO_PREFIX="${HARBOR_HOST}/library" |
| 162 | +VERSIONS=("3.11.3" "3.10.1" "3.9.2" "3.8.5") |
| 163 | +ARCHES=("amd64" "arm64") |
| 164 | + |
| 165 | +# 是否删除旧标签(可选) |
| 166 | +DELETE_OLD_TAGS=false |
| 167 | + |
| 168 | +echo "🚀 开始处理镜像标签与多架构清单..." |
| 169 | +echo "📡 Harbor 地址: https://${HARBOR_HOST}" |
| 170 | + |
| 171 | +# 检查登录状态 |
| 172 | +if ! docker login https://${HARBOR_HOST} 2>/dev/null; then |
| 173 | + echo "⚠️ Docker 未登录到 Harbor,请先登录:" |
| 174 | + echo " docker login https://${HARBOR_HOST}" |
| 175 | + exit 1 |
| 176 | +fi |
| 177 | + |
| 178 | +# 1. 先为所有镜像打新标签并推送 |
| 179 | +for version in "${VERSIONS[@]}"; do |
| 180 | + for arch in "${ARCHES[@]}"; do |
| 181 | + OLD_TAG="${REPO_PREFIX}/java-gdal-local-${arch}:${version}" |
| 182 | + NEW_TAG="${REPO_PREFIX}/java-gdal-local:${version}-${arch}" |
| 183 | + |
| 184 | + if docker inspect "$OLD_TAG" &> /dev/null; then |
| 185 | + echo "🏷️ 打标签: $OLD_TAG -> $NEW_TAG" |
| 186 | + docker tag "$OLD_TAG" "$NEW_TAG" |
| 187 | + |
| 188 | + echo "📤 推送: $NEW_TAG" |
| 189 | + docker push "$NEW_TAG" |
| 190 | + |
| 191 | + if [ "$DELETE_OLD_TAGS" = true ]; then |
| 192 | + echo "🗑️ 删除旧标签: $OLD_TAG" |
| 193 | + docker rmi "$OLD_TAG" || true |
| 194 | + fi |
| 195 | + else |
| 196 | + echo "⚠️ 镜像不存在,跳过: $OLD_TAG" |
| 197 | + fi |
| 198 | + done |
| 199 | +done |
| 200 | + |
| 201 | +# 2. 为每个版本创建多架构 manifest |
| 202 | +echo "" |
| 203 | +echo "🔧 开始创建多架构 manifest..." |
| 204 | + |
| 205 | +for version in "${VERSIONS[@]}"; do |
| 206 | + MANIFEST_TAG="${REPO_PREFIX}/java-gdal-local:${version}" |
| 207 | + TAG_AMD64="${REPO_PREFIX}/java-gdal-local:${version}-amd64" |
| 208 | + TAG_ARM64="${REPO_PREFIX}/java-gdal-local:${version}-arm64" |
| 209 | + |
| 210 | + echo "📦 创建多架构镜像清单: $MANIFEST_TAG" |
| 211 | + |
| 212 | + # 删除可能存在的旧 manifest |
| 213 | + docker manifest rm "$MANIFEST_TAG" 2>/dev/null || true |
| 214 | + |
| 215 | + # 创建 manifest |
| 216 | + docker manifest create "$MANIFEST_TAG" \ |
| 217 | + --amend "$TAG_AMD64" \ |
| 218 | + --amend "$TAG_ARM64" |
| 219 | + |
| 220 | + # 添加平台信息(关键步骤) |
| 221 | + docker manifest annotate "$MANIFEST_TAG" "$TAG_AMD64" --os linux --arch amd64 |
| 222 | + docker manifest annotate "$MANIFEST_TAG" "$TAG_ARM64" --os linux --arch arm64 |
| 223 | + |
| 224 | + # 推送 manifest |
| 225 | + echo "📤 推送多架构镜像: $MANIFEST_TAG" |
| 226 | + docker manifest push "$MANIFEST_TAG" |
| 227 | + |
| 228 | + echo "✅ 完成: $MANIFEST_TAG" |
| 229 | +done |
| 230 | + |
| 231 | +echo "" |
| 232 | +echo "🎉 所有操作完成!" |
| 233 | +echo "" |
| 234 | +echo "你现在可以通过以下方式拉取:" |
| 235 | +for version in "${VERSIONS[@]}"; do |
| 236 | + echo " docker pull ${HARBOR_HOST}/library/java-gdal-local:${version}" |
| 237 | +done |
| 238 | +``` |
| 239 | + |
| 240 | +### 4. 手动操作流程 |
| 241 | + |
| 242 | +如果需要手动创建多架构镜像,可以按以下步骤: |
| 243 | + |
| 244 | +**Step 1: 登录 Harbor** |
| 245 | +```bash |
| 246 | +docker login 110.1.20.3 |
| 247 | +# 或者 |
| 248 | +docker login -u admin -p [email protected] 110.1.20.3 |
| 249 | +``` |
| 250 | + |
| 251 | +**Step 2: 推送单架构镜像** |
| 252 | +```bash |
| 253 | +# 为现有镜像打新标签 |
| 254 | +docker tag 110.1.20.3/library/java-gdal-local-amd64:3.8.5 \ |
| 255 | + 110.1.20.3/library/java-gdal-local:3.8.5-amd64 |
| 256 | + |
| 257 | +docker tag 110.1.20.3/library/java-gdal-local-arm64:3.8.5 \ |
| 258 | + 110.1.20.3/library/java-gdal-local:3.8.5-arm64 |
| 259 | + |
| 260 | +# 推送镜像 |
| 261 | +docker push 110.1.20.3/library/java-gdal-local:3.8.5-amd64 |
| 262 | +docker push 110.1.20.3/library/java-gdal-local:3.8.5-arm64 |
| 263 | +``` |
| 264 | + |
| 265 | +**Step 3: 创建多架构 manifest** |
| 266 | +```bash |
| 267 | +# 创建 manifest |
| 268 | +docker manifest create 110.1.20.3/library/java-gdal-local:3.8.5 \ |
| 269 | + --amend 110.1.20.3/library/java-gdal-local:3.8.5-amd64 \ |
| 270 | + --amend 110.1.20.3/library/java-gdal-local:3.8.5-arm64 |
| 271 | + |
| 272 | +# 添加平台信息(重要!) |
| 273 | +docker manifest annotate 110.1.20.3/library/java-gdal-local:3.8.5 \ |
| 274 | + 110.1.20.3/library/java-gdal-local:3.8.5-amd64 --os linux --arch amd64 |
| 275 | + |
| 276 | +docker manifest annotate 110.1.20.3/library/java-gdal-local:3.8.5 \ |
| 277 | + 110.1.20.3/library/java-gdal-local:3.8.5-arm64 --os linux --arch arm64 |
| 278 | + |
| 279 | +# 推送 manifest(注意:使用 manifest push,不是普通 push) |
| 280 | +docker manifest push 110.1.20.3/library/java-gdal-local:3.8.5 |
| 281 | +``` |
| 282 | + |
| 283 | +**Step 4: 验证结果** |
| 284 | +```bash |
| 285 | +# 查看 manifest 信息 |
| 286 | +docker manifest inspect 110.1.20.3/library/java-gdal-local:3.8.5 |
| 287 | + |
| 288 | +# 测试拉取(会根据当前平台自动选择架构) |
| 289 | +docker pull 110.1.20.3/library/java-gdal-local:3.8.5 |
| 290 | +``` |
| 291 | + |
| 292 | +### 5. 故障排除 |
| 293 | + |
| 294 | +#### 常见错误及解决方法 |
| 295 | + |
| 296 | +**1. 证书相关错误** |
| 297 | +``` |
| 298 | +x509: certificate signed by unknown authority |
| 299 | +``` |
| 300 | +**解决:** 确保正确配置了 CA 证书或使用 `insecure-registries` |
| 301 | + |
| 302 | +**2. Manifest 推送错误** |
| 303 | +``` |
| 304 | +tag does not exist: 110.1.20.3/library/java-gdal-local:3.8.5 |
| 305 | +``` |
| 306 | +**解决:** 使用 `docker manifest push` 而不是 `docker push` |
| 307 | + |
| 308 | +**3. 实验性功能未启用** |
| 309 | +``` |
| 310 | +docker manifest is only supported when experimental cli features are enabled |
| 311 | +``` |
| 312 | +**解决:** 在 `daemon.json` 中设置 `"experimental": true` |
| 313 | + |
| 314 | +#### 检查清单 |
| 315 | + |
| 316 | +- [ ] Docker daemon 配置了 `"experimental": true` |
| 317 | +- [ ] 证书文件使用正确的扩展名(`.crt`) |
| 318 | +- [ ] 证书目录不包含私钥文件 |
| 319 | +- [ ] 已正确登录 Harbor |
| 320 | +- [ ] 单架构镜像已成功推送 |
| 321 | +- [ ] 使用 `docker manifest push` 推送 manifest |
| 322 | + |
| 323 | +## 完整配置脚本 |
| 324 | + |
| 325 | +将所有配置步骤整合成一个脚本: |
| 326 | + |
| 327 | +```bash |
| 328 | +#!/bin/bash |
| 329 | +# Harbor 多架构镜像配置脚本 |
| 330 | + |
| 331 | +echo "🔧 配置 Harbor 多架构镜像支持..." |
| 332 | + |
| 333 | +# 1. 配置客户端证书 |
| 334 | +echo "📜 配置客户端证书..." |
| 335 | +mkdir -p /etc/docker/certs.d/110.1.20.3/ |
| 336 | +\cp -rvf /data/opt/installharbor/certs/{ca.crt,harbor.crt} /etc/docker/certs.d/110.1.20.3/ |
| 337 | +cd /etc/docker/certs.d/110.1.20.3/ |
| 338 | +chmod 644 ca.crt harbor.crt |
| 339 | + |
| 340 | +# 2. 重启 Docker |
| 341 | +echo "🔄 重启 Docker 服务..." |
| 342 | +systemctl daemon-reload |
| 343 | +systemctl restart docker |
| 344 | + |
| 345 | +# 3. 重启 Harbor(同台机器上需要) |
| 346 | +echo "🔄 重启 Harbor..." |
| 347 | +cd /data/opt/installharbor/ |
| 348 | +bash install.sh |
| 349 | + |
| 350 | +# 4. 等待服务启动 |
| 351 | +echo "⏳ 等待服务启动..." |
| 352 | +sleep 30 |
| 353 | + |
| 354 | +# 5. 测试配置 |
| 355 | +echo "🧪 测试配置..." |
| 356 | +docker login 110.1.20.3 |
| 357 | + |
| 358 | +# 6. 创建测试 manifest |
| 359 | +echo "📦 创建测试 manifest..." |
| 360 | +docker manifest create 110.1.20.3/library/java-gdal-local:3.8.5 \ |
| 361 | + 110.1.20.3/library/java-gdal-local:3.8.5-amd64 \ |
| 362 | + 110.1.20.3/library/java-gdal-local:3.8.5-arm64 |
| 363 | + |
| 364 | +docker manifest annotate 110.1.20.3/library/java-gdal-local:3.8.5 \ |
| 365 | + 110.1.20.3/library/java-gdal-local:3.8.5-amd64 --os linux --arch amd64 |
| 366 | + |
| 367 | +docker manifest annotate 110.1.20.3/library/java-gdal-local:3.8.5 \ |
| 368 | + 110.1.20.3/library/java-gdal-local:3.8.5-arm64 --os linux --arch arm64 |
| 369 | + |
| 370 | +docker manifest push 110.1.20.3/library/java-gdal-local:3.8.5 |
| 371 | + |
| 372 | +# 7. 验证结果 |
| 373 | +echo "✅ 验证结果..." |
| 374 | +docker manifest inspect 110.1.20.3/library/java-gdal-local:3.8.5 |
| 375 | + |
| 376 | +echo "🎉 配置完成!" |
| 377 | +``` |
| 378 | + |
| 379 | +## 总结 |
| 380 | + |
| 381 | +通过本文的配置和脚本,我们成功解决了在自建 Harbor 中管理多架构镜像的问题。关键要点包括: |
| 382 | + |
| 383 | +1. **正确配置 HTTPS 证书**:使用正确的文件名和权限 |
| 384 | +2. **启用实验性功能**:Docker manifest 需要实验性功能支持 |
| 385 | +3. **理解 manifest 操作**:区分 `docker push` 和 `docker manifest push` |
| 386 | +4. **自动化流程**:使用脚本批量处理多个版本和架构 |
| 387 | + |
| 388 | +这套方案可以帮助团队高效地管理多架构容器镜像,支持在不同 CPU 架构的环境中无缝部署应用。 |
| 389 | + |
| 390 | +## 参考资源 |
| 391 | + |
| 392 | +- [Docker Multi-platform images](https://docs.docker.com/build/building/multi-platform/) |
| 393 | +- [Docker manifest command](https://docs.docker.com/engine/reference/commandline/manifest/) |
| 394 | +- [Harbor Documentation](https://goharbor.io/docs/) |
0 commit comments