Méthodes de résolution du problème 2003 (HY000) : Impossible de se connecter au serveur MySQL 'db_mysql:3306' (111)
P粉178132828
2023-09-05 11:18:47
<p>J'ai récemment essayé de conteneuriser mon serveur FastAPI Python (également à des fins de réplication/doublement). Avant, je n'avais qu'un serveur MySQL dans un conteneur Docker et tout allait bien, mais lorsque j'ai également fait de mon serveur Web un service, il ne pouvait pas se connecter au serveur MySQL, donc maintenant l'application ne fonctionne plus.</p>
<p>Ce qui suit est un extrait de code pour le connecteur d'initialisation de la base de données du serveur dans l'application</p>
<pre class="lang-py Prettyprint-override"><code>à partir de l'importation fastapi FastAPI
importer mysql.connector
application = FastAPI()
dbconfig = {
"hôte": "localhost",
"base de données": "server_db",
"utilisateur": "db_user",
"mot de passe": "mot de passe utilisateur"
}
# Vérifier la connexion à la base de données
essayer:
init_cnx = mysql.connector.connect(
hôte='localhost',
utilisateur='db_user',
mot de passe='mot de passe utilisateur'
)
curseur = init_cnx.cursor()
curseur.execute("AFFICHER LES BASES DE DONNÉES COMME 'server_db'")
si curseur.fetchone() == Aucun :
# Si la base de données n'existe pas, créez la base de données
curseur.execute("CREATE DATABASE server_db")
curseur.execute("USE server_db")
curseur.execute("CREATE TABLE Messages (""
"message_id INT NON NULL AUTO_INCREMENT","
"nom_expéditeur VARCHAR(32)"
"message_text VARCHAR(64)"
"créé_au DATE",
"user_messages_count INT",
"CLÉ PRIMAIRE (message_id));")
print('La base de données a été créée !')
curseur.close()
init_cnx.close()
sauf mysql.connector.Error comme erreur :
print("Une erreur s'est produite dans init_cnx:", euh)
# Fonction d'E/S de base de données
async def execute_db_query(query, curseur_buffered=False) :
cnx = mysql.connector.connect(**dbconfig)
essayer:
curseur = cnx.cursor(buffered=cursor_buffered)
curseur.execute("USE server_db")
curseur.execute (requête)
résultat = curseur.fetchall()
cnx.commit()
print("Requête exécutée avec succès !")
résultat de retour
sauf exception comme e :
print("Erreur lors de l'exécution de la requête :", e)
enfin:
si cnx :
cnx.close()
# Récupère la fonction du répertoire racine, juste utilisée pour vérifier si l'application est connectée à la base de données
@app.get("/")
async def get_root() :
essayer:
entrées_count = attendre exécuter_db_query("SELECT COUNT(*) FROM Messages", curseur_buffered=True)
return {"Entrées de messages" : entrées_count[0][0]}
sauf exception comme e :
return {"Erreur": e}
</code></pre>
<p> Dockerfile du serveur</p>
<pre class="brush:php;toolbar:false;">DEpuis python:3.11.4-slim-bookworm
TRAVAIL/application
COPIER exigences.txt .
EXÉCUTER pip install --no-cache-dir -r conditions.txt
COPIER serveur.py .
EXPOSER 8000
CMD ["uvicorn", "serveur: application", "--hôte", "0.0.0.0", "--port", "8000"]</pre>
<p>script init.sql</p>
<pre class="lang-sql Prettyprint-override"><code>CREATE USER 'db_user'@'%' IDENTIFIÉ PAR 'user-password';
ACCORDEZ TOUS LES PRIVILÈGES SUR *.* À 'db_user'@'%' AVEC OPTION D'OBTENTION ;
PRIVILÈGES FLUSH ;
</code></pre>
<p>et docker-compose.yml</p>
<pre class="lang-yaml Prettyprint-override"><code>version : "3.8"
prestations de service:
db_mysql :
image : mysql:8
redémarrer : toujours
environnement:
MYSQL_ROOT_PASSWORD : "racine"
tomes :
- "./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql"
- "./mysql/db_mysql_data:/var/lib/mysql"
- "./mysql/mysql_logs:/var/log/mysql"
réseaux :
- réseau_fictif
serveur_1 :
image : dummy_msg_server
ports :
- "8081:8000"
réseaux :
- réseau_fictif
#commande : sh -c "dormir 60 s"
dépend de:
-db_mysql
serveur_2 :
image : dummy_msg_server
ports :
- "8082:8000"
réseaux :
- réseau_fictif
#commande : sh -c "dormir 60 s"
dépend de:
-db_mysql
tomes :
db_mysql_data : #external : vrai
réseaux :
réseau_fictif :
pilote : pont
</code></pre>
<p>Bien que tenter d'utiliser l'API avant que le conteneur MySQL ne soit complètement initialisé puisse entraîner une erreur, ce n'est pas un problème car j'attends que le serveur MySQL indique qu'il est prêt à traiter la requête. A part ça, je n'essaye pas de me connecter au serveur MySQL. </p>
<p>J'ai essayé de me connecter en utilisant le nom d'hôte/l'adresse IP.
Essayez de remplacer l'image python:3.11.4 dans le fichier docker par une version Debian antérieure et de ne pas utiliser l'image slim.Essayez d'utiliser explicitement un réseau public pour le conteneur. Docker continue d'afficher que le conteneur est dans un réseau et la requête curl du conteneur du serveur renvoie quelque chose.
De plus, docker-compose.yml fournissait précédemment le port 3306:3306 pour le service db_mysql. Je suppose que ce n'est pas un problème non plus. </p>
<p><strong>Mise à jour 1. </strong>Au cours de l'enquête, il a été découvert que si la base de données a déjà été créée, l'application n'a aucun problème à lui envoyer des requêtes et à obtenir la réponse correcte. Le seul problème est que la base de données ne peut pas être créée à l’aide du script de création dans le code. </p><p>
(Je suppose que je devrais mettre à jour le bloc de code puisque le projet est maintenant dans une autre phase.)</p>
J'ai rencontré un problème où les conteneurs du serveur et de la base de données étaient démarrés en même temps, provoquant des problèmes. La première (et dernière) tentative de connexion au serveur de base de données a lieu avant que le serveur de base de données ne soit prêt à accepter la connexion.
Pour résoudre ce problème, j'ai décidé d'ajouter un bilan de santé dans le fichier docker-compose.yml :
Avec cette configuration, le conteneur du serveur ne démarrera pas tant que le bilan de santé n'aura pas confirmé que le serveur de base de données est prêt.
Cependant, il existe une meilleure façon de gérer cette situation, qui consiste à utiliser le script wait-for-it.sh. Je connais personnellement des développeurs backend expérimentés qui utilisent également des conteneurs Docker pour diviser leurs applications en microservices. Ils ont exprimé des commentaires positifs sur l'utilisation de ce script. Bien que je ne l'ai pas personnellement essayé, je recommande de l'envisager comme une solution alternative.