Docker离线部署实战:制作兼容X86与ARM架构的完整部署包
1. 项目概述与核心价值
最近在给几个客户做现场技术支持,环境挺有意思的,有在老旧工厂车间里用X86工控机的,也有在移动巡检终端上用ARM开发板的,共同点就一个: 没网 ,或者网络环境差到让你怀疑人生。客户的需求也很明确,要把一套基于微服务的应用系统跑起来,这套系统依赖了十多个Docker容器,通过docker-compose编排。这种“离线部署Docker全家桶”的需求,在工业物联网、边缘计算、涉密环境或者海外项目现场越来越常见。网上教程不少,但要么只讲X86,要么只提ARM,能把两者打通、把离线包制作、架构适配、依赖排查这些坑一次性讲明白的,真不多。今天我就结合最近踩过的坑,把从零开始制作一个同时兼容X86_64和ARM64架构的Docker与Docker Compose离线部署包的全过程,以及背后的原理掰开揉碎了讲清楚。无论你手里是Intel的服务器还是树莓派,看完这篇,应该都能搞定。
2. 离线部署的核心思路与方案选型
离线部署,说白了就是“把需要从互联网下载的东西,提前准备好,带到现场去安装”。但Docker的离线部署比普通软件复杂,因为它涉及多层依赖:Docker引擎本身、docker-compose工具、以及最重要的——各种架构的容器镜像。
2.1 为什么离线部署这么麻烦?
Docker的设计哲学是“一次构建,随处运行”,但这个“随处”有个前提:能连上Docker Hub或某个镜像仓库。在离线环境,这个前提不复存在。你需要自己扮演这个仓库的角色。麻烦主要来自三点:
- 多重依赖 :Docker CE(社区版)在Linux上的安装依赖一堆包(
containerd.io,docker-ce-cli等),这些包之间还有版本约束。 - 架构差异 :X86_64和ARM64是两种完全不同的CPU指令集架构。一个为X86编译的二进制程序或镜像,无法在ARM机器上直接运行,反之亦然。必须准备对应架构的版本。
- 镜像搬运 :容器镜像本质上是多层只读文件的集合(tar包)。离线环境下,你需要把所有用到的镜像及其所有依赖层,从有网环境“拉取”并“保存”出来,再到离线环境“加载”进去。
2.2 主流方案对比与我们的选择
面对这些麻烦,通常有几种思路:
| 方案 | 核心操作 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 全手动下载deb/rpm包 | 从镜像站手动下载Docker所有组件的deb/rpm包及其依赖包。 | 最直接,对网络理解要求低。 | 1. 依赖解析繁琐,易遗漏。 2. 需分别准备X86和ARM版本,工作量大。 3. 难以应对复杂依赖链。 | 单一架构、Docker版本固定的极简环境。 |
使用 apt-offline / yumdownloader | 在有网机器模拟离线环境,用工具生成依赖包清单并批量下载。 | 能相对自动地解决系统包依赖。 | 1. 配置复杂,需要搭建本地模拟环境。 2. 对跨架构支持不友好。 3. 不解决Docker镜像问题。 | 熟悉系统包管理工具链的运维人员,用于部署基础软件。 |
| 制作内网镜像仓库 | 搭建私有Docker Registry(如Harbor),将所有镜像推送进去,离线环境指向该仓库。 | 最规范,最接近在线体验,便于管理。 | 1. 前期搭建和镜像同步工作量大。 2. 离线机器仍需能解析仓库域名或IP。 3. 对于一次性或临时性任务,显得笨重。 | 有稳定内网环境、需要持续进行镜像分发和管理的企业。 |
| 镜像保存为tar + 二进制部署 | 将Docker引擎和docker-compose以二进制方式安装,将所需镜像保存为tar文件。 | 灵活轻量 ,无需复杂服务,架构隔离清晰,非常适合混合架构环境。 | 1. 二进制文件需区分架构。 2. 镜像加载命令需手动执行。 | 临时性、移动性、多架构混合的离线部署场景。 |
结合我们开头提到的场景(多架构、一次性或临时部署), 第四种方案“镜像保存为tar + 二进制部署” 是最佳选择。它就像准备一个“绿色软件包”:把Docker主程序、docker-compose工具、以及所有需要的容器镜像打包在一起,带到目标机器上解压、安装、加载即可。架构界限清晰,X86的包和ARM的包分开准备,逻辑简单,不易出错。
3. 离线资源包制作全流程
接下来,我们就在一台 有互联网连接 的“打包机”上,制作这个离线资源包。假设我们的目标环境是Linux(Ubuntu/CentOS均可,命令略有差异,本文以Ubuntu为例)。
3.1 环境准备与工具确认
首先,确保打包机本身已经安装了Docker和docker-compose。这很好验证:
# 检查Docker
docker --version
# 检查docker-compose
docker-compose --version
如果没有,请先用在线方式安装它们,这是后续所有操作的基础。
我们需要为两个目标架构准备资源。虽然打包机可能是X86的,但我们可以利用Docker的特性来获取ARM架构的镜像。资源包最终目录结构规划如下:
offline-docker-package/
├── README.md
├── install.sh
├── binaries/
│ ├── x86_64/
│ │ ├── docker-<version>.tgz
│ │ └── docker-compose-<version>
│ └── aarch64/ (即ARM64)
│ ├── docker-<version>.tgz
│ └── docker-compose-<version>
└── images/
├── x86_64-images.tar
└── aarch64-images.tar
3.2 第一步:获取多架构Docker引擎二进制文件
Docker官方提供了静态编译的二进制包,非常适合离线部署。我们需要分别下载X86_64和ARM64版本。
- 确定版本 :访问 Docker GitHub Release 页面,选择一个稳定的版本,例如
24.0.7。记住版本号。 - 下载二进制包 :
# 创建目录 mkdir -p offline-docker-package/binaries/{x86_64,aarch64} cd offline-docker-package # 下载X86_64版本 wget https://download.docker.com/linux/static/stable/x86_64/docker-24.0.7.tgz -O binaries/x86_64/docker.tgz # 下载ARM64版本 (注意链接中的aarch64) wget https://download.docker.com/linux/static/stable/aarch64/docker-24.0.7.tgz -O binaries/aarch64/docker.tgz注意 :
aarch64就是ARM64架构在Linux中的标准称谓。务必从官方渠道下载,确保文件完整性。
3.3 第二步:获取多架构docker-compose二进制文件
docker-compose是一个独立的Python二进制文件(或Go编译的单文件)。从v2开始,官方也提供了多架构的二进制发布。
- 确定版本 :访问 docker-compose GitHub Release ,例如选择
v2.24.5。 - 下载二进制文件 :
# 下载X86_64版本 wget https://github.com/docker/compose/releases/download/v2.24.5/docker-compose-linux-x86_64 -O binaries/x86_64/docker-compose # 下载ARM64版本 wget https://github.com/docker/compose/releases/download/v2.24.5/docker-compose-linux-aarch64 -O binaries/aarch64/docker-compose # 赋予执行权限(很重要!) chmod +x binaries/x86_64/docker-compose binaries/aarch64/docker-compose
3.4 第三步:准备多架构的Docker镜像
这是最核心也最容易出错的一步。我们的应用 docker-compose.yml 里可能声明了像 nginx:alpine , mysql:8.0 , redis:7 这样的镜像。默认情况下, docker pull 拉取的是与当前机器架构匹配的镜像。如何在X86打包机上获取ARM镜像呢?
答案是使用Docker的** buildx **工具,它可以构建和拉取多平台镜像。
-
启用并检查buildx :
# 确保buildx可用 docker buildx version # 如果没有,一般安装较新版本的Docker都会自带。 # 创建一个新的builder实例(支持多架构) docker buildx create --name multi-arch-builder --use docker buildx inspect --bootstrapinspect命令输出中看到linux/amd64, linux/arm64等平台,即表示成功。 -
拉取多架构镜像清单 :我们并不需要真的在X86机上运行ARM镜像,只需要把镜像的“文件层”拉取到本地仓库。这可以通过
--platform参数和docker pull的--all-tags变通实现,但更规范的方式是使用docker buildx imagetools或直接pull时指定平台。# 方法:为每个需要的镜像,分别拉取不同架构的版本 # 拉取X86架构的镜像(默认就是,所以正常pull) docker pull nginx:alpine docker pull mysql:8.0 docker pull redis:7-alpine # 拉取ARM64架构的镜像,关键参数:--platform=linux/arm64 docker pull --platform=linux/arm64 nginx:alpine docker pull --platform=linux/arm64 mysql:8.0 docker pull --platform=linux/arm64 redis:7-alpine执行后,使用
docker images --digests查看,你会看到同一个镜像标签(如nginx:alpine)出现了两次,但它们的DIGEST和IMAGE ID不同,这就是不同架构的镜像。实操心得 :有些镜像可能没有提供你所需架构的版本。拉取时如果报错“no matching manifest for linux/arm64 in the manifest list”,说明该镜像官方不支持ARM64。这时你需要寻找替代镜像(例如,很多软件提供了
-arm64v8标签),或者自己用buildx构建。 -
保存镜像为tar文件 :我们需要把不同架构的镜像分别打包。
# 创建镜像目录 mkdir -p offline-docker-package/images # 保存X86架构的镜像 docker save -o offline-docker-package/images/x86_64-images.tar \ nginx:alpine \ mysql:8.0 \ redis:7-alpine # 保存ARM64架构的镜像 # 注意:我们需要先筛选出ARM64的镜像ID # 一个小技巧:通过`docker image inspect --format='{{.Id}} {{.Architecture}}' <image_name>`查看架构 # 假设我们查得ARM版nginx的ID是 sha256:abc123... # 更稳妥的方法是打上标签区分: docker tag nginx:alpine nginx:alpine-arm64 docker tag mysql:8.0 mysql:8.0-arm64 docker tag redis:7-alpine redis:7-alpine-arm64 # 然后保存带标签的镜像 docker save -o offline-docker-package/images/aarch64-images.tar \ nginx:alpine-arm64 \ mysql:8.0-arm64 \ redis:7-alpine-arm64现在,
images目录下就有了两个巨大的tar文件,分别包含了对应架构的镜像层。
3.5 第四步:编写自动化安装脚本
为了让离线部署更简单,我们写一个 install.sh 脚本,让它自动检测架构、安装二进制文件、加载镜像。
#!/bin/bash
# offline-docker-package/install.sh
set -e # 遇到错误立即退出
echo "开始离线安装Docker与Docker Compose..."
# 1. 检测系统架构
ARCH=$(uname -m)
case "$ARCH" in
x86_64|amd64)
TARGET_ARCH="x86_64"
;;
aarch64|arm64)
TARGET_ARCH="aarch64"
;;
*)
echo "不支持的架构: $ARCH"
exit 1
;;
esac
echo "检测到系统架构为: $TARGET_ARCH"
BIN_DIR="./binaries/$TARGET_ARCH"
IMAGE_TAR="./images/${TARGET_ARCH}-images.tar"
# 2. 检查必要文件是否存在
if [[ ! -d "$BIN_DIR" ]]; then
echo "错误:未找到对应架构($TARGET_ARCH)的二进制文件目录。"
exit 1
fi
if [[ ! -f "$IMAGE_TAR" ]]; then
echo "警告:未找到对应架构的镜像包 $IMAGE_TAR,跳过镜像加载。"
fi
# 3. 安装Docker二进制文件
echo "正在安装Docker引擎..."
tar -xzf "$BIN_DIR/docker.tgz" -C /tmp
sudo cp /tmp/docker/* /usr/bin/
sudo chmod +x /usr/bin/docker*
# 4. 创建Docker系统服务(适用于systemd系统)
if command -v systemctl &> /dev/null; then
echo "配置Docker系统服务..."
sudo groupadd docker 2>/dev/null || true
# 这里需要准备docker.service文件,我们可以将其嵌入脚本,或者单独放在package里
# 简单起见,我们使用从官方包提取的service文件。假设我们有一个预制的docker.service文件在package根目录
if [[ -f ./docker.service ]]; then
sudo cp ./docker.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable docker
echo "Docker服务已配置为开机自启。"
else
echo "未找到docker.service文件,请手动配置服务。"
fi
fi
# 5. 安装docker-compose
echo "正在安装Docker Compose..."
sudo cp "$BIN_DIR/docker-compose" /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# 6. 加载Docker镜像
if [[ -f "$IMAGE_TAR" ]]; then
echo "正在加载Docker镜像..."
sudo docker load -i "$IMAGE_TAR"
echo "镜像加载完成。"
fi
# 7. 验证安装
echo "验证安装..."
docker --version
docker-compose --version
echo "离线安装完成!请将当前用户加入'docker'组以非root运行docker命令:"
echo " sudo usermod -aG docker \$USER"
echo "然后注销并重新登录。"
这个脚本做了几件关键事:架构检测、文件拷贝、服务注册、镜像加载。注意, docker.service 文件需要你提前从一台在线安装的相同发行版机器上拷贝一份(位于 /lib/systemd/system/docker.service ),并放入离线包根目录。
4. 离线环境部署实操与问题排查
现在,我们将制作好的 offline-docker-package 目录整个拷贝到目标离线机器上(用U盘、内网共享都可以)。
4.1 部署步骤
- 传输并解压 :将整个包放到目标机器,例如
/opt/下。 - 执行安装 :
cd /opt/offline-docker-package # 给脚本执行权限 chmod +x install.sh # 执行安装,使用sudo sudo ./install.sh - 配置用户组 (安装脚本最后已提示):
然后必须注销当前用户,重新登录 ,才能使组权限生效。sudo usermod -aG docker $USER - 验证与使用 :
# 验证安装 docker version docker-compose version # 查看已加载的镜像 docker images # 进入你的应用目录,使用docker-compose启动服务 cd /path/to/your-app docker-compose up -d
4.2 常见问题与排查技巧实录
即使准备充分,离线环境部署仍可能遇到各种问题。下面是我踩过坑后总结的排查清单:
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
执行 docker ps 报错: Cannot connect to the Docker daemon | 1. Docker服务未启动。 2. 当前用户不在 docker 组。 3. 环境变量 DOCKER_HOST 设置错误。 | 1. sudo systemctl status docker 检查服务状态。未启动则 sudo systemctl start docker 。 2. groups $USER 查看是否在 docker 组。不在则用 usermod 添加并 重新登录 。 3. echo $DOCKER_HOST 检查,离线环境通常不应设置此变量。 |
docker load 镜像时报错: open /var/lib/docker/tmp/...: no space left on device | 磁盘空间不足,尤其是 /var/lib/docker 所在分区。 | 1. df -h 查看磁盘使用情况。 2. Docker默认存储目录是 /var/lib/docker 。如果空间不足,可以清理无用镜像或修改Docker数据目录(需在服务启动前配置 /etc/docker/daemon.json 中的 data-root )。 |
加载镜像后, docker run 时报错: exec format error | 架构不匹配! 这是最典型的多架构问题。在ARM机器上加载了X86的镜像,或者反之。 | 1. docker image inspect <image_name> | grep Architecture 确认镜像架构。 2. uname -m 确认机器架构。 3. 解决方案 :确保加载了正确架构的镜像包。在打包阶段务必严格区分 x86_64-images.tar 和 aarch64-images.tar 。 |
docker-compose up 报错: image for service XXX not found | 虽然镜像已加载,但 docker-compose.yml 中使用的镜像标签与本地加载的镜像标签对不上。 | 1. docker images 查看本地已有镜像的 REPOSITORY:TAG 。 2. 对比 docker-compose.yml 文件中 image: 字段指定的名称。 3. 解决方案 :修改 docker-compose.yml 中的镜像名,使其与本地镜像名一致。或者在保存/加载镜像时,使用与 docker-compose.yml 中一致的标签。 |
| 系统重启后Docker服务无法启动 | 1. 服务未设置开机自启。 2. 内核模块或系统依赖问题(如iptables、cgroup)。 3. 二进制文件损坏或路径错误。 | 1. sudo systemctl enable docker 确保启用。 2. 检查内核版本是否支持Docker(需3.10以上)。对于较旧系统,可能需要升级内核或使用旧版Docker。 3. 使用 which docker 和 which dockerd 检查命令路径,确保二进制文件已正确安装到 /usr/bin/ 。 |
| 在ARM机器上,docker-compose命令执行慢或报错 | 可能误用了X86版本的 docker-compose 二进制文件。 | 1. file /usr/local/bin/docker-compose 查看文件信息,确认是ARM可执行文件。 2. 确保安装脚本正确拷贝了 binaries/aarch64/docker-compose 文件。 |
独家避坑技巧 :在制作离线包时,可以在打包机上用
qemu-user-static这个“神器”提前验证ARM镜像。安装它(sudo apt-get install qemu-user-static)并注册到Docker(docker run --rm --privileged multiarch/qemu-user-static --reset -p yes),之后你就可以在X86机器上直接docker run --platform=linux/arm64 your-arm-image来模拟运行ARM容器,提前发现应用在ARM架构下的兼容性问题,比如某些依赖库是否存在。
5. 进阶:构建真正跨架构的Compose项目
上面的方法解决了基础部署,但每次都要手动区分两个镜像包,还是有些麻烦。对于更复杂的项目,我们可以利用Docker Buildx和镜像清单(Manifest)来创建 单镜像多架构支持 。
原理是:为同一个镜像标签(如 myapp:v1 )创建包含 linux/amd64 和 linux/arm64 两个架构镜像的“清单列表”。当用户 docker pull myapp:v1 时,Docker会自动根据当前机器架构拉取对应的镜像层。
在有网打包机上的操作:
# 前提:已安装并配置好docker buildx,并创建了multi-arch-builder
# 1. 为你的应用构建多架构镜像(假设你有Dockerfile)
docker buildx build --platform linux/amd64,linux/arm64 -t myregistry.local/myapp:v1 --push .
# 如果你只是整合现有公共镜像,可以创建多架构清单
# 2. 拉取各架构镜像
docker pull --platform=linux/amd64 nginx:alpine
docker pull --platform=linux/arm64 nginx:alpine
# 3. 为它们打上带架构后缀的标签
docker tag nginx:alpine myregistry.local/nginx:alpine-amd64
docker tag nginx:alpine myregistry.local/nginx:alpine-arm64
# 4. 推送到你的私有仓库(离线环境此步跳过,但概念要懂)
# docker push myregistry.local/nginx:alpine-amd64
# docker push myregistry.local/nginx:alpine-arm64
# 5. 创建并推送合并的清单(Manifest)
docker manifest create myregistry.local/nginx:alpine \
myregistry.local/nginx:alpine-amd64 \
myregistry.local/nginx:alpine-arm64
docker manifest push myregistry.local/nginx:alpine
在离线环境下,我们无法使用这种动态清单。但我们可以借鉴其思想: 准备一个统一的镜像包,里面包含所有架构的镜像层 。虽然 docker save 不支持直接保存多架构清单,但我们可以将两个架构的镜像都打上相同的标签但不同的后缀,一起保存到一个tar文件。在加载后,再通过一个判断架构的脚本,自动给对应架构的镜像打上应用所需的统一标签。这需要更复杂的安装后处理脚本,但对于大型、固定的多架构部署环境,能极大简化管理。
最后,别忘了给你的离线包写一个清晰的 README.md ,说明包含的组件版本、支持的架构、安装步骤、以及已知问题和注意事项。这个包就是你应对无网环境的“瑞士军刀”,准备得越充分,现场部署就越从容。
更多推荐


所有评论(0)