查看docker 镜像大小(详解六种减小Docker镜像大小的方法)
查看docker 镜像大小
详解六种减小Docker镜像大小的方法我从2017年做Vulhub开始,一直在和一个麻烦的问题做斗争:在编写Dockerfile的时候, 如何减小 docker build
生成的镜像大小 ?这篇文章就给大家总结一下我自己使用过的六种减小镜像大小的方法。
1. 使用Alpine Linux
Alpine Linux是一个基于BusyBox和Musl Libc的Linux发行版,其最大的优势就是小。一个纯的基础Alpine Docker镜像在压缩后仅有2.67MB。
不少Docker官方镜像都有Alpine版本,比如PHP:
比较之下就可以发现,alpine版本镜像大小是普通版本的1/5左右。
但是在Docker Hub中,大部分镜像是没有Alpine版本的,比如Mysql和PHP-Apache,如果我们需要基于这些环境开发,就不得不自己编写Alpine版本,或者找一些第三方镜像。
另外,Alpine的另一个缺点是,其使用了Musl Libc作为传统的glibc的替代,编译软件的时候可能会遇到一些不可预知的问题,这一点会导致我们耗费不少不必要的时间。
2. 只安装最少的依赖
apt-get、yum、apk等软件包管理器是我们编译镜像时必然需要用到的工具,纯净的Docker基础镜像通常会缺少wget、curl、git、gcc等工具,需要我们手工来安装。
我们以apt为例,apt-get在安装软件的时候,可以指定一个选项: --no-install-recommends
,指定这个参数后,有一些非必须的依赖将不会被一起安装。比如,我们安装wget时,如果增加这个选项,待安装的包将从6个减少为3个:
这在一定程度上缩小了镜像的大小,但这样做带来的副作用就是,可能导致目标软件缺少一些功能。
比如,此时的wget将无法验证服务器证书的真伪,导致命令出错:
所以,我们一般的做法是,使用apt时尽量增加 --no-install-recommends
,等后面出现一些错误再及时纠正。像wget这种已知的问题,可以提前预判并进行处理:
apt-get install --no-install-recommends wget ca-certificates
3. 为apt擦屁股
某些工具只有编译阶段使用,我不希望它们占用我宝贵的镜像容量,就可以在镜像编译完成后,将这些中间依赖删掉。
我们以apt为例,在使用完成后,我们需要做的事情有:
- 删除那些 不需要 的依赖:
apt-get pruge --autoremove ...
- 删除本地的软件包列表:
rm -rf /var/lib/apt/lists/*
这个过程中我们会遇到一个非常难解的问题,究竟哪些依赖是“不需要”的?
比如,在编译PHP时,我们可能会用到三个工具:wget、libxml、gcc。这三个工具,在编译PHP前都需要安装。但是在编译完成后,我们可以卸载wget和gcc,但不能卸载libxml。
原因是,libxml为PHP所依赖的一个动态链接库,如果我们将其卸载,将会出现找不到共享链接库的错误:
root@8eab53da8d5b:/# php -v php: error while loading shared libraries: libxml2.so.2: cannot open shared object file: No such file or directory
那么,有没有一个比较方便的办法,我自动只找出那些不是“共享链接库”的依赖并删除他们呢?
当然有,比较简单的办法是,我们遍历刚编译的可执行文件,使用ldd命令列出其依赖的共享链接库文件名,并在源中搜索这个文件名对应的包名:
这些包就是PHP依赖的所有动态链接库,接着我们将这些包用 apt-mark
声明为“手工安装的包”,即可阻止 apt purge
的自动卸载。
然后,我们再自动卸载其余没有用到的包即可。完整shell脚本如下:
find /usr/local -type f -executable -exec ldd '{}' ';' \ | awk '/=>/ { print $(NF-1) }' \ | sort -u \ | xargs -r dpkg-query --search \ | cut -d: -f1 \ | sort -u \ | xargs -r apt-mark manual \ ; \ apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false;
4. 尽量将中间依赖的安装与卸载操作放在一个步骤中
docker镜像是一个由“层”来堆叠起来的“千层饼”,我们可以使用 docker history <image name>
这条命令来查看任意一个镜像是由哪些层组成的,以及每一层的大小:
对于Dockerfile来说,这些层的数据都将会被保存在镜像中,即使后一层删除了前一层内保存的文件。
比如,我们有如下Dockerfile:
FROM alpine:3.12 RUN truncate -s 50M /sample.dat RUN rm -rf /sample.dat
我们可以试试看这个镜像编译出来有多大,58MB:
相比起来,正常的alpine:3.12只有5.57MB,说明即使我们已经删除了 /sample.dat
文件,在最后的镜像中也没有这个内容,但是它永远留在了镜像的history中。
所以,在删除上文说到的“中间依赖”时,我们需要将安装、使用、卸载三个部分写在一个步骤中,才能保证空间被释放。比如:
FROM debian:buster RUN apt-get update \ && apt-get install gcc \ && gcc ... \ && apt-get purge --autoremove gcc \ && rm -rf /var/lib/apt/lists/*
5. 多阶段编译
在Docker 17.05版本以后,新引入了 multi-stage builds 这一概念,这将会极大地简化我们上述的所有操作。
简单来说,multi-stage builds支持我们将Docker镜像的编译分成多个“阶段”。比如常见的软件编译的情况,我们可以将编译阶段单独提出来,软件编译完成后直接将二进制文件拷贝到一个新的基础镜像中,这样做最大的好处就是,第二个镜像不再包含任何编译阶段使用的中间依赖,干干净净明明白白。
以最常见的Java项目为例,编译Jar包的时候,我们需要使用到JDK、Maven等工具,但在实际运行阶段,我们只需要JRE环境即可。简单比较下 maven:3-openjdk-8
和 openjdk:8-jre
两个镜像的大小:
差别一倍有余。
以Vulhub中的Shiro 1.2.4环境为例,在其Dockerfile中可以看到两个 FROM
命令:
FROM maven:3-jdk-8 AS builder LABEL MAINTAINER="phithon <root@leavesongs.com>" COPY ./code/ /usr/src/ WORKDIR /usr/src RUN cd /usr/src; \ mvn -U clean package -Dmaven.test.skip=true FROM openjdk:8u102-jre LABEL MAINTAINER="phithon <root@leavesongs.com>" COPY --from=builder /usr/src/target/shirodemo-1.0-SNAPSHOT.jar /shirodemo-1.0-SNAPSHOT.jar EXPOSE 8080 CMD ["java", "-jar", "/shirodemo-1.0-SNAPSHOT.jar"]
第一个 FROM
用来进入 maven:3-jdk-8
环境,使用maven对源码进行编译;第二个 FROM
进入较小的 openjdk:8u102-jre
环境,使用 COPY --from=
语法,从前一个阶段的编译结果中将jar文件复制到jre的环境中。
最后,在机器上将会留下两个镜像,一个是builder,一个是最终我们需要的那个shiro 1.2.4的环境,后者可以被其他任何用户独立使用,而前者可以直接删除。
对于使用者来说,我们无需再纠结编译软件时中间依赖如何删除才能让镜像比较小的问题,反正第一阶段使用的任何依赖多不会被遗留到正式的生产环境中。
但多阶段编译对于动态链接库的依赖仍然有上述的问题,如果我们拷贝编译成果时只拷贝了可执行文件,在新环境下运行仍然会出现找不到共享链接库的错误。所以个人觉得,多段式编译仅适合于Java、golang等能够跨平台或静态编译的语言,对于C、Python这些依赖较多的项目仍然不友好。
6. 使用slim版本的镜像
细心的同学可能注意过,Docker官方的Debian镜像有个slim版本,这个版本的大小比默认的版本要小一倍多:
slim的中文意思就是“苗条的”,顾名思义, debian:stretch-slim
确实苗条的多,原因是其删除了man文档等许多不会在容器里用到的文件。
有一些上层的镜像会基于slim版本的debian进行编写,比如python。如果我们开发python的项目,可以使用 python:slim
这个基础镜像。
总结一下,六种方法,互相不会影响,我们可以同时使用。但第5个,多阶段编译将会是以后的主流方式。
到此这篇关于详解六种减小Docker镜像大小的方法的文章就介绍到这了,更多相关减小Docker镜像大小内容请搜索开心学习网以前的文章或继续浏览下面的相关文章希望大家以后多多支持开心学习网!
- 如何使用docker启动tomcat(简述Docker安装Tomcat镜像并部署web项目)
- dockernginx服务器教程(Docker镜像+nginx 部署 vue 项目的方法)
- docker的postgres镜像包(Docker中运行PostgreSQL并推荐几款连接工具)
- docker 重启容器后数据丢失(如何恢复docker容器数据)
- docker删除多个镜像命令(Docker 彻底删除私有库镜像的操作)
- dockerpull镜像的过程(详解docker pull 下来的镜像都存到了哪里)
- docker容器解决隔离的技术(Docker+selenium实现自动化健康报备的方法)
- docker运行环境centos(如何在centos的docker里安装jupyter并开放端口)
- kalilinux安装到u盘教程学习(kali安装docker和portainer的配置方法)
- docker端口访问不了(docker设置了端口映射,不能访问的解决方案)
- docker中国加速镜像怎么设置(Docker 安装及配置镜像加速的实现)
- docker端口映射和暴露端口的区别(解决docker指定udp端口号的问题)
- 如何增大docker内存使用(docker 内存监控与压测方式)
- docker容器启动失败怎么查找原因(解决docker容器启动后马上退出的问题)
- docker配置说明(Docker安装Kong API Gateway并使用的详细教程)
- idea连接不上docker(IDEA使用Docker插件远程部署项目到云服务器的方法步骤)
- 日本菜有什么好吃(日本菜有什么好吃的做法)
- 韩国泡菜做法(韩国泡菜的做法步骤)
- 泰国旅游攻略(泰国旅游攻略必去景点)
- 越难春卷(越难春卷皮怎么用)
- 休闲VS新古典 办公家居简约设计(办公家居简约设计)
- 15个新成 园 位置公布 深圳龙岗2022年共建花园建设又有大动作(15个新成园位置公布)
热门推荐
- mysql有哪些约束(MySQL完整性约束的定义与实例教程)
- python复杂的验证码处理(Python 通过打码平台实现验证码的实现)
- C# Checklistbox的用法
- nginx故障处理(详解Nginx启动失败的几种错误处理)
- dedecms栏目静态化(DedeCms获取任意栏目N级列表链接树形菜单的方法)
- 三种css选择符(浅谈css之属性及剩余的选择符)
- JavaScript 阻止超链接跳转的操作方法(多种写法)(JavaScript 阻止超链接跳转的操作方法多种写法)
- ASP.NET抓取网页内容
- bootstrap 左右布局(详解Bootstrap网格垂直和水平对齐方式)
- css如何做出矩形三角流程效果(css做个波浪悬浮球的实现方法)
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9