关于python:制作-Python-Docker-镜像的最佳实践

51次阅读

共计 5641 个字符,预计需要花费 15 分钟才能阅读完成。

概述

📚️Reference:

制作容器镜像的最佳实际

这篇文章是对于制作 Python Docker 容器镜像的最佳实际。(2022 年 12 月更新)
最佳实际的目标一方面是为了减小镜像体积,晋升 DevOps 效率,另一方面是为了进步安全性。心愿对各位有所帮忙。

通用 Docker 容器镜像最佳实际

这里也再次列举一下对 Python Docker 镜像也实用的一些通用最佳实际。

  • 应用 LABEL maintainer
  • 标记重要端口
  • 设置环境变量
  • 应用非 root 用户运行容器过程
  • 应用 .dockerignore 排除无关文件

Python 镜像举荐设置的环境变量

Python 中举荐的常见环境变量如下:

# 设置环境变量
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
  1. ENV PYTHONDONTWRITEBYTECODE 1: 倡议构建 Docker 镜像时始终为 1, 避免 python 将 pyc 文件写入硬盘
  2. ENV PYTHONUNBUFFERED 1: 倡议构建 Docker 镜像时始终为 1, 避免 python 缓冲 (buffering) stdout 和 stderr, 以便更容易地进行容器日志记录
  3. ❌不再倡议应用 ENV DEBUG 0 环境变量,没必要。

应用非 root 用户运行容器过程

出于平安思考,举荐运行 Python 程序前,创立 非 root 用户并切换到该用户。

# 创立一个具备明确 UID 的非 root 用户,并减少拜访 /app 文件夹的权限。RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app
USER appuser

应用 .dockerignore 排除无关文件

须要排除的无关文件个别如下:

**/__pycache__
**/*venv
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
*.db
.python-version
LICENSE
README.md

这里抉择几个阐明下:

  1. **/__pycache__: python 缓存目录
  2. **/*venv: Python 虚拟环境目录。很多 Python 开发习惯将虚拟环境目录创立在我的项目下,个别命名为:.venvvenv
  3. **/.env: Python 环境变量文件
  4. **/.git **/.gitignore: git 相干目录和文件
  5. **/.vscode: 编辑器、IDE 相干目录
  6. **/charts: Helm Chart 相干文件
  7. **/docker-compose*: docker compose 相干文件
  8. *.db: 如果应用 sqllite 的相干数据库文件
  9. .python-version: pyenv 的 .python-version 文件

不倡议应用 Alpine 作为 Python 的根底镜像

为什么呢?大多数 Linux 发行版应用 GNU 版本(glibc)的规范 C 库,简直每个 C 程序都须要这个库,包含 Python。然而 Alpine Linux 应用 musl, Alpine 禁用了 Linux wheel 反对。

理由如下:

  • 短少大量依赖

    • CPython 语言运行时的相干依赖
    • openssl 相干依赖
    • libffi 相干依赖
    • gcc 相干依赖
    • 数据库驱动相干依赖
    • pip 相干依赖
  • 构建可能更耗时

    • Alpine Linux 应用 musl,一些二进制 wheel 是针对 glibc 编译的,然而 Alpine 禁用了 Linux wheel 反对。当初大多数 Python 包都包含 PyPI 上的二进制 wheel,大大放慢了安装时间。然而如果你应用 Alpine Linux,你可能须要编译你应用的每个 Python 包中的所有 C 代码。
  • 基于 Alpine 构建的 Python 镜像反而可能更大

    • 乍一听仿佛违反常识,然而认真一想,因为下面列举的起因,的确会导致镜像更大的状况。

📚️Reference:

Using Alpine can make Python Docker builds 50× slower (pythonspeed.com)

这里以这个 Demo FastAPI Python 程序 为例,其基于 Alpine 的 Dockerfile 地址是这个:https://github.com/east4ming/…

因为短少很多依赖,所以在用 pip 装置之前,就须要尽可能全地装置相干依赖:

RUN set -eux \
    && apk add --no-cache --virtual .build-deps build-base \
    openssl-dev libffi-dev gcc musl-dev python3-dev \
    && pip install --upgrade pip setuptools wheel \
    && pip install --upgrade -r /app/requirements.txt \
    && rm -rf /root/.cache/pip

这里也展现一下基于 Alpine 构建实现后的 镜像未压缩大小:

△ 基于 Alpine 的 Python Demo 镜像大小:472 MB; 相比之下,基于 slim 的只有 189 MB

在下面代码的这一步,就占用了太多空间:

🤔 思考 :

可能下面一段能够精简,然而要判断对于哪个 Python 我的项目,能够精简哪些包,切实是太难了。

+ apk add --no-cache --virtual .build-deps build-base openssl-dev libffi-dev gcc musl-dev python3-dev
fetch https://dl-cdn.alpinelinux.org/alpine/v3.17/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.17/community/x86_64/APKINDEX.tar.gz
(1/28) Installing libgcc (12.2.1_git20220924-r4)
(2/28) Installing libstdc++ (12.2.1_git20220924-r4)
(3/28) Installing binutils (2.39-r2)
(4/28) Installing libmagic (5.43-r0)
(5/28) Installing file (5.43-r0)
(6/28) Installing libgomp (12.2.1_git20220924-r4)
(7/28) Installing libatomic (12.2.1_git20220924-r4)
(8/28) Installing gmp (6.2.1-r2)
(9/28) Installing isl25 (0.25-r0)
(10/28) Installing mpfr4 (4.1.0-r0)
(11/28) Installing mpc1 (1.2.1-r1)
(12/28) Installing gcc (12.2.1_git20220924-r4)
(13/28) Installing libstdc++-dev (12.2.1_git20220924-r4)
(14/28) Installing musl-dev (1.2.3-r4)
(15/28) Installing libc-dev (0.7.2-r3)
(16/28) Installing g++ (12.2.1_git20220924-r4)
(17/28) Installing make (4.3-r1)
(18/28) Installing fortify-headers (1.1-r1)
(19/28) Installing patch (2.7.6-r8)
(20/28) Installing build-base (0.5-r3)
(21/28) Installing pkgconf (1.9.3-r0)
(22/28) Installing openssl-dev (3.0.7-r0)
(23/28) Installing linux-headers (5.19.5-r0)
(24/28) Installing libffi-dev (3.4.4-r0)
(25/28) Installing mpdecimal (2.5.1-r1)
(26/28) Installing python3 (3.10.9-r1)
(27/28) Installing python3-dev (3.10.9-r1)
(28/28) Installing .build-deps (20221214.074929)
Executing busybox-1.35.0-r29.trigger
OK: 358 MiB in 65 packages
...

倡议应用官网的 python slim 镜像作为根底镜像

持续下面,所以我是倡议:应用官网的 python slim 镜像作为根底镜像

镜像库是这个:https://hub.docker.com/_/python

并且应用 python:<version>-slim 作为根底镜像,能用 python:<version>-slim-bullseye 作为根底镜像更好(因为更新,绝对就更平安一些).

这个镜像不蕴含默认标签中的罕用包,只蕴含运行 python 所需的最小包。这个镜像是基于 Debian 的。

应用官网 python slim 的理由还包含:

  • 稳定性
  • 平安降级更及时
  • 依赖更新更及时
  • 依赖更全
  • Python 版本升级更及时
  • 镜像更小

📚️Reference:

The best Docker base image for your Python application (Sep 2022) (pythonspeed.com)

个别状况下,Python 镜像构建不须要应用 ” 多阶段构建 ”

个别状况下,Python 镜像构建不须要应用 ” 多阶段构建 ”.

理由如下:

  • Python 没有像 Golang 一样,能够把所有依赖打成一个繁多的二进制包
  • Python 也没有像 Java 一样,能够在 JDK 上构建,在 JRE 上运行
  • Python 简单而散落的依赖关系,在 ” 多阶段构建 ” 时会减少复杂度

如果有一些非凡状况,能够尝试应用 ” 多阶段构建 ” 压缩镜像体积:

  • 构建阶段须要装置编译器
  • Python 我的项目简单,用到了其余语言代码(如 C/C++/Rust)

pip 小技巧

应用 pip 装置依赖时,能够增加 --no-cache-dir 缩小镜像体积:

# 装置 pip 依赖
COPY requirements.txt .
RUN python -m pip install --no-cache-dir --upgrade -r requirements.txt

Python Dockerfile 最佳实际样例

最初, 就是基于以上最佳实际的残缺样例, 也能够在这里找到: https://github.com/east4ming/…

FROM python:3.10-slim

LABEL maintainer="cuikaidong@foxmail.com"

EXPOSE 8000

# Keeps Python from generating .pyc files in the container
ENV PYTHONDONTWRITEBYTECODE=1

# Turns off buffering for easier container logging
ENV PYTHONUNBUFFERED=1

# Install pip requirements
COPY requirements.txt .
RUN python -m pip install --no-cache-dir --upgrade -r requirements.txt

WORKDIR /app
COPY . /app

# Creates a non-root user with an explicit UID and adds permission to access the /app folder
RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app
USER appuser

CMD ["uvicorn", "shortener_app.main:app", "--host", "0.0.0.0"]

总结

制作 Python Docker 容器镜像的最佳实际。最佳实际的目标一方面是为了减小镜像体积,晋升 DevOps 效率,另一方面是为了进步安全性.

最佳实际如下:

  • 举荐 2 个 Python 的环境变量

    • ENV PYTHONDONTWRITEBYTECODE 1
    • ENV PYTHONUNBUFFERED 1
  • 应用非 root 用户运行容器过程
  • 应用 .dockerignore 排除无关文件
  • 不倡议应用 Alpine 作为 Python 的根底镜像
  • 倡议应用官网的 python slim 镜像作为根底镜像
  • 个别状况下, Python 镜像构建不须要应用 ” 多阶段构建 ”
  • pip 小技巧: --no-cache-dir

心愿对大家有所帮忙.

最初也感叹一下, 在云原生时代, python 在散发这块, 特地是镜像构建这块, 的确体验、效率、镜像大小等方面差 golang 太多了。😭😭😭

📚️参考文档

  • Using Alpine can make Python Docker builds 50× slower (pythonspeed.com)
  • The best Docker base image for your Python application (Sep 2022) (pythonspeed.com)
  • Multi-stage builds #2: Python specifics (pythonspeed.com)
  • 制作容器镜像的最佳实际 – 东风微鸣技术博客 (ewhisper.cn)

正文完
 0