第一章:缘起——我们为什么需要“导入容器”?
想象一下这个场景:你,一位才华横溢的开发者,刚刚在办公室里用Docker容器完美复现了一个堪比“梵高作画”的复杂应用环境。你心满意足地打包下班,准备回家在自己的电脑上继续coding。
结果,家里的网络……挂了!docker pull?不存在的。
或者,你的公司出于安全考虑,生产服务器是一台与互联网物理隔离的“宇宙孤岛”。你怎么把开发好的应用环境完整地、无差错地部署上去?
再或者,你想把某个正在运行的容器,连同它所有被修改过的文件、安装的软件,完美地“克隆”一份给隔壁工位的同事debug用。
这时候,如果你只会docker pull和docker run,那就如同只会用手机点外卖,一旦断网,就只能饿肚子。而真正的“大厨”,懂得如何提前备菜,甚至准备好半成品料理包,随时随地都能做出一顿大餐。
这个“料理包”,就是Docker的镜像(.tar文件)或容器(.tar文件)的导出文件。而“导入”,就是拆开料理包,将其变成冰箱里的食材(镜像)或直接下锅做成菜(容器)的过程。
今天,我们就来彻底搞懂这门“数字料理”的打包与还原艺术。
第二章:核心概念——镜像与容器,到底是啥“亲戚关系”?
在动手之前,我们必须先理清一个最关键的概念:镜像(Image) 和 容器(Container) 的区别。这直接决定了你应该使用哪一对“打包-签收”命令。
- 镜像(Image):一个只读的模板。
它好比是一个.iso系统安装光盘,或者一个房子的设计蓝图。它是静态的、分层的、不可改变的。它定义了运行环境的一切:操作系统、运行时环境、应用代码、依赖库、环境变量等。docker pull就是从镜像仓库下载这个蓝图。 - 容器(Container):一个镜像的运行实例。
它好比是用那张.iso光盘安装好的正在运行的操作系统,或是根据蓝图建造好的、有人入住的房子。它是动态的、可读写的。容器会在镜像的只读层之上,创建一个可写的容器层(Container Layer),所有对运行中系统的修改都发生在这里。
理解了这一点,我们就可以引出今天的两对核心命令:
docker save&docker load:操作对象是【镜像】
-
docker save:将一个或多个镜像打包成一个完整的.tar文件(保存了整个蓝图)。docker load:将.tar文件加载回本地镜像库,恢复成一个或多个镜像。- 类比:把设计蓝图(镜像)打印成一大摞纸(
.tar文件),然后快递给别人。别人收到后,把图纸塞进自己的图纸库(镜像列表)里。
docker export&docker import:操作对象是【容器】
-
docker export:将一个正在运行或停止的容器的文件系统快照打包成一个.tar文件(只保存了当前房子的状态,忘了蓝图)。docker import:将这个.tar文件导入,创建一个新的镜像(根据当前房子状态,反推出一张新的蓝图)。- 类比:把现在这栋房子里的所有东西(包括家具、装修、甚至垃圾桶里的废纸)全部打包成一个箱子(
.tar文件),快递给别人。别人收到后,可以根据这个箱子里的东西,“逆向工程”出一张新的设计图(镜像),再用这张图盖新房。
核心区别总结表:
|
特性 |
/ |
/ |
|
操作对象 |
镜像(Image) |
容器(Container) |
|
输出内容 |
完整镜像(包含所有历史层、元数据) |
容器文件系统的快照(扁平化,丢失历史) |
|
是否保留历史 |
是 |
否 |
|
是否保留层信息 |
是 |
否 |
|
典型用途 |
备份、迁移、离线共享镜像 |
从一个容器的当前状态创建新镜像的基底 |
绝大多数情况下,docker save和docker load才是你真正需要的“镜像快递”组合,因为它保留了镜像的全部信息。而export/import通常用于一些特殊场景,比如“抢救”一个被改得面目全非又忘了最初镜像名的容器。
第三章:实战演练——完整示例:打包一个Web应用并离线部署
理论说得再多,不如动手一试。我们来完成一个完整的离线迁移示例。
目标: 在一台有网的机器(Machine-A)上,打包一个Nginx镜像。然后在一台无网的机器(Machine-B)上,加载这个镜像并运行容器。
Machine-A(有网环境):打包与导出
拉取镜像(可选,如果你本地没有)
docker pull nginx:alpine
alpine版本更小,更适合演示。
查看镜像
docker imagestext复制下载
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx alpine b46db85084b8 2 weeks ago 41.1MB
记下这个IMAGE ID或名字。
使用 docker save 将镜像导出为.tar文件
# 语法:docker save -o <输出的tar文件名> <镜像名:标签>
docker save -o nginx_alpine.tar nginx:alpine
# 或者使用IMAGE ID
# docker save -o nginx_alpine.tar b46db85084b8
(可选)验证tar包
file nginx_alpine.tar
输出应为:nginx_alpine.tar: POSIX tar archive
现在,nginx_alpine.tar文件就出现在了你的当前目录。你可以用U盘、内网SCP、甚至飞鸽传书(如果你有的话)把这个文件拷贝到Machine-B上。
Machine-B(离线环境):导入与运行
- 传输文件:将
nginx_alpine.tar文件放到Machine-B的某个目录,例如/home/user/。
使用 docker load 导入镜像
# 语法:docker load -i <输入的tar文件名>
docker load -i /home/user/nginx_alpine.tar
如果成功,你将看到类似输出:
Loaded image: nginx:alpine
验证镜像已存在
docker images
你应该在列表中看到刚刚导入的nginx:alpine镜像。
运行容器
docker run -d -p 8080:80 --name my_offline_nginx nginx:alpine
这条命令的含义是:后台运行(-d)一个名为my_offline_nginx的容器,将容器的80端口映射到本机的8080端口(-p 8080:80),使用的镜像是nginx:alpine。
验证容器运行
docker ps
看到my_offline_nginx容器正在运行。
打开浏览器,访问 http://<Machine-B的IP>:8080,你应该能看到经典的“Welcome to nginx!”页面!
恭喜你!你已经成功完成了一次完美的Docker离线部署!
第四章:举一反三——export/import的骚操作
虽然不常用,但了解下export/import也能在关键时刻救急。
场景: 你在一个基础镜像(如ubuntu:20.04)运行的容器里安装了一堆软件(如vim, git, nodejs),但忘了记录安装步骤,无法写成Dockerfile。现在你想创建一个新镜像保存当前状态。
# 1. 首先,运行一个基础容器并进入(Machine-A)
docker run -it --name messy_container ubuntu:20.04 /bin/bash
# (在容器内)模拟一顿胡乱操作
root@container-id:/# apt update && apt install -y vim git nodejs
root@container-id:/# exit
# 2. 现在这个容器已经停止了,并且被我们改得“面目全非”
# 使用 `docker export` 导出这个容器的快照
docker export messy_container > messy_container_snapshot.tar
# 3. 使用 `docker import` 将这个快照导入为一个新镜像,并命名为my_custom_image:v1
cat messy_container_snapshot.tar | docker import - my_custom_image:v1
# 4. 现在你可以用这个新镜像运行容器了,它包含了所有你安装的软件
docker run -it --name new_container my_custom_image:v1 /bin/bash
root@new-container-id:/# which vim
/usr/bin/vim
root@new-container-id:/# which node
/usr/bin/node
注意: 这样创建的镜像非常臃肿,且丢失了所有历史层信息,不利于维护和追踪。最佳实践永远应该是使用Dockerfile来构建镜像。export/import仅应作为“抢救”或特殊需求的工具。
第五章:结语——做自己环境的掌控者
通过本文的深度分析和完整示例,相信你已经对Docker的容器导入/导出机制了如指掌。docker save/load是你的主力“镜像快递车”,而docker export/import则是一把特殊的“手术刀”。
掌握这些技能,意味着你不再被网络枷锁所束缚,可以在任何“与世隔绝”的环境中原封不动地复制你的应用世界。这种掌控力,正是Docker和容器化技术带给开发者真正的自由与优雅。
现在,就去给你的容器们打包快递吧!

被折叠的 条评论
为什么被折叠?



