Docker Swarm部署时Flask出现TemplateNotFound错误求助
问题:Docker Swarm部署后Flask模板加载失败(TemplateNotFound)
背景
原有服务通过Docker Compose启动正常,为使用Docker Swarm的secrets保护敏感信息切换部署方式后,UI服务的Flask模板无法加载,抛出jinja2.exceptions.TemplateNotFound: public/index.html错误,但非模板渲染的调试页面、Consul注册等功能正常。
相关配置与信息
精简后的Swarm版docker-compose.yml
version: '3.7' services: postgresql: image: docker.io/bitnami/postgresql:12.11.0 volumes: - 'postgresql_data:/bitnami/postgresql' environment: #- ALLOW_EMPTY_PASSWORD=yes - POSTGRESQL_USERNAME=user - POSTGRESQL_DATABASE=user - POSTGRESQL_PASSWORD_FILE=/path/to/docker/secret secrets: - secret keycloak: image: docker.io/bitnami/keycloak:16.1.1 depends_on: - postgresql environment: - KEYCLOAK_ADMIN_USER=user - KEYCLOAK_ADMIN_PASSWORD_FILE=/path/to/docker/secret - KEYCLOAK_DATABASE_PORT=5432 - KEYCLOAK_DATABASE_HOST=postgresql - KEYCLOAK_DATABASE_NAME=user - KEYCLOAK_DATABASE_USER=user - KEYCLOAK_DATABASE_PASSWORD_FILE=/path/to/docker/secret - KEYCLOAK_HTTP_PORT=8083 - KEYCLOAK_HTTPS_PORT=8443 - KEYCLOAK_ENABLE_TLS=true - KEYCLOAK_TLS_KEYSTORE_FILE=/path/to/keystore.jks - KEYCLOAK_TLS_KEYSTORE_PASSWORD_FILE=/path/to/docker/secret - KEYCLOAK_TLS_TRUSTSTORE_FILE=/path/to/truststore.jks - KEYCLOAK_TLS_TRUSTSTORE_PASSWORD_FILE=/path/to/docker/secret volumes: - "./path/to/keycloak.jks:/opt/bitnami/keycloak/certs/keystore.jks" - "./path/to/keycloak.jks:/opt/bitnami/keycloak/certs/truststore.jks" networks: default: aliases: - keycloak.host.net ports: - 8083:8083 - 8443:8443 secrets: - secret ui: image: <image from AWS ECR> labels: COMPOSE_PATH: ${PWD} depends_on: - keycloak ports: - 8000:8000 restart: always environment: - HOSTNAME=ui - NAME=UI - PORT=8000 - REGISTER_WITH_KEYCLOAK=True - ENVIRONMENT=prod - CONSUL_URL=http://consul-server-bootstrap:8500 - MONGO_URI=mongodb://user:password@mongodb:27017/db - QUEUE_HOSTNAME=queue secrets: - secret
注:Swarm版与Compose版的唯一差异为添加了secrets配置
UI服务关键代码(config.py)
# Landing page @app.route("/") def index(): try: current_app.logger.info(f"public/index.html page being displayed") return Response(render_template("public/index.html"), mimetype="text/html") except exception_with_data as ex: return ex.repr_as_dict(), 400
报错信息
2022-07-26T08:21:03.252452 Thread-83 UI app ERROR Exception on / [GET] Traceback (most recent call last): File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 2070, in wsgi_app response = self.full_dispatch_request() File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1515, in full_dispatch_request rv = self.handle_user_exception(e) File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1513, in full_dispatch_request rv = self.dispatch_request() File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1499, in dispatch_request return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args) File "/usr/local/lib/python3.9/site-packages/ui/main/config.py", line 209, in index return Response(render_template("public/index.html"), mimetype="text/html") File "/usr/local/lib/python3.9/site-packages/flask/templating.py", line 148, in render_template ctx.app.jinja_env.get_or_select_template(template_name_or_list), File "/usr/local/lib/python3.9/site-packages/jinja2/environment.py", line 1068, in get_or_select_template return self.get_template(template_name_or_list, parent, globals) File "/usr/local/lib/python3.9/site-packages/jinja2/environment.py", line 997, in get_template return self._load_template(name, globals) File "/usr/local/lib/python3.9/site-packages/jinja2/environment.py", line 958, in _load_template template = self.loader.load(self, name, self.make_globals(globals)) File "/usr/local/lib/python3.9/site-packages/jinja2/loaders.py", line 125, in load source, filename, uptodate = self.get_source(environment, name) File "/usr/local/lib/python3.9/site-packages/flask/templating.py", line 59, in get_source return self._get_source_fast(environment, template) File "/usr/local/lib/python3.9/site-packages/flask/templating.py", line 95, in _get_source_fast raise TemplateNotFound(template) jinja2.exceptions.TemplateNotFound: public/index.html
代码目录结构
ui /main app.py config.py /templates /admin <all admin html pages> /public index.html <all other public html pages>
解决方案
1. 显式指定Flask模板文件夹绝对路径
Flask默认从当前工作目录的templates文件夹加载模板,Swarm部署时可能因工作目录与Compose不一致导致路径解析失败。在Flask应用初始化时,显式指定模板文件夹的绝对路径:
# 在app.py或config.py的Flask初始化代码中修改 app = Flask(__name__, template_folder="/usr/src/app/ui/templates")
替换为容器内
templates文件夹的实际绝对路径,可通过docker exec -it <ui-container-id> pwd和ls命令确认。
2. 统一容器工作目录
在Swarm版docker-compose.yml的ui服务中添加working_dir配置,确保与Compose部署时的工作目录一致:
ui: # 其他配置不变 working_dir: /usr/src/app/ui
3. 验证模板文件权限
检查容器内templates文件夹及文件的权限,确保Flask运行用户(通常为python或www-data)拥有读取权限:
docker exec -it <ui-container-id> ls -l /usr/src/app/ui/templates/public
若权限不足,在UI服务的Dockerfile中添加权限设置:
RUN chown -R python:python /usr/src/app/ui/templates
4. 排查环境变量差异
对比Compose与Swarm部署下UI容器的环境变量,确认无影响模板加载的差异(如FLASK_ENV、PYTHONPATH等):
# Compose环境 docker-compose exec ui env # Swarm环境 docker exec -it <ui-container-id> env
内容的提问来源于stack exchange,提问作者W_ Jonesey
相关产品推荐
相关产品推荐

