Flask项目打包前须确认三类依赖状态:一是真实来源(避免conda或系统包遗漏),二是开发/生产环境分离(禁用pip install -e .),三是版本安全性(用pip list --outdated检查CVE)。

Flask项目打包前必须确认的三个依赖状态
不搞清依赖的真实来源,pip freeze 生成的 requirements.txt 很可能漏掉本地开发时没显式安装、但实际在环境中跑通的包(比如通过 conda 装的,或系统级 Python 自带的)。更常见的是把开发用的包(如 pytest、black)一起打进生产镜像,徒增体积和攻击面。
- 用
pip list --outdated检查有没有已知安全风险的老版本(比如Werkzeug<2.1.0会触发 CVE-2022-29358) - 运行
pip install --dry-run -r requirements.txt验证文件是否能真正复现环境(注意:它不校验包签名,仅检查可安装性) - 区分环境:把
dev-requirements.txt单独拎出来,Dockerfile 中只COPY requirements.txt并pip install -r requirements.txt,别用pip install -e .—— 它会把当前目录作为可编辑包挂进去,导致镜像里混入本地源码路径
Dockerfile 中最常写错的 WORKDIR 和 COPY 顺序
WORKDIR 不是摆设。很多同学把 COPY . /app 放在 WORKDIR /app 前面,结果文件全拷进根目录;或者 COPY requirements.txt . 后没立刻 pip install,等后面 COPY . . 把整个项目覆盖掉,反而让 pip 缓存失效、重复下载。
- 必须先
WORKDIR /app,再COPY requirements.txt .,紧接着RUN pip install -r requirements.txt -
COPY . .放在最后,且确保项目根目录下有app.py或wsgi.py,别依赖上级目录结构 - 加
.dockerignore:至少包含__pycache__、*.pyc、.git、venv、tests—— 否则 COPY 会把本地测试代码也塞进镜像
启动命令 RUN vs CMD 的权限与执行时机差异
用 RUN python app.py 测试能跑通?那只是构建阶段跑了一次就退出了,容器启动时根本不会执行。反过来,如果写成 CMD ["python", "app.py"] 却忘了 app.py 里没调 if __name__ == "__main__":,或者没设 host='0.0.0.0',容器一启就退出,日志里只有 Address already in use 或静默死亡。
-
RUN是构建时执行,适合安装、编译、预热缓存;CMD是容器启动时执行,必须是前台长期运行的命令 - Flask 默认是调试模式,禁止在 Docker 里用
debug=True—— 它会开启重载器,监听文件变化,而容器里文件系统是只读层+可写层,行为不可控 - 推荐用
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "2", "app:app"],而不是原生flask run;gunicorn需提前pip install进镜像,且app:app表示模块名:Flask 实例名,别写成app.py:app
端口暴露和健康检查怎么配才不算白写
EXPOSE 5000 只是文档性质,不打开端口;curl http://localhost:5000/health 返回 200 也不代表服务真 ready —— Flask 应用可能刚启动,数据库连接池还没建好,请求就 500 了。
立即学习“Python免费学习笔记(深入)”;
-
EXPOSE要和CMD中绑定的端口严格一致(比如 gunicorn 绑0.0.0.0:8000,EXPOSE 就得写8000),否则docker run -p 8000:8000会转发失败 - 加
HEALTHCHECK:用curl -f http://localhost:5000/health || exit 1,并设--interval=30s --timeout=3s --start-period=40s --retries=3,给应用留够初始化时间 - 健康接口自己写:在
app.py里加路由@app.route("/health"),里面尝试db.engine.execute("SELECT 1")(如果用了 SQLAlchemy),而不是只返回{"status": "ok"}
真正麻烦的不是写 Dockerfile,是验证容器里那个 app 实例到底连没连上 Redis、能不能读到 config.py 里的 SECRET_KEY 环境变量 —— 这些都得靠日志和进容器 exec 查,不能光看 build 成功就以为完事了。










