写点什么

用 Alpine 会让 Python Docker 的构建慢 50 倍

  • 2020-03-25
  • 本文字数:3968 字

    阅读完需:约 13 分钟

用Alpine会让Python Docker的构建慢50倍


当你为 Docker 镜像选择基础镜像时,Alpine Linux 可能被推荐。有人告诉你,用 Alpine 将使你的镜像更小,并能加快你的 builds。如果你正在用 Go,这无疑是个合理的建议。


但如果你使用 Python,Alpine Linux 会经常:


  1. 让你的构建更慢

  2. 让你的镜像更大

  3. 浪费你的时间

  4. 偶尔引入一些令人费解的运行时 Bug


让我们看看为什么人们推荐使用 Alpine,以及为什么不应该在 Python 应用程序中使用它。

为什么人们推荐使用 Alpine

假设我们需要安装 gcc 作为镜像构建的一部分,并且我们想看看 Alpine Linux 在构建时间和镜像大小方面与 Ubuntu 18.04 有何不同。


首先,我将拉取两个镜像,并检查他们的大小:


$ docker pull --quiet ubuntu:18.04docker.io/library/ubuntu:18.04$ docker pull --quiet alpinedocker.io/library/alpine:latest$ docker image ls ubuntu:18.04REPOSITORY          TAG        IMAGE ID         SIZEubuntu              18.04      ccc6e87d482b     64.2MB$ docker image ls alpineREPOSITORY          TAG        IMAGE ID         SIZEalpine              latest     e7d92cdc71fe     5.59MB
复制代码


如你所见,Alpine 的基础镜像要小得多。


接下来,我们将尝试在它们两个中安装 gcc。首先,在 Ubuntu 中:


FROM ubuntu:18.04RUN apt-get update && \    apt-get install --no-install-recommends -y gcc && \    apt-get clean && rm -rf /var/lib/apt/lists/*
复制代码


注意:在我们讨论的主题之外,本文中的 Dockerfile 并不是最佳实践的示例,因为增加的复杂性会掩盖本文的主要观点。因此,如果你打算用 Docker 在生产环境中运行你的 Python 应用程序,这里有两种方法可以应用最佳实践:


如果你想 DIY:一个详细的清单、例子和参考资料


如果你想要尽快拥有一个基本够用的设置:一个模板和为你实现的最佳实践


然后,我们可以构建并记录时间:


$ time docker build -t ubuntu-gcc -f Dockerfile.ubuntu --quiet .sha256:b6a3ee33acb83148cd273b0098f4c7eed01a82f47eeb8f5bec775c26d4fe4aaereal    0m29.251suser    0m0.032ssys     0m0.026s$ docker image ls ubuntu-gccREPOSITORY   TAG      IMAGE ID      CREATED         SIZEubuntu-gcc   latest   b6a3ee33acb8  9 seconds ago   150MB
复制代码


现在,我们编制一个类似的 Alpine Dockerfile:


FROM alpineRUN apk add --update gcc
复制代码


同样地,构建镜像并检查大小:


$ time docker build -t alpine-gcc -f Dockerfile.alpine --quiet .sha256:efd626923c1478ccde67db28911ef90799710e5b8125cf4ebb2b2ca200ae1ac3real    0m15.461suser    0m0.026ssys     0m0.024s$ docker image ls alpine-gccREPOSITORY   TAG      IMAGE ID       CREATED         SIZEalpine-gcc   latest   efd626923c14   7 seconds ago   105MB
复制代码


就像我们所说的那样,Alpine 镜像构建速度更快,体积更小:15 秒而不是 30 秒,镜像大小是 105MB 而不是 150MB。这很好!


但是当我们打包 Python 应用程序时,情况就开始变得糟糕了。

让我们构建一个 Python 镜像

我们希望打包一个使用了 panda 和 matplotlib 的 Python 应用程序。因此,一种选择是使用基于 Debian 的官方 Python 镜像(我提前拉取的),和以下这个 Dockerfile:


FROM python:3.8-slimRUN pip install --no-cache-dir matplotlib pandas
复制代码


然后,我们构建它:


$ docker build -f Dockerfile.slim -t python-matpan.Sending build context to Docker daemon  3.072kBStep 1/2 : FROM python:3.8-slim ---> 036ea1506a85Step 2/2 : RUN pip install --no-cache-dir matplotlib pandas ---> Running in 13739b2a0917Collecting matplotlib  Downloading matplotlib-3.1.2-cp38-cp38-manylinux1_x86_64.whl (13.1 MB)Collecting pandas  Downloading pandas-0.25.3-cp38-cp38-manylinux1_x86_64.whl (10.4 MB)...Successfully built b98b5dc06690Successfully tagged python-matpan:latestreal    0m30.297suser    0m0.043ssys     0m0.020s
复制代码


结果镜像大小为 363MB。


用 Alpine 会获得更好的结果吗?让我们试一试。


FROM python:3.8-alpineRUN pip install --no-cache-dir matplotlib pandas
复制代码


然后,我们构建它:


$ docker build -t python-matpan-alpine -f Dockerfile.alpine .                                 Sending build context to Docker daemon  3.072kB                                               Step 1/2 : FROM python:3.8-alpine                                                              ---> a0ee0c90a0db                                                                            Step 2/2 : RUN pip install --no-cache-dir matplotlib pandas                                                   ---> Running in 6740adad3729                                                                 Collecting matplotlib                                                                           Downloading matplotlib-3.1.2.tar.gz (40.9 MB)                                                   ERROR: Command errored out with exit status 1:                                                 command: /usr/local/bin/python -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-a3olrixa/matplotlib/setup.py'"'"'; __file__='"'"'/tmp/pip-install-a3olrixa/matplotlib/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /tmp/pip-install-a3olrixa/matplotlib/pip-egg-info                              ...ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.The command '/bin/sh -c pip install matplotlib pandas' returned a non-zero code: 1
复制代码


发生了什么?

标准的 PyPI wheel 在 Alpine 上无效

如果你仔细查看上面基于 Debian 的构建,就会看到它正在下载 matplotlib-3.1.2-cp38-cp38-manylinux1_x86_64.whl。这是一个预编译的二进制 wheel。与此相反,Alpine 会下载源代码(matplotlib-3.1.2.tar.gz),因为标准的 Linux wheel 在 Alpine Linux 上无效。


为什么?大多数 Linux 发行版使用标准 C 库的 GNU 版本(glibc),Python 以及几乎所有的 C 程序都需要它。但是 Alpine Linux 使用 musl,这些二进制 wheel 是针对 glibc 编译的,因此 Alpine 禁用了 Linux wheel 支持。


现在,大多数 Python 包都包含了 PyPI 上的二进制 wheel,这大大缩短了安装时间。但是,如果你使用的是 Alpine Linux,那么你就需要编译你所使用的每个 Python 包中的所有 C 代码。


这也意味着你需要自己找出每个系统库依赖项。在这种情况下,为了找出依赖项,我做了一些研究,最后得到下面这个经过更新的 Dockerfile:


FROM python:3.8-alpineRUN apk --update add gcc build-base freetype-dev libpng-dev openblas-devRUN pip install --no-cache-dir matplotlib pandas
复制代码


然后我们构建它,它需要……25 分钟 57 秒!得到的镜像是 851MB。


下面是两个基本镜像的对比:



Alpine 的构建速度要慢得多,镜像要大得多,而且我不得不做了很多研究。

你不能解决这些问题吗?

构建时间

为了缩短构建时间,Alpine Edge(最终将成为下一个稳定版本)会包含 matplotlib 和 panda。而且安装系统包非常快。但是,到 2020 年 1 月为止,当前的稳定版本还不包括这些流行的包。


然而,即使它们可用了,系统包也几乎总是滞后于 PyPI 上的包,Alpine 也不太可能打包 PyPI 上的所有东西。据我所知,在实践中,大多数 Python 团队并没有将系统包用于 Python 依赖项,而是依赖于 PyPI 或 Conda Forge。

镜像大小

一些读者指出,你可以删除最初安装的包,或者添加不缓存包下载的选项,或者使用多阶段构建。一位读者尝试生成了一个470MB的镜像


是的,你可以得到一个与基于 slim 的镜像大致相当的镜像,但是 Alpine Linux 的全部动机是更小的镜像和更快的构建。如果工作做够了,你可能会得到一个更小的镜像,但是你仍然要忍受长达 1500 秒的构建时间,当你使用 python:3.8-slim 镜像时,构建时间只有 30 秒。


但是等等,还有!

Alpine Linux 会导致意料之外的运行时 Bug

虽然理论上,Alpine 使用的 musl C 库与其他 Linux 发行版使用的 glibc基本兼容,但在实践中,这种差异可能会导致问题。当问题确实发生时,可能会很奇怪且出乎意料。


下面是一些例子:


  1. Alpine 线程的默认堆栈大小更小,这可能导致Python崩溃

  2. Alpine 的一位用户发现,由于 musl 分配内存的方式与 glibc 不同,他们的 Python 应用程序要慢很多

  3. 在使用 WeWork 工作空间的 WiFi 时,我曾经无法在 minikube(虚拟机中的 Kubernetes)上运行的 Alpine 镜像中查找 DNS。原因是 WeWork 糟糕的 DNS 设置、Kubernetes 和 minikube 实现 DNS 的方式,以及 musl 对这种边缘情况的处理与 glibc 的方式不同。musl 没有错(它符合 RFC),但是我不得不浪费时间找出问题所在,然后切换到基于 glibc 的镜像。

  4. 另一个用户发现了时间格式和解析的问题。


大多数或者说所有这些问题可能都已经得到解决,但毫无疑问,还有更多的问题有待发现。这种出人意料的破坏是又一件需要担心的事情。

不要将 Alpine Linux 用于 Python 镜像

除非你想要更长的构建时间、更大的镜像、更多的工作,以及潜在的隐藏 Bug,否则你应该避免使用 Alpine Linux 作为基础镜像。


关于应该使用哪些镜像的建议,请参阅我的文章“选择一个好的基础镜像”。


英文原文:


Using Alpine can make Python Docker builds 50× slower


2020-03-25 15:479856
用户头像

发布了 742 篇内容, 共 482.1 次阅读, 收获喜欢 1549 次。

关注

评论 5 条评论

发布
用户头像
这个标题吓到我了,图片也是乱配。一看,哦,营销号,乱写就行了。
2020-06-18 09:12
回复
用户头像
什么鬼啊?
2020-06-18 09:11
回复
用户头像
用Ubuntu基础镜像时使用了习惯良好的构建方式,使用alpine基础镜像却比较随意。能不能去看看别人用alpine基础镜像打包的python镜像多大。得出这种结论的人,不太理解Docker,不要自己构建了,用官方提供的就行了
2020-03-27 19:26
回复
同意。没有理解alpine的特点在哪里,小巧的同时必然会牺牲一些库,要无脑省心还是跑在完整版的虚拟机里吧。
2020-03-31 11:12
回复
用户头像
自己不会构建,不要怪基础镜像
2020-03-27 19:23
回复
没有更多了
发现更多内容

为什么你总觉得自己是对的?

池建强

个人成长

week9作业1

一文带你掌握OBS的两种常见的鉴权方式

华为云开发者联盟

Rest OBS 鉴权 Header携带签名 URL携带签名

凭实力蝉联第一!Flink 又双叒叕上榜啦

Apache Flink

flink

埋点全解析,你最关心的可视化埋点在这里!(文末附开源地址)

易观大数据

零/低代码编程风口已到,请快快上车!

代码制造者

编程语言 低代码 零代码 iVX

垃圾回收&秒杀设计

dony.zhang

垃圾回收

架构师训练营第8周作业

Hanson

阿里最“短命”的P10?入职不到3周,钉钉副总裁张志琦因个人原因离职

程序员生活志

阿里巴巴 互联网热点

加快速度推进区块链融合发展

CECBC

区块链 行业资讯 产业落地

第九周.命题作业-GC原理

刘璐

奈学:一起了解几种序列化协议

古月木易

序列化 序列化协议

不仅性能秒杀Hadoop,现在连分布式集群功能也开源了

爱倒腾的程序员

大数据 涛思数据 tdengine 物联网 时序数据库

cxuan读者的外包面试之旅

苹果看辽宁体育

Java 面试 程序人生

架构师0期Week9作业1

Nan Jiang

数据库实践丨MySQL多表join分析

华为云开发者联盟

MySQL 数据库 华为云 join 多表join

高教授:区块链软硬件相结合能提升系统性能 也能保持一定的灵活性

CECBC

分布式 行业资讯 区块链技术 落地应用

架构师训练营第8周总结

Hanson

最右JS2Flutter框架——通信机制(三)

刘剑

flutter 大前端 探索与实践

白鹭引擎助力《梦幻西游网页版》刷新h5游戏新高度

DT极客

架构师训练营第九章作业

吴吴

地方政府争夺数字货币试点,互联网巨头参与测试,央行数字货币即将来临

CECBC

行业资讯 央行数字货币

JVM类加载与执行

superman

JVM 类加载

开源、云、传统IT的三国杀

郭华

云计算 开源 商业 IT

【得物技术】流量之战—性能优化三十六计

得物技术

Java 流量 得物技术部 商品 电商平台

总结

Kiroro

设计一个秒杀系统挑战和问题及方案或思路

Young

作业一

Kiroro

秒杀系统

Z冰红茶

week09 总结

Z冰红茶

技术贴丨教你使用华为云鲲鹏服务器部署Discuz!论坛

华为云开发者联盟

鲲鹏920 服务器 华为云 Discuz! 华为云鲲鹏

用Alpine会让Python Docker的构建慢50倍_文化 & 方法_Itamar Turner-Trauring_InfoQ精选文章