解决问题2003(HY000):无法连接到MySQL服务器' db_mysql:3306'(111)的方法
P粉178132828
P粉178132828 2023-09-05 11:18:47
[MySQL讨论组]

我最近尝试将我的FastAPI Python服务器容器化(也是为了复制/双倍化)。之前,我只有一个MySQL服务器在一个Docker容器中,并且一切都很好,但是当我将我的Web服务器也作为一个服务时,它无法连接到MySQL服务器,所以现在应用程序无法工作。

下面是应用程序中服务器数据库初始化连接器的代码片段

from fastapi import FastAPI
import mysql.connector


app = FastAPI()

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

# 检查数据库连接
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:
        # 如果数据库不存在,则创建数据库
        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('数据库已创建!')
    cursor.close()
    init_cnx.close()
except mysql.connector.Error as err:
    print("在init_cnx中发生错误:", err)


# 数据库I/O函数
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("查询成功执行!")
        return result
    except Exception as e:
        print("执行查询时出错:", e)
    finally:
        if cnx:
            cnx.close()


# 获取根目录函数,只是用来检查应用程序是否连接到数据库
@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 {"错误": 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容器完全初始化之前尝试使用API可能会导致错误,但这不是问题,因为我等到MySQL服务器表示已准备好处理请求。除此之外,我不尝试连接到MySQL服务器。

我尝试使用主机名/ IP地址进行连接。 尝试将dockerfile中的python:3.11.4映像更改为早期的debian版本并且不使用slim映像。 尝试显式地为容器使用一个公共网络。 Docker一直显示容器在一个网络中,而来自服务器容器的curl请求返回了一些内容。 此外,docker-compose.yml以前为db_mysql服务提供了3306:3306的端口。猜测这也不是问题。

更新 1。在调查过程中,发现如果数据库已经创建,则应用程序在向其发送请求并获得正确响应方面没有问题。它唯一的问题是无法使用代码中的创建脚本创建数据库。

(猜测,我应该更新代码块,因为项目现在处于另一个阶段。)

P粉178132828
P粉178132828

全部回复(1)
P粉141455512

我遇到了一个问题,即服务器和数据库容器同时启动,导致出现问题。在数据库服务器准备好接受连接之前,第一次(也是最后一次)尝试连接数据库服务器就已经发生了。

为了解决这个问题,我决定在docker-compose.yml文件中添加一个健康检查:

version: "3.8"
services:
  db_mysql:
    image: mysql:8
    restart: always
    ports:
      - 3306:3306
    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"
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-proot"]
      timeout: 1s
      interval: 40s
      retries: 5

  server_1:
    build:
      context: .
      dockerfile: Dockerfile
    restart: always
    ports:
      - "8081:8000"
    depends_on:
      db_mysql:
        condition: service_healthy

  server_2:
    build:
      context: .
      dockerfile: Dockerfile
    restart: always
    ports:
      - "8082:8000"
    depends_on:
      db_mysql:
        condition: service_healthy

volumes:
  db_mysql_data: #external: true

使用这个配置,服务器的容器在健康检查确认数据库服务器准备好之前不会启动。

然而,有一种可能更好的处理这种情况的方法,涉及使用wait-for-it.sh脚本。我个人认识一些有经验的后端开发人员,他们也使用Docker容器将他们的应用程序拆分成微服务。他们对使用这个脚本表示了积极的评价。虽然我个人没有尝试过,但我建议考虑它作为一个替代解决方案。

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号