为什么要docker减小镜像?
在使用docekrfile文件构建docker镜像时,即使时安装一个比较小的应用,构建后也是镜像,小到好几百M大到几个G, 这样非常不利与镜像之间的传输与上传到镜像仓库,部署的时候也会浪费时间,安装更多的工具会导致构建的镜像安全系数大幅度降低。
初学者常规构建
在刚学习dockerfile构建镜像的时候,我们的docker文件一般都是 RUN,命令行一行的写,这样会导致每个RUN命令就会创建一个layer层,从而增大镜像的大小。例如:
使用nginx源码进行构建nginx, 基础镜像使用centos:7 大约200M,使用du -sh nginx,可以查看nginx的大小只有1M多一点。如果构建的镜像非常大,那是用容器就没必要了。
[root@localhost nginx_docker]# du -sh nginx-1.22.1.tar.gz
1.1M nginx-1.22.1.tar.gz
Dockerfile事例:
FROM centos:7
ENV PROJECT_PATH=/opt
WORKDIR ${PROJECT_PATH}
ADD nginx-1.22.1.tar.gz .
RUN /bin/sh -c "yum -y update"
RUN /bin/sh -c "yum -y install openssl-devel"
RUN /bin/sh -c "yum -y install pcre-devel"
RUN /bin/sh -c "yum -y install gcc"
RUN /bin/sh -c "yum -y install make"
ENV PROJECT_PATH=/opt/nginx-1.22.1
WORKDIR ${PROJECT_PATH}
RUN /bin/sh -c "./configure --prefix=/usr/local/nginx --sbin-path=/bin/ --with-http_ssl_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre"
RUN /bin/sh -c "make"
RUN /bin/sh -c "make install"
EXPOSE 80
ENTRYPOINT ["nginx", "-g", "daemon off;"]
分析:一共构建了17镜像层,因为一共17行命令,构建后的镜像大小一共是1.4GB,Oh My God!, 为什么会那么大,可以使用docker history nginx:v1检查一下每一步构建的大小。虽然使用docker run 启动也是没问题的,但是传输也太不方便了。
优化方法:
1、安装后删除软件包(不推荐)
因为在构建的时候我们使用yum 安装装了好多构建包,而且还有nginx源码也存在镜像中,那么我们删除可以删除这些包,并且清除yum的环境,因为编译安装后的nginx是不需要这些包的。尝试一下
1)删除nginx的源码文件
2)卸载构建是的工具包, 如:gcc make oepssl-devel pcre-devel
3)清除yum 缓存
FROM centos:7
ENV PROJECT_PATH=/opt
WORKDIR ${PROJECT_PATH}
ADD nginx-1.22.1.tar.gz .
RUN /bin/sh -c "yum -y update"
RUN /bin/sh -c "yum -y install openssl-devel"
RUN /bin/sh -c "yum -y install pcre-devel"
RUN /bin/sh -c "yum -y install gcc"
RUN /bin/sh -c "yum -y install make"
ENV PROJECT_PATH=/opt/nginx-1.22.1
WORKDIR ${PROJECT_PATH}
RUN /bin/sh -c "./configure --prefix=/usr/local/nginx --sbin-path=/bin/ --with-http_ssl_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre"
RUN /bin/sh -c "make"
RUN /bin/sh -c "make install"
ENV PROJECT_PATH=/usr/local/nginx
WORKDIR ${PROJECT_PATH}
# 删除nign源码,卸载构建工具,清除yum缓存
RUN /bin/sh -c "rm -rf /opt/nginx-1.22.1"
RUN /bin/sh -c "yum -y remove openssl-devel"
RUN /bin/sh -c "yum -y remove pcre-devel"
RUN /bin/sh -c "yum -y remove gcc"
RUN /bin/sh -c "yum -y remove make"
RUN /bin/sh -c "yum clean all"
RUN /bin/sh -c "yum clean packages"
EXPOSE 80
ENTRYPOINT ["nginx", "-g", "daemon off;"]
分析:使用docker images 查看构建的nginx:v2镜像的大小比第一次构建的nginx:v1镜像还大,为什么呢,我们使用docker history nginx:v2 查看构建步骤,足足有20多层,而且后边使用RUN命令的时候每增加一个就会增加24M左后,所以不减反增。
小结:除非是很大的文件需要删除,才推荐加RUN命令,否者会事半功倍的。
2、最小化镜像层(强烈推荐)
因为dockerfile文件中每一个镜像的命令如ADD COPY RUN,都会是一个镜像层,可以减少这些镜像命令的使用。在Linux操作系统中,可以使用 && 符合连接两个命令,可以使用 \ 进行长命令换行,在Dockerfile文件中也是可以这样使用的,把所有的命令放到一层中。
FROM centos:7
ENV PROJECT_PATH=/opt
WORKDIR ${PROJECT_PATH}
ADD nginx-1.22.1.tar.gz .
RUN /bin/sh -c "yum -y update && \
yum -y install openssl-devel pcre-devel gcc make && \
cd /opt/nginx-1.22.1 && \
./configure --prefix=/usr/local/nginx --sbin-path=/bin/ --with-http_ssl_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre && \
make && make install && \
cd /usr/local/nginx && \
rm -rf /opt/nginx-1.22.1 && \
yum -y remove openssl-devel pcre-devel gcc make && \
yum clean all && yum clean packages"
EXPOSE 80
ENTRYPOINT ["nginx", "-g", "daemon off;"]
与上一个dockerfile 相比 我们把所有的RUN 命令都合并到了一个RUN 命令里面,命令操作都是一样的。使用docker images命令查看镜像大小,docker history 查看镜像的构建步骤,镜像nginx:v3大约400M,比刚才的 nginx:v1, 1.4G 减少了3-4倍。
分析:因为我们把所有的RUN 命令都放到了同一个RUN命令中,在实际的构建过程中,相当于把好几层压缩到了一层。
小结:这种方法可以大幅度减小镜像的大小。
3、使用多阶段构建镜像(推荐)
所谓的多阶段构建就是至少有两个阶段,第一阶段是编译应用,第二阶段就是运行,将第一个阶段编译好的应用跑在第二个阶段上。原因,第一个阶段上有很多编译的工具不一定能完全卸载删除。而应用运行的时候又不依赖这些编译工具。所以编译工具就是多余的。
# stage-1
FROM centos:7 as build
ENV PROJECT_PATH=/opt
WORKDIR ${PROJECT_PATH}
ADD nginx-1.22.1.tar.gz .
RUN /bin/sh -c "yum -y update && \
yum -y install openssl-devel pcre-devel gcc make && \
cd /opt/nginx-1.22.1 && \
./configure --prefix=/usr/local/nginx --sbin-path=/bin/ --with-http_ssl_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre && \
make && make install && \
cd /usr/local/nginx && \
rm -rf /opt/nginx-1.22.1 && \
yum -y remove openssl-devel pcre-devel gcc make && \
yum clean all && yum clean packages"
# stage-2
FROM centos:7
COPY --from=build /usr/local/nginx /usr/local/nginx
COPY --from=build /bin/nginx /bin/nginx
EXPOSE 80
ENTRYPOINT ["nginx", "-g", "daemon off;"]
分析:与上一个nginx:v3相比 我们使用的多阶段构建,第一个阶段构建nginx,第二个阶段运行nginx。使用docker images命令查看镜像大小,docker history 查看镜像的构建步骤。nginx:v4 镜像的大小只有210M,, 只比基础镜像多了6M,运行也是没有问题的。简直不要太爽。
小结:这种方法也是可以大成倍的减少镜像的大小。
4、使用比较小的基础镜像(比较推荐:但是有时环境不同,会缺少动态链接库)
dockerfile事例:centos构建,ubuntu 运行
# stage-1
FROM centos:7 as build
ENV PROJECT_PATH=/opt
WORKDIR ${PROJECT_PATH}
ADD nginx-1.22.1.tar.gz .
RUN /bin/sh -c "yum -y update && \
yum -y install openssl-devel pcre-devel gcc make && \
cd /opt/nginx-1.22.1 && \
./configure --prefix=/usr/local/nginx --sbin-path=/bin/ --with-http_ssl_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre && \
make && make install && \
cd /usr/local/nginx && \
rm -rf /opt/nginx-1.22.1 && \
yum -y remove openssl-devel pcre-devel gcc make && \
yum clean all && yum clean packages"
# stage-2
FROM ubuntu:latest
COPY --from=build /usr/local/nginx /usr/local/nginx
COPY --from=build /bin/nginx /bin/nginx
COPY --from=build /lib64/libpcre.so.1 /lib/x86_64-linux-gnu/libpcre.so.1
COPY --from=build /lib64/libssl.so.10 /lib/x86_64-linux-gnu/libssl.so.10
COPY --from=build /lib64/libcrypto.so.10 /lib/x86_64-linux-gnu/libcrypto.so.10
RUN /bin/sh -c "sed -i 's/#user nobody;/user nobody nogroup;/' /usr/local/nginx/conf/nginx.conf"
EXPOSE 80
ENTRYPOINT ["nginx", "-g", "daemon off;"]
寻找比较小的基础镜像代替centos:7,例如使用ubuntu基础镜像,代替nginx运行,还可以使用alpine 基础镜像, 也可以使用google的debian,构建完之后的镜像要更小。
使用 ubuntu 作为第二阶段的运用来构建nginx镜像。使用ubuntu之后构建完之后的镜像大小只有83.9M,还不到100M,但是运行nginx命令会出现找不到动态链接的报错信息,进入镜像,使用ldd 查看启动nginx 需要的动态链接库有哪些,需要从第一阶段的环境中COPY过来。
使用docker run 运行构建的镜像,运行结果OK,
总结:以上是几个项目构建中的常用优化方法,其它的方法也有,例如使用一些开源三方库检测dockerfile文件。但是不是很推荐,掌握好基础比较重要。我们第一次构建的镜像大约 1.4个G ,使用最小层构建的大约400M,使用多阶段构建的大约200M,使用 多阶段+小基础镜像构建的大约只有85M,这是因为ubuntu镜像基础大小为78M,如果使用apline,或者更小镜像,估计会更小。