Python 是一种广泛使用的高级编程语言,属于解释型语言。其设计追求代码的简洁和可读性,语法简单明了,支持多种编程范式,包括面向对象、过程化和函数式编程。Python 拥有强大的标准库和丰富的第三方模块,适用于数据分析、人工智能、网络开发等多个领域。此外,Python 跨平台兼容,能在多种操作系统上运行,是初学者和专业开发者的热门选择。
背景介绍
Python 作为解释型语言,其容器化部署具有以下特点:
- 依赖管理:Python 应用通常依赖众多第三方库,容器可确保环境一致性
- 跨平台兼容:解决不同系统版本和环境差异带来的问题
- 隔离性:应用与其依赖在容器中运行,避免与系统其他部分冲突
本文将遵循设计篇中的逻辑分层思想,采用标准化的构建方式:
- 工具环境:基于 Debian 构建 Python 开发环境,提供完整的 Python 解释器和开发工具
- 运行环境:创建精简的 Python 应用运行环境,使用非 root 用户提高安全性
- 应用镜像:将 Python 应用代码部署到运行环境中,确保最佳实践
由于 Python 是解释型语言,不同于 Go 的多阶段构建流程,我们的构建过程会有所不同,但同样会遵循镜像分层与职责分离的原则。
构建 Python 工具镜像
Python 工具环境负责提供完整的 Python 解释器和开发工具链,用于开发、测试和构建 Python 应用。
创建 Python 工具环境目录
首先创建 Python 工具镜像的目录:
mkdir -p common/tools/python
cd common/tools/python
Python 工具环境 Dockerfile 详解
#syntax=harbor.leops.local/library/docker/dockerfile:1
FROM harbor.leops.local/common/os/debian:bullseye
ARG PYTHON_VERSION=3.11.12
LABEL org.opencontainers.image.authors="ops@leops.local" \
org.opencontainers.image.source="http://git.leops.local/ops/dockerfiles-base/common/tools/openjdk/Dockerfile" \
org.opencontainers.image.description="python ${PYTHON_VERSION} compiler environment."
ENV LANG=C.UTF-8 \
PATH=/usr/local/bin:$PATH \
PYTHON_VERSION=$PYTHON_VERSION \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_TRUSTED_HOST=nexus.leops.local \
PIP_INDEX_URL=http://nexus.leops.local/repository/pypi-group/simple
# install dependencies
RUN set -eux \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential \
default-libmysqlclient-dev \
libreadline-dev \
libncursesw5-dev \
libssl-dev \
libsqlite3-dev \
libgdbm-dev \
libc6-dev \
libbz2-dev \
libffi-dev \
zlib1g-dev \
libbluetooth-dev \
tk-dev \
uuid-dev \
&& apt-get clean \
&& rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& truncate -s 0 /var/log/*log
# install python
RUN set -eux; \
\
wget -O python.tar.xz "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz"; \
mkdir -p /usr/src/python; \
tar --extract --directory /usr/src/python --strip-components=1 --file python.tar.xz; \
rm python.tar.xz; \
\
cd /usr/src/python; \
gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; \
./configure \
--build="$gnuArch" \
--enable-loadable-sqlite-extensions \
--enable-optimizations \
--enable-option-checking=fatal \
--enable-shared \
--with-lto \
--with-ensurepip \
; \
nproc="$(nproc)"; \
EXTRA_CFLAGS="$(dpkg-buildflags --get CFLAGS)"; \
LDFLAGS="$(dpkg-buildflags --get LDFLAGS)"; \
make -j "$nproc" \
"EXTRA_CFLAGS=${EXTRA_CFLAGS:-}" \
"LDFLAGS=${LDFLAGS:-}" \
; \
# https://github.com/docker-library/python/issues/784
# prevent accidental usage of a system installed libpython of the same version
rm python; \
make -j "$nproc" \
"EXTRA_CFLAGS=${EXTRA_CFLAGS:-}" \
"LDFLAGS=${LDFLAGS:--Wl},-rpath='\$\$ORIGIN/../lib'" \
python \
; \
make install; \
\
# enable GDB to load debugging data: https://github.com/docker-library/python/pull/701
bin="$(readlink -ve /usr/local/bin/python3)"; \
dir="$(dirname "$bin")"; \
mkdir -p "/usr/share/gdb/auto-load/$dir"; \
cp -vL Tools/gdb/libpython.py "/usr/share/gdb/auto-load/$bin-gdb.py"; \
\
cd /; \
rm -rf /usr/src/python; \
\
find /usr/local -depth \
\( \
\( -type d -a \( -name test -o -name tests -o -name idle_test \) \) \
-o \( -type f -a \( -name '*.pyc' -o -name '*.pyo' -o -name 'libpython*.a' \) \) \
\) -exec rm -rf '{}' + \
; \
\
ldconfig; \
\
export PYTHONDONTWRITEBYTECODE=1; \
python3 --version; \
\
pip3 install \
--disable-pip-version-check \
--no-cache-dir \
--no-compile \
'setuptools==65.5.1' \
# https://github.com/docker-library/python/issues/1023
'wheel<0.46' \
; \
pip3 --version; \
for src in idle3 pip3 pydoc3 python3 python3-config; do \
dst="$(echo "$src" | tr -d 3)"; \
[ -s "/usr/local/bin/$src" ]; \
[ ! -e "/usr/local/bin/$dst" ]; \
ln -svT "$src""/usr/local/bin/$dst"; \
done
CMD ["python3"]
Dockerfile 关键点解析
这个 Python 工具环境 Dockerfile 具有以下重要特点:
- 基于 Debian:使用标准化的 Debian 基础镜像,确保一致的基础环境
- 动态版本:通过 ARG PYTHON_VERSION 参数化 Python 版本,方便更新和维护
- 环境配置:设置了 Python 开发所需的环境变量:
- LANG=C.UTF-8:确保 Unicode 文本处理的一致性
- PYTHONDONTWRITEBYTECODE=1:避免生成 .pyc 文件
- PYTHONUNBUFFERED=1:防止 Python 输出缓冲,确保日志实时输出
- PIP_TRUSTED_HOST 和 PIP_INDEX_URL:配置内部 PyPI 镜像源
- 依赖安装:包含编译 Python 需要的系统库和开发工具
- 源码编译:从官方源码编译 Python,而非使用发行版打包的版本,确保版本精确控制
- 优化选项:编译时启用了优化和共享库支持
- 清理步骤:删除测试文件、编译缓存和临时文件,减小镜像体积
- 符号链接:创建无版本号的符号链接,提高使用便利性
镜像构建脚本
使用以下脚本 (build.sh) 来构建和推送 Python 工具镜像:
#!/bin/bash
set -e
# 配置
REGISTRY="harbor.leops.local"
IMAGE_BASE_NAME="common/tools/python"
VERSION="3.11.12"
# 声明镜像地址数组
declare -a IMAGE_PATHS
IMAGE_PATHS+=(
"${REGISTRY}/${IMAGE_BASE_NAME}:${VERSION}"
"${REGISTRY}/${IMAGE_BASE_NAME}:${VERSION%.*}"
"${REGISTRY}/${IMAGE_BASE_NAME}:${VERSION%%.*}"
"${REGISTRY}/${IMAGE_BASE_NAME}:${VERSION}-debian11"
"${REGISTRY}/${IMAGE_BASE_NAME}:${VERSION%.*}-debian11"
"${REGISTRY}/${IMAGE_BASE_NAME}:${VERSION%%.*}-debian11"
)
build_image() {
echo "Building and pushing image:"
for img in "${IMAGE_PATHS[@]}"; do echo -e " $img"; done
# 构建镜像
docker buildx build \
$(for img in "${IMAGE_PATHS[@]}"; do echo -n "-t $img "; done) \
--label "org.opencontainers.image.created=$(date --rfc-3339=seconds)" \
--build-arg "PYTHON_VERSION=${VERSION}" \
--add-host nexus.leops.local=192.168.77.140 \
--provenance=false \
--pull \
--push \
.
echo "Build complete."
}
# 参数处理
case "$1" in
"list-tags")
# 输出镜像标签列表
printf '%s\n'"${IMAGE_PATHS[@]}"
;;
*)
build_image
;;
esac
构建脚本解析
构建脚本具有以下特点:
- 版本灵活性:生成多个标签版本,包括完整版本号、主次版本号以及系统标识组合
- 构建优化:使用 docker buildx 命令,支持更高级的构建功能
- 网络配置:通过 --add-host 添加内部域名解析,确保能访问私有 PyPI 仓库
- 易用性设计:支持列出标签功能,方便其他工具引用镜像标签
构建 Python 应用的运行镜像
Python 工具环境用于开发和构建,而运行镜像则专注于高效安全地运行 Python 应用。对于 Python 这种解释型语言,运行环境通常需要包含解释器本身。
创建 Python 运行环境目录
mkdir -p common/runtime/python
cd common/runtime/python
Python 运行环境 Dockerfile
#syntax=harbor.leops.local/library/docker/dockerfile:1
ARG PYTHON_VERSION=3.11
FROM harbor.leops.local/common/tools/python:${PYTHON_VERSION}
ARG PYTHON_VERSION=3.11
LABEL org.opencontainers.image.authors="ops@leops.local" \
org.opencontainers.image.source="http://git.leops.local/ops/dockerfiles-base/common/runtime/python/Dockerfile" \
org.opencontainers.image.description="Minimal base runtime for python applications with non-root user."
RUN \
groupadd -r nonroot \
&& useradd -r -m -g nonroot nonroot \
&& mkdir -p /app/logs \
&& chown nonroot:nonroot -R /app
USER nonroot:nonroot
运行镜像关键点解析
这个 Python 运行环境镜像有以下特点:
- 基于工具环境:与 Go 不同,Python 运行时需要完整的解释器,所以直接基于工具镜像构建
- 非 root 用户:创建专用的 nonroot 用户,提高应用运行安全性
- 标准目录结构:预先创建 /app 和 /app/logs 目录,并设置正确的所有权
- 权限限制:通过 USER 指令切换到非特权用户,防止容器内进程获取过高权限
- 版本参数化:使用 ARG 指令参数化 Python 版本,保持灵活性
运行镜像构建脚本
使用以下脚本(build.sh)构建运行环境镜像:
#!/bin/bash
set -e
# 配置
REGISTRY="harbor.leops.local"
IMAGE_BASE_NAME="common/runtime/python"
VERSION="3.11"
# 声明镜像地址数组
declare -a IMAGE_PATHS
IMAGE_PATHS+=(
"${REGISTRY}/${IMAGE_BASE_NAME}:${VERSION}"
"${REGISTRY}/${IMAGE_BASE_NAME}:${VERSION%.*}"
)
build_image() {
echo "Building and pushing image:"
for img in "${IMAGE_PATHS[@]}"; do echo -e " $img"; done
# 构建镜像
docker buildx build \
$(for img in "${IMAGE_PATHS[@]}"; do echo -n "-t $img "; done) \
--label "org.opencontainers.image.created=$(date --rfc-3339=seconds)" \
--build-arg "PYTHON_VERSION=${VERSION}" \
--provenance=false \
--pull \
--push \
.
echo "Build complete."
}
# 参数处理
case "$1" in
"list-tags")
# 输出镜像标签列表
printf '%s\n'"${IMAGE_PATHS[@]}"
;;
*)
build_image
;;
esac
构建应用镜像
与 Go 应用不同,Python 是解释型语言,不需要编译步骤。但我们仍然需要一个优化的构建过程来创建生产级别的应用镜像。
准备示例应用
首先,我们获取一个简单的 Django 应用示例:
git clone https://github.com/lework/ci-demo-django.git
cd ci-demo-django
应用 Dockerfile 详解
下面是一个 Python Django 应用的 Dockerfile:
#syntax=harbor.leops.local/library/docker/dockerfile:1
# ---- 运行环境 ----
FROM harbor.leops.local/common/runtime/python:3.11 AS running
ARG APP_ENV=test \
APP=undefine \
GIT_BRANCH= \
GIT_COMMIT_ID=
ENV APP_ENV=$APP_ENV \
APP=$APP \
GIT_BRANCH=$GIT_BRANCH \
GIT_COMMIT_ID=$GIT_COMMIT_ID \
PATH=$PATH:/home/nonroot/.local/bin/
WORKDIR /app
#USER root
# 下载依赖
COPY requirements.txt .
RUN --mount=type=cache,id=pip,target=/home/nonroot/.cache,uid=999,gid=999 \
pip install -r requirements.txt
#USER nonroot
# 拷贝代码
COPY --chown=nonroot:nonroot . .
CMD ["bash", "-c", "python manage.py flush --no-input && python manage.py migrate && gunicorn demo.wsgi:application --bind 0.0.0.0:${SERVER_PORT:-8080}"]
Dockerfile 关键点解析
这个 Django 应用 Dockerfile 有以下特点:
- 基于运行环境:使用之前构建的 Python 运行环境作为基础镜像
- 环境变量配置:设置应用环境、名称以及 Git 信息等变量,便于运行时识别
- 路径配置:添加 /home/nonroot/.local/bin/ 到 PATH,确保用户安装的包可以正常访问
- 分层安装依赖:
- 先复制 requirements.txt 文件
- 然后安装依赖,利用 BuildKit 缓存机制加速重复构建
- 这样即使应用代码改变,依赖层也可以复用缓存
- 文件权限:使用 --chown 确保复制的文件归 nonroot 用户所有
- 启动命令:使用 Django 初始化数据库并通过 gunicorn 启动应用,支持环境变量配置端口
构建应用镜像
执行以下命令构建示例应用:
bash /data/dockerfiles-base/app-build/build-app.sh dev ci-demo-django
构建完成后,会生成如下格式的镜像标签:
harbor.leops.local/test/ci-demo-django:master-6c225c9-202504282313
版本控制
完成构建后,将所有文件提交到 Git 仓库进行版本控制:
git add -A .
git commit -m "feat: add python"
git push
运行 Python 应用容器
最终,我们可以运行构建好的 Python 应用容器,并验证其功能。
容器运行与验证
# 运行容器
docker run --rm -d --name ci-demo-django -p 18083:8080 harbor.leops.local/test/ci-demo-django:master-6c225c9-202504282313
# 访问应用
curl http://localhost:18083/
# 查看日志
docker logs ci-demo-django
# 停止容器
docker stop ci-demo-django
生产环境最佳实践
在生产环境中部署 Python 应用容器时,可以考虑以下最佳实践:
- 资源限制:使用 --memory 和 --cpus 设置容器资源上限,防止单个容器消耗过多资源
- 健康检查:配置健康检查端点和机制,确保应用正常运行
- 环境变量管理:使用环境变量或配置文件注入配置,避免硬编码
- 日志处理:将日志输出到 stdout/stderr,便于容器编排系统收集
- 依赖锁定:使用 pip freeze 或 Poetry 等工具生成精确的依赖版本列表
- 安全扫描:定期扫描容器镜像中的安全漏洞
- 多环境配置:使用环境变量区分不同环境的配置
总结
通过本文,我们详细介绍了 Python 应用的容器化构建过程,包括:
- 工具环境构建:创建完整的 Python 开发环境
- 运行环境构建:创建安全、高效的 Python 应用运行环境
- 应用镜像构建:优化的 Dockerfile 设计,确保镜像分层合理、构建高效
- 容器运行与验证:部署并测试应用容器
与编译型语言不同,Python 应用的容器化更加关注依赖管理、环境一致性和运行时配置。通过合理的镜像分层设计,我们既确保了开发和运行环境的隔离,又保证了应用部署的一致性和高效性。
这套构建流程适用于各种 Python 应用,包括 Web 应用、数据处理应用、机器学习应用等,为企业级 Python 应用的容器化部署提供了标准化的解决方案。