¿Cómo puedo inicializar una base de datos MySQL con esquema en un contenedor Docker?

165

Estoy tratando de crear un contenedor con una base de datos MySQL y agregar un esquema a estas bases de datos.

Mi Dockerfile actual es:

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

Para crear el contenedor, sigo la documentación proporcionada en Docker y ejecuto este comando:

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

Pero cuando ejecuto este comando, el Contenedor no se crea y en el estado del Contenedor es posible ver que el CMD no se ejecutó con éxito, de hecho solo mysqlse ejecuta el comando.

De todos modos, ¿hay alguna manera de inicializar la base de datos con el esquema o necesito realizar estas operaciones manualmente?

Marcus Gomes
fuente
¿Supongo que desea crear una nueva imagen con una base de datos sembrada y lista para usar?
Greg
Sí, eso es exactamente lo que quiero hacer.
Marcus Gomes
Explicado aquí también: medium.com/@lvthillo/…
lvthillo

Respuestas:

105

Lamento esta respuesta súper larga, pero tienes un pequeño camino por recorrer para llegar a donde quieres. Diré que normalmente no colocaría el almacenamiento para la base de datos en el mismo contenedor que la base de datos en sí, montaría un volumen de host para que los datos persistieran en el host del acoplador o, quizás, se podría utilizar un contenedor para mantener los datos (/ var / lib / mysql). Además, soy nuevo en mysql, por lo tanto, esto podría no ser súper eficiente. Dicho eso ...

Creo que puede haber algunos problemas aquí. El Dockerfile se usa para crear una imagen. Necesita ejecutar el paso de compilación. Como mínimo, desde el directorio que contiene el Dockerfile debería hacer algo como:

docker build .

El Dockerfile describe la imagen a crear. No sé mucho acerca de mysql (soy un fanático de postgres), pero hice una búsqueda en los interwebs para "cómo inicializo un contenedor de docker mysql". Primero creé un nuevo directorio para trabajar, lo llamé mdir, luego creé un directorio de archivos en el que deposité un archivo epcis_schema.sql que crea una base de datos y una sola tabla:

create database test;
use test;

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

Luego creé un script llamado init_db en el directorio de archivos:

#!/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 mayor parte de este script se levantó de aquí: https://gist.github.com/pda/9697520 )

Aquí está el script files / run_db que creé:

# 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

Finalmente, el Dockerfile para unirlos a todos:

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"

Entonces, ingresé a mi directorio mdir (que tiene el Dockerfile junto con el directorio de archivos). Luego ejecuto el comando:

docker build --no-cache .

Debería ver una salida como esta:

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

Ahora tiene una imagen '7f6d5215fe8d'. Podría ejecutar esta imagen:

docker run -d 7f6d5215fe8d

y la imagen comienza, veo una cadena de instancia:

4b377ac7397ff5880bc9218abe6d7eadd49505d50efb5063d6fab796ee157bd3

Entonces podría 'detenerlo' y reiniciarlo.

docker stop 4b377
docker start 4b377

Si observa los registros, la primera línea contendrá:

docker logs 4b377

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

Luego, al final de los registros:

Running with existing database in /var/lib/mysql

Estos son los mensajes del script / tmp / run_db, el primero indica que la base de datos se desempaquetó de la versión guardada (inicial), el segundo indica que la base de datos ya estaba allí, por lo que se utilizó la copia existente.

Aquí hay un ls -lR de la estructura de directorios que describo anteriormente. Tenga en cuenta que init_db y run_db son scripts con el conjunto de bits de ejecución:

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
fuente
3
Hola Gary, primero gracias por ayudarme. Seguí tus instrucciones, pero cuando ejecuto Docker Build --no-cache. , Tengo un error de permiso para el init_dbscript.
Marcus Gomes
Ya trato de arreglar eso dando permisos en el RUNcomando y el problema aparentemente está resuelto. Pero luego, cuando ejecuto docker run, tengo el mismo problema con el run_dbscript.
Marcus Gomes
1
init_db y run_db necesitan tener permiso 0755 (son scripts, necesitan tener el bit de ejecución establecido. Hacer un chmod + x init_db run_db
Greg
Actualicé la respuesta con una lista de directorio que muestra los permisos en cada uno de los archivos. Nota: chmod + x files / * _ db logrará los permisos necesarios.
Greg
2
@ Greg Probé los pasos anteriores, sin embargo, recibo un error ([ERROR] Error grave: lea la sección "Seguridad" del manual para saber cómo ejecutar mysqld como root). Alguna sugerencia ?
Binish John
121

Tuve el mismo problema en el que quería inicializar el esquema de mi instancia de MySQL Docker, pero tuve dificultades para hacer que esto funcionara después de buscar en Google y seguir los ejemplos de otros. Así es como lo resolví.

1) Volcar su esquema MySQL a un archivo.

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

2) Use el comando AGREGAR para agregar su archivo de esquema al /docker-entrypoint-initdb.ddirectorio en el contenedor Docker. El docker-entrypoint.sharchivo ejecutará cualquier archivo en este directorio que termine en ".sql"la base de datos 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) Inicie la instancia de Docker MySQL.

docker-compose build
docker-compose up

¡Gracias a Configurar MySQL e importar el volcado dentro de Dockerfile por darme pistas sobre docker-entrypoint.sh y el hecho de que ejecuta scripts SQL y shell!

Brett Cooper
fuente
2
Esta es definitivamente la mejor respuesta. Copiar DDL y scripts de inicialización en el directorio /docker-entrypoint-initdb.d es lo que los documentos dockerhub mysql dicen hacer con tales cosas.
chacewells
2
¿Qué sucede cuando algo cambia schema.sql? Quiero que mi base de datos se actualice en consecuencia. Además, ¿qué sucede cuando construyo este contenedor por segunda vez? ¿DB será destruido y creado de nuevo?
Goga Koreli
1
@Goga Koreli Docker ejecuta este comando de inicio solo si no hay entrada de datos en / var / lib / mysql
medTech
38

Otra forma basada en una combinación de varias respuestas aquí antes:

docker-compose file:

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

dónde /home/user ... es una carpeta compartida en el host

Y en la /home/user/db/mysql/initcarpeta ... simplemente suelte un archivo sql, con cualquier nombre, por ejemplo que init.sqlcontenga:

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 ...
[ ... ]

De acuerdo con la documentación oficial de mysql , puede colocar más de un archivo sql en docker-entrypoint-initdb.d, se ejecutan en orden alfabético

phico
fuente
28

La otra forma simple, usa docker-compose con las siguientes líneas:

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

Coloque el esquema de su base de datos en ./database/install_db.sql. Cada vez que construya su contenedor, se ejecutará install_db.sql.

Balázs Zámbó
fuente
26

He intentado la respuesta de Greg con cero éxito, debo haber hecho algo mal ya que mi base de datos no tenía datos después de todos los pasos: estaba usando la última imagen de MariaDB, por si acaso.

Luego decidí leer el punto de entrada para la imagen oficial de MariaDB , y lo usé para generar un archivo simple de 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"

¡Ahora puedo conservar mis datos Y generar una base de datos con mi propio esquema!

mrArias
fuente
1
Hola, estoy intentando hacer lo mismo, pero el script nunca se ejecuta. ¿Hay algún otro parámetro que deba proporcionarse? Acabo de agregar este volumen para mariadb - ./database/schema.sql:/docker-entrypoint-initdb.d/schema.sql:ro. Gracias
bilak
Nota: el script se ejecutará solo una vez, durante la inicialización del contenedor. Si luego agrega un nuevo script, no se ejecutará.
ALex_hha
23

Después del 4 de agosto de 2015, si está utilizando la imagen oficial de MySQL Docker, puede AGREGAR / COPIAR un archivo en el directorio /docker-entrypoint-initdb.d/ y se ejecutará con el contenedor inicializado. Consulte github: https://github.com/docker-library/mysql/commit/14f165596ea8808dfeb2131f092aabe61c967225 si desea implementarlo en otras imágenes de contenedor

JDWatkins
fuente
1
Esta es la respuesta correcta para las versiones actuales de la imagen oficial de Docker MSQL.
NaN
11

La solución más fácil es usar tutum / mysql

Paso 1

docker pull tutum/mysql:5.5

Paso 2

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

Paso 3

Supera el CONTAINER_ID y luego ejecuta el comando docker logspara ver la información de contraseña generada.

docker logs #<CONTAINER_ID>
mainframer
fuente
¿Has conseguido que esto se ejecute correctamente? He estado jugando con tutum / mysql durante semanas sin éxito.
Iammesol
2
Si. Yo uso tutum/mysql:5.5y tuvo éxito. docker run -d -p 3306:3306 -v /tmp:/tmp -e MYSQL_PASS="admin" -e STARTUP_SQL="/tmp/mysqldump.mysql" tutum/mysql:5.5. Siempre puede ejecutar el comando docker logs CONTAINER_IDpara ver los mensajes de error si tiene problemas.
mainframer
No puedo entender de dónde /tmp/to_be_imported.mysqlviene el camino . ¿Es esa una ruta en el sistema operativo host? ¿Dónde está un COPYo ADDpara poner cosas en el sistema de archivos del contenedor?
Randy L
@ the0ther no necesita COPY/ ADDporque el directorio de host / tmp está montado en / tmp del contenedor. Mire de cerca el comando allí está la parte -v /tmp:/tmp. Personalmente, no montaría un volumen simplemente para poner el archivo a disposición del contenedor, pero supongo que es una elección personal.
Willa
1
Para ser justos, no debe usar imágenes no oficiales, a menos que desee que esto vuelva a suceder.
Dragas
3

Para aquellos que no desean crear un script de punto de entrada como yo, en realidad pueden iniciar mysqld en el tiempo de compilación y luego ejecutar los comandos mysql en su Dockerfile de la siguiente manera:

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 clave aquí es enviar mysqld_safe a segundo plano con el &signo único .

roothahn
fuente
1

A continuación se muestra el Dockerfile que utilicé con éxito para instalar xampp, crear un MariaDB con esquema y rellenado previamente con la información utilizada en el servidor local (usrs, pedidos de fotos, 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
fuente
0

Luego de luchar un poco con eso, eche un vistazo al Dockerfile usando volúmenes con nombre (db-data). Es importante declarar un plus en la parte final, donde mencioné que el volumen es[external]

¡Todo funcionó muy bien de esta manera!

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
fuente