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

如何解决FastAPI容器无法连接MySQL容器的2003(HY000)错误

FastAPI容器无法连接MySQL容器(仅初始化创建DB时失败)

近期尝试将FastAPI Python服务器Docker化并部署多实例。单独运行MySQL容器时一切正常,但将Web服务器封装为容器后,始终无法连接到MySQL服务器,导致应用无法正常运行。

更新1:经排查发现,若数据库已提前创建,应用可以正常发送请求并获取响应,仅在通过代码中的初始化脚本创建数据库时会出现连接失败问题。


应用数据库连接代码片段

from fastapi import FastAPI
import mysql.connector


app = FastAPI()

dbconfig = {
  "host": "localhost",
  "database": "server_db",
  "user": "db_user",
  "password": "user-password"
}

# Checking DB connection
try:
    init_cnx = mysql.connector.connect(
        host='localhost',
        user='db_user',
        password='user-password'
    )
    cursor = init_cnx.cursor()
    cursor.execute("SHOW DATABASES LIKE 'server_db'")
    if cursor.fetchone() == None:
        # Create DB in case one doesn't exist
        cursor.execute("CREATE DATABASE server_db")
        cursor.execute("USE server_db")
        cursor.execute("CREATE TABLE Messages ("
                        "message_id INT NOT NULL AUTO_INCREMENT,"
                        "sender_name VARCHAR(32),"
                        "message_text VARCHAR(64),"
                        "created_at DATE,"
                        "user_messages_count INT,"
                        "PRIMARY KEY (message_id));")
        print('DB Created!')
    cursor.close()
    init_cnx.close()
except mysql.connector.Error as err:
    print("On init_cnx:", err)


# DB I/O function
async def execute_db_query(query, cursor_buffered=False):
    cnx = mysql.connector.connect(**dbconfig)
    try:
        cursor = cnx.cursor(buffered=cursor_buffered)
        cursor.execute("USE server_db")
        cursor.execute(query)
        result = cursor.fetchall()
        cnx.commit()
        print("Query executed successfully!")
        return result
    except Exception as e:
        print("Error executing query:", e)
    finally:
        if cnx:
            cnx.close()


# Get root function, just to check if app is connected to DB
@app.get("/")
async def get_root():
    try:
        entries_count = await execute_db_query("SELECT COUNT(*) FROM Messages", cursor_buffered=True)
        return {"Messages entries": entries_count[0][0]}
    except Exception as e:
        return {"Error": e}

服务器Dockerfile

FROM python:3.11.4-slim-bookworm

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY server.py .
EXPOSE 8000
CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "8000"]

init.sql脚本

CREATE USER 'db_user'@'%' IDENTIFIED BY 'user-password';
GRANT ALL PRIVILEGES ON *.* TO 'db_user'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;

docker-compose.yml配置

version: "3.8"
services:
  db_mysql:
    image: mysql:8
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: "root"
    volumes:
      - "./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql"
      - "./mysql/db_mysql_data:/var/lib/mysql"
      - "./mysql/mysql_logs:/var/log/mysql"
    networks:
      - dummy_network

  server_1:
    image: dummy_msg_server
    ports:
      - "8081:8000"
    networks:
      - dummy_network
    #command: sh -c "sleep 60s"
    depends_on:
      - db_mysql

  server_2:
    image: dummy_msg_server
    ports:
      - "8082:8000"
    networks:
      - dummy_network
    #command: sh -c "sleep 60s"
    depends_on:
      - db_mysql

volumes:
  db_mysql_data: #external: true

networks:
  dummy_network:
    driver: bridge

已排查情况

  • 确认MySQL容器完全就绪后才尝试连接,排除初始化未完成问题
  • 尝试过使用主机名/IP地址连接、更换Python基础镜像、显式配置共享网络
  • 曾配置MySQL端口映射3306:3306,非问题根源

解决方案

核心问题定位

代码中**初始化连接的硬编码localhost**是关键问题:在Docker容器内,localhost指向容器自身,而非MySQL容器。更新1中提到"DB提前创建后可正常运行",说明后续查询用的dbconfig可能已修正,但初始化段仍用localhost,导致创建DB时连接失败。

修复步骤

  1. 统一数据库连接地址
    将初始化代码的硬编码localhost改为Docker Compose中MySQL服务的名称db_mysql,并复用dbconfig配置避免重复:

    dbconfig = {
      "host": "db_mysql",  # 替换localhost为MySQL服务名
      "database": "server_db",
      "user": "db_user",
      "password": "user-password"
    }
    
    # 修改初始化连接逻辑
    init_cnx = mysql.connector.connect(
        host=dbconfig["host"],
        user=dbconfig["user"],
        password=dbconfig["password"]
    )
    

    Docker的内置DNS会自动将服务名db_mysql解析为对应容器的IP,确保网络互通。

  2. 优化初始化逻辑(可选,推荐)
    将创建数据库和表的逻辑移到init.sql中,由MySQL容器自动执行,避免在应用代码中处理初始化:

    CREATE USER 'db_user'@'%' IDENTIFIED BY 'user-password';
    GRANT ALL PRIVILEGES ON *.* TO 'db_user'@'%' WITH GRANT OPTION;
    -- 新增DB和表初始化
    CREATE DATABASE IF NOT EXISTS server_db;
    USE server_db;
    CREATE TABLE IF NOT EXISTS Messages (
        message_id INT NOT NULL AUTO_INCREMENT,
        sender_name VARCHAR(32),
        message_text VARCHAR(64),
        created_at DATE,
        user_messages_count INT,
        PRIMARY KEY (message_id)
    );
    FLUSH PRIVILEGES;
    

    之后可删除应用代码中创建DB和表的逻辑,简化代码结构。

  3. 用环境变量传递配置(可选)
    在docker-compose.yml中为Web服务添加环境变量,避免代码硬编码:

    server_1:
      image: dummy_msg_server
      ports:
        - "8081:8000"
      networks:
        - dummy_network
      environment:
        DB_HOST: db_mysql
        DB_USER: db_user
        DB_PASSWORD: user-password
        DB_NAME: server_db
      depends_on:
        - db_mysql
    

    应用代码读取环境变量:

    import os
    dbconfig = {
      "host": os.getenv("DB_HOST", "db_mysql"),
      "database": os.getenv("DB_NAME", "server_db"),
      "user": os.getenv("DB_USER", "db_user"),
      "password": os.getenv("DB_PASSWORD", "user-password")
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 20:14:57