很多人第一次遇到离线服务器部署 Docker,会想到一个最直接的问题:
这台服务器访问不了 Docker Hub,我能不能在另一台电脑把镜像下载好,然后拷过去?
当然可以。
比如本地:
bash
docker pull nginx:latest
已经下载成功。
目标服务器:
text
没有公网
访问 Docker Hub 超时
不能配置代理
甚至完全隔离网络
那最简单的思路就是:
text
有网机器
↓
docker pull
↓
打包镜像
↓
U盘 / SCP / 内网
↓
目标服务器
↓
导入镜像
↓
docker run
Docker 自己就提供了:
text
docker save
docker load
专门解决这个问题。
但很多人继续搜索以后,又会发现另外两个命令:
text
docker export
docker import
然后彻底懵了:
save 和 export 都能生成 tar,到底有什么区别?
load 和 import 又有什么区别?
如果命令用错,确实都可能得到一个:
text
.tar
文件。
但里面保存的东西:

完全不是一回事。
今天继续围绕:
text
https://github.com/Rodert/DockerHub
把 Docker 离线镜像分发彻底讲清楚。
核心就四个命令:
text
docker save
docker load
docker export
docker import
如果只想提前知道结论:
搬 Docker 镜像,通常用 save + load。
搬某个 Container 当前文件系统,才考虑 export + import。
一、先看最标准的离线部署流程
假设目标服务器需要:
text
nginx:latest
redis:7
mysql:8.4
但是服务器:
text
无法访问 Docker Hub。
先在一台可以联网的机器:
bash
docker pull nginx:latest
docker pull redis:7
docker pull mysql:8.4
确认:
bash
docker images
看到:
text
nginx latest
redis 7
mysql 8.4
然后:
bash
docker save \
-o images.tar \
nginx:latest \
redis:7 \
mysql:8.4
Docker 官方当前对 docker image save 的定义就是:把一个或多个 Image 保存到 tar Archive,同时保留对应 Image 的父层、Tag 和版本信息。
现在得到:
text
images.tar
二、把 tar 拷到目标服务器
比如:
bash
scp images.tar \
root@192.168.1.100:/tmp/
或者:
text
U盘
移动硬盘
NAS
内网文件服务器
都可以。
目标服务器:
bash
docker load \
-i /tmp/images.tar
Docker 官方当前说明,docker load 可以从 tar 文件或者标准输入中载入 Image Repository,并恢复其中的 Image 和 Tag。
完成后:
bash
docker images
应该重新看到:
text
nginx latest
redis 7
mysql 8.4
服务器:
全程不需要访问 Docker Hub。
三、这就是最核心的一对
text
docker save
↓
image.tar
↓
docker load
它操作的对象:
Docker Image。
所以你要:
text
备份镜像
搬镜像
离线安装镜像
把一批 Image 送进内网
基本第一反应都应该是:
save / load。
四、docker save 保存的到底是什么?
例如:
bash
docker save \
nginx:latest \
-o nginx.tar
里面不是简单:
text
nginx 的最终 rootfs。
它会保留镜像本身的:
text
Layer
Image Metadata
Tag
Repository Information
Docker 官方当前明确指出,docker save 会生成一个 tar Repository,其中包括父 Layer,以及被保存 Image 对应的 Tag/Version 信息。
这点非常关键。
五、为什么 Layer 要保留?
因为我们前面讲过:
text
Docker Image
不是:
text
一个最终目录压缩包。
而是:
text
Manifest
+
Config
+
Layer 1
+
Layer 2
+
Layer 3
这些 Layer:
text
有 Digest
可以复用
可以共享
save/load:
尽量保持 Image 的 Docker 镜像语义。
六、这有什么好处?
比如你保存:
text
myapp:v1
myapp:v2
两者共享:
text
Ubuntu Base
Node Runtime
npm dependencies
那么:
text
公共 Layer
不需要在 tar 里无脑复制成两套完全独立文件系统。
Docker:
text
知道哪些 Layer 是共享的。
这就是 Image Format 本身的价值。
七、Tag 也会保留
例如:
bash
docker save \
-o myapp.tar \
mycompany/app:1.0 \
mycompany/app:1.1
目标机器:
bash
docker load \
-i myapp.tar
可以恢复:
text
mycompany/app:1.0
mycompany/app:1.1
而不是只剩:
text
一个不知道叫什么的 rootfs。
所以:
做正式离线镜像交付时,save/load 更符合预期。
八、甚至可以一次保存很多镜像
例如一个完整 Compose:
yaml
services:
nginx:
image: nginx:1.28
redis:
image: redis:7
mysql:
image: mysql:8.4
api:
image: mycompany/api:1.5
你完全可以:
bash
docker save \
-o production-images.tar \
nginx:1.28 \
redis:7 \
mysql:8.4 \
mycompany/api:1.5
交付给目标环境。
然后:
bash
docker load \
-i production-images.tar
最后:
bash
docker compose up \
-d \
--pull never
整个生产服务器:
可以完全不碰公网 Registry。
九、为什么这里建议 --pull never?
因为镜像:
text
已经提前 load。
你既然是:
text
离线部署。
就没有必要让 Compose:
text
再尝试 Registry。
所以:
bash
docker compose up \
-d \
--pull never
表达非常明确:
只使用本地已经导入的镜像。
十、这就可以形成标准离线发布包
例如:
text
release-1.5.0/
│
├── docker-compose.yml
│
├── images.tar
│
├── .env.example
│
├── install.sh
└── README.md
客户拿到:
text
release-1.5.0.tar.gz
解压:
bash
tar -xzvf \
release-1.5.0.tar.gz
执行:
bash
bash install.sh
十一、install.sh 可以很简单
bash
#!/usr/bin/env bash
set -e
echo "[1/3] Load images..."
docker load \
-i images.tar
echo "[2/3] Start services..."
docker compose up \
-d \
--pull never
echo "[3/3] Status..."
docker compose ps
这样用户:
text
不需要知道 Docker Hub
不需要知道 Mirror
不需要知道 Registry。
真正做到:
离线一键部署。
十二、images.tar 会不会特别大?
会。
例如:
text
mysql
800MB
api
1GB
node
1GB
一个发布包:
text
几 GB
很正常。
但注意:
text
docker save
默认生成:
Tar Archive。
并不是:
text
追求最大压缩率的发布文件。
所以我们通常还可以压缩。
十三、最简单 gzip
bash
docker save \
nginx:latest \
redis:7 \
| gzip \
> images.tar.gz
目标:
bash
gzip -dc images.tar.gz \
| docker load
Docker 当前 docker load 也支持读取 gzip、bzip2、xz 和 zstd 压缩的 Archive。
所以甚至可以直接:
bash
docker load \
-i images.tar.gz
十四、也可以 zstd
例如:
bash
docker save \
nginx:latest \
redis:7 \
| zstd -T0 \
> images.tar.zst
目标:
bash
docker load \
-i images.tar.zst
对于大镜像:
text
zstd
通常:
text
压缩/解压速度
都不错。
适合:
大型离线发布包。
十五、还能边传边 Load
如果两台机器:
text
网络互通
但目标机:
text
无法访问 Docker Hub。
其实不一定非要:
text
先生成 tar
↓
再 scp。
可以:
bash
docker save \
nginx:latest \
redis:7 \
| ssh root@server \
docker load
流程:
text
本地 Docker
↓
save stdout
↓
SSH
↓
远程 docker load
中间:
不落地 tar。
十六、再加压缩
bash
docker save \
nginx:latest \
redis:7 \
| gzip \
| ssh root@server \
'gzip -dc | docker load'
或者:
text
zstd
类似操作。
对于:
text
跳板机
内网服务器
临时搬镜像
特别方便。
十七、接下来讲最容易混淆的 docker export
假设我们启动:
bash
docker run \
--name demo \
ubuntu:24.04
然后进入:
bash
docker exec -it demo bash
在 Container 里:
bash
apt-get update
apt-get install -y nginx
再创建:
bash
echo hello \
> /hello.txt
现在这个 Container:
text
已经发生变化。
如果:
bash
docker export demo \
> demo.tar
它保存的是:
Container 当前文件系统内容。
十八、不是 Image Layer 结构
export 的逻辑更接近:
text
Container 当前 root filesystem
↓
打平
↓
tar
它不关心:
text
原来 Layer 1
Layer 2
Layer 3
哪个 Dockerfile RUN 生成了什么。
只关心:
当前看起来是什么文件系统。
十九、然后 docker import
bash
docker import \
demo.tar \
mydemo:v1
会把这个:
text
tar Filesystem
变成:
一个新的 Image。
Docker 当前对 docker image import 的定义正是:从 tarball 内容创建一个 filesystem image。
注意这个描述:
text
filesystem image
和 save/load:
差别非常大。
二十、最直观的对比
save
输入:
text
Image
输出:
text
Image Archive
再:
text
load
恢复:
text
Image。
export
输入:
text
Container
输出:
text
Filesystem Tar。
再:
text
import
创建:
text
新的 Image。
二十一、可以用一句话记
text
save / load
=
搬镜像
text
export / import
=
搬容器文件系统
只要记住这句话:
大多数场景都不会选错。
二十二、为什么 export/import 不适合镜像正常备份?
假设 Image:
text
myapp:v1
有:
text
Layer A
Layer B
Layer C
CMD
ENTRYPOINT
ENV
WORKDIR
LABEL
你启动成 Container:
text
demo。
然后:
bash
docker export demo
你得到的主要是:
text
当前 rootfs。
原来镜像:
text
Layer History
不会按照原 Image 结构保留下来。
所以:
镜像备份不要用 export/import。
二十三、import 后启动可能发现 CMD 没了
例如原 Image:
Dockerfile
CMD ["nginx", "-g", "daemon off;"]
export:
text
只管文件系统。
import 以后:
bash
docker run mydemo:v1
可能:
text
不知道该执行什么。
这时候你需要:
bash
docker run \
mydemo:v1 \
nginx -g 'daemon off;'
或者 import 时:
text
手工重新加 Config。
二十四、docker import 可以 --change
Docker 当前支持:
text
-c
--change
在 import 时应用部分 Dockerfile Instruction。
例如:
bash
docker import \
--change \
'CMD ["nginx","-g","daemon off;"]' \
demo.tar \
nginx-custom:v1
但你会发现:
已经很麻烦。
原来 Image 本身就有这些 Metadata,
结果:
text
export 打掉
↓
import 再手工补。
所以正常迁移镜像:
何必绕这个弯?
直接:
text
save/load。
二十五、那 export/import 到底有什么用?
比如你有一个:
text
已经运行很久的 Container。
里面:
text
手工安装了各种软件
改了很多文件
没有 Dockerfile
没有原始镜像构建流程。
现在只是想:
把这个 Container 当前文件系统拍个快照。
export:
bash
docker export \
old-container \
-o filesystem.tar
就很直接。
二十六、但这种方式不适合长期工程管理
因为:
text
你不知道这个状态
是怎么一步一步产生的。
更好的长期方式:
text
写 Dockerfile。
例如:
Dockerfile
FROM ubuntu:24.04
RUN apt-get update \
&& apt-get install -y nginx
COPY nginx.conf \
/etc/nginx/nginx.conf
CMD [
"nginx",
"-g",
"daemon off;"
]
这样:
可重复构建。
而不是:
text
靠某个 Container 手工改了一下午。
二十七、Docker 容器应该尽量 Immutable
比较理想:
text
Image
↓
Run Container
↓
Container 出问题
↓
删掉
↓
重新创建
而不是:
text
Container
↓
手工 SSH
↓
apt install
↓
改配置
↓
再 export 保存。
后者:
越来越像传统服务器。
Docker 的价值反而被削弱了。
二十八、再说一个非常危险的误区:Volume 不在 export 里
假设:
bash
docker run \
--name mysql \
-v mysql_data:/var/lib/mysql \
mysql:8.4
你的数据库数据:
text
/var/lib/mysql
实际:
text
在 Docker Volume。
你:
bash
docker export mysql \
> mysql.tar
就以为:
数据库备份好了。
非常危险。
二十九、Container Filesystem 和 Volume 是两套东西
结构:
text
Container
│
├── Writable Layer
│
└── /var/lib/mysql
↓
Volume
export:
text
主要处理 Container Filesystem。
Volume:
需要单独备份。
所以:
docker export 不是数据库备份工具。
三十、Docker Image save 也不会备份 Volume
同样:
bash
docker save mysql:8.4
只是在保存:
text
mysql Image。
它和你运行中的:
text
数据库数据
毫无关系。
这个概念一定要分清:
text
Image
≠
Container Data
三十一、离线迁移一个完整业务系统,需要哪些东西?
例如 Compose:
yaml
services:
mysql:
image:
mysql:8.4
volumes:
- mysql_data:/var/lib/mysql
api:
image:
mycompany/api:1.5
volumes:
mysql_data:
完整迁移至少包含:
text
1. Images
2. Compose
3. Environment Config
4. Volumes / Database Data
只:
text
docker save
是不够的。
三十二、镜像部分
bash
docker save \
-o images.tar \
mysql:8.4 \
mycompany/api:1.5
三十三、配置部分
保存:
text
docker-compose.yml
.env
nginx.conf
application config
当然:
text
.env
如果有密码:
要按敏感文件管理。
三十四、数据库应该做数据库级备份
MySQL:
bash
mysqldump ...
PostgreSQL:
bash
pg_dump ...
通常比:
text
直接打包整个 Volume
更加稳妥。
尤其:
text
数据库还在运行
时。
三十五、为什么不能随便 tar 正在写的数据库目录?
因为:
text
数据库文件
不是普通静态文件。
你复制过程中:
text
一半属于时间点 A
另一半属于时间点 B
可能得到:
text
不一致状态。
所以真正的数据迁移:
应该使用数据库自己的 Backup / Dump 机制。
三十六、这样整个离线发布包可以分成两个部分
text
Application Artifact
│
├── Docker Images
├── Compose
├── Config
└── Install Scripts
和:
text
Business Data
│
├── MySQL Dump
├── PostgreSQL Dump
└── Upload Files
不要:
混成一个"docker 备份"。
三十七、再看一个很实用的场景:从 Mac 搬镜像到 Linux Server
假设你 Mac:
text
M4
=
ARM64
服务器:
text
Intel
=
AMD64。
Mac 上:
bash
docker pull nginx:latest
默认会拉:
text
linux/arm64
然后:
bash
docker save nginx:latest \
-o nginx.tar
把它拿到:
text
linux/amd64 Server
会发生什么?
三十八、这时候平台非常重要
我们上一篇刚讲过:
text
Multi-Arch Image。
Tag:
text
nginx:latest
背后可能同时:
text
linux/amd64
linux/arm64。
但你本地实际 Image Store:
text
保存了哪个 Platform
会影响:
text
save 出去什么。
三十九、现在 docker save 已经支持 --platform
Docker 当前 docker image save 支持:
text
--platform
来指定需要保存的 Platform,例如:
text
linux/amd64
linux/arm64/v8
等。
例如你的目标:
text
AMD64 Linux Server。
就应该特别注意:
bash
docker pull \
--platform linux/amd64 \
nginx:latest
然后:
bash
docker save \
--platform linux/amd64 \
-o nginx-amd64.tar \
nginx:latest
四十、目标机器再 Load
bash
docker load \
-i nginx-amd64.tar
这样:
text
平台明确。
避免:
text
ARM Mac
打包 ARM Image
拿到 x86 Server
产生:
text
platform mismatch。
四十一、docker load 现在也支持 --platform
Docker 当前的 docker image load 同样支持:
text
--platform
用于从多平台 Archive 中只加载指定 Platform。
例如:
bash
docker load \
--platform linux/amd64 \
-i myapp.tar
这个能力在:
text
多平台离线发布包
场景非常有价值。
四十二、能不能做一个同时包含 amd64 和 arm64 的离线包?
现在是可以讨论的。
随着 Docker Engine / Docker Desktop 的 Image Store 逐渐使用 Containerd,现代 Docker 对 Multi-Platform Image 的本地存储能力已经增强;Docker 当前文档说明,Docker Desktop 和 Docker Engine 29+ 默认使用支持 Image Index / Manifest List 的 Containerd Image Store,而经典 Image Store 不支持完整的 Multi-Platform Image Index。
所以在现代环境中:
text
Multi-Platform Save / Load
的能力比以前完整得多。
但是:
仍然必须看你的 Docker Image Store 和 Engine 版本。
四十三、老 Docker 为什么经常处理不了完整 Multi-Arch?
因为传统:
text
Classic Image Store
本身:
text
不支持 Image Index / Manifest List。
Docker 当前 Containerd Image Store 文档仍明确指出,经典 Image Store 不能完整保存这些多平台结构。
所以:
text
老服务器
离线部署:
我会更保守:
明确按 Platform 打包。
四十四、比如准备两个包
text
release-amd64/
└── images-amd64.tar.zst
text
release-arm64/
└── images-arm64.tar.zst
客户选择:
text
Intel / AMD Server
→ amd64
text
ARM Server
→ arm64
这种交付方式:
最不容易出问题。
四十五、安装脚本还可以自动判断架构
例如:
bash
ARCH=$(uname -m)
case "$ARCH" in
x86_64)
IMAGE_FILE="images-amd64.tar.zst"
;;
aarch64|arm64)
IMAGE_FILE="images-arm64.tar.zst"
;;
*)
echo "Unsupported architecture: $ARCH"
exit 1
;;
esac
docker load \
-i "$IMAGE_FILE"
用户:
bash
bash install.sh
自动选择。
这就是:
真正成熟的离线安装包。
四十六、甚至 Docker 版本也可以检测
bash
docker version
如果:
text
Docker 不存在
提示:
text
Please install Docker first.
如果:
text
Docker Daemon 没启动
提示:
text
Docker daemon is unavailable.
不要等:
text
docker load
以后才抛一堆错误。
四十七、发布包还应该有 Checksum
比如:
text
images-amd64.tar.zst
几 GB。
通过:
text
U盘
网盘
内网
传输以后:
text
有没有损坏?
可以提供:
bash
sha256sum \
images-amd64.tar.zst
输出:
text
abc123...
发布:
text
SHA256SUMS
四十八、安装前验证
bash
sha256sum \
-c SHA256SUMS
如果:
text
OK
再:
text
docker load。
这比:
text
Load 到一半才发现 tar 损坏
友好很多。
四十九、这里的 SHA256 和 Docker Digest 是不是一回事?
不是完全一回事。
例如:
text
images.tar.zst SHA256
是:
你这个离线发布文件的 Hash。
而:
text
Docker Image Digest
对应:
text
Manifest / Image 内容身份。
两个 Hash:
text
解决不同层的问题。
五十、可以把它理解成
text
Docker Digest
↓
证明 Image Content Identity
text
Release File SHA256
↓
证明交付文件传输完整性
离线发布时:
两层都值得保留。
五十一、再进一步可以签名发布包
例如:
text
SHA256SUMS
再:
text
GPG Sign
或者:
text
其他 Artifact Signature。
于是客户不仅知道:
text
文件没坏。
还可以进一步验证:
text
是谁发布的。
这又和前面讲:
text
Cosign
Supply Chain
连起来了。
五十二、离线部署还有一个问题:Tag 是否覆盖
假设目标机原来:
text
myapp:latest
→ Old Image
你:
bash
docker load \
-i new-images.tar
里面:
text
myapp:latest
→ New Image
Tag:
text
可能更新指向。
但是原来:
text
已经运行中的 Container
不会因为:
text
load 了新 Image
就自动变成新版本。
五十三、为什么?
Container 创建时:
text
已经绑定具体 Image。
你只是:
text
更新 Tag。
现有 Container:
text
还是那个 Container。
所以 Load 完:
bash
docker compose up \
-d \
--pull never
让 Compose:
text
根据新 Image
重新创建需要更新的 Service。
五十四、不要只 docker compose restart
例如:
bash
docker load \
-i app-v2.tar
docker compose restart
restart:
text
只是重启旧 Container。
并不会自动:
text
根据新 Image 创建新 Container。
所以标准离线升级:
text
Load New Image
↓
Compose Up
↓
Recreate
更合理。
五十五、生产版本最好不要用 latest
离线发布更应该:
text
myapp:1.5.0
而不是:
text
myapp:latest。
因为发布包:
text
release-1.5.0
里面:
text
myapp:1.5.0
一一对应。
将来:
text
release-1.5.1
就是:
text
myapp:1.5.1。
版本关系:
非常清晰。
五十六、回滚也容易
当前:
text
1.5.1
有问题。
本地还有:
text
myapp:1.5.0。
Compose:
yaml
image:
myapp:1.5.0
然后:
bash
docker compose up \
-d \
--pull never
即可。
如果旧 Image:
text
已经被 prune。
可以重新:
bash
docker load \
-i release-1.5.0/images.tar
再回滚。
这就是:
离线 Artifact Archive。
五十七、甚至可以保留每个 Release
例如:
text
releases/
│
├── 1.4.0/
│ └── images.tar.zst
│
├── 1.5.0/
│ └── images.tar.zst
│
└── 1.5.1/
└── images.tar.zst
这相当于:
一个非常朴素的离线 Registry。
当然规模大以后:
text
还是应该上 Harbor / Registry。
但小规模交付:
text
特别实用。
五十八、什么时候不应该使用 save/load?
如果你有:
text
50 台
100 台
500 台
服务器。
每次发布:
text
给每台 SCP 5GB tar。
效率会越来越低。
这时候:
text
内网 Registry
明显更合理。
五十九、小规模
text
1~5 台机器
save/load:
非常简单。
六十、中等规模
text
几十台机器
可以:
text
Internal Registry
例如:
text
Distribution
Harbor
节点:
text
统一 pull。
六十一、完全离线企业环境
可以:
text
联网区
↓
下载 / 扫描 / 签名
↓
离线介质
↓
隔离区 Registry
↓
内网服务器
而不是:
text
每台服务器单独 load tar。
这种:
Offline Registry
更专业。
六十二、典型 Air-Gapped 架构
text
Internet
│
↓
Staging Machine
│
│ Pull / Scan
↓
Offline Package
│
│ USB / Controlled Transfer
↓
Air-Gapped Network
│
↓
Internal Harbor
│
├── Server A
├── Server B
└── Kubernetes
这里:
text
save/load
更像:
跨安全边界的运输手段。
而不是最终:
text
所有机器都靠 tar。
六十三、这个思路特别适合政企内网
例如:
text
生产区
不能访问互联网。
开发区:
text
可以联网。
流程:
text
开发区
docker pull
↓
安全扫描
↓
固定 Digest
↓
docker save
↓
SHA256
↓
审批
↓
物理介质
↓
生产区
↓
docker load / Internal Registry
每一步:
都可以审计。
六十四、这就不只是"传一个 tar"
而是:
Artifact Promotion。
镜像从:
text
Public Internet
进入:
text
Trusted Internal Environment。
中间经过:
text
扫描
验证
审批
记录
这就是:
企业软件供应链。
六十五、Rodert/DockerHub 项目可以增加离线包功能
例如用户输入:
text
nginx:1.28
redis:7
mysql:8.4
工具生成:
bash
docker pull \
--platform linux/amd64 \
nginx:1.28
docker pull \
--platform linux/amd64 \
redis:7
docker pull \
--platform linux/amd64 \
mysql:8.4
然后:
bash
docker save \
--platform linux/amd64 \
-o docker-images.tar \
nginx:1.28 \
redis:7 \
mysql:8.4
用户:
直接复制执行。
六十六、再生成压缩命令
bash
zstd -T0 \
docker-images.tar \
-o docker-images.tar.zst
生成:
bash
sha256sum \
docker-images.tar.zst \
> SHA256SUMS
目标:
bash
sha256sum -c SHA256SUMS
docker load \
-i docker-images.tar.zst
这样你的网站:
从镜像地址工具变成离线交付工具。
六十七、还能生成不同平台的安装包
用户选择:
text
目标平台:
○ Linux AMD64
○ Linux ARM64
生成:
text
--platform linux/amd64
或者:
text
--platform linux/arm64
这和上一篇:
text
Multi-Arch Checker
正好联动。
六十八、甚至可以自动分析 Compose
用户上传:
yaml
services:
nginx:
image:
nginx:1.28
redis:
image:
redis:7
api:
image:
mycompany/api:1.5
工具自动提取:
text
3 个 Image。
然后生成:
bash
docker pull ...
docker save ...
最终:
text
offline-bundle.tar.zst
这会非常实用。
六十九、还可以检查是否有 build
比如 Compose:
yaml
services:
api:
build:
.
却没有:
text
image:
工具提示:
text
⚠️ 当前 Service 只有 build,
没有明确 Image Tag。
制作离线发布包前,
建议先 Build 并 Tag。
例如:
bash
docker build \
-t mycompany/api:1.5 \
.
然后:
text
加入 save。
七十、离线部署脚本甚至可以做 Preflight
比如:
bash
#!/usr/bin/env bash
set -e
command -v docker \
>/dev/null \
|| {
echo "Docker not found"
exit 1
}
docker info \
>/dev/null \
|| {
echo "Docker daemon unavailable"
exit 1
}
sha256sum \
-c SHA256SUMS
docker load \
-i images.tar.zst
docker compose \
up \
-d \
--pull never \
--wait
整个流程:
text
环境检查
↓
包完整性检查
↓
镜像导入
↓
离线部署
↓
健康检查
已经非常完整。
七十一、再回到 export/import
什么时候我真的会用?
大概只有这种场景:
text
有一个历史 Container
不知道 Dockerfile
里面被人手工改了一堆东西
现在必须先把文件系统抢救出来。
可以:
bash
docker export \
legacy-container \
-o legacy-rootfs.tar
至少:
text
保存当前 rootfs。
然后:
bash
docker import
临时重新运行。
但长期:
一定应该重新整理 Dockerfile。
七十二、正常项目不要把 export/import 当备份流程
如果你已经:
text
有 Dockerfile
有 Image
有 Registry
有版本号
那:
text
save/load
更加符合:
Docker 镜像模型。
而不是:
text
把 Container 打平
再重建 Image。
七十三、四个命令最终这样记
text
docker save
对象:
text
Image
目的:
text
保存完整镜像结构。
text
docker load
对象:
text
Image Archive
目的:
text
恢复 Image + Tags。
text
docker export
对象:
text
Container
目的:
text
导出当前文件系统。
text
docker import
对象:
text
Filesystem Archive
目的:
text
创建一个新的 Filesystem Image。
七十四、如果只看输入输出就更清晰
text
Image
↓
save
↓
Tar
↓
load
↓
Image
这是:
可逆的镜像搬运链。
text
Container
↓
export
↓
RootFS Tar
↓
import
↓
New Image
这是:
文件系统快照链。
两条:
不要混。
七十五、最容易记错的一个点
有人问:
我要把运行中的 Container 搬到另一台机器,是不是应该 export?
要看目的。
如果这个 Container:
text
根本没有发生值得保存的手工修改。
真正需要的是原 Image:
text
直接 save Image。
如果它的业务数据在:
text
Volume
那:
text
单独备份 Volume / DB。
不要因为:
text
对象叫 Container
就下意识:
text
export。
七十六、完整 Docker 系统迁移应该分对象
text
Image
→ save/load
text
Compose / Config
→ 文件复制 / Git
text
Database
→ dump/restore
text
Volume Files
→ 单独备份
text
Secret
→ Secret 管理
text
Container
→ 尽量重新创建
这就是:
Docker 化系统真正正确的迁移方式。
总结
服务器访问不了 Docker Hub,并不意味着:
text
Docker 项目就没办法部署。
最简单的离线流程就是:
text
有网机器
docker pull
↓
docker save
↓
images.tar
↓
传输
↓
docker load
↓
docker compose up --pull never
对于少量机器:
这已经非常可靠。
而且一定要记住:
text
save / load
操作的是:
Docker Image。
它会尽量保留:
text
Layer
Image Metadata
Tag。
Docker 官方当前也明确说明,docker save 保存 Repository/Image Layer 和 Tag,而 docker load 会恢复 Image 与 Tag。
而:
text
export / import
操作的主要是:
Container Filesystem。
docker import 是把一个 tar 文件系统重新创建成 Filesystem Image,而不是恢复原 Image 的完整 Layer History。
所以如果只记住一句话:
搬镜像,用 docker save + docker load;抢救某个容器当前文件系统,才考虑 docker export + docker import。
如果目标还有:
text
AMD64
ARM64
架构差异,
还要进一步关注:
text
--platform。
现代 Docker 的 save/load 已经支持指定 Platform,而完整 Multi-Platform Archive 能否被本地 Image Store 正确处理,还需要考虑是否使用支持 Image Index 的 Containerd Image Store。
而当规模从:
text
1~5 台服务器
变成:
text
几十台
几百台
就不应该继续:
text
到处 SCP 5GB tar。
而应该:
text
离线包
↓
Internal Harbor / Registry
↓
内网所有机器
把:
文件搬运
升级成:
内部 Artifact Distribution。
对于:
text
https://github.com/Rodert/DockerHub
这个项目来说,我觉得非常适合继续增加:
text
Compose Image Extractor
+
AMD64 / ARM64 离线包生成器
+
docker save 命令生成
+
SHA256 校验
+
离线 install.sh
这样用户甚至可以:
上传一个 docker-compose.yml,直接生成完整的 Docker 离线部署方案。
项目地址:
text
https://github.com/Rodert/DockerHub