web-dev-qa-db-fra.com

Comment créer une image MySQL Docker remplie au moment de la compilation

J'aimerais créer une image MySQL Docker avec des données déjà remplies.

Je veux créer 3 couches comme ceci:

        |---------------------|---------------------|
Layer 3 | Customer 1 Database | Customer 2 Database |
        |---------------------|---------------------|
Layer 2 |   Database image with tables but no data  |
        |-------------------------------------------|
Layer 1 |                mysql:5.6.26               |
        |-------------------------------------------|

Ma question est maintenant comment créer un fichier Docker correct pour les couches 2 et 3? Où mon fichier empty_with_tables.sql est chargé dans la couche 2 et customer1.sql et customer2.sql sont chargés dans deux images de la couche 3. mettre des fichiers SQL dans '/docker-entrypoint-initdb.d'. Cependant, les données seraient lors du premier démarrage de l’image. Ce n'est pas ce que je veux. Je veux que les données soient prêtes dans l'image (par exemple, être rapidement disponibles lors des tests).

Je pourrais démarrer l'image mysql, charger les données à partir de la ligne de commande et effectuer un "commit", mais ceci n'est pas reproductible, ce qui nécessite de répéter l'opération lorsque les données des fichiers SQL sont modifiées.

Comment cela peut-il être fait?

Meilleures salutations,

  • Morten Green Hermansen
16

Ma solution à ce problème consistait donc simplement à NE PAS superposer tous les éléments mais créer une image de base et utiliser --volumes-from pour injecter les fichiers de base de données à partir d'un conteneur contenant uniquement des données.

3

J'ai rencontré le même problème cette semaine. J'ai trouvé une solution de travail sans avoir besoin de --volumes-from

Le problème déjà mentionné est que /var/lib/mysql est un volume et, comme Docker ne supportera pas UNVOLUME dans son fichier Docker dans un avenir proche, vous ne pouvez pas utiliser cet emplacement pour votre stockage de base de données si vous souhaitez commencer avec une base de données vide. par défaut. ( https://github.com/docker/docker/issues/18287 ). C'est pourquoi j'ai écrasé etc/mysqld.my.cnf, donnant à mysql un nouveau datadir.

Avec sa réponse, vous pouvez créer un Dockerile comme ceci:

FROM mysql:5.6

ENV MYSQL_DATABASE db
ENV MYSQL_ROOT_PASSWORD pass
COPY db.sql /docker-entrypoint-initdb.d/db.sql
COPY my.cnf /etc/mysql/my.cnf
RUN /entrypoint.sh mysqld & sleep 30 && killall mysqld
RUN rm /docker-entrypoint-initdb.d/db.sql

Le seul changement qui existe dans my.cnf est l'emplacement du datadir:

.... 
[mysqld]
skip-Host-cache
skip-name-resolve
user        = mysql
pid-file    = /var/run/mysqld/mysqld.pid
socket      = /var/run/mysqld/mysqld.sock
port        = 3306
basedir     = /usr
datadir     = /var/lib/mysql2 <-- can be anything except /var/lib/mysql
tmpdir      = /tmp
lc-messages-dir = /usr/share/mysql
explicit_defaults_for_timestamp
....
17
MegaWubs

Cela ne peut pas être fait proprement comme vous le souhaitez, du moins en vous basant sur l'image mysql officielle, car vous devez communiquer avec le serveur pour importer les données et le serveur n'est pas exécuté ni initialisé (à partir de mysql docker-entrypoint.sh ) jusqu’à ce que le conteneur soit exécuté, ce qui n’est que lorsque l’image est déjà construite.

Le moyen le plus simple consiste à exécuter le processus dans le conteneur, à l'aide du script /entrypoint.sh de l'image mysql, mais vous devez prendre en compte tous les paramètres requis par le point d'entrée (comme $MYSQL_ROOT_PASSWORD), ainsi qu'un moyen propre de stopper la démon juste après l'importation des données. Quelque chose comme:

FROM mysql:5.6

ADD data.sql /docker-entrypoint-initdb.d/00-import-data.sql
ENV MYSQL_ROOT_PASSWORD somepassword
ENV MYSQL_DATABASE db1
RUN /entrypoint.sh mysqld & sleep 30 && killall mysqld

est un moyen bidirectionnel qui aboutit à une base de données pré-initialisée, mais ... cela ne fonctionne pas. La raison en est que /var/lib/mysql est déclaré en tant que volume dans le fichier Docker de MySQL, et toute modification apportée à ce répertoire au cours du processus de construction est perdue après la fin de la construction. Ceci peut être observé dans le fichier Docker suivant:

FROM mysql:5.6

RUN touch /var/lib/mysql/some-file && ls /var/lib/mysql
RUN touch /var/lib/mysql/some-file2 && ls /var/lib/mysql

Je suggère donc d’utiliser la méthode docker commit que vous avez décrite. Le résultat final est identique à celui que vous souhaitez atteindre, à l'exception peut-être de la couche 2.

UPDATE: Comme l'OP commenté ci-dessous, la validation ne contient pas non plus de volumes. Ainsi, le seul moyen semble être d’éditer MySQL Dockerfile et de supprimer VOLUME afin de conserver les données à l’intérieur du conteneur, ou de gérer les volumes séparément des conteneurs.

5
pwes

En me basant sur la réponse de MegaWubs, j’ai trouvé que ce fichier Docker était suffisant.

FROM mysql:5.6
RUN sed -i 's|/var/lib/mysql|/var/lib/mysql2|g' /etc/mysql/my.cnf
4
Chomeh

La réponse de MegaWubs est excellente, sauf pour ce "sommeil 30" qui vous oblige à deviner le temps d'exécution d'initdb . Pour éviter cela, j'ai mis un petit script Shell à exécuter après chaque autre dans /docker-entrypoint-initdb.d:

/docker-entrypoint-initdb.d/
  |- 01_my_data1.sql
  |- 02_my_data2.sql
  ...
  |- 99_last_processed_file.sh

Avec 99_last_processed_file.sh:

#!/usr/bin/env bash
touch /tmp/server_can_shutdown.txt

En parallèle, dans Dockerfile, je lance un autre script en remplacement de "sleep && killall" de Mortenn:

# Dockerfile
# ...
COPY wait_then_shutdown.sh /tmp/wait_then_shutdown.sh
RUN /entrypoint.sh mysqld & /tmp/wait_then_shutdown.sh  # <-- 
RUN rm /docker-entrypoint-initdb.d/*

Avec wait_then_shutdown.sh:

#!/usr/bin/env bash
while [ ! -f /tmp/server_can_shutdown.txt ] # <-- created by 99_last_processed_file.sh
do
  sleep 2
done
kill $(pidof mysqld)

-

Et maintenant, mysqld ne s’arrête que lorsque tous les autres fichiers sont traités dans /docker-entrypoint-initdb.d

0
Phil Sabaty