Docker部署Flask+MongoDB POST报ServerSelectionTimeoutError
问题描述
开发了对接MongoDB的Flask Web服务,计划将Web服务与MongoDB分别部署在独立Docker容器中。本地直接运行Flask服务连接MongoDB容器时所有功能运行正常,但双容器部署后出现异常:GET请求可正常响应,POST请求执行数据库插入、修改等写操作时会卡顿数秒,最终抛出pymongo.errors.ServerSelectionTimeoutError超时错误。
涉及的代码与配置如下:
Flask服务核心代码
from flask import Flask, request, Response, jsonify import json from pymongo import MongoClient from datetime import date client = MongoClient("mongodb://localhost:27017") db=client["DigitalNotes"] users=db["Users"] admins=db["Admins"] notes=db["Notes"] app=Flask(__name__) @app.route("/sign_up",methods=["POST"]) def sign_up(): try: data=json.loads(request.data) except Exception as e: return Response("Bad json content",status=500,mimetype='application/json') if data==None: return Response("Bad request",status=500,mimetype='application/json') if not "email" in data or not "username" in data or not "name" in data or not "password" in data: return Response("Information incompleted",status=500,mimetype='application/json') if users.count_documents({"email":data["email"]})==0 or users.count_documents({"username":data["username"]})==0: user = {"email":data["email"],"username":data["username"],"name":data["name"],"password":data["password"]} users.insert_one(user) return Response(data["username"]+" was added to the DataBase",status=200,mimetype='application/json') else: return Response("A user with the given username or password already exists",status=200,mimetype='application/json')
Flask服务Dockerfile
FROM ubuntu:20.04 RUN apt-get update RUN apt-get install -y python3 python3-pip RUN pip3 install flask pymongo RUN pip3 install datetime RUN mkdir /app COPY app.py /app/app.py EXPOSE 5000 WORKDIR /app ENTRYPOINT ["python3","-u","app.py"]
docker-compose配置
version: '2' services: mongodb: image: mongo restart: always container_name: mongodb ports: - 27017:27027 volumes: - ./mongodb/data:/data/db flask-service: build: context: ./flask-service restart: always container_name: flask depends_on: - mongodb ports: - 5000:5000 environment: - "MONGO_HOSTNAME=mongodb"
根因分析
- 核心错误是数据库连接地址硬编码:Flask代码中写死MongoDB连接地址为
localhost:27017,在Docker容器网络环境下,localhost指向Flask容器自身,无法访问同网络下的MongoDB容器。虽然compose中配置了MONGO_HOSTNAME=mongodb的环境变量,但代码未读取该变量。 - PyMongo的MongoClient是懒连接机制:初始化客户端时不会实际建立数据库连接,只有第一次执行真实IO操作时才会发起连接。如果GET接口未触发实际数据库交互,就不会抛出连接错误,只有执行写操作的POST请求触发真实连接时才会报超时,这就是GET看似正常、POST报错的原因。
- 配置笔误:MongoDB服务的端口映射写为
27017:27027,但MongoDB默认在容器内监听27017端口,该配置会导致宿主机无法通过27017端口访问容器内MongoDB(容器间内部通信不受该配置影响,但属于错误配置需要修正)。 depends_on配置仅保证容器启动顺序,不保证MongoDB服务本身处于可连接的就绪状态,可能存在服务启动时序问题,但不是本次超时的核心原因。
修复方案
- 修改Flask数据库连接逻辑,读取环境变量中的MongoDB地址,移除硬编码的localhost:
在代码开头导入os模块,将连接初始化部分修改为:import os # 优先读取环境变量中的MongoDB地址,本地调试时默认用localhost mongo_host = os.getenv("MONGO_HOSTNAME", "localhost") client = MongoClient(f"mongodb://{mongo_host}:27017", serverSelectionTimeoutMS=5000) - 修正docker-compose.yml中MongoDB的端口映射配置,将错误的
- 27017:27027修改为- 27017:27017,保证宿主机可正常访问MongoDB服务。 - (可选优化)增加服务就绪检测,避免启动时序问题:
给mongodb服务增加健康检查配置,同时调整depends_on规则,保证Flask服务在MongoDB可连接后再启动:services: mongodb: # 原有配置保留,新增下面的健康检查 healthcheck: test: ["CMD", "mongo", "--eval", "db.adminCommand('ping')"] interval: 5s timeout: 3s retries: 10 flask-service: # 原有配置保留,修改depends_on规则 depends_on: mongodb: condition: service_healthy - 重新构建Flask镜像并启动服务,在docker-compose.yml所在目录执行以下命令即可:
docker-compose down docker-compose up -d --build
内容的提问来源于stack exchange,提问作者pankal
相关产品推荐
相关产品推荐

