You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker部署Flask应用web容器启动报Jinja2导入Markup错误求助

问题根因
  • 核心报错直接诱因:Flask==0.12为2017年发布的老旧版本,未适配Jinja2 3.1+版本的API变更。Jinja2从3.1版本开始将Markup类迁移至jinja2.utils模块,不再支持顶层直接导入,老版本Flask的导入逻辑会直接触发该导入错误。
  • 版本不匹配问题:你的Dockerfile指定基础镜像为python:3.6-stretch,但报错日志显示依赖加载路径为/usr/local/lib/python3.11/site-packages/,是因为docker-compose配置了全目录卷挂载- .:/app,加上旧构建缓存干扰,导致容器内原本按requirements.txt安装的依赖被覆盖,出现基础镜像版本与实际运行环境不一致的问题。
  • 配置冗余问题:Dockerfile中USER app指令后多余的COPY . .属于重复操作,会覆盖之前设置好的/app目录权限,且切换非root用户后执行拷贝操作可能触发权限异常。
  • 潜在可用性问题:depends_on仅控制容器启动顺序,不会等待Redis服务完成初始化,极端场景下Flask启动时会因Redis未就绪抛出连接错误。
修复步骤

1. 修复依赖兼容问题

优先选择升级依赖到维护中的稳定版本,修改requirements.txt内容如下:

Flask==2.3.3
flask-redis==0.4
Jinja2==3.1.2
Werkzeug==2.3.7

如果必须保留Flask 0.12版本,则锁定兼容的老版本依赖:

Flask==0.12
flask-redis==0.3
Jinja2<3.1
Werkzeug<2.0
MarkupSafe==2.0.1

2. 修正Dockerfile配置

移除冗余指令,调整构建顺序优化缓存,修改后内容如下:

# 选用仍在维护期的Python稳定版基础镜像,避免已停更版本的源与安全问题
FROM python:3.10-slim

# 创建非root运行用户
RUN adduser --system --group --no-create-home app

WORKDIR /app

# 先拷贝依赖文件单独安装,充分利用Docker构建缓存
COPY requirements.txt .
RUN pip install -r requirements.txt --no-cache-dir

# 拷贝应用代码并设置权限
COPY . .
RUN chown -R app:app /app

# 切换非root用户运行后续进程
USER app

3. 修正docker-compose.yml配置

调整卷挂载规则避免覆盖容器内依赖,关闭生产环境调试模式,修改后内容如下:

version: '3'

services:
  web:
     build: .
     command: flask run --host=0.0.0.0 --port=5000
     ports:
        - "5000:5000"
     environment:
      - FLASK_APP=app.py
      - FLASK_DEBUG=0
     volumes:
        # 仅挂载需要热更新的代码文件,禁止全目录挂载覆盖容器内依赖
        - ./app.py:/app/app.py
     depends_on:
        - redis
     restart: unless-stopped

  redis:
    image: "redis:alpine"
    ports:
      - '6379:6379'
    restart: unless-stopped

4. (可选)添加Redis就绪等待逻辑

修改app.py,增加启动时的Redis连接重试,避免Redis初始化慢导致服务启动失败:

import time
import os
from flask import Flask
from flask_redis import FlaskRedis
from redis.exceptions import ConnectionError

app = Flask(__name__)
app.config['REDIS_URL'] = 'redis://redis:6379/0'

redis = FlaskRedis(app)

# 等待Redis服务就绪
while True:
    try:
        redis.ping()
        break
    except ConnectionError:
        time.sleep(1)

@app.route('/')
def counter():
    return '{0} {1} {2}'.format('This webpage has been viewed',str(redis.incr('web2_counter')),' time(s).')
验证操作

执行以下命令清理旧缓存并重新启动服务:

docker-compose down -v
docker system prune -f
docker-compose up -d --build
curl localhost:5000

正常会返回预期结果:

This webpage has been viewed 1 time(s).
多次调用curl接口,返回的计数会逐次递增,说明功能正常。

内容的提问来源于stack exchange,提问作者kabax

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 01:54:33