Alpine 为 Python 编译 Docker 版本的速度慢了 50 倍,并且镜像的重量增加了 2 倍

Alpine 为 Python 编译 Docker 版本的速度慢了 50 倍,并且镜像的重量增加了 2 倍

Alpine Linux 通常被推荐作为 Docker 的基础镜像。 您被告知使用 Alpine 将使您的构建更小,构建过程更快。

但如果您将 Alpine Linux 用于 Python 应用程序,那么它:

  • 使你的构建速度变慢
  • 让你的图像更大
  • 浪费你的时间
  • 最终可能会导致运行时错误


让我们看看为什么推荐 Alpine,但为什么你仍然不应该将它与 Python 一起使用。

为什么人们推荐阿尔派?

假设我们需要 gcc 作为镜像的一部分,并且我们想要在构建速度和最终镜像大小方面比较 Alpine Linux 和 Ubuntu 18.04。

首先,我们下载两张图片并比较它们的大小:

$ docker pull --quiet ubuntu:18.04
docker.io/library/ubuntu:18.04
$ docker pull --quiet alpine
docker.io/library/alpine:latest
$ docker image ls ubuntu:18.04
REPOSITORY          TAG        IMAGE ID         SIZE
ubuntu              18.04      ccc6e87d482b     64.2MB
$ docker image ls alpine
REPOSITORY          TAG        IMAGE ID         SIZE
alpine              latest     e7d92cdc71fe     5.59MB

正如您所看到的,Alpine 的基础镜像要小得多。 现在让我们尝试安装 gcc 并从 Ubuntu 开始:

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

编写完美的 Dockerfile 超出了本文的范围。

我们来测量一下组装速度:

$ time docker build -t ubuntu-gcc -f Dockerfile.ubuntu --quiet .
sha256:b6a3ee33acb83148cd273b0098f4c7eed01a82f47eeb8f5bec775c26d4fe4aae

real    0m29.251s
user    0m0.032s
sys     0m0.026s
$ docker image ls ubuntu-gcc
REPOSITORY   TAG      IMAGE ID      CREATED         SIZE
ubuntu-gcc   latest   b6a3ee33acb8  9 seconds ago   150MB

我们对 Alpine (Dockerfile) 重复相同的操作:

FROM alpine
RUN apk add --update gcc

我们集合,看看集合的时间和规模:

$ time docker build -t alpine-gcc -f Dockerfile.alpine --quiet .
sha256:efd626923c1478ccde67db28911ef90799710e5b8125cf4ebb2b2ca200ae1ac3

real    0m15.461s
user    0m0.026s
sys     0m0.024s
$ docker image ls alpine-gcc
REPOSITORY   TAG      IMAGE ID       CREATED         SIZE
alpine-gcc   latest   efd626923c14   7 seconds ago   105MB

正如所承诺的,基于 Alpine 的图像收集速度更快且更小:需要 15 秒而不是 30 秒,图像大小为 105MB 而不是 150MB。 这个很不错!

但如果我们转而构建 Python 应用程序,那么一切就不那么乐观了。

Python图像

Python 应用程序经常使用 pandas 和 matplotlib。 因此,一种选择是使用以下 Dockerfile 获取基于 Debian 的官方映像:

FROM python:3.8-slim
RUN pip install --no-cache-dir matplotlib pandas

我们来收集一下吧:

$ docker build -f Dockerfile.slim -t python-matpan.
Sending build context to Docker daemon  3.072kB
Step 1/2 : FROM python:3.8-slim
 ---> 036ea1506a85
Step 2/2 : RUN pip install --no-cache-dir matplotlib pandas
 ---> Running in 13739b2a0917
Collecting 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 b98b5dc06690
Successfully tagged python-matpan:latest

real    0m30.297s
user    0m0.043s
sys     0m0.020s

我们得到一张大小为 363MB 的图像。
我们与 Alpine 的合作会做得更好吗? 咱们试试吧:

FROM python:3.8-alpine
RUN 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('"'"'rn'"'"', '"'"'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

这是怎么回事呢?

Alpine 不支持轮子

如果您查看基于 Debian 的构建,您将看到它下载了 matplotlib-3.1.2-cp38-cp38-manylinux1_x86_64。WHL.

这是车轮的二进制文件。 Alpine 下载源文件“matplotlib-3.1.2.tar”。gz` 因为它不支持标准 车轮.

为什么? 大多数 Linux 发行版都使用 C 标准库的 GNU 版本 (glibc),实际上每个用 C 编写的程序(包括 Python)都需要它。 但 Alpine 使用“musl”,并且由于这些二进制文件是为“glibc”设计的,因此它们根本不是一个选择。

因此,如果使用Alpine,则需要编译每个Python包中所有用C编写的代码。

哦,是的,您必须查找需要自己编译的所有此类依赖项的列表。
在这种情况下我们得到这个:

FROM python:3.8-alpine
RUN apk --update add gcc build-base freetype-dev libpng-dev openblas-dev
RUN pip install --no-cache-dir matplotlib pandas

构建时间需要...

... 25分57秒! 图像大小为851MB。

基于 Alpine 的镜像的构建时间要长得多,它们的尺寸也更大,而且您仍然需要查找所有依赖项。 您当然可以使用以下方法减小组件尺寸 多阶段构建 但这意味着还需要做更多的工作。

那不是全部!

Alpine 可能会在运行时导致意外错误

  • 理论上,musl 与 glibc 兼容,但在实践中,这些差异可能会导致很多问题。 如果是的话,他们可能会感到不愉快。 以下是可能出现的一些问题:
  • Alpine 默认情况下具有较小的线程堆栈大小,这可能会导致 Python 中的错误
  • 一些用户发现 Python 应用程序速度较慢 因为 musl 分配内存的方式(与 glibc 不同)。
  • 用户之一 格式化日期时发现错误

当然,这些错误已经得到纠正,但谁知道还会有多少。

不要将 Alpine 图像用于 Python

如果您不想费心进行大型且冗长的构建、搜索依赖项和潜在错误,请不要使用 Alpine Linux 作为基础镜像。 选择一个好的基础镜像.

来源: habr.com

添加评论