# ============================================================ # 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 mysql-devel && \ # yum clean all && rm -rf /var/cache/yum # ------------------------------------------------------------ # 配置 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 ${PYFILE}.py runserver 0.0.0.0:${PORT} --noreload"]