本文介绍一种docker image瘦身的技巧,它基于Chiselled Ubuntu来实现,希望能帮助大家将自己的docker image体积缩小约50%(或者至少上百MB)。
Chiselled Ubuntu 容器是一个生产就绪的、安全的超小型容器镜像,侧重于效率和安全性。这些容器镜像允许用户构建的镜像仅包含其应用程序及运行时依赖,而不包含不必要的操作系统级包、实用程序或库。另外,Canonical 还承诺提供安全维护与支持。Chiselled Ubuntu 容器也是旨在缩小容器基础镜像。它也带来了同样的好处,比如最小化依赖,减少膨胀和资源使用,加快启动速度,并通过减少镜像中不需要的文件来增强安全性。
以Java为例介绍Docker image瘦身效果
我在以前通过Docker 镜像瘦身技巧介绍了一些常见后端语言编写的程序的整体镜像瘦身技术点,并通过Docker 镜像 OpenJDK 基础镜像选型详细列举了Java语言具体的基础镜像选择的最佳实践,但是一般来讲Java程序相关的Docker image体积至少都是200MB。那么我们是否可以进一步的降低image体积呢?我们可以尝试下Chiselled Ubuntu 容器镜像,验证是否如ubuntu的宣称的这样。
我们可以使用任意的java程序进行测试。我这里使用spring boot web最小项目制作的jar包,将这个jar文件放置到image里面制作成一个可运行的image。其dockerfile为:
# 使用ubuntu/jre,它已经是chiselled版本: https://hub.docker.com/r/ubuntu/jre FROM ubuntu/jre:17-22.04_39 # 指定环境变量 ENV APP_NAME=demo ENV PORT=8080 # 标签 LABEL "AUTHOR"="ThinkTik" LABEL "MAIL_ADDRESS"="thinktik@outlook.com" LABEL "DOMAIN_NAME"="omoz.cc" LABEL "SERVICE_PATH"="${APP_NAME}" # 切换工作目录 WORKDIR /home/app # 将当前项目的可执行文件添加到工作目录:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#add-or-copy ADD ./build/libs/${APP_NAME}-*.jar ./app.jar # This image does not include bash nor a package manager nor the OpenJDK. Its purpose is to serve as a runtime, final-stage base image for compatible Java applications. # 因为chiselled版本的jre没有bash、包管理器、OpenJDK以及其他的Linux工具组件。所以没有我们常见的mkdir、curl、useradd等几乎全部的Linux命令!也没有/tmp目录! # 所以当java程序需要读写/tmp文件夹时,我们只能提前通过其他方式绕过去 ADD ./README.md /tmp/README.md # 暴露的端口号 EXPOSE ${PORT} # 使用该镜像来启动容器,指定启动命令 # 因为ubuntu/jre base image里面已经自带了ENTRYPOINT(目的是指定java命令的具体路径),这时我们只需要通过CMD来补充启动参数即可:https://docs.docker.com/engine/reference/builder/#dockerfile-reference CMD ["-jar","./app.jar"]
作为对比我们使用amazon corretto jdk作为base image的话,做好Docker 镜像瘦身技巧和Docker 镜像 OpenJDK 基础镜像选型后,其dockerfile为:
# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/ # https://docs.aws.amazon.com/AmazonECS/latest/bestpracticesguide/application.html # 使用JDK21 FROM amazoncorretto:21-al2023-headless # 指定环境变量 ENV USER=omoz ENV HOME_PATH=/home/${USER} ENV LOG_PATH=${HOME_PATH}/logs ENV APP_NAME=demo ENV PORT=8080 # 标签 LABEL "AUTHOR"="ThinkTik" LABEL "MAIL_ADDRESS"="thinktik@outlook.com" LABEL "DOMAIN_NAME"="omoz.cc" LABEL "SERVICE_PATH"="${APP_NAME}" # 创建omoz-developers用户组,创建用户omoz(自动创建home目录)并加入omoz-developers用户组 RUN dnf update -y && dnf install shadow-utils procps-ng -y RUN groupadd -r ${USER}-developers && useradd -r -m -g ${USER}-developers ${USER} # 切换用户(以非root用户启动服务) USER ${USER} # 指定工作目录 WORKDIR ${HOME_PATH} # 将当前项目的可执行文件添加到工作目录 # https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#add-or-copy ADD ./build/libs/${APP_NAME}-*.jar ${HOME_PATH}/app.jar # 周期性健康检查 # https://docs.docker.com/engine/reference/builder/#healthcheck HEALTHCHECK --start-period=180s --interval=60s --timeout=8s --retries=3 CMD curl -s -f -k https://localhost:${PORT}/ping || exit 1 # 暴露的端口号 EXPOSE ${PORT} # 使用该镜像来启动容器,指定启动命令 ENTRYPOINT ["java","-jar","app.jar"]
我分别使用上面2种dockerfile制作了2个版本的docker image并都做了最大程度的体积优化,最后的结果是两者对比之下,就我的简单测试而言Chiselled Ubuntu 容器作为base image可以把java docker image体积缩小为原先的25%左右或者至少100MB,达到了官方所宣传的效果!!!
总结
Chiselled Ubuntu 容器的适用场景和建议
目前直接支持的解释型语言还不是太多,主要是python,java,dotnet-runtime 你可以自己为一些不直接支持的解释型语言制作Chiselled版image,但是技术要求比较高,这里不详细展开 编译型语言比如go,rust可以直接使用debian base image 和ubuntu base image由于Chiselled Ubuntu删除了除运行所必须的组件外的其他非必需组件,所以达到了更好的瘦身效果,但是这也对一些我们在容器运行中的debug、linux命令行的使用有明显的影响。
建议搭配分布式的日志服务来接收程序产生的日志而不是使用本地卷。如前面讲到的,有些非必要的目录可能都没有,比如/tmp文件夹 建议在image发布到正式环境前做好详细的功能测试和验证(因为一旦需要debug,在bash都没有的情况下,很难docker exec进入到容器)总之,相比alpine linux,Chiselled Ubuntu是标准的Linux,兼容性更好、适用范围更大并且体积有时还更低,因为大多数Linux发行版都使用GNU版本的标准C库(glibc),但Alpine Linux使用的是musl,那些二进制安装包是针对glibc编译的;相比传统的其他方案,Chiselled Ubuntu的体积明显有优势。