Docker 镜像是由多个文件系统(只读层)叠加而成,每个层仅包含了前一层的差异部分。当我们启动一个容器的时候,Docker 会加载镜像层并在其上添加一个可写层。容器上所做的任何更改,譬如新建文件、更改文件、删除文件,都将记录与可写层上。当我们使docker save形式尝试导出镜像时会导出该镜像的所有文件层,当然这个行为是必要的,因为你不知道这个镜像的被导入环境是否已包含基础镜像的文件层。但是如果我们有一批镜像且都依赖某一个或两个基础镜像构建,且不具备批量 save的调价(必须一个一个分开了打包),这种情况下如果一个一个save的话对硬盘资源是极大的浪费,那么有没有办法去掉哪些重复的镜像层呢,答案是OK的。(也许有人会吐槽为啥不用docekrfile呢,是的一般情况下是ok的,但是用dockerfile build出来的镜像id时不一样的)

看下docker save 导出了写啥

以如下镜像为例子

FROM centos:7
COPY main /home/main
RUN chmod +x /home/main CMD /home/mai

构建

docker build -t ip-server:1.0.0 .

导出

[root@localhost demo]# docker save -o ip-server.tar ip-server:1.0.0
[root@localhost demo]# du -sh ip-server.tar
212M ip-server.tar
[root@localhost demo]# docker save -o centos7.tar centos:7
[root@localhost demo]# du -sh centos7.tar
202M centos7.tar

可以看到基于centos7构建的ip-server镜像只是大了一丁,也就是我们COPY进来的一个可执行文件大小,那么在已知被导入环境存在centos7镜像的文件层是该如何减小ip-server.tar的体积呢?

拆解导出的镜像

[root@localhost demo]# mkdir ip-server && tar xf ip-server.tar -C ip-server
[root@localhost demo]# tree ip-server
ip-server
├── 3efdc87cec68e28bccf6c0d96894c903e12157ed0797651a2eaa565108de5bd8
│   ├── json
│   ├── layer.tar -> ../fad57ccc4dd192e49d3979f477525a4b4c8fb8532ae31c2c74b5403474a26e4d/layer.tar
│   └── VERSION
├── d8f46057879e2e8614caa5511934d403d7ebea0af9b196bf29b68161fda76766
│   ├── json
│   ├── layer.tar
│   └── VERSION
├── f898e9c3d94a1617bac63c962155327671957f2bcbd35e6411153b7730d6558e.json
├── fad57ccc4dd192e49d3979f477525a4b4c8fb8532ae31c2c74b5403474a26e4d
│   ├── json
│   ├── layer.tar
│   └── VERSION
├── manifest.json
└── repositories 3 directories, 12 files
# manifest.json
[
{
"Config": "f898e9c3d94a1617bac63c962155327671957f2bcbd35e6411153b7730d6558e.json",
"RepoTags": [
"ip-server:1.0.0"
],
"Layers": [
"d8f46057879e2e8614caa5511934d403d7ebea0af9b196bf29b68161fda76766/layer.tar",
"fad57ccc4dd192e49d3979f477525a4b4c8fb8532ae31c2c74b5403474a26e4d/layer.tar",
"3efdc87cec68e28bccf6c0d96894c903e12157ed0797651a2eaa565108de5bd8/layer.tar"
]
}
]
# f898e9c3d94a1617bac63c962155327671957f2bcbd35e6411153b7730d6558e.json
{
"architecture": "amd64",
"config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/sh",
"-c",
"/home/main"
],
"Image": "sha256:04da435eb95c963effbf9aba060e6f2ddd962b4861ce1bf7bdc5f3cbffceb4b5",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {
"org.label-schema.build-date": "20201113",
"org.label-schema.license": "GPLv2",
"org.label-schema.name": "CentOS Base Image",
"org.label-schema.schema-version": "1.0",
"org.label-schema.vendor": "CentOS",
"org.opencontainers.image.created": "2020-11-13 00:00:00+00:00",
"org.opencontainers.image.licenses": "GPL-2.0-only",
"org.opencontainers.image.title": "CentOS Base Image",
"org.opencontainers.image.vendor": "CentOS"
}
},
"container": "5d3e8d3176babbe767294d6834bf1dc6e64ce9838bc08993751724a92231d6ac",
"container_config": {
"Hostname": "5d3e8d3176ba",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"/bin/sh\" \"-c\" \"/home/main\"]"
],
"Image": "sha256:04da435eb95c963effbf9aba060e6f2ddd962b4861ce1bf7bdc5f3cbffceb4b5",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {
"org.label-schema.build-date": "20201113",
"org.label-schema.license": "GPLv2",
"org.label-schema.name": "CentOS Base Image",
"org.label-schema.schema-version": "1.0",
"org.label-schema.vendor": "CentOS",
"org.opencontainers.image.created": "2020-11-13 00:00:00+00:00",
"org.opencontainers.image.licenses": "GPL-2.0-only",
"org.opencontainers.image.title": "CentOS Base Image",
"org.opencontainers.image.vendor": "CentOS"
}
},
"created": "2022-10-26T15:53:47.898864929Z",
"docker_version": "20.10.17",
"history": [
{
"created": "2021-09-15T18:20:23.417639551Z",
"created_by": "/bin/sh -c #(nop) ADD file:b3ebbe8bd304723d43b7b44a6d990cd657b63d93d6a2a9293983a30bfc1dfa53 in / "
},
{
"created": "2021-09-15T18:20:23.819893035Z",
"created_by": "/bin/sh -c #(nop) LABEL org.label-schema.schema-version=1.0 org.label-schema.name=CentOS Base Image org.label-schema.vendor=CentOS org.label-schema.license=GPLv2 org.label-schema.build-date=20201113 org.opencontainers.image.title=CentOS Base Image org.opencontainers.image.vendor=CentOS org.opencontainers.image.licenses=GPL-2.0-only org.opencontainers.image.created=2020-11-13 00:00:00+00:00",
"empty_layer": true
},
{
"created": "2021-09-15T18:20:23.99863383Z",
"created_by": "/bin/sh -c #(nop) CMD [\"/bin/bash\"]",
"empty_layer": true
},
{
"created": "2022-10-26T15:53:46.693528022Z",
"created_by": "/bin/sh -c #(nop) COPY file:d0c12b416e2bad24636a0f240cc09c4a6b6a0def701b5aaeeca4963507e468c4 in /home/main "
},
{
"created": "2022-10-26T15:53:47.752968676Z",
"created_by": "/bin/sh -c chmod +x /home/main"
},
{
"created": "2022-10-26T15:53:47.898864929Z",
"created_by": "/bin/sh -c #(nop) CMD [\"/bin/sh\" \"-c\" \"/home/main\"]",
"empty_layer": true
}
],
"os": "linux",
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:174f5685490326fc0a1c0f5570b8663732189b327007e47ff13d2ca59673db02",
"sha256:185d24f4ae72bebf9b31c3d26486d52163e38e6c09167507a6a4f28d491aeb28",
"sha256:185d24f4ae72bebf9b31c3d26486d52163e38e6c09167507a6a4f28d491aeb28"
]
}
}

参考docker-ce的相关导出模块的源代码(源代码解读就不做了,不算复杂)

docekr-ce/components/engin/save
docekr-ce/components/engin/load

可以找出打包出的tar包中放layer文件夹与diff_ids标记的文件层的对应关系。

1. 解压压缩包
2. 读取 centos7 和 ip-server 的inspect
3. 找出 centos7 和 ip-server 的diff_ids同的层数
4. 按照相同的层数依次找到manifest.json中记录的layers文件目录,并把layer.tar的压缩包置空
5. 重新打包

测试改处理的导出包在centos:7镜像已存在的环境中可以被正常导入操作。

代码实现

参考 github.com/zn-chen/dockerdiff

懒得琢磨也可以直接使用,在安装好go环境下git clone 下来 make && make install 后即可食用。

精简docker的导出镜像的更多相关文章

  1. docker 导入导出镜像

    docker容器导入导出有两种方法: 一种是使用save和load命令 使用例子如下: docker save ubuntu:load>/root/ubuntu.tar docker load& ...

  2. docker导入导出镜像

    docker容器导入导出有两种方法: 一种是使用save和load命令 使用例子如下: docker save ubuntu:load>/root/ubuntu.tar docker load& ...

  3. 精简Docker镜像的五种通用方法

    http://dockone.io/article/8163 精简Docker镜像的好处很多,不仅可以节省存储空间和带宽,还能减少安全隐患.优化镜像大小的手段多种多样,因服务所使用的基础开发语言不同而 ...

  4. docker 保存 加载(导入 导出镜像

    tensorflow 的docker镜像很大,pull一次由于墙经常失败.其实docker 可以将镜像导出再导入. 保存加载(tensorflow)镜像 1) 查看镜像 docker images 如 ...

  5. docker怎么导出导入镜像

    https://blog.csdn.net/dest_dest/article/details/80612231 把某个docker镜像保存到本地文件,命令如下docker save -o 镜像名.t ...

  6. 精简Docker镜像的几个方法

    一.使用更精简的镜像 常用的Linux系统镜像一般有 Debian.Ubuntu.CentOS和Alpine,其中Alpine是面向安全的轻量级Linux发行版本.Docker的Alpine镜像仅有不 ...

  7. docker镜像 - 下载、创建镜像和导入导出镜像

    实验环境 CentOS 7.5 安装并启动docker yum install -y docker systemctl start docker 镜像 安装镜像 docker pull [OPTION ...

  8. Docker 学习之镜像导入导出及推送阿里云服务器(三)

    在前面两节里主要就是记录一些docker的基本的操作,包括搜索镜像,拉取镜像,根据镜像创建容器等等,在这一节主要就是记录Docker对于镜像文件的导入导出,及推送到阿里云再从阿里云获取镜像. 一.镜像 ...

  9. docker save 保存导出镜像

    Docker保存镜像 tag 镜像 # 镜像打 tag 标签 # docker tag 镜像id/名 新名字 docker tag fce91102e17d tomcat01 commit 镜像 注意 ...

随机推荐

  1. Sentinel控制台1.8.3修改源码,修改配置后推送到Nacos

    目录 1. 接着上一篇 2. 思路 3. 下载Sentinel源码 4. 看Gateway里面读取的配置信息 5. 修改Sentinel控制台源码 6. 熔断规则测试 7. 限流规则测试 8. 打包使 ...

  2. ubuntu 搭建网络文件系统

    一.安装 miniserver sudo curl -L https://github.com/svenstaro/miniserve/releases/download/v0.4.1/miniser ...

  3. Zookeeper 分布式事务锁的使用

    使用Netflix的包 curator-recipes pom文件引入相关依赖 <dependency> <groupId>org.apache.zookeeper</g ...

  4. K8S之YAML配置文件

    通过 YAML 配置文件 部署 Deployment 使用命令(类似 docker-compose) // 部署 kubectl create -f xxx.yml // 删除 kubectl del ...

  5. 使用【阿里云】服务器、【Xshell】搭建自己的【网站】—— { }

    重置实例密码 打开Xshell连接主机 Apache 服务 安装 yum install httpd* -y 操作 启动 systemctl start httpd.service 查看状态 syst ...

  6. typora收费了,最后一个免费版提供下载

    typora收费了,在这里,博主提供最后一个免费版下载,地址如下,顺便把typora导入和导出word时需要的工具也一同提供.最看不惯免费用着别人的软件,还搞引流的垃圾网站和公众号.地址如下 http ...

  7. 清除已安装的rook-ceph集群

    官方文档地址:https://rook.io/docs/rook/v1.8/ceph-teardown.html 如果要拆除群集并启动新群集,请注意需要清理的以下资源: rook-ceph names ...

  8. kubernetes1.20 部署 traefik2.3

    一.概述 Traefik 是一个开源的可以使服务发布变得轻松有趣的边缘路由器.它负责接收你系统的请求,然后使用合适的组件来对这些请求进行处理. 除了众多的功能之外,Traefik 的与众不同之处还在于 ...

  9. 2>&1到底是什么意思?

    java -jar snapshot.jar > snapshot.log 2>&1 & 写Java的朋友一定对上面的命令很熟悉,相信大部分人都知道>表示的是重定向, ...

  10. Libgdx游戏学习(1)——环境配置及demo运行

    原文: Libgdx游戏学习(1)--环境配置及demo运行 - Stars-One的杂货小窝 Libgdx游戏是基于Java的一款游戏引擎,可以发布Android,桌面端,Html,IOS等游戏,出 ...