Comment puis-je initialiser une base de données MySQL avec un schéma dans un conteneur Docker?

165

J'essaie de créer un conteneur avec une base de données MySQL et d'ajouter un schéma à ces bases de données.

Mon Dockerfile actuel est:

FROM mysql
MAINTAINER  (me) <email>

# Copy the database schema to the /data directory
COPY files/epcis_schema.sql /data/epcis_schema.sql

# Change the working directory
WORKDIR data

CMD mysql -u $MYSQL_USER -p $MYSQL_PASSWORD $MYSQL_DATABASE < epcis_schema.sql

Afin de créer le conteneur, je suis la documentation fournie sur Docker et j'exécute cette commande:

docker run --name ${CONTAINER_NAME} -e MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD} -e MYSQL_USER=${DB_USER} -e MYSQL_PASSWORD=${DB_USER_PASSWORD} -e MYSQL_DATABASE=${DB_NAME} -d mvpgomes/epcisdb

Mais lorsque j'exécute cette commande, le conteneur n'est pas créé et dans l'état du conteneur, il est possible de voir que le CMD n'a pas été exécuté avec succès, en fait seule la mysqlcommande est exécutée.

Quoi qu'il en soit, existe-t-il un moyen d'initialiser la base de données avec le schéma ou dois-je effectuer ces opérations manuellement?

Marcus Gomes
la source
je suppose que vous voulez créer une nouvelle image avec une base de données qui est amorcée et prête à l'emploi?
Greg
Oui, c'est exactement ce que je veux faire.
Marcus Gomes
Expliqué ici aussi: medium.com/@lvthillo/…
lvthillo

Réponses:

105

Je suis désolé pour cette réponse très longue, mais vous avez un petit chemin à parcourir pour arriver là où vous le souhaitez. Je dirai que normalement, vous ne mettriez pas le stockage de la base de données dans le même conteneur que la base de données elle-même, vous monteriez soit un volume hôte afin que les données persistent sur l'hôte docker, soit un conteneur pourrait peut-être être utilisé pour contenir les données (/ var / lib / mysql). De plus, je suis nouveau sur mysql, donc ce n'est peut-être pas très efficace. Cela dit...

Je pense qu'il y a peut-être quelques problèmes ici. Le Dockerfile est utilisé pour créer une image. Vous devez exécuter l'étape de construction. Au minimum, à partir du répertoire qui contient le Dockerfile, vous feriez quelque chose comme:

docker build .

Le Dockerfile décrit l'image à créer. Je ne sais pas grand-chose sur mysql (je suis un fanboy postgres), mais j'ai fait une recherche autour des interwebs pour savoir «comment initialiser un conteneur de docker mysql». J'ai d'abord créé un nouveau répertoire dans lequel travailler, je l'ai appelé mdir, puis j'ai créé un répertoire de fichiers dans lequel j'ai déposé un fichier epcis_schema.sql qui crée une base de données et une seule table:

create database test;
use test;

CREATE TABLE testtab
(
id INTEGER AUTO_INCREMENT,
name TEXT,
PRIMARY KEY (id)
) COMMENT='this is my test table';

Ensuite, j'ai créé un script appelé init_db dans le répertoire des fichiers:

#!/bin/bash

# Initialize MySQL database.
# ADD this file into the container via Dockerfile.
# Assuming you specify a VOLUME ["/var/lib/mysql"] or `-v /var/lib/mysql` on the `docker run` command
# Once built, do e.g. `docker run your_image /path/to/docker-mysql-initialize.sh`
# Again, make sure MySQL is persisting data outside the container for this to have any effect.

set -e
set -x

mysql_install_db

# Start the MySQL daemon in the background.
/usr/sbin/mysqld &
mysql_pid=$!

until mysqladmin ping >/dev/null 2>&1; do
  echo -n "."; sleep 0.2
done

# Permit root login without password from outside container.
mysql -e "GRANT ALL ON *.* TO root@'%' IDENTIFIED BY '' WITH GRANT OPTION"

# create the default database from the ADDed file.
mysql < /tmp/epcis_schema.sql

# Tell the MySQL daemon to shutdown.
mysqladmin shutdown

# Wait for the MySQL daemon to exit.
wait $mysql_pid

# create a tar file with the database as it currently exists
tar czvf default_mysql.tar.gz /var/lib/mysql

# the tarfile contains the initialized state of the database.
# when the container is started, if the database is empty (/var/lib/mysql)
# then it is unpacked from default_mysql.tar.gz from
# the ENTRYPOINT /tmp/run_db script

(la plupart de ce script a été levé d'ici: https://gist.github.com/pda/9697520 )

Voici le script files / run_db que j'ai créé:

# start db

set -e
set -x

# first, if the /var/lib/mysql directory is empty, unpack it from our predefined db
[ "$(ls -A /var/lib/mysql)" ] && echo "Running with existing database in /var/lib/mysql" || ( echo 'Populate initial db'; tar xpzvf default_mysql.tar.gz )

/usr/sbin/mysqld

Enfin, le Dockerfile pour les lier tous:

FROM mysql
MAINTAINER  (me) <email>

# Copy the database schema to the /data directory
ADD files/run_db files/init_db files/epcis_schema.sql /tmp/

# init_db will create the default
# database from epcis_schema.sql, then
# stop mysqld, and finally copy the /var/lib/mysql directory
# to default_mysql_db.tar.gz
RUN /tmp/init_db

# run_db starts mysqld, but first it checks
# to see if the /var/lib/mysql directory is empty, if
# it is it is seeded with default_mysql_db.tar.gz before
# the mysql is fired up

ENTRYPOINT "/tmp/run_db"

Donc, je suis entré dans mon répertoire mdir (qui contient le Dockerfile avec le répertoire des fichiers). J'exécute ensuite la commande:

docker build --no-cache .

Vous devriez voir une sortie comme celle-ci:

Sending build context to Docker daemon 7.168 kB
Sending build context to Docker daemon 
Step 0 : FROM mysql
 ---> 461d07d927e6
Step 1 : MAINTAINER (me) <email>
 ---> Running in 963e8de55299
 ---> 2fd67c825c34
Removing intermediate container 963e8de55299
Step 2 : ADD files/run_db files/init_db files/epcis_schema.sql /tmp/
 ---> 81871189374b
Removing intermediate container 3221afd8695a
Step 3 : RUN /tmp/init_db
 ---> Running in 8dbdf74b2a79
+ mysql_install_db
2015-03-19 16:40:39 12 [Note] InnoDB: Using atomics to ref count buffer pool pages
...
/var/lib/mysql/ib_logfile0
 ---> 885ec2f1a7d5
Removing intermediate container 8dbdf74b2a79
Step 4 : ENTRYPOINT "/tmp/run_db"
 ---> Running in 717ed52ba665
 ---> 7f6d5215fe8d
Removing intermediate container 717ed52ba665
Successfully built 7f6d5215fe8d

Vous avez maintenant une image '7f6d5215fe8d'. Je pourrais exécuter cette image:

docker run -d 7f6d5215fe8d

et l'image commence, je vois une chaîne d'instance:

4b377ac7397ff5880bc9218abe6d7eadd49505d50efb5063d6fab796ee157bd3

Je pourrais alors «l'arrêter» et le redémarrer.

docker stop 4b377
docker start 4b377

Si vous regardez les journaux, la première ligne contiendra:

docker logs 4b377

Populate initial db
var/lib/mysql/
...

Puis, à la fin des logs:

Running with existing database in /var/lib/mysql

Ce sont les messages du script / tmp / run_db, le premier indique que la base de données a été décompressée à partir de la version enregistrée (initiale), le second indique que la base de données était déjà là, donc la copie existante a été utilisée.

Voici un ls -lR de la structure de répertoires que je décris ci-dessus. Notez que init_db et run_db sont des scripts avec le bit d'exécution défini:

gregs-air:~ gfausak$ ls -Rl mdir
total 8
-rw-r--r--  1 gfausak  wheel  534 Mar 19 11:13 Dockerfile
drwxr-xr-x  5 gfausak  staff  170 Mar 19 11:24 files

mdir/files:
total 24
-rw-r--r--  1 gfausak  staff   126 Mar 19 11:14 epcis_schema.sql
-rwxr-xr-x  1 gfausak  staff  1226 Mar 19 11:16 init_db
-rwxr-xr-x  1 gfausak  staff   284 Mar 19 11:23 run_db
Greg
la source
3
Hey Gary, d'abord merci de m'aider. J'ai suivi vos instructions, mais lorsque j'exécute docker build --no-cache. , J'ai une erreur d'autorisation pour le init_dbscript.
Marcus Gomes
J'essaie déjà de résoudre ce problème en donnant des autorisations dans la RUNcommande et le problème est apparemment résolu. Mais quand j'exécute, docker runj'ai le même problème avec le run_dbscript.
Marcus Gomes
1
init_db et run_db doivent avoir l'autorisation 0755 (ce sont des scripts, ils doivent avoir le bit d'exécution défini. Faites un chmod + x init_db run_db
Greg
J'ai mis à jour la réponse avec une liste de répertoires indiquant les autorisations sur chacun des fichiers. Remarque: chmod + x files / * _ db accomplira les permissions nécessaires.
Greg
2
@Greg J'ai essayé les étapes ci-dessus mais j'obtiens une erreur ([ERROR] Fatal error: Veuillez lire la section "Sécurité" du manuel pour savoir comment exécuter mysqld en tant que root!). Aucune suggestion ?
Binish John
121

J'ai eu ce même problème lorsque je voulais initialiser le schéma de mon instance MySQL Docker, mais j'ai rencontré des difficultés pour le faire fonctionner après avoir fait quelques recherches sur Google et suivi des exemples d'autres. Voici comment je l'ai résolu.

1) Sauvegardez votre schéma MySQL dans un fichier.

mysqldump -h <your_mysql_host> -u <user_name> -p --no-data <schema_name> > schema.sql

2) Utilisez la commande ADD pour ajouter votre fichier de schéma au /docker-entrypoint-initdb.drépertoire dans le conteneur Docker. Le docker-entrypoint.shfichier exécutera tous les fichiers de ce répertoire se terminant par ".sql"la base de données MySQL.

Dockerfile:

FROM mysql:5.7.15

MAINTAINER me

ENV MYSQL_DATABASE=<schema_name> \
    MYSQL_ROOT_PASSWORD=<password>

ADD schema.sql /docker-entrypoint-initdb.d

EXPOSE 3306

3) Démarrez l'instance Docker MySQL.

docker-compose build
docker-compose up

Merci à la configuration de MySQL et à l'importation de dump dans Dockerfile pour m'avoir donné des indices sur le docker-entrypoint.sh et le fait qu'il exécute à la fois des scripts SQL et shell!

Brett Cooper
la source
2
C'est certainement la meilleure réponse. La copie des DDL et des scripts d'initialisation dans le répertoire /docker-entrypoint-initdb.d est ce que la documentation dockerhub mysql dit de faire avec de telles choses.
chacewells
2
Que se passe-t-il quand quelque chose change schema.sql? Je veux que ma base de données soit mise à jour en conséquence. Que se passe-t-il également lorsque je crée ce conteneur une deuxième fois? La base de données sera-t-elle détruite et créée à nouveau?
Goga Koreli
1
@Goga Koreli Docker exécute cette commande init uniquement s'il n'y a pas d'entrée de données dans / var / lib / mysql
medTech
38

Une autre méthode basée sur une fusion de réponses serveur ici avant:

fichier docker-compose:

version: "3"
services:
    db:
      container_name: db
      image: mysql
      ports:
       - "3306:3306"  
      environment:
         - MYSQL_ROOT_PASSWORD=mysql
         - MYSQL_DATABASE=db

      volumes:
         - /home/user/db/mysql/data:/var/lib/mysql
         - /home/user/db/mysql/init:/docker-entrypoint-initdb.d/:ro

/home/user.. est un dossier partagé sur l'hôte

Et dans le /home/user/db/mysql/initdossier .. déposez simplement un fichier sql, avec n'importe quel nom, init.sqlcontenant par exemple :

CREATE DATABASE mydb;
GRANT ALL PRIVILEGES ON mydb.* TO 'myuser'@'%' IDENTIFIED BY 'mysql';
GRANT ALL PRIVILEGES ON mydb.* TO 'myuser'@'localhost' IDENTIFIED BY 'mysql';
USE mydb
CREATE TABLE CONTACTS (
    [ ... ]
);
INSERT INTO CONTACTS VALUES ...
[ ... ]

Selon la documentation officielle de mysql , vous pouvez mettre plus d'un fichier sql dans le docker-entrypoint-initdb.d, ils sont exécutés dans l'ordre alphabétique

phico
la source
28

L'autre manière simple, utilisez docker-compose avec les lignes suivantes:

mysql:
  from: mysql:5.7
  volumes:
    - ./database:/tmp/database
  command: mysqld --init-file="/tmp/database/install_db.sql"

Placez votre schéma de base de données dans le ./database/install_db.sql. Chaque fois que vous construisez votre conteneur, le fichier install_db.sql sera exécuté.

Balázs Zámbó
la source
26

J'ai essayé la réponse de Greg sans succès, j'ai dû faire quelque chose de mal car ma base de données ne contenait aucune donnée après toutes les étapes: j'utilisais la dernière image de MariaDB, juste au cas où.

Ensuite, j'ai décidé de lire le point d'entrée de l' image officielle MariaDB et de l'utiliser pour générer un simple fichier docker-compose :

database:
  image: mariadb
  ports:
     - 3306:3306
  expose:
     - 3306
  volumes:
     - ./docker/mariadb/data:/var/lib/mysql:rw
     - ./database/schema.sql:/docker-entrypoint-initdb.d/schema.sql:ro
  environment:
     MYSQL_ALLOW_EMPTY_PASSWORD: "yes"

Maintenant, je suis capable de conserver mes données ET de générer une base de données avec mon propre schéma!

mrArias
la source
1
Salut, j'essaye également de faire la même chose, mais le script n'est jamais exécuté. Y a-t-il un autre paramètre qui doit être fourni? Je viens d'ajouter ce volume pour mariadb - ./database/schema.sql:/docker-entrypoint-initdb.d/schema.sql:ro. Merci
bilak
Remarque: le script ne sera exécuté qu'une seule fois, lors de l'initialisation du conteneur. Si vous ajoutez plus tard un nouveau script, il ne sera pas exécuté.
ALex_hha
23

Après le 4 août 2015, si vous utilisez l'image officielle de mysql Docker, vous pouvez simplement AJOUTER / COPIER un fichier dans le répertoire /docker-entrypoint-initdb.d/ et il fonctionnera avec le conteneur initialisé. Voir github: https://github.com/docker-library/mysql/commit/14f165596ea8808dfeb2131f092aabe61c967225 si vous souhaitez l'implémenter sur d'autres images de conteneurs

JDWatkins
la source
1
C'est la bonne réponse pour les versions actuelles de l'image officielle Docker MSQL.
NaN
11

La solution la plus simple est d'utiliser tutum / mysql

Étape 1

docker pull tutum/mysql:5.5

Étape 2

docker run -d -p 3306:3306 -v /tmp:/tmp  -e STARTUP_SQL="/tmp/to_be_imported.mysql" tutum/mysql:5.5

Étape 3

Obtenez au-dessus de CONTAINER_ID, puis exécutez la commande docker logspour voir les informations de mot de passe générées.

docker logs #<CONTAINER_ID>
mainframer
la source
Avez-vous réussi à exécuter cela? Je tripote tutum / mysql depuis des semaines sans succès.
Iammesol
2
Oui. Je l'utilise tutum/mysql:5.5et ça a réussi. docker run -d -p 3306:3306 -v /tmp:/tmp -e MYSQL_PASS="admin" -e STARTUP_SQL="/tmp/mysqldump.mysql" tutum/mysql:5.5. Vous pouvez toujours exécuter la commande docker logs CONTAINER_IDpour voir les messages d'erreur si vous rencontrez des problèmes.
mainframer
Je ne peux pas comprendre d'où vient le chemin /tmp/to_be_imported.mysql. est-ce un chemin sur le système d'exploitation hôte? où est un COPYou ADDpour mettre des choses sur le système de fichiers du conteneur?
Randy L
@ the0ther vous n'avez pas besoin de COPY/ ADDcar le répertoire hôte / tmp est monté sur / tmp du conteneur. Regardez attentivement la commande, il y a la pièce -v /tmp:/tmp. Personnellement, je ne monterais pas un volume simplement pour rendre le fichier disponible dans le conteneur, mais je suppose que c'est un choix personnel.
Willa
1
Pour être juste, vous ne devez pas utiliser d'images non officielles, à moins que vous ne souhaitiez que cela se reproduise.
Dragas
3

Pour ceux qui ne veulent pas créer un script de point d'entrée comme moi, vous pouvez en fait démarrer mysqld au moment de la construction, puis exécuter les commandes mysql dans votre Dockerfile comme ceci:

RUN mysqld_safe & until mysqladmin ping; do sleep 1; done && \
    mysql -uroot -e "CREATE DATABASE somedb;" && \
    mysql -uroot -e "CREATE USER 'someuser'@'localhost' IDENTIFIED BY 'somepassword';" && \
    mysql -uroot -e "GRANT ALL PRIVILEGES ON somedb.* TO 'someuser'@'localhost';"

La clé ici est d'envoyer mysqld_safe en arrière-plan avec le &signe unique .

roothahn
la source
1

Voici le Dockerfile que j'ai utilisé avec succès pour installer xampp, créer un MariaDB avec un schéma et pré-rempli avec les informations utilisées sur le serveur local (utilisateurs, commandes de photos, etc.)

FROM ubuntu:14.04

COPY Ecommerce.sql /root

RUN apt-get update \
 && apt-get install wget -yq \
 && apt-get install nano \
 && wget https://www.apachefriends.org/xampp-files/7.1.11/xampp-linux-x64-7.1.11-0-installer.run \
 && mv xampp-linux-x64-7.1.11-0-installer.run /opt/ \
 && cd /opt/ \
 && chmod +x xampp-linux-x64-7.1.11-0-installer.run \
 && printf 'y\n\y\n\r\n\y\n\r\n' | ./xampp-linux-x64-7.1.11-0-installer.run \
 && cd /opt/lampp/bin \
 && /opt/lampp/lampp start \
 && sleep 5s \

 && ./mysql -uroot -e "CREATE DATABASE Ecommerce" \
 && ./mysql -uroot -D Ecommerce < /root/Ecommerce.sql \
 && cd / \
 && /opt/lampp/lampp reload \
 && mkdir opt/lampp/htdocs/Ecommerce

COPY /Ecommerce /opt/lampp/htdocs/Ecommerce

EXPOSE 80
Paddy Popeye
la source
0

Après avoir lutté un peu avec cela, jetez un œil au Dockerfile en utilisant des volumes nommés (db-data). Il est important de déclarer un plus à la dernière partie, où j'ai mentionné que le volume est[external]

Tout a très bien fonctionné de cette façon!

version: "3"

services:

  database:
    image: mysql:5.7
    container_name: mysql
    ports:
      - "3306:3306"
    volumes:
      - db-data:/docker-entrypoint-initdb.d
    environment:
      - MYSQL_DATABASE=sample
      - MYSQL_ROOT_PASSWORD=root

volumes:
  db-data:
    external: true
allysson vieira
la source