使用 docker
打包 python
项目时,如何让镜像小一点,一直不太理想。
常见的python 项目,动则就是几百M 甚至上G 的镜像大小。
我能理解,我所依赖的第三方库越多,最后镜像肯定就越大,例如python 里面的很多算法库,都是几十上百M,特别是一些动态的依赖 —— 依赖于linux上的软件,在运行时需要,无法卸载掉来减少镜像大小 —— 镜像整体就会更大,达到论 G 算。
但是如果项目本身小,同时依赖的第三方库少,系统运行依赖少甚至无,我该如何尽量减小镜像的大小?
以Docker Hub 中python官方维护的镜像为例,一个简单的python3.6 环境,就算是alpine,也至少有70M,远远大于我们的代码 —— 一两个简单源码文件,当通过 FROM python:3.6-alpine
后,镜像大小就向100M 看齐了。
那该如何来缩小python 镜像的大小呢?
常见操作
# 继承自一个python+linux 环境
FROM python:3.6-slim
# 拷贝项目文件
COPY xxx/ /projects/
ENV TZ=Asia/Shanghai
WORKDIR /project
USER root
# 系统安装软件 debian 系,不安装推荐软件
RUN apt-get update \
&& apt-get install -y --no-install-recommends xxx xxx
# 安装python第三方库,no-cache-dir 用于不使用本地下载缓存,同时清理不需要的linux 安装和日志,
RUN pip install --no-cache-dir requirements.txt \
&& apt-get clean \
&& apt-get --purge autoremove -y gcc \
&& rm -rf /tmp/* /var/lib/apt/* /var/cache/* /var /log/*
# web 应用指定端口
EXPOSE 8080
CMD ["python","xxx.py"]
这是最常见的镜像打包过程,通过去掉安装过程生成的不必要的文件来缩小镜像,但镜像本身不够小,也不会特别大。
这里需要说明一下,python 的官方镜像分为基于alpine
,buster
, stretch
和 slim
的四种。其中,buster
和stretch
标签的镜像约900M,因为过大而通常不选择,alpine
镜像约5-7M,但因为包管理不同,以及使用不同的C库(不是更常见的glibc)可能会导致奇怪的问题,也不是一个首选方案。
所以一般是选择标签slim
:镜像大小约150M。
这样的思路,几乎能够适用于各种项目,打包镜像结果会根据依赖多少而呈现出不同的大小 —— 甚至,更推荐那种依赖复杂一点的应用,以这种方式发布。
那如果 python 项目本身并不大,甚至第三方库很少,无系统依赖(定位于,微小的应用,微小的服务),使用python:slim
标签镜像至少150M ,python: alpine
标签镜像也至少70M ,又能如何缩小镜像大小呢?
非常规操作
目前的想法和尝试,主要是基于pyinstaller 的编译,和Dockerfile 的多阶段构建
python:3.6-alpine
编译,alpine:3.12
中运行
这里的思路是,通过pyinstaller 编译后,选择没有python 的环境,来运行脚本。
值得注意的是,alpine
上相关依赖安装麻烦(参考文献不多),alpine
上pyinstaller 坑很多。
但是这样的方式,打包出来的镜像,如果项目小(单个文件,一行代码为例),大概就11M+。
FROM bigpangl/python:3.6-alpine-pyinstaller3.4
WORKDIR /app
COPY app.py ./app.py
RUN apk --update --no-cache add g++ scons patchelf \
&& pip install staticx \
&& pyinstaller -F app.py
FROM bigpangl/alpine:3.12
COPY --from=complier /app/dist/app /app
CMD ./app
python:3.6-slim
编译,alpine:3.12
中运行
选择slim
上编译是因为alpine
上编译,问题太多了,以我为代表的人群,可能无法搜寻到解决方案,slim
基于debian
,参考资料多了很多。
但是需要在编译后,通过python 的第三方库staticx
(Bundle dynamic executables with their library dependencies so they can be run anywhere, just like a static executable.) 进行再次打包。这样的项目,打包镜像基础大概12M+
FROM bigpangl/python:3.6-slim AS complier
WORKDIR /app
COPY app.py ./app.py
RUN apt-get update && apt-get install -y build-essential patchelf \
&& pip install staticx pyinstaller && pyinstaller -F app.py && staticx /app/dist/app /app
FROM bigpangl/alpine:3.12
WORKDIR /app
COPY --from=complier /app /app
CMD ./app
关于staticx
我本以为它的目的是,打包静态可执行文件。然后我就可以用 scratch 镜像了 —— 但是,好像不能?




