Configurer MySQL et importer un vidage dans Dockerfile

127

J'essaye de configurer un Dockerfile pour mon projet LAMP, mais j'ai quelques problèmes lors du démarrage de MySQL. J'ai les lignes suivantes sur mon Dockerfile:

VOLUME ["/etc/mysql", "/var/lib/mysql"]
ADD dump.sql /tmp/dump.sql
RUN /usr/bin/mysqld_safe & sleep 5s
RUN mysql -u root -e "CREATE DATABASE mydb"
RUN mysql -u root mydb < /tmp/dump.sql

Mais je continue à recevoir cette erreur:

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (111)

Des idées sur la façon de configurer la création de base de données et l'importation de vidage lors d'une construction Dockerfile?

vinnylinux
la source
Cela est dû au fait que chaque RUNcommande est exécutée dans un conteneur différent. C'est bien expliqué ici: stackoverflow.com/questions/17891669/…
Kuhess
Cela explique simplement que les commandes RUN ont des contextes différents. Mais je dépend d'un démon, pas du contexte.
vinnylinux le
1
Oui, mais cela explique pourquoi vous ne pouvez pas vous connecter à MySQL. C'est parce qu'il ne fonctionne que dans votre première RUNligne.
Kuhess
Pour exécuter vos instructions SQL, vous devez démarrer MySQL et utiliser le client MySQL dans le même conteneur: un RUNavec plusieurs étapes. Vous pouvez trouver un exemple d'installation en plusieurs étapes d'un logiciel ici: stackoverflow.com/questions/25899912/install-nvm-in-docker
...
Vous pouvez également jeter un oeil sur le service avec docker-compose: docs.docker.com/compose/wordpress/#build-the-project avec ça, mysql peut être attaché à votre application
aurny2420289

Réponses:

121

Chaque RUNinstruction de a Dockerfileest exécutée dans une couche différente (comme expliqué dans la documentation deRUN ).

Dans votre Dockerfile, vous avez trois RUNinstructions. Le problème est que le serveur MySQL ne démarre que dans le premier. Dans les autres, aucun MySQL n'est en cours d'exécution, c'est pourquoi vous obtenez votre erreur de connexion avec le mysqlclient.

Pour résoudre ce problème, vous avez 2 solutions.

Solution 1: utilisez une ligne RUN

RUN /bin/bash -c "/usr/bin/mysqld_safe --skip-grant-tables &" && \
  sleep 5 && \
  mysql -u root -e "CREATE DATABASE mydb" && \
  mysql -u root mydb < /tmp/dump.sql

Solution 2: utilisez un script

Créez un script exécutable init_db.sh:

#!/bin/bash
/usr/bin/mysqld_safe --skip-grant-tables &
sleep 5
mysql -u root -e "CREATE DATABASE mydb"
mysql -u root mydb < /tmp/dump.sql

Ajoutez ces lignes à votre Dockerfile:

ADD init_db.sh /tmp/init_db.sh
RUN /tmp/init_db.sh
Kuhess
la source
Avez-vous une idée de la façon dont je peux conserver ces informations? Les bases de données et les tables que je crée sur la version Dockerfile ne sont pas disponibles lorsque j'exécute des éléments sur des conteneurs. :(
vinnylinux
1
Cela dépend de ce que vous entendez par persister. Si vous souhaitez que les données soient conservées pendant plusieurs docker runexécutions, vous devez monter des volumes. Si vous voulez juste avoir un conteneur avec votre vidage, mais que vous ne voulez pas persister d'autres modifications, vous pouvez vous débarrasser de l' VOLUMEinstruction dans votre fichier Dockerfile.
Kuhess
16
Je reçois ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)en essayant la solution 1. Suivre les journaux mysqld_safe Starting mysqld daemon with databases from /var/lib/mysqletmysqld_safe mysqld from pid file /var/run/mysqld/mysqld.pid ended
Vituel
1
Je trouve qu'il est beaucoup mieux d'avoir une vérification de script au démarrage pour voir si mysql a créé une base de données. Si c'est le cas, laissez-le seul, sinon exécutez mysql_init_dbet chargez dans la structure de votre base de données. Cela permet la flexibilité de réinitialiser automatiquement la base de données lors du test, mais si vous voulez qu'elle persiste ou teste différents ensembles de données, il vous suffit de monter un volume dans / var / lib / mysql en utilisant docker run -v ....
tu-Reinstate Monica-dor duh
1
@trevorgrayson - Cela ne fonctionne pas non plus. Maintenant, il donne une erreur ERROR 2003 (HY000): Impossible de se connecter au serveur MySQL sur '127.0.0.1' (111)
Profond
166

La dernière version de l' image officielle du docker mysql vous permet d'importer des données au démarrage. Voici mon docker-compose.yml

data:
  build: docker/data/.
mysql:
  image: mysql
  ports:
    - "3307:3306"
  environment:
    MYSQL_ROOT_PASSWORD: 1234
  volumes:
    - ./docker/data:/docker-entrypoint-initdb.d
  volumes_from:
    - data

Ici, j'ai mon data-dump.sql sous docker/datalequel est relatif au dossier à partir duquel docker-compose s'exécute. Je monte ce fichier sql dans ce répertoire /docker-entrypoint-initdb.dsur le conteneur.

Si vous souhaitez voir comment cela fonctionne, jetez un œil à leur docker-entrypoint.shdans GitHub. Ils ont ajouté ce bloc pour permettre l'importation de données

    echo
    for f in /docker-entrypoint-initdb.d/*; do
        case "$f" in
            *.sh)  echo "$0: running $f"; . "$f" ;;
            *.sql) echo "$0: running $f"; "${mysql[@]}" < "$f" && echo ;;
            *)     echo "$0: ignoring $f" ;;
        esac
        echo
    done

Une note supplémentaire, si vous voulez que les données soient persistantes même après l'arrêt et la suppression du conteneur mysql, vous devez avoir un conteneur de données séparé comme vous le voyez dans le fichier docker-compose.yml. Le contenu du conteneur de données Dockerfile est très simple.

FROM n3ziniuka5/ubuntu-oracle-jdk:14.04-JDK8

VOLUME /var/lib/mysql

CMD ["true"]

Le conteneur de données n'a même pas besoin d'être à l'état de démarrage pour la persistance.

Rajiv
la source
1
C'est la meilleure réponse à mon humble avis. Cela permet de ne PAS créer nous-même une image. Merci pour cette astuce.
Metal3d
5
Une modification mineure pour les volumes: - - ./docker/data:/docker-entrypoint-initdb.dsuppression du répertoire .devant le conteneur.
David Sinclair le
7
Bien que ce soit la procédure correcte, elle ne répond pas très bien au cas d'utilisation. L'un des avantages de Docker est que vous pouvez créer un environnement très rapidement. Si vous devez attendre 3-4 minutes pendant que Docker importe une base de données MySQL lors du démarrage, vous perdez cet avantage. L'objectif est d'avoir un conteneur qui contient déjà les données dans la base de données, afin que vous puissiez l'utiliser le plus rapidement possible.
Garreth McDaid
3
cette réponse fonctionne-t-elle toujours avec les dernières versions? ne semble plus fonctionner
Daniel
2
Notez que l'exemple de docker-compose ici est v2 et non v3. "volumes_from:" n'est pas pris en charge dans la v3.
Ernest
37

Ce que j'ai fait a été de télécharger mon dump sql dans un dossier "db-dump" et de le monter:

mysql:
 image: mysql:5.6
 environment:
   MYSQL_ROOT_PASSWORD: pass
 ports:
   - 3306:3306
 volumes:
   - ./db-dump:/docker-entrypoint-initdb.d

Lorsque je lance docker-compose uppour la première fois, le vidage est restauré dans la base de données.

Petru
la source
3
+1, avec un ajout: le lien vers le script en cours d'exécution pour mieux comprendre ce qui se passe réellement: github.com/docker-library/mariadb/blob
Tom Imrei
6
le dernier mysqlsemble ne pas charger le fichier sql vidé, et même en utilisant 5.6 j'ai un problème avec les moteurs de stockage InnoDb.
zinking
1
même ici, il a une telle instruction et j'ai même vu dans la journalisation que le chargement des fichiers init, mais db est vide!
holms
11

J'ai utilisé l'approche docker-entrypoint-initdb.d (Merci à @Kuhess) Mais dans mon cas, je veux créer ma base de données en fonction de certains paramètres que j'ai définis dans le fichier .env, alors je l'ai fait

1) Tout d'abord, je définis le fichier .env quelque chose comme ça dans mon répertoire de projet racine docker

MYSQL_DATABASE=my_db_name
MYSQL_USER=user_test
MYSQL_PASSWORD=test
MYSQL_ROOT_PASSWORD=test
MYSQL_PORT=3306

2) Ensuite, je définis mon fichier docker-compose.yml. J'ai donc utilisé la directive args pour définir mes variables d'environnement et je les ai définies à partir du fichier .env

version: '2'
services:
### MySQL Container
    mysql:
        build:
            context: ./mysql
            args:
                - MYSQL_DATABASE=${MYSQL_DATABASE}
                - MYSQL_USER=${MYSQL_USER}
                - MYSQL_PASSWORD=${MYSQL_PASSWORD}
                - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
        ports:
            - "${MYSQL_PORT}:3306"

3) Ensuite, je définis un dossier mysql qui comprend un Dockerfile. Donc le Dockerfile est ceci

FROM mysql:5.7
RUN chown -R mysql:root /var/lib/mysql/

ARG MYSQL_DATABASE
ARG MYSQL_USER
ARG MYSQL_PASSWORD
ARG MYSQL_ROOT_PASSWORD

ENV MYSQL_DATABASE=$MYSQL_DATABASE
ENV MYSQL_USER=$MYSQL_USER
ENV MYSQL_PASSWORD=$MYSQL_PASSWORD
ENV MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD

ADD data.sql /etc/mysql/data.sql
RUN sed -i 's/MYSQL_DATABASE/'$MYSQL_DATABASE'/g' /etc/mysql/data.sql
RUN cp /etc/mysql/data.sql /docker-entrypoint-initdb.d

EXPOSE 3306

4) Maintenant, j'utilise mysqldump pour vider ma base de données et mettre le data.sql dans le dossier mysql

mysqldump -h <server name> -u<user> -p <db name> > data.sql

Le fichier est juste un fichier de vidage sql normal mais j'ajoute 2 lignes au début pour que le fichier ressemble à ceci

--
-- Create a database using `MYSQL_DATABASE` placeholder
--
CREATE DATABASE IF NOT EXISTS `MYSQL_DATABASE`;
USE `MYSQL_DATABASE`;

-- Rest of queries
DROP TABLE IF EXISTS `x`;
CREATE TABLE `x` (..)
LOCK TABLES `x` WRITE;
INSERT INTO `x` VALUES ...;
...
...
...

Donc, ce qui se passe, c'est que j'ai utilisé la commande "RUN sed -i 's / MYSQL_DATABASE /' $ MYSQL_DATABASE '/ g' /etc/mysql/data.sql" pour remplacer l' MYSQL_DATABASEespace réservé par le nom de ma base de données dans laquelle je l'ai défini fichier .env.

|- docker-compose.yml
|- .env
|- mysql
     |- Dockerfile
     |- data.sql

Vous êtes maintenant prêt à créer et exécuter votre conteneur

Saman Shafigh
la source
J'aime l'approche consistant à utiliser un .envfichier. Cependant, selon cela , la .envfonctionnalité de fichier ne fonctionne que lorsque vous utilisez la docker-compose upcommande et ne fonctionne pas avec docker stack deploy. Ainsi, si vous souhaitez utiliser le .envfichier en production, vous voudrez peut-être consulter les secrets de docker introduits avec docker 17.06. Ensuite, vous pouvez utiliser votre fichier .env en conjonction avec des secrets dans les phases de développement docker compose upet de productiondocker stack deploy
ira
Belle approche mais je suggérerais d'utiliser RUN sed -i '1s/^/CREATE DATABASE IF NOT EXISTS $ MYSQL_DATABASE ;\nUSE $ MYSQL_DATABASE ;\n/' data.sqlau lieu de la sedcommande que vous avez suggérée. De cette façon, vous pouvez utiliser n'importe quel fichier de vidage que vous avez - c'est utile si vous travaillez avec une grande quantité de données :)
Tomasz Kapłoński
9

Voici une version fonctionnelle utilisant v3de docker-compose.yml. La clé est la directive volumes :

mysql:
  image: mysql:5.6
  ports:
    - "3306:3306"
  environment:
    MYSQL_ROOT_PASSWORD: root
    MYSQL_USER: theusername
    MYSQL_PASSWORD: thepw
    MYSQL_DATABASE: mydb
  volumes:
    - ./data:/docker-entrypoint-initdb.d

Dans le répertoire que j'ai, docker-compose.ymlj'ai un répertoire datacontenant .sqldes fichiers de vidage. C'est bien car vous pouvez avoir un .sqlfichier de vidage par table.

Je cours simplement docker-compose upet je suis prêt à partir. Les données persistent automatiquement entre les arrêts. Si vous voulez supprimer les données et « aspirer » les nouveaux .sqlfichiers exécutent docker-compose downensuitedocker-compose up .

Si quelqu'un sait comment faire en sorte que le mysqldocker retravaille les fichiers /docker-entrypoint-initdb.dsans supprimer le volume, veuillez laisser un commentaire et je mettrai à jour cette réponse.

rynop
la source
2
cette réponse m'a aidé. la note sur docker-compose downétait très importante car je ne voyais aucun changement malgré le redémarrage de docker-compose. MYSQL_DATABASE est une variable obligatoire dans ce cas
nxmohamad
0

Je n'aime pas vraiment la réponse acceptée par sleep 5Kuhess car cela me semble un peu hackish car il suppose que le démon mysql db s'est correctement chargé dans ce laps de temps. C'est une hypothèse, aucune garantie. De plus, si vous utilisez une image docker mysql fournie, l'image elle-même s'occupe déjà du démarrage du serveur; Je n'interférerais pas avec cela avec une coutume /usr/bin/mysqld_safe.

J'ai suivi les autres réponses ici et copié les scripts bash et sql dans le dossier /docker-entrypoint-initdb.d/du conteneur docker car c'est clairement la manière prévue par le fournisseur d'images mysql. Tout ce qui se trouve dans ce dossier est exécuté une fois que le démon db est prêt, vous devriez donc pouvoir vous y fier.

En complément des autres - car aucune autre réponse ne le mentionne explicitement: outre les scripts SQL, vous pouvez également copier des scripts bash dans ce dossier ce qui pourrait vous donner plus de contrôle.

C'est ce dont j'avais besoin par exemple car j'avais également besoin d'importer un vidage, mais le vidage seul n'était pas suffisant car il ne fournissait pas la base de données dans laquelle il devait importer. Donc, dans mon cas, j'ai un script nommé db_custom_init.shavec ce contenu:

mysql -u root -p$MYSQL_ROOT_PASSWORD -e 'create database my_database_to_import_into'
mysql -u root -p$MYSQL_ROOT_PASSWORD my_database_to_import_into < /home/db_dump.sql

et ce Dockerfile copiant ce script:

FROM mysql/mysql-server:5.5.62
ENV MYSQL_ROOT_PASSWORD=XXXXX
COPY ./db_dump.sql /home/db_dump.sql
COPY ./db_custom_init.sh /docker-entrypoint-initdb.d/

edit: Notez que cela n'importera le vidage SQL qu'au moment de la création du conteneur, pas au moment de la construction de l'image!

onoSendai
la source