解決問題2003(HY000):無法連接到MySQL伺服器' db_mysql:3306'(111)的方法
P粉178132828
2023-09-05 11:18:47
<p>我最近嘗試將我的FastAPI Python伺服器容器化(也是為了複製/雙倍化)。之前,我只有一個MySQL伺服器在一個Docker容器中,而且一切都很好,但是當我將我的網路伺服器也作為一個服務時,它無法連接到MySQL伺服器,所以現在應用程式無法運作。</p>
<p>以下是應用程式中伺服器資料庫初始化連接器的程式碼片段</p>
<pre class="lang-py prettyprint-override"><code>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}
</code></pre>
<p>伺服器的Dockerfile</p>
<pre class="brush:php;toolbar:false;">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"]</pre>
<p>init.sql腳本</p>
<pre class="lang-sql prettyprint-override"><code>CREATE USER 'db_user'@'%' IDENTIFIED BY 'user-password';
GRANT ALL PRIVILEGES ON *.* TO 'db_user'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
</code></pre>
<p>以及docker-compose.yml</p>
<pre class="lang-yaml prettyprint-override"><code>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
</code></pre>
<p>儘管在MySQL容器完全初始化之前嘗試使用API可能會導致錯誤,但這不是問題,因為我等到MySQL伺服器表示已準備好處理請求。除此之外,我不嘗試連接到MySQL伺服器。 </p>
<p>我嘗試使用主機名稱/ IP位址進行連線。
嘗試將dockerfile中的python:3.11.4映像更改為早期的debian版本並且不使用slim映像。
嘗試明確地為容器使用一個公共網路。 Docker一直顯示容器在一個網路中,而來自伺服器容器的curl請求回傳了一些內容。
此外,docker-compose.yml先前為db_mysql服務提供了3306:3306的連接埠。猜測這也不是問題。 </p>
<p><strong>更新 1。 </strong>在調查過程中,發現如果資料庫已經創建,則應用程式在向其發送請求並獲得正確回應方面沒有問題。它唯一的問題是無法使用程式碼中的建立腳本建立資料庫。 </p><p>
(猜測,我應該更新程式碼區塊,因為專案現在處於另一個階段。)</p>
我遇到了一個問題,即伺服器和資料庫容器同時啟動,導致出現問題。在資料庫伺服器準備好接受連線之前,第一次(也是最後一次)嘗試連線資料庫伺服器就已經發生了。
為了解決這個問題,我決定在docker-compose.yml檔案中加入一個健康檢查:
使用這個配置,伺服器的容器在健康檢查確認資料庫伺服器準備好之前不會啟動。
然而,有一種可能更好的處理這種情況的方法,涉及使用wait-for-it.sh腳本。我個人認識一些有經驗的後端開發人員,他們也使用Docker容器將他們的應用程式拆分成微服務。他們對使用這個腳本表示了正面的評價。雖然我個人沒有嘗試過,但我建議考慮它作為一個替代解決方案。