Configurar MySQL e importar volcado dentro de Dockerfile

127

Estoy tratando de configurar un Dockerfile para mi proyecto LAMP, pero tengo algunos problemas al iniciar MySQL. Tengo las siguientes líneas en mi 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

Pero sigo recibiendo este error:

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

¿Alguna idea sobre cómo configurar la creación de bases de datos y la importación de volcados durante una compilación de Dockerfile?

vinnylinux
fuente
Esto se debe al hecho de que cada RUNcomando se ejecuta en un contenedor diferente. Está bien explicado aquí: stackoverflow.com/questions/17891669/…
Kuhess
Esto simplemente explica que los comandos RUN tienen contextos diferentes. Pero estoy dependiendo de un demonio, no del contexto.
vinnylinux
1
Sí, pero eso explica por qué no puede conectarse a MySQL. Es porque, solo se está ejecutando en su primera RUNlínea.
Kuhess
Para ejecutar sus sentencias SQL, necesita iniciar MySQL y usar el cliente MySQL en el mismo contenedor: uno RUNcon varios pasos. Puede encontrar un ejemplo con una instalación de varios pasos de un software aquí: stackoverflow.com/questions/25899912/install-nvm-in-docker/…
Kuhess
También puede echar un vistazo al servicio con docker-compose: docs.docker.com/compose/wordpress/#build-the-project con eso, mysql se puede adjuntar a su aplicación
aurny2420289

Respuestas:

122

Cada RUNinstrucción en a Dockerfilese ejecuta en una capa diferente (como se explica en la documentación deRUN ).

En tu Dockerfile, tienes tres RUNinstrucciones. El problema es que el servidor MySQL solo se inicia en el primero. En los demás, no se está ejecutando MySQL, es por eso que obtiene su error de conexión con el mysqlcliente.

Para resolver este problema tienes 2 soluciones.

Solución 1: use una línea 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

Solución 2: use un script

Crea un script ejecutable 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

Agregue estas líneas a su Dockerfile:

ADD init_db.sh /tmp/init_db.sh
RUN /tmp/init_db.sh
Kuhess
fuente
¿Tienes alguna idea de cómo puedo persistir esa información? Las bases de datos y las tablas que estoy creando en la compilación de Dockerfile no están disponibles cuando ejecuto cosas en contenedores. :(
vinnylinux
1
Depende de lo que quieras decir con persistir. Si desea que los datos persistan en varias docker runejecuciones, debe montar volúmenes. Si solo desea tener un contenedor con su volcado, pero no desea continuar con las modificaciones adicionales, puede deshacerse de las VOLUMEinstrucciones en su Dockerfile.
Kuhess
16
Obtengo ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)cuando intento la Solución 1. Siguiendo los registros mysqld_safe Starting mysqld daemon with databases from /var/lib/mysqlymysqld_safe mysqld from pid file /var/run/mysqld/mysqld.pid ended
Vituel
1
Creo que es mucho mejor tener una comprobación de script en el inicio para ver si mysql ha creado una base de datos. Si es así, déjelo solo, de lo contrario, ejecute mysql_init_dby cargue en la estructura de su base de datos. Esto permite la flexibilidad de restablecer automáticamente la base de datos durante la prueba, pero si desea que persista o pruebe diferentes conjuntos de datos, simplemente monte un volumen en / var / lib / mysql usando docker run -v ....
tu-Reinstate Monica-dor duh
1
@trevorgrayson - Eso tampoco funciona. Ahora se da error ERROR 2003 (HY000): No se puede conectar al servidor MySQL en '127.0.0.1' (111)
Profundo
166

La última versión de la imagen oficial de MySQL Docker le permite importar datos al inicio. Aquí está mi 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

Aquí, tengo mi data-dump.sql en docker/datarelación con la carpeta desde la que se ejecuta docker-compose. Estoy montando ese archivo sql en este directorio /docker-entrypoint-initdb.den el contenedor.

Si está interesado en ver cómo funciona esto, eche un vistazo a su docker-entrypoint.shen GitHub. Han agregado este bloque para permitir la importación de datos.

    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

Una nota adicional, si desea que los datos se conserven incluso después de que el contenedor mysql se detenga y se elimine, debe tener un contenedor de datos separado como se ve en docker-compose.yml. El contenido del contenedor de datos Dockerfile es muy simple.

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

VOLUME /var/lib/mysql

CMD ["true"]

El contenedor de datos ni siquiera tiene que estar en estado de inicio para persistencia.

Rajiv
fuente
1
Esta es la mejor respuesta en mi humilde opinión. Permite NO crear una imagen nosotros mismos. Gracias por este consejo
Metal3d
55
Un cambio menor para los volúmenes: se - ./docker/data:/docker-entrypoint-initdb.deliminó el .directorio frente al contenedor.
David Sinclair
77
Si bien este es el procedimiento correcto, no aborda el caso de uso muy bien. Una de las ventajas de Docker es que puede activar un entorno muy rápidamente. Si tiene que esperar de 3 a 4 minutos mientras Docker importa una base de datos MySQL durante el inicio, pierde esta ventaja. El objetivo es tener un contenedor que ya contenga los datos en la base de datos, para que pueda usarlo lo más rápido posible.
Garreth McDaid el
3
¿Esta respuesta sigue funcionando con las últimas versiones? parece que ya no funciona
Daniel
2
Tenga en cuenta que el ejemplo de docker-compose aquí es v2 no v3. "volume_from:" no es compatible con v3.
Ernest
37

Lo que hice fue descargar mi volcado de sql en una carpeta "db-dump" y montarlo:

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

Cuando ejecuto docker-compose uppor primera vez, el volcado se restaura en la base de datos.

Petru
fuente
3
+1, con una adición: el enlace al script que se ejecuta para comprender mejor lo que realmente está sucediendo: github.com/docker-library/mariadb/blob/…
Tom Imrei
66
mysqlParece que lo último no carga el archivo sql volcado, e incluso usando 5.6 tengo un problema con los motores de almacenamiento InnoDb.
Zinking
1
Lo mismo aquí, tiene tal instrucción e incluso vi en el registro que está cargando archivos init, ¡pero db está vacío!
holms
11

Usé el enfoque docker-entrypoint-initdb.d (Gracias a @Kuhess) Pero en mi caso quiero crear mi base de datos basada en algunos parámetros que definí en el archivo .env, así que hice estos

1) Primero defino el archivo .env como este en mi directorio de proyectos raíz de Docker

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

2) Luego defino mi archivo docker-compose.yml. Entonces utilicé la directiva args para definir mis variables de entorno y las configuré desde el archivo .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) Luego defino una carpeta mysql que incluye un Dockerfile. Entonces el Dockerfile es este

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) Ahora uso mysqldump para volcar mi base de datos y poner data.sql dentro de la carpeta mysql

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

El archivo es solo un archivo de volcado sql normal pero agrego 2 líneas al principio para que el archivo se vea así

--
-- 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 ...;
...
...
...

Entonces, ¿qué sucede? Utilicé el comando "EJECUTAR sed -i / MYSQL_DATABASE / '$ MYSQL_DATABASE' / g '/etc/mysql/data.sql" para reemplazar el MYSQL_DATABASEmarcador de posición con el nombre de mi DB en el que lo configuré archivo .env.

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

Ahora está listo para construir y ejecutar su contenedor

Saman Shafigh
fuente
Me gusta el enfoque de usar un .envarchivo. Sin embargo, de acuerdo con esto , la .envfunción de archivo solo funciona cuando usa el docker-compose upcomando y no funciona docker stack deploy. Por lo tanto, en caso de que desee utilizar el .envarchivo en producción, es posible que desee consultar los secretos de Docker que se introdujeron con docker 17.06. Entonces puede usar su archivo .env en conjunción con secretos en docker compose uplas docker stack deployfases de desarrollo y producción
ira
Buen enfoque, pero sugeriría usar RUN sed -i '1s/^/CREATE DATABASE IF NOT EXISTS $ MYSQL_DATABASE ;\nUSE $ MYSQL_DATABASE;\n/' data.sql lugar del sedcomando que sugirió. De esta manera puede usar cualquier archivo de volcado que tenga, es útil si está trabajando con una gran cantidad de datos :)
Tomasz Kapłoński
9

Aquí está utilizando una versión de trabajo v3de docker-compose.yml. La clave es la directiva de volúmenes :

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

En el directorio que tengo docker-compose.yml, tengo un datadirectorio que contiene .sqlarchivos de volcado. Esto es bueno porque puede tener un .sqlarchivo de volcado por tabla.

Simplemente corro docker-compose upy estoy listo para ir. Los datos persisten automáticamente entre paradas. Si desea eliminar los datos y "absorber" los nuevos .sqlarchivos ejecutados, docker-compose downentoncesdocker-compose up .

Si alguien sabe cómo hacer que la mysqlventana acoplable vuelva a procesar los archivos /docker-entrypoint-initdb.dsin eliminar el volumen, deje un comentario y actualizaré esta respuesta.

rynop
fuente
2
Esta respuesta me ayudó. la nota docker-compose downfue muy importante ya que no veía cambios a pesar de reiniciar docker-compose. MYSQL_DATABASE es una variable requerida en este caso
nxmohamad