This commit is contained in:
2026-09-12 02:14:40 +08:00
parent 23075b5679
commit e390fabc24
2 changed files with 291 additions and 137 deletions
+91 -137
View File
@@ -1,200 +1,154 @@
# ============================================================
# Dockerfile:多阶段构建 Python 应用镜像
# Dockerfile:单阶段构建 Python 应用镜像(RockyLinux 8 + Python 3.12.8)
#
# 构建思路:
# - builder 阶段:安装编译工具链,编译并安装 Python 依赖
# - runtime 阶段:只保留运行期必需内容,丢弃编译器
# 设计思路:
# - 单阶段构建,所有依赖一次装齐,避免多阶段"包找不到/库缺失"问题
# - 编译工具链和运行库都保留在最终镜像里,换取构建稳定性和排错简单
# - 通过 --build-arg 动态注入变量,便于 CI 复用同一份 Dockerfile
#
# 通过 --build-arg 动态注入以下变量,便于在 CI 中复用同一份 Dockerfile:
# VAR_BASE_IMAGE 基础镜像(如 python:3.12-slim、自定义的带 esxi 用户的镜像)
# VAR_PYTHON_PREFIX Python 安装前缀(依赖安装位置)
# VAR_PYFILE_PATH 项目源码目录(相对构建上下文)
# VAR_PROFILES 应用 profile(dev / prod ...)
# 构建参数说明:
# VAR_BASE_IMAGE 基础镜像(RockyLinux 8 + Python 3.12.8 + esxi 用户)
# VAR_PROFILES 应用 profile(dev / prod)
# VAR_PORT 应用监听端口
# VAR_PYFILE 启动入口文件名(不含 .py)
# VAR_PYFILE 启动入口文件名(不含 .py),如 manage
# VAR_PYFILE_PATH 项目源码目录(相对构建上下文)
# ============================================================
# ------------------------------------------------------------
# 声明构建参数
# 基础镜像用 ARG 而不是直接 FROM,方便 CI 动态切换
# Python 前缀单独抽出,避免在 COPY / pip --prefix 里重复硬编码
# ------------------------------------------------------------
ARG VAR_BASE_IMAGE
ARG VAR_PYTHON_PREFIX=/usr/local/python3.12.8
# ============================================================
# 阶段一:builder —— 编译并安装 Python 依赖
# ============================================================
FROM ${VAR_BASE_IMAGE} AS builder
# 基础镜像末尾可能设置了非 root 用户(如 esxi),
# 这里切回 root,否则 yum install 会因权限不足失败
USER root
# 镜像作者信息(仅元数据)
LABEL MAINTAINER="djangoadmin@126.com"
# ------------------------------------------------------------
# 编译阶段环境变量
# PYTHONUNBUFFERED=1 Python 输出不缓冲,日志实时可见
# PYTHONDONTWRITEBYTECODE=1 不生成 .pyc,避免污染构建产物
# PIP_NO_CACHE_DIR=1 pip 不缓存 wheel,减小体积
# PIP_DISABLE_PIP_VERSION_CHECK=1 关闭 pip 版本检查,加快安装
# 基础镜像
# ------------------------------------------------------------
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
# ------------------------------------------------------------
# 安装编译工具链与开发头文件
# yum-utils / net-tools 基础系统工具
# gcc / gcc-c++ C/C++ 编译器(编译 C 扩展必需)
# make / cmake 构建工具
# openssl-devel 编译 _ssl / cryptography
# bzip2-devel 编译 _bz2
# libffi-devel 编译 _ctypes
# mysql-devel 编译 mysqlclient 等 MySQL 驱动
# 末尾清理 yum 缓存,避免缓存进入镜像层
# ------------------------------------------------------------
RUN yum install -y yum-utils net-tools && \
yum install -y gcc gcc-c++ make cmake \
openssl-devel bzip2-devel libffi-devel \
mariadb-devel pkgconf-pkg-config && \
yum clean all && rm -rf /var/cache/yum
#RUN --mount=type=cache,target=/var/cache/dnf,sharing=locked,id=dnf-cache \
# dnf install -y yum-utils net-tools && \
# dnf install -y gcc gcc-c++ make cmake \
# openssl-devel bzip2-devel libffi-devel \
# mariadb-devel pkgconf-pkg-config
# ------------------------------------------------------------
# 配置 pip 国内镜像并升级 pip / setuptools / wheel
# 老版本的 pip 解析依赖容易出错,先升级到最新
# ------------------------------------------------------------
RUN pip3 config set global.index-url http://mirrors.aliyun.com/pypi/simple && \
pip3 config set install.trusted-host mirrors.aliyun.com && \
python3 -m pip install --upgrade pip setuptools wheel
# ------------------------------------------------------------
# 拷贝依赖清单
# 只拷贝 requirements.txt 而不是整个项目,
# 这样源码变化但 requirements 未变时可以复用 pip install 层缓存
# ------------------------------------------------------------
ARG VAR_PYFILE_PATH
WORKDIR /opt/apps
COPY ${VAR_PYFILE_PATH}/requirements.txt ./
# ------------------------------------------------------------
# 将依赖安装到 Python 的 prefix 下
# --prefix=${VAR_PYTHON_PREFIX} 与 runtime 阶段的拷贝路径保持一致
# python3 -m pip 确保使用同一个解释器
# uwsgi 生产环境 WSGI 服务器,单独安装
# 注意:末尾不要多写反斜杠,否则会把下一行注释拼进来导致语法错误
# ------------------------------------------------------------
RUN python3 -m pip install --prefix=${VAR_PYTHON_PREFIX} -r requirements.txt && \
python3 -m pip install --prefix=${VAR_PYTHON_PREFIX} uwsgi
# ============================================================
# 阶段二:runtime —— 只保留运行时内容
# ============================================================
FROM ${VAR_BASE_IMAGE} AS runtime
# 切回 root 以便 yum install / chown
USER root
FROM ${VAR_BASE_IMAGE}
# 镜像作者信息
LABEL MAINTAINER="djangoadmin@126.com"
LABEL MAINTAINER="djangoadmin@163.com"
# ------------------------------------------------------------
# 运行期环境变量
# PYTHONUNBUFFERED=1 日志实时输出,便于 docker logs 观察
# PYTHONDONTWRITEBYTECODE=1 不写 .pyc,避免容器内产生垃圾文件
# 切回 root
# 基础镜像末尾通常设置了非 root 用户(如 esxi),
# 不切回 root,后面的 dnf install / mkdir / chown 都会因权限不足失败
# ------------------------------------------------------------
USER root
# ------------------------------------------------------------
# Python 运行期环境变量
# PYTHONUNBUFFERED=1 Python 输出不缓冲,日志实时可见
# PYTHONDONTWRITEBYTECODE=1 不生成 .pyc,避免容器内产生垃圾文件
# ------------------------------------------------------------
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
# ------------------------------------------------------------
# 运行期构建参数
# VAR_PYTHON_PREFIX Python 前缀(必须与 builder 保持一致)
# VAR_PROFILES 配置 profile(dev / prod)
# VAR_PORT 应用监听端口
# VAR_PYFILE 启动入口文件名(不含 .py)
# VAR_PYFILE_PATH 项目源码目录
# 声明运行期构建参数
# ------------------------------------------------------------
ARG VAR_PYTHON_PREFIX=/usr/local/python3.12.8
ARG VAR_PROFILES
ARG VAR_PORT
ARG VAR_PYFILE
ARG VAR_PYFILE_PATH
# ------------------------------------------------------------
# 将 ARG 转成 ENV,运行期可通过环境变量读取
# PORT 供 EXPOSE 与 CMD 使用
# PYFILE 入口文件名
# PROFILES 应用读取的 profile
# 将 ARG 转为 ENV,运行期可通过环境变量读取
# ------------------------------------------------------------
ENV PORT=${VAR_PORT} \
PYFILE=${VAR_PYFILE} \
PROFILES=${VAR_PROFILES}
# ------------------------------------------------------------
# 安装运行期依赖
# 只装运行库,不装 -devel 头文件,可显著减小镜像体积:
# net-tools netstat 等网络排查工具(如不需要可去掉)
# openssl-libs SSL 运行库(替代 openssl-devel)
# bzip2-libs bz2 运行库(替代 bzip2-devel)
# libffi ctypes 运行库(替代 libffi-devel)
# mysql-libs MySQL 客户端运行库(替代 mysql-devel)
# 注意:若应用运行期确实需要头文件(如运行期编译),再改回 -devel
# 安装系统依赖(dnf 源换成国内镜像)
#
# RockyLinux 8 官方源可用,但换阿里云镜像更快:
# baseurl=https://mirrors.aliyun.com/rockylinux
#
# 编译期工具:
# gcc / gcc-c++ C/C++ 编译器
# make / cmake 构建工具
# openssl-devel 编译 _ssl / cryptography
# bzip2-devel 编译 _bz2
# libffi-devel 编译 _ctypes
# mariadb-devel 编译 mysqlclient(提供 .pc 文件)
# pkgconf-pkg-config 提供 pkg-config 命令(mysqlclient 2.2.x 必需)
#
# 运行期库:
# net-tools netstat 等网络排查工具
# openssl-libs SSL 运行库
# bzip2-libs bz2 运行库
# libffi ctypes 运行库
# mariadb-connector-c MySQL/MariaDB 客户端运行库
#
# 注意:
# 1) RockyLinux 8 里 MySQL 实际由 MariaDB 提供,
# 运行库包名为 mariadb-connector-c(不是 mysql-libs)
# 2) mariadb-devel 和 mysql-devel 不要同时安装,会冲突
# 3) 末尾清理 dnf 缓存,避免缓存进入镜像层
# ------------------------------------------------------------
RUN yum install -y net-tools openssl-libs bzip2-libs libffi mysql-libs && \
yum clean all && rm -rf /var/cache/yum
RUN set -eux; \
# ---- 把 RockyLinux 8 官方源替换为阿里云镜像 ----
sed -e 's|^mirrorlist=|#mirrorlist=|g' \
-e 's|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.aliyun.com/rockylinux|g' \
-i.bak /etc/yum.repos.d/*.repo; \
# ---- 安装编译工具链 + 运行库 ----
dnf install -y --setopt=install_weak_deps=False \
yum-utils net-tools \
gcc gcc-c++ make cmake \
openssl-devel bzip2-devel libffi-devel \
mariadb-devel pkgconf-pkg-config \
openssl-libs bzip2-libs libffi mariadb-connector-c; \
# ---- 清理 dnf 缓存,减小镜像体积 ----
dnf clean all; rm -rf /var/cache/dnf
# ------------------------------------------------------------
# 从 builder 拷贝已编译安装好的 Python 及依赖
# 源与目标都使用 ${VAR_PYTHON_PREFIX},保证 runtime 的 Python
# 能在自己的 site-packages 里找到 Django 等包
# 配置 pip 国内镜像源(阿里云 PyPI)
# ------------------------------------------------------------
COPY --from=builder ${VAR_PYTHON_PREFIX} ${VAR_PYTHON_PREFIX}
RUN pip3 config set global.index-url http://mirrors.aliyun.com/pypi/simple && \
pip3 config set install.trusted-host mirrors.aliyun.com
# ------------------------------------------------------------
# 工作区
# 升级 pip / setuptools / wheel
# --no-cache-dir 避免 pip 缓存进入镜像层
# ------------------------------------------------------------
RUN python3 -m pip install --upgrade --no-cache-dir pip setuptools wheel
# ------------------------------------------------------------
# 创建工作区
# ------------------------------------------------------------
RUN mkdir -p /opt/apps
WORKDIR /opt/apps
# ------------------------------------------------------------
# 拷贝项目源码
# 建议配合 .dockerignore 排除 .git、__pycache__、本地虚拟环境等
# ------------------------------------------------------------
COPY ${VAR_PYFILE_PATH}/ ./
# ------------------------------------------------------------
# 一次性修正属主:
# /opt/apps 保证 esxi 可读写源码
# ${VAR_PYTHON_PREFIX} 保证 esxi 可访问 Python 及其 site-packages
# 合并成一条 RUN,减少镜像层
# 安装 Python 依赖
# -r requirements.txt 项目依赖
# uwsgi 生产环境 WSGI 服务器
# ------------------------------------------------------------
RUN chown -R esxi:esxi /opt/apps ${VAR_PYTHON_PREFIX}
RUN python3 -m pip install --no-cache-dir -r requirements.txt && \
python3 -m pip install --no-cache-dir uwsgi
# ------------------------------------------------------------
# 切回非 root 用户运行应用(安全加固)
# 修正属主
# ------------------------------------------------------------
RUN chown -R esxi:esxi /opt/apps
# ------------------------------------------------------------
# 切回非 root 用户运行应用
# ------------------------------------------------------------
USER esxi
# ------------------------------------------------------------
# 声明暴露端口(实际映射由 docker run -p 决定)
# 声明暴露端口
# ------------------------------------------------------------
EXPOSE ${PORT}
# ------------------------------------------------------------
# 启动命令
# 使用 sh -c 以便解析 ${PYFILE}、${PORT} 环境变量
# 注意:runserver 仅适合开发/调试;
# 生产环境建议改为 uwsgi / gunicorn,例如:
# CMD ["sh","-c","uwsgi --http 0.0.0.0:${PORT} \
# --module ${PYFILE}.wsgi:application \
# --processes 4 --threads 2"]
# ${PYFILE} 为入口文件名(不含 .py),如 manage
# 生产环境建议改用 uwsgi
# ------------------------------------------------------------
CMD ["sh", "-c", "python3 manage.py runserver 0.0.0.0:${PORT} --noreload"]
CMD ["sh", "-c", "python3 ${PYFILE}.py runserver 0.0.0.0:${PORT} --noreload"]
+200
View File
@@ -0,0 +1,200 @@
# ============================================================
# Dockerfile:多阶段构建 Python 应用镜像
#
# 构建思路:
# - builder 阶段:安装编译工具链,编译并安装 Python 依赖
# - runtime 阶段:只保留运行期必需内容,丢弃编译器
#
# 通过 --build-arg 动态注入以下变量,便于在 CI 中复用同一份 Dockerfile:
# VAR_BASE_IMAGE 基础镜像(如 python:3.12-slim、自定义的带 esxi 用户的镜像)
# VAR_PYTHON_PREFIX Python 安装前缀(依赖安装位置)
# VAR_PYFILE_PATH 项目源码目录(相对构建上下文)
# VAR_PROFILES 应用 profile(dev / prod ...)
# VAR_PORT 应用监听端口
# VAR_PYFILE 启动入口文件名(不含 .py)
# ============================================================
# ------------------------------------------------------------
# 声明构建参数
# 基础镜像用 ARG 而不是直接 FROM,方便 CI 动态切换
# Python 前缀单独抽出,避免在 COPY / pip --prefix 里重复硬编码
# ------------------------------------------------------------
ARG VAR_BASE_IMAGE
ARG VAR_PYTHON_PREFIX=/usr/local/python3.12.8
# ============================================================
# 阶段一:builder —— 编译并安装 Python 依赖
# ============================================================
FROM ${VAR_BASE_IMAGE} AS builder
# 基础镜像末尾可能设置了非 root 用户(如 esxi),
# 这里切回 root,否则 yum install 会因权限不足失败
USER root
# 镜像作者信息(仅元数据)
LABEL MAINTAINER="djangoadmin@126.com"
# ------------------------------------------------------------
# 编译阶段环境变量
# PYTHONUNBUFFERED=1 Python 输出不缓冲,日志实时可见
# PYTHONDONTWRITEBYTECODE=1 不生成 .pyc,避免污染构建产物
# PIP_NO_CACHE_DIR=1 pip 不缓存 wheel,减小体积
# PIP_DISABLE_PIP_VERSION_CHECK=1 关闭 pip 版本检查,加快安装
# ------------------------------------------------------------
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
# ------------------------------------------------------------
# 安装编译工具链与开发头文件
# yum-utils / net-tools 基础系统工具
# gcc / gcc-c++ C/C++ 编译器(编译 C 扩展必需)
# make / cmake 构建工具
# openssl-devel 编译 _ssl / cryptography
# bzip2-devel 编译 _bz2
# libffi-devel 编译 _ctypes
# mysql-devel 编译 mysqlclient 等 MySQL 驱动
# 末尾清理 yum 缓存,避免缓存进入镜像层
# ------------------------------------------------------------
RUN yum install -y yum-utils net-tools && \
yum install -y gcc gcc-c++ make cmake \
openssl-devel bzip2-devel libffi-devel \
mariadb-devel pkgconf-pkg-config && \
yum clean all && rm -rf /var/cache/yum
#RUN --mount=type=cache,target=/var/cache/dnf,sharing=locked,id=dnf-cache \
# dnf install -y yum-utils net-tools && \
# dnf install -y gcc gcc-c++ make cmake \
# openssl-devel bzip2-devel libffi-devel \
# mariadb-devel pkgconf-pkg-config
# ------------------------------------------------------------
# 配置 pip 国内镜像并升级 pip / setuptools / wheel
# 老版本的 pip 解析依赖容易出错,先升级到最新
# ------------------------------------------------------------
RUN pip3 config set global.index-url http://mirrors.aliyun.com/pypi/simple && \
pip3 config set install.trusted-host mirrors.aliyun.com && \
python3 -m pip install --upgrade pip setuptools wheel
# ------------------------------------------------------------
# 拷贝依赖清单
# 只拷贝 requirements.txt 而不是整个项目,
# 这样源码变化但 requirements 未变时可以复用 pip install 层缓存
# ------------------------------------------------------------
ARG VAR_PYFILE_PATH
WORKDIR /opt/apps
COPY ${VAR_PYFILE_PATH}/requirements.txt ./
# ------------------------------------------------------------
# 将依赖安装到 Python 的 prefix 下
# --prefix=${VAR_PYTHON_PREFIX} 与 runtime 阶段的拷贝路径保持一致
# python3 -m pip 确保使用同一个解释器
# uwsgi 生产环境 WSGI 服务器,单独安装
# 注意:末尾不要多写反斜杠,否则会把下一行注释拼进来导致语法错误
# ------------------------------------------------------------
RUN python3 -m pip install --prefix=${VAR_PYTHON_PREFIX} -r requirements.txt && \
python3 -m pip install --prefix=${VAR_PYTHON_PREFIX} uwsgi
# ============================================================
# 阶段二:runtime —— 只保留运行时内容
# ============================================================
FROM ${VAR_BASE_IMAGE} AS runtime
# 切回 root 以便 yum install / chown
USER root
# 镜像作者信息
LABEL MAINTAINER="djangoadmin@126.com"
# ------------------------------------------------------------
# 运行期环境变量
# PYTHONUNBUFFERED=1 日志实时输出,便于 docker logs 观察
# PYTHONDONTWRITEBYTECODE=1 不写 .pyc,避免容器内产生垃圾文件
# ------------------------------------------------------------
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
# ------------------------------------------------------------
# 运行期构建参数
# VAR_PYTHON_PREFIX Python 前缀(必须与 builder 保持一致)
# VAR_PROFILES 配置 profile(dev / prod)
# VAR_PORT 应用监听端口
# VAR_PYFILE 启动入口文件名(不含 .py)
# VAR_PYFILE_PATH 项目源码目录
# ------------------------------------------------------------
ARG VAR_PYTHON_PREFIX=/usr/local/python3.12.8
ARG VAR_PROFILES
ARG VAR_PORT
ARG VAR_PYFILE
ARG VAR_PYFILE_PATH
# ------------------------------------------------------------
# 将 ARG 转成 ENV,运行期可通过环境变量读取
# PORT 供 EXPOSE 与 CMD 使用
# PYFILE 入口文件名
# PROFILES 应用读取的 profile
# ------------------------------------------------------------
ENV PORT=${VAR_PORT} \
PYFILE=${VAR_PYFILE} \
PROFILES=${VAR_PROFILES}
# ------------------------------------------------------------
# 安装运行期依赖
# 只装运行库,不装 -devel 头文件,可显著减小镜像体积:
# net-tools netstat 等网络排查工具(如不需要可去掉)
# openssl-libs SSL 运行库(替代 openssl-devel)
# bzip2-libs bz2 运行库(替代 bzip2-devel)
# libffi ctypes 运行库(替代 libffi-devel)
# mysql-libs MySQL 客户端运行库(替代 mysql-devel)
# 注意:若应用运行期确实需要头文件(如运行期编译),再改回 -devel
# ------------------------------------------------------------
RUN yum install -y net-tools openssl-libs bzip2-libs libffi mysql-libs && \
yum clean all && rm -rf /var/cache/yum
# ------------------------------------------------------------
# 从 builder 拷贝已编译安装好的 Python 及依赖
# 源与目标都使用 ${VAR_PYTHON_PREFIX},保证 runtime 的 Python
# 能在自己的 site-packages 里找到 Django 等包
# ------------------------------------------------------------
COPY --from=builder ${VAR_PYTHON_PREFIX} ${VAR_PYTHON_PREFIX}
# ------------------------------------------------------------
# 工作区
# ------------------------------------------------------------
WORKDIR /opt/apps
# ------------------------------------------------------------
# 拷贝项目源码
# 建议配合 .dockerignore 排除 .git、__pycache__、本地虚拟环境等
# ------------------------------------------------------------
COPY ${VAR_PYFILE_PATH}/ ./
# ------------------------------------------------------------
# 一次性修正属主:
# /opt/apps 保证 esxi 可读写源码
# ${VAR_PYTHON_PREFIX} 保证 esxi 可访问 Python 及其 site-packages
# 合并成一条 RUN,减少镜像层
# ------------------------------------------------------------
RUN chown -R esxi:esxi /opt/apps ${VAR_PYTHON_PREFIX}
# ------------------------------------------------------------
# 切回非 root 用户运行应用(安全加固)
# ------------------------------------------------------------
USER esxi
# ------------------------------------------------------------
# 声明暴露端口(实际映射由 docker run -p 决定)
# ------------------------------------------------------------
EXPOSE ${PORT}
# ------------------------------------------------------------
# 启动命令
# 使用 sh -c 以便解析 ${PYFILE}、${PORT} 环境变量
# 注意:runserver 仅适合开发/调试;
# 生产环境建议改为 uwsgi / gunicorn,例如:
# CMD ["sh","-c","uwsgi --http 0.0.0.0:${PORT} \
# --module ${PYFILE}.wsgi:application \
# --processes 4 --threads 2"]
# ------------------------------------------------------------
CMD ["sh", "-c", "python3 manage.py runserver 0.0.0.0:${PORT} --noreload"]