Alpine构建Docker镜像填坑

我平时构建 Docker image 的时候,为了减小镜像的大小,方便存储传输,一般都用 alpine 的分发镜像作为 Base image,而且自以为 alpine 和 ubuntu 的区别最大就在于大小和环境全不全,正常用的话,apt->apk 就得了,不过最近在学习用 matplotlib 进行绘图,发现了 alpine 的坑,特此记录一下

镜像构建无法安装 matplotlib 包

我本以为 matplotlib 包与 requests/pandas 等一样,在 alpine 下直接 pip install 是可以的,实际发现不行🚫,以下是排错过程,着急的电梯在这 🔗

无法构建镜像

构建镜像失败

提示信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
      Edit mplsetup.cfg to change the build options; suppress output with --quiet.

BUILDING MATPLOTLIB
python: yes [3.9.13 (main, May 25 2022, 21:34:36) [GCC 11.2.1 20220219]]
platform: yes [linux]
tests: no [skipping due to configuration]
macosx: no [Mac OS-X only]

[end of output]

note: This error originates from a subprocess, and is likely not a problem with pip.
error: metadata-generation-failed

× Encountered error while generating package metadata.
╰─> See above for output.

note: This is an issue with the package mentioned above, not pip.
hint: See above for details.

进入临时容器排查

1
2
3
4
5
# 启动临时容器测试
docker run --rm -it python:3.9-alpine sh

# 安装依赖
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests uvicorn fastapi matplotlib

发现报一样的错误

排除新添加的包测试

1
2
# 先去掉mathplotlib包
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --no-cache-dir requests uvicorn fastapi
测试正常

确定问题出在 matplotlib 包导致安装失败

解决过程

尝试搜索关键字 🔍alpine matplotlib 3.5.2

通过直接安装包尝试

其中,知乎1的评论,提示可以通过 apk add py3-matplotlib 实现

1
apk add py3-matplotlib
安装完成

安装上了,不过确实有点大,246MB... 并且,这个 workaround,并不能解决 pip install matplotlib 报错的问题,安装依旧报同样的错误,并且...产生了一个新问题,👇🏻

添加了一个py3.10...

我这个 base image 是基于 3.9 的,这个又加了一个 3.10 的版本...我裂开...

通过安装编译依赖尝试

Stack Overflow2上找到一篇答案,是通过直接添加编译安装的组件解决

1
2
3
4
5
6
7
# 使用清华源
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

pip install --upgrade pip setuptools wheel && \
apk add --no-cache --virtual .build-deps gcc g++ zlib-dev make python3-dev py-numpy-dev jpeg-dev && \
pip install matplotlib && \
apk del .build-deps

关于 apk add --virtual 命令相关的解释3这个参数就是类似 python 的 virtualenv --target 一样,指定安装的目录,便于后续执行删除操作

报错...

无法安装py-numpy-dev

转而解决安装py-numpy-dev依赖, 依旧是通过Stack Overflow4解决的

1
apk --no-cache add musl-dev linux-headers g++

再次尝试安装 matplotlib 包

成功安装

汇总命令如下:

1
2
3
4
5
6
7
8
# 使用清华源
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories

pip install --upgrade pip setuptools wheel
apk add --no-cache --virtual .build-deps musl-dev linux-headers g++ gcc zlib-dev make python3-dev jpeg-dev
pip install matplotlib
apk del .build-deps

不过这个虽然解决了问题,但是编译时间真的太久了...接近 20 分钟了

用时接近 20 分钟

其他参考资料

官方提供的安装手册

Installation — Matplotlib 3.5.2 documentation

不过并没有 alpine 相关的安装内容,其他环境的可以参考

问题解决

  1. 非必要情况,建议直接改用 ubuntu base 的镜像或其他 Linux 完全体镜像。

  2. 真的非要用 alpine 作为 base 镜像?确定可以忍受每次构建起步 20 分钟的情况吗?依旧要用的话,下面是命令

    1
    2
    3
    4
    5
    6
    7
    8
    # 使用清华源
    RUN python -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade pip && \
    pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple && \
    sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && \
    pip install --upgrade setuptools wheel && \
    apk add --no-cache --virtual .build-deps musl-dev linux-headers g++ gcc zlib-dev make python3-dev jpeg-dev && \
    pip install matplotlib && \
    apk del .build-deps

反思

这个问题在一开始搜索到知乎5的答案的时候其实就明确了,应该直接改用 ubuntu 的

搜索过程中,其实有些博文已经说明了有些情况不适合采用 alpine 作为基础镜像

Using Alpine can make Python Docker builds 50× slower

翻译版: 用Alpine会让Python Docker的构建慢50倍 - 腾讯云开发者社区-腾讯云

构建Dockerfile

alpine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
FROM python:3.9-alpine
MAINTAINER Rex Chow <879582094@qq.com>

ENV TZ='Asia/Shanghai'
ENV TIME_OUT=20
ENV LOG_LEVEL='info'
ENV CF_ZONE='example.com'

COPY app /app
WORKDIR /app
EXPOSE 80

RUN python -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade pip && \
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple && \
sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && \
pip install --upgrade setuptools wheel && \
apk add --no-cache --virtual .build-deps musl-dev linux-headers g++ gcc zlib-dev make python3-dev jpeg-dev && \
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --no-cache-dir -r /app/requirements.txt && \
apk del .build-deps && \
rm -rf ~/.cache/pip
CMD ["sh", "-c", "uvicorn main:app --host 0.0.0.0 --port 80 --log-level $LOG_LEVEL"]

slim

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FROM python:3.9-slim
MAINTAINER Rex Chow <879582094@qq.com>

ENV TZ='Asia/Shanghai'
ENV TIME_OUT=20
ENV LOG_LEVEL='info'
ENV CF_ZONE='example.com'

COPY app /app
WORKDIR /app
EXPOSE 80

RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --no-cache-dir -r /app/requirements.txt && \
rm -rf ~/.cache/pip
CMD ["sh", "-c", "uvicorn main:app --host 0.0.0.0 --port 80 --log-level $LOG_LEVEL"]

ubuntu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FROM python:3.9
MAINTAINER Rex Chow <879582094@qq.com>

ENV TZ='Asia/Shanghai'
ENV TIME_OUT=20
ENV LOG_LEVEL='info'
ENV CF_ZONE='example.com'

COPY app /app
WORKDIR /app
EXPOSE 80

RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --no-cache-dir -r /app/requirements.txt && \
rm -rf ~/.cache/pip
CMD ["sh", "-c", "uvicorn main:app --host 0.0.0.0 --port 80 --log-level $LOG_LEVEL"]

实际来看, 比对一下最终的构建情况

大小对比

大小对比

时间对比

ubuntu构建时间
slim构建时间
alpine构建时间

数据汇总

环境 原始镜像大小 构建后大小 镜像增长大小 构建时间
alpine 47.4MB 203MB 155.6MB 29m54s
slim 125MB 322MB 197MB 45s
ubuntu 915MB 1.11GB 221.64MB 42s

结论

用仅 120MB 的镜像大小区别,换取每次近 30min 的构建时间,这个是否划算呢?可能真的应了那句话:听人劝,吃饱饭~ Anyway,这次的坑算填上了,就当学习了


  1. 还在用Alpine作为你Docker的Python开发基础镜像?其实Ubuntu更好一点 - 知乎↩︎

  2. python - How to install matplotlib on Alpine - Stack Overflow↩︎

  3. docker - What is .build-deps for apk add --virtual command? - Stack Overflow↩︎

  4. python - Installing numpy on Docker Alpine - Stack Overflow↩︎

  5. 还在用Alpine作为你Docker的Python开发基础镜像?其实Ubuntu更好一点 - 知乎↩︎