docker save与docker export的区别
很久没有写博客了,坟头草都长了老高了。写博客要靠惯性,一旦停下来时间长了,就很难再坚持下去。今天艰难地捡起来,以后每周至少写一篇。
这篇文章讲一讲docker save和docker export的区别。
缘起
docker save和docker export都能导出镜像包,咋看起来区别似乎不大。本文就针对这个问题,试图搞清楚docker save和docker export的功能是什么?适用于什么应用场景?
本文的测试的Docker版本如下,不保证所有版本的docker都能重现本文的结果。
1
|
>docker version
|
另外我是在Windows on bash里面操作docker,有些命令如ls
并不是windows命令,如果想要复现我的试验,请换成相应的windows命令。
docker save
docker的命令行接口设计得很优雅,很多命令的帮助直接在后面加--help
就可以查看。
docker save的帮助如下:
1
|
>docker save --help
|
从命令行帮助可以看出,docker save是用来将一个或多个image打包保存的工具。
例如我们想将镜像库中的postgres和mongo打包,那么可以执行:
1
|
docker save -o images.tar postgres:9.6 mongo:3.4
|
打包之后的images.tar
包含postgres:9.6
和mongo:3.4
这两个镜像。
虽然命令行参数要求指定image,实际上也可以对container进行打包,例如:
1
|
>docker ps
|
通过以上命令可以看到,b.tar
和c.tar
是完全一模一样的。这说明,docker save如果指定的是container,docker save将保存的是容器背后的image。
将打包后的镜像载入进来使用docker load,例如:
1
|
docker load -i images.tar
|
上述命令将会把postgres:9.6
和mongo:3.4
载入进来,如果本地镜像库已经存在这两个镜像,将会被覆盖。
docker save的应用场景是,如果你的应用是使用docker-compose.yml编排的多个镜像组合,但你要部署的客户服务器并不能连外网。这时,你可以使用docker save将用到的镜像打个包,然后拷贝到客户服务器上使用docker load载入。
docker export
照例查看下docker export的帮助:
1
|
>docker export --help
|
从帮助可以看出,docker export是用来将container的文件系统进行打包的。例如:
1
|
docker export -o postgres-export.tar postgres
|
docker export需要指定container,不能像docker save那样指定image或container都可以。
将打包的container载入进来使用docker import,例如:
1
|
docker import postgres-export.tar postgres:latest
|
从上面的命令可以看出,docker import将container导入后会成为一个image,而不是恢复为一个container。
另外一点是,docker import可以指定IMAGE[:TAG],说明我们可以为镜像指定新名称。如果本地镜像库中已经存在同名的镜像,则原有镜像的名称将会被剥夺,赋给新的镜像。原有镜像将成为孤魂野鬼,只能通过IMAGE ID进行操作。
docker export的应用场景主要用来制作基础镜像,比如你从一个ubuntu镜像启动一个容器,然后安装一些软件和进行一些设置后,使用docker export保存为一个基础镜像。然后,把这个镜像分发给其他人使用,比如作为基础的开发环境。
docker save和docker export的区别
总结一下docker save和docker export的区别:
- docker save保存的是镜像(image),docker export保存的是容器(container);
- docker load用来载入镜像包,docker import用来载入容器包,但两者都会恢复为镜像;
- docker load不能对载入的镜像重命名,而docker import可以为镜像指定新名称。
脑洞
前面所讲的内容都是些基础知识,相信各位读者只要仔细看下官方文档就能知晓。这一节我来讲讲文档上没有的东西。
docker load和docker import都可以将tar包导入为镜像,我不禁脑洞一下,docker load能不能导入docker export的容器包,docker import能不能导入docker save的镜像包呢?
以下开始试验,准备以下两个文件:
1
|
>ls -al
|
其中postgres-export.tar
是通过docker export导出的容器包,postgres-save.tar
是通过docker save保存的镜像包,两者都是基于postgres:9.6
镜像。从文件大小可以直观的发现,postgres-export.tar
显然要比postgres-save.tar
小100多M。
现在试试docker load容器包postgres-export.tar
:
1
|
>docker load -i postgres-export.tar
|
显然,docker load不能载入容器包。
那么,反过来,docker import载入镜像包可不可以呢?
1
|
>docker import postgres-save.tar postgres
|
WTF,竟然成功了!!!
莫慌,再试试启动一个postgres容器:
1
|
>docker run postgres
|
虽然能够成功地导入为一个镜像,然而这个镜像并不能使用。
要搞清楚到底是怎么回事,我们先看看镜像包和容器包由什么区别:
从上面可以看出右边的postgres-export.tar
的内容是一个linux系统的文件目录,猜测就是一个linux镜像。而postgres-save.tar
里面到底是什么内容呢?点开一个文件夹看看:
其实就是一个分层的文件系统。Docker镜像实际上就是由这样的一层层文件进行叠加起来的,上层的文件会覆盖下层的同名文件。如果将postgres-save.tar
中的各层文件合并到一起,基本就是postgres-export.tar
的内容。由于postgres-save.tar
里面的各层文件会存在很多重复的文件,这也解释了为什么postgres-save.tar
会比postgres-export.tar
大100多M。
docker load必须要载入的是一个分层文件系统,而postgres-export.tar
并不具有这样的结构,因此无法载入。
而docker import仅仅是将tar包里面的文件复制进来,所以不管tar包里面的文件结构是怎样的,都可以载入进来,所以能够载入postgres-save.tar
。但postgres-save.tar
并不是一个有效的操作系统镜像,因此当我试图以改镜像启动容器时,容器并不能启动。
我们再来看看docker import的帮助:
1
|
Usage: docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]
|
似乎和docker commit很像:
1
|
Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
|
发现docker import和docker commit都有--change
和--message
选项。我们可以将docker import理解为将外部文件复制进来形成只有一层文件系统的镜像,而docker commit则是将当前的改动提交为一层文件系统,然后叠加到原有镜像之上。
关于docker save和docker export的区别讲得差不多,拜了个拜。
参考文献
Docker的save和export命令的区别
我最近在玩Docker,一种应用程序容器和Linux的虚拟技术。它太酷了,创建Docker镜像和容器只需要几分钟。所有的工作都是开箱即用的。
在结束我一天的工作之前,我希望能保存下我的工作。但我在Docker的save和export命令之间,我凌乱了。我不知道它们之间有什么区别。所以,我上StackOverflow问了一个问题,接着得到mbarthelemy很棒的回复。以下是我发掘到的内容:
Docker是如何工作的(简单说明)
Docker是基于镜像的。镜像类似于已经包含了文件、配置和安装好的程序的虚拟机镜像。同样的,你可以像启动虚拟机一样启动多个镜像实例。运行中的镜像称为容器。你可以修改容器(比如删除一个文件),但这些修改不会影响到镜像。不过,你使用docker commit <container-id> <image-name>
命令可以把一个正在运行的容器变成一个新的镜像。
举个例子:
# 像Docker官方的hello world例子一样,拉取一个叫busybox的镜像
sudo docker pull busybox
# 查看本地已经有哪些镜像
# 我们可以看到busybox
sudo docker images
# 现在让我们来修改下busybox镜像的容器
# 这次,我们创建一个文件夹
sudo docker run busybox mkdir /home/test
# 让我们再看看我们有哪些镜像了。
# 注意每条命令执行后容器都会停止
# 可以看到有一个busybox容器
sudo docker ps -a
# 现在,可以提交修改了。
# 提交后会看到一个新的镜像busybox-1
# <CONTAINER ID> 是刚刚修改容器后得到的ID
sudo docker commit <CONTAINER ID> busybox-1
# 再看看我们有哪些镜像。
# 我们现在同时有busybox和busybox-1镜像了。
sudo docker images
# 我们执行以下命令,看看这两个镜像有什么不同
sudo docker run busybox [ -d /home/test ] && echo 'Directory found' || echo 'Directory not found'
sudo docker run busybox-1 [ -d /home/test ] && echo 'Directory found' || echo 'Directory not found'
现在,我们有两个不同的镜像了(busybox和busybox-1),还有一个通过修改busybox容器得来的容器(多了一个/home/test文件夹)。下面来看看,是如何持久化这些修改的。
导出(Export)
Export命令用于持久化容器(不是镜像)。所以,我们就需要通过以下方法得到容器ID:
sudo docker ps -a
接着执行导出:
sudo docker export <CONTAINER ID> > /home/export.tar
最后的结果是一个2.7MB大小的Tar文件(比使用save命令稍微小些)。
保存(Save)
Save命令用于持久化镜像(不是容器)。所以,我们就需要通过以下方法得到镜像名称:
sudo docker images
接着执行保存:
sudo docker save busybox-1 > /home/save.tar
最后的结果是一个2.8MB大小的Tar文件(比使用export命令稍微大些)。
它们之间的不同
现在我们创建了两个Tar文件,让我们来看看它们是什么。首先做一下小清理——把所有的容器和镜像都删除:
# 查看所有的容器
sudo docker ps -a
# 删除它们
sudo docker rm <CONTAINER ID>
# 查看所有的镜像
sudo docker images
# 删除它们
sudo docker rmi busybox-1
sudo docker rmi busybox
译注:可以使用 docker rm $(docker ps -q -a) 一次性删除所有的容器,docker rmi $(docker images -q) 一次性删除所有的镜像。
现在开始导入刚刚导出的容器:
# 导入export.tar文件
cat /home/export.tar | sudo docker import - busybox-1-export:latest
# 查看镜像
sudo docker images
# 检查是否导入成功,就是启动一个新容器,检查里面是否存在/home/test目录(是存在的)
sudo docker run busybox-1-export [ -d /home/test ] && echo 'Directory found' || echo 'Directory not found'
使用类似的步骤导入镜像:
# 导入save.tar文件
docker load < /home/save.tar
# 查看镜像
sudo docker images
# 检查是否导入成功,就是启动一个新容器,检查里面是否存在/home/test目录(是存在的)
sudo docker run busybox-1 [ -d /home/test ] && echo 'Directory found' || echo 'Directory not found'
**那,它们之间到底存在什么不同呢?**我们发现导出后的版本会比原来的版本稍微小一些。那是因为导出后,会丢失历史和元数据。执行下面的命令就知道了:
# 显示镜像的所有层(layer)
sudo docker images --tree
执行命令,显示下面的内容。正你看到的,导出后再导入(exported-imported)的镜像会丢失所有的历史,而保存后再加载(saveed-loaded)的镜像没有丢失历史和层(layer)。这意味着使用导出后再导入的方式,你将无法回滚到之前的层(layer),同时,使用保存后再加载的方式持久化整个镜像,就可以做到层回滚(可以执行docker tag <LAYER ID> <IMAGE NAME>
来回滚之前的层)。
vagrant@ubuntu-13:~$ sudo docker images --tree
├─f502877df6a1 Virtual Size: 2.489 MB Tags: busybox-1-export:latest
└─511136ea3c5a Virtual Size: 0 B
└─bf747efa0e2f Virtual Size: 0 B
└─48e5f45168b9 Virtual Size: 2.489 MB
└─769b9341d937 Virtual Size: 2.489 MB
└─227516d93162 Virtual Size: 2.489 MB Tags: busybox-1:latest
祝好, Thomas
1. 语法知识
- Create a new image from a container's changes
- Usage
- docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAGI]]
Name, shorthand | Defaul | Description |
---|---|---|
-author, -a | Author (e.g. "Jjohn Hannibal Smith hannibal@a-teamcom") | |
--change, -o | Apply Dockerfile instruction to the created image | |
-message, -m | Commit message | |
-pause, -p | true | Pause container during commit |
2. 基于容器制作镜像
# 运行b1容器
docker run --name b1 -it busybox
# 基于容器制作镜像,此时没有指明docker镜像的tag均为none
docker commit -p b1
# 给刚创建的镜像指定tag
docker tag f93e983a803e john/httpd:v0.1-1
# 给有tag的镜像再次添加一个tag
docker tag john/httpd:v0.1-1 john/httpd:latest
# docker 删除标签
docker image rm john/httpd:latest
# 运行自定义的镜像
docker run --name t1 -it john/httpd:v0.1-1
# 再次制作一个更完善的镜像(-a 指明作者信息,-c 配置httpd阿帕奇的配置信息,-p 表示暂停状态,b1是制作镜像的容器名称,john/httpd:v0.2是指明镜像及标签)
docker commit -a "john <508110504@qq.com>" -c 'CMD ["/bin/httpd","-f","-h","/data/html"]' -p b1 john/httpd:v0.2
# 运行新自定义的容器
docker run --name t2 john/httpd:v0.2
查看容器状态
通过docker inspect t2
获取容器ip地址,访问容器服务
3. 将制作的自定义镜像发布到dockerhub
# 将制作的自定义镜像发布到dockerhub上面
# 1. 先注册dockerhub账号,创建一个repository
https://cloud.docker.com/repository/create
# 需要确保自己制作的镜像名称和dockerhub中一致
docker commit -a "john <qnyt1993@gmail.com>" -c 'CMD ["/bin/httpd","-f","-h","/data/html"]' -p b1 john1993/httpd:v0.2
# 将自定义的镜像推送到dockerhub上去(再次之前先进行登录)
docker login
docker push john1993/httpd
4. 将镜像推到阿里云容器镜像服务
# 将镜像推到阿里云容器镜像服务
# 地址 https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors
# 配置镜像加速器(官方文档)
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://ostfibsg.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
# 1.注册账号,配置镜像加速,创建镜像仓库
# 阿里云的镜像仓库创建完毕,开始将本地的自定义镜像推送到阿里云上
# 1. 先重新打标签
docker tag john1993/httpd:v0.2 registry.cn-beijing.aliyuncs.com/john1993/httpd:v0.2
# 2. 先退出dockerhub,在登录阿里云docker
docker logout
docker login --username=qnytytqn registry.cn-beijing.aliyuncs.com
# 3. 开始推送
docker push registry.cn-beijing.aliyuncs.com/john1993/httpd:[镜像版本号]
docker push registry.cn-beijing.aliyuncs.com/john1993/httpd:v0.2
5. 镜像的导入和导出
# 1. docker 导出(myimages.gz是导出的压缩包,可指定多个镜像)
docker save -o myimages.gz john1993/httpd:v0.2 john/httpd:v0.1-1
# 2. docker的导入(在需要导入镜像的docker服务器中执行)
docker load -i myimages.gz
我们知道,要创建一个Docker容器,要先有Docker镜像,Docker镜像怎么创建的呢,一般有两种创建方式:
1. 根据基础镜像,生成新镜像
2. 使用Dockerfile生成镜像
在制作镜像之前,先看下怎么查找镜像
列出所有在主机上的镜像
docker images列出所有镜像
我主机上有两个镜像centos和ubuntu,我们在镜像列表中看到三个至关重要的东西。
- 镜像源,例如 ubuntu
- 镜像的标签(tags),例如 latest
- 镜像ID,每个镜像都有镜像ID
从DockerHub中查找镜像
DockerHub是远程镜像仓库,保存种各样的镜像,可以从DockerHub官网上搜索你想要的镜像
也可以在本机使用docker search 查找 DockerHub上的镜像
知道镜像的查找方式后,下来我们开始制作镜像
1. 根据基础镜像,生成新镜像
这种方式首先需要有一个基础镜像,可以通过docker pull命令,从DockerHub中先下载一个镜像,作为我们的基础镜像
下载时,如果没有指定tag,默认latest,使用docker images查看发现centos镜像已经下载下来了
下面开始基于镜像运行一个容器,修改容器内容,重新Commit生成新镜像
这里容器ID(9dc737402a4d)已经生成,并已进入到容器中,在运行的容器内创建一个text.txt的文件,然后exit退出容器
使用docker commit生成新镜像
这里我们指定了两个标识(flags) -m 和 -a 。-m 标识我们指定提交的信息,就像你提交一个版本控制。-a 标识允许对我们的更新来指定一个作者,最后用docker images查看镜像发现新镜像已经生成了
验证:
运行新镜像生成容器,到容器中查看是否存在刚才创建的text.txt文件
经验证后发现,在新生成的容器中,存在有刚创建的text.txt文件,说明新镜像已经生效
示例1:做一个yum安装的nginx镜像
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
- 运行并进入一个centos容器:docker run -it --rm 49f7960eb7e4 bash - yum install wget -y - wget -O /etc/yum.repos.d/CentOS-Base.repo http: //mirrors.aliyun.com/repo/Centos-7.repo # 替换为阿里的yum源 - yum install https: //mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm # 安装epel源 - yum install vim pcre pcre-devel zlib zlib-devel openssl openssl-devel iproute net-tools iotop -y # 安装基础命令 - yum install nginx -y # yum安装nginx - 更改/etc/nginx/nginx.conf,关闭nginx的后台运行,即增加 daemon off; - 自定义主页 [root@cee964f73ce4 ~]# cat /usr/share/nginx/html/index.html <h1> Yum Nginx 2018-07-09 21:00:00 </h1> [root@cee964f73ce4 ~]# - 把当前容器提交为一个镜像,指定名称和版本号 root@Docker-10.2.217.217[20:49:02]$ docker commit -m "nginx-v1" cee964f73ce4 nginx-yum:v1 sha256:84d367dcb28f03a09354051ee6cfe06107068f6890c0e20ded2bf60a87acb9b0 root@Docker-10.2.217.217[20:50:22]$ root@Docker-10.2.217.217[20:50:34]$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx-yum v1 84d367dcb28f 15 seconds ago 531MB nginx latest 3c5a05123222 2 days ago 109MB ubuntu latest 113a43faa138 4 weeks ago 81.2MB centos latest 49f7960eb7e4 4 weeks ago 200MB centos 7.2.1511 0a2bad7da9b5 8 months ago 195MB root@Docker-10.2.217.217[20:50:37]$ - 使用做好的nginx镜像启动nginx root@Docker-10.2.217.217[20:59:12]$ docker run -it --rm -d -p 8080:80 nginx-yum:v1 nginx c1997e0460ebc155333efd3a616d467f2cc8475200234c773b37daf0c1cf7a68 root@Docker-10.2.217.217[20:59:28]$ root@Docker-10.2.217.217[20:59:29]$ root@Docker-10.2.217.217[20:59:29]$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c1997e0460eb nginx-yum:v1 "nginx" 3 seconds ago Up 2 seconds 0.0.0.0:8080->80/tcp naughty_bose cee964f73ce4 49f7960eb7e4 "bash" 19 minutes ago Up 19 minutes tender_darwin root@Docker-10.2.217.217[20:59:31]$ root@Docker-10.2.217.217[20:59:37]$ ss -tnl State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 *:22 *:* LISTEN 0 100 127.0.0.1:25 *:* LISTEN 0 128 :::8080 :::* LISTEN 0 128 :::22 :::* LISTEN 0 100 ::1:25 :::* root@Docker-10.2.217.217[20:59:39]$ 注意:如果使用相同的版本号提交镜像会把之前提交的覆盖掉! |
Docker镜像的导入导出
Docker镜像的导入导出
本文介绍Docker镜像的导入导出,用于迁移、备份、升级等场景,准备环境如下:
- CentOS 7.0
- Docker 1.18
导入导出命令介绍
涉及的命令有export、import、save、load
save
- 命令
docker save [options] images [images...]
- 示例
docker save -o nginx.tar nginx:latest
或docker save > nginx.tar nginx:latest
其中-o和>表示输出到文件,nginx.tar
为目标文件,nginx:latest
是源镜像名(name:tag)
load
- 命令
docker load [options]
- 示例
docker load -i nginx.tar
或docker load < nginx.tar
其中-i和<表示从文件输入。会成功导入镜像及相关元数据,包括tag信息
export
- 命令
docker export [options] container
- 示例
docker export -o nginx-test.tar nginx-test
其中-o表示输出到文件,nginx-test.tar
为目标文件,nginx-test
是源容器名(name)
import
- 命令
docker import [options] file|URL|- [REPOSITORY[:TAG]]
- 示例
docker import nginx-test.tar nginx:imp
或cat nginx-test.tar | docker import - nginx:imp
区别
- export命令导出的tar文件略小于save命令导出的
- export命令是从容器(container)中导出tar文件,而save命令则是从镜像(images)中导出
- 基于第二点,export导出的文件再import回去时,无法保留镜像所有历史(即每一层layer信息,不熟悉的可以去看Dockerfile),不能进行回滚操作;而save是依据镜像来的,所以导入时可以完整保留下每一层layer信息。如下图所示,
nginx:latest
是save导出load导入的,nginx:imp
是export导出import导入的。
建议
可以依据具体使用场景来选择命令
- 若是只想备份images,使用save、load即可
- 若是在启动容器后,容器内容有变化,需要备份,则使用export、import