实践篇:13-构建 Python 应用程序镜像

Python 是一种广泛使用的高级编程语言,属于解释型语言。其设计追求代码的简洁和可读性,语法简单明了,支持多种编程范式,包括面向对象、过程化和函数式编程。Python 拥有强大的标准库和丰富的第三方模块,适用于数据分析、人工智能、网络开发等多个领域。此外,Python 跨平台兼容,能在多种操作系统上运行,是初学者和专业开发者的热门选择。

背景介绍
Python 作为解释型语言,其容器化部署具有以下特点:

  • 依赖管理:Python 应用通常依赖众多第三方库,容器可确保环境一致性
  • 跨平台兼容:解决不同系统版本和环境差异带来的问题
  • 隔离性:应用与其依赖在容器中运行,避免与系统其他部分冲突

本文将遵循设计篇中的逻辑分层思想,采用标准化的构建方式:

  1. 工具环境:基于 Debian 构建 Python 开发环境,提供完整的 Python 解释器和开发工具
  2. 运行环境:创建精简的 Python 应用运行环境,使用非 root 用户提高安全性
  3. 应用镜像:将 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 具有以下重要特点:

  1. 基于 Debian:使用标准化的 Debian 基础镜像,确保一致的基础环境
  2. 动态版本:通过 ARG PYTHON_VERSION 参数化 Python 版本,方便更新和维护
  3. 环境配置:设置了 Python 开发所需的环境变量:
    • LANG=C.UTF-8:确保 Unicode 文本处理的一致性
    • PYTHONDONTWRITEBYTECODE=1:避免生成 .pyc 文件
    • PYTHONUNBUFFERED=1:防止 Python 输出缓冲,确保日志实时输出
    • PIP_TRUSTED_HOST 和 PIP_INDEX_URL:配置内部 PyPI 镜像源
  4. 依赖安装:包含编译 Python 需要的系统库和开发工具
  5. 源码编译:从官方源码编译 Python,而非使用发行版打包的版本,确保版本精确控制
  6. 优化选项:编译时启用了优化和共享库支持
  7. 清理步骤:删除测试文件、编译缓存和临时文件,减小镜像体积
  8. 符号链接:创建无版本号的符号链接,提高使用便利性

镜像构建脚本

使用以下脚本 (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

构建脚本解析

构建脚本具有以下特点:

  1. 版本灵活性:生成多个标签版本,包括完整版本号、主次版本号以及系统标识组合
  2. 构建优化:使用 docker buildx 命令,支持更高级的构建功能
  3. 网络配置:通过 --add-host 添加内部域名解析,确保能访问私有 PyPI 仓库
  4. 易用性设计:支持列出标签功能,方便其他工具引用镜像标签

构建 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 运行环境镜像有以下特点:

  1. 基于工具环境:与 Go 不同,Python 运行时需要完整的解释器,所以直接基于工具镜像构建
  2. 非 root 用户:创建专用的 nonroot 用户,提高应用运行安全性
  3. 标准目录结构:预先创建 /app 和 /app/logs 目录,并设置正确的所有权
  4. 权限限制:通过 USER 指令切换到非特权用户,防止容器内进程获取过高权限
  5. 版本参数化:使用 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 有以下特点:

  1. 基于运行环境:使用之前构建的 Python 运行环境作为基础镜像
  2. 环境变量配置:设置应用环境、名称以及 Git 信息等变量,便于运行时识别
  3. 路径配置:添加 /home/nonroot/.local/bin/ 到 PATH,确保用户安装的包可以正常访问
  4. 分层安装依赖:
    • 先复制 requirements.txt 文件
    • 然后安装依赖,利用 BuildKit 缓存机制加速重复构建
    • 这样即使应用代码改变,依赖层也可以复用缓存
  5. 文件权限:使用 --chown 确保复制的文件归 nonroot 用户所有
  6. 启动命令:使用 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 应用容器时,可以考虑以下最佳实践:

  1. 资源限制:使用 --memory 和 --cpus 设置容器资源上限,防止单个容器消耗过多资源
  2. 健康检查:配置健康检查端点和机制,确保应用正常运行
  3. 环境变量管理:使用环境变量或配置文件注入配置,避免硬编码
  4. 日志处理:将日志输出到 stdout/stderr,便于容器编排系统收集
  5. 依赖锁定:使用 pip freeze 或 Poetry 等工具生成精确的依赖版本列表
  6. 安全扫描:定期扫描容器镜像中的安全漏洞
  7. 多环境配置:使用环境变量区分不同环境的配置

总结

通过本文,我们详细介绍了 Python 应用的容器化构建过程,包括:

  1. 工具环境构建:创建完整的 Python 开发环境
  2. 运行环境构建:创建安全、高效的 Python 应用运行环境
  3. 应用镜像构建:优化的 Dockerfile 设计,确保镜像分层合理、构建高效
  4. 容器运行与验证:部署并测试应用容器

与编译型语言不同,Python 应用的容器化更加关注依赖管理、环境一致性和运行时配置。通过合理的镜像分层设计,我们既确保了开发和运行环境的隔离,又保证了应用部署的一致性和高效性。

这套构建流程适用于各种 Python 应用,包括 Web 应用、数据处理应用、机器学习应用等,为企业级 Python 应用的容器化部署提供了标准化的解决方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

企鹅侠客

您的打赏是我创作旅程中的关键燃

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值