Como posso inicializar um banco de dados MySQL com esquema em um contêiner do Docker?

165

Estou tentando criar um contêiner com um banco de dados MySQL e adicionar um esquema a esses bancos de dados.

Meu Dockerfile atual é:

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 criar o contêiner, estou seguindo a documentação fornecida no Docker e executando 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

Porém, quando executo esse comando, o Container não é criado e, no status do Container, é possível ver que o CMD não foi executado com êxito; na verdade, apenas o mysqlcomando é executado.

De qualquer forma, existe uma maneira de inicializar o banco de dados com o esquema ou preciso executar essas operações manualmente?

Marcus Gomes
fonte
Eu suponho que você deseja criar uma nova imagem com um banco de dados semeado e pronto para uso?
Greg
Sim, é exatamente isso que eu quero fazer.
Marcus Gomes
Explicado aqui também: medium.com/@lvthillo/…
lvthillo 13/04/18

Respostas:

105

Sinto muito por esta resposta super longa, mas você tem um pequeno caminho a percorrer para chegar onde deseja. Eu direi que normalmente você não colocaria o armazenamento para o banco de dados no mesmo contêiner que o próprio banco de dados, montaria um volume do host para que os dados persistissem no host do docker ou talvez um contêiner pudesse ser usado para mantenha os dados (/ var / lib / mysql). Além disso, eu sou novo no mysql, portanto, isso pode não ser super eficiente. Dito isto...

Eu acho que pode haver alguns problemas aqui. O Dockerfile é usado para criar uma imagem. Você precisa executar a etapa de construção. No mínimo, no diretório que contém o Dockerfile, você faria algo como:

docker build .

O Dockerfile descreve a imagem a ser criada. Eu não sei muito sobre mysql (eu sou um fanboy do postgres), mas fiz uma pesquisa nas interwebs por 'como faço para inicializar um contêiner do mysql docker'. Primeiro, criei um novo diretório para trabalhar, chamei-o de mdir e, em seguida, criei um diretório de arquivos em que depositei um arquivo epcis_schema.sql que cria um banco de dados e uma única tabela:

create database test;
use test;

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

Então eu criei um script chamado init_db no diretório files:

#!/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

(a maior parte deste script foi retirada daqui: https://gist.github.com/pda/9697520 )

Aqui está o script files / run_db que criei:

# 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

Por fim, o Dockerfile para vincular todos eles:

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"

Então, liguei para o meu diretório mdir (que possui o Dockerfile junto com o diretório de arquivos). Eu então executo o comando:

docker build --no-cache .

Você deve ver uma saída 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

Agora você tem uma imagem '7f6d5215fe8d'. Eu poderia executar esta imagem:

docker run -d 7f6d5215fe8d

e a imagem começa, vejo uma string de instância:

4b377ac7397ff5880bc9218abe6d7eadd49505d50efb5063d6fab796ee157bd3

Eu poderia então 'pará-lo' e reiniciá-lo.

docker stop 4b377
docker start 4b377

Se você olhar para os logs, a primeira linha conterá:

docker logs 4b377

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

Então, no final dos logs:

Running with existing database in /var/lib/mysql

Essas são as mensagens do script / tmp / run_db, a primeira indica que o banco de dados foi descompactado da versão salva (inicial), a segunda indica que o banco de dados já estava lá e, portanto, a cópia existente foi usada.

Aqui está um ls -lR da estrutura de diretórios que descrevi acima. Observe que o init_db e o run_db são scripts com o bit de execução definido:

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
fonte
3
Ei Gary, primeiro obrigado por me ajudar. Eu segui suas instruções, mas quando executo a docker build --no-cache. , Tenho um erro de permissão para o init_dbscript.
Marcus Gomes
Eu já tento corrigir isso, dando permissões no RUNcomando e o problema aparentemente está resolvido. Mas quando executo docker run, tenho o mesmo problema com o run_dbscript.
Marcus Gomes
1
init_db e necessidade run_db ser 0755 permissão (eles são certificados, eles precisam ter o conjunto bit de execução Fazer uma chmod + x init_db run_db.
Greg
Atualizei a resposta com uma lista de diretórios mostrando as permissões em cada um dos arquivos. Nota: chmod + x files / * _ db realizará as permissões necessárias.
Greg
2
@ Greg Eu tentei as etapas acima, porém estou recebendo um erro ([ERRO] Erro fatal: Por favor, leia a seção "Segurança" do manual para descobrir como executar o mysqld como root!). Alguma sugestão ?
Binish John
121

Eu tive esse mesmo problema em que queria inicializar o esquema da minha instância do MySQL Docker, mas tive dificuldade em fazê-lo funcionar depois de pesquisar no Google e seguir os exemplos de outros. Aqui está como eu resolvi isso.

1) Despeje seu esquema MySQL em um arquivo.

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

2) Use o comando ADD para adicionar seu arquivo de esquema ao /docker-entrypoint-initdb.ddiretório no contêiner do Docker. O docker-entrypoint.sharquivo executará todos os arquivos deste diretório que terminem com ".sql"o banco de dados 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 a instância do Docker MySQL.

docker-compose build
docker-compose up

Obrigado por configurar o MySQL e importar o dump no Dockerfile por me informar sobre o docker-entrypoint.sh e pelo fato de ele executar scripts SQL e shell!

Brett Cooper
fonte
2
Esta é definitivamente a melhor resposta. Copiar DDLs e scripts de inicialização para o diretório /docker-entrypoint-initdb.d é o que os docs do dockerhub mysql dizem fazer com essas coisas.
chacewells
2
O que acontece quando algo muda schema.sql? Quero que meu db seja atualizado de acordo. Além disso, o que acontece quando eu construo esse contêiner pela segunda vez? O db será destruído e criado novamente?
precisa saber é o seguinte
1
@Goga Koreli Docker executa esse comando init somente se não houver nenhuma entrada de dados em / var / lib / mysql
Medtech
38

Outra maneira baseada em uma mescla de respostas servidoras aqui antes:

arquivo de encaixe-compor:

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

onde /home/user.. é uma pasta compartilhada no host

E na /home/user/db/mysql/initpasta .. basta soltar um arquivo sql, com qualquer nome, por exemplo, init.sqlcontendo:

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 acordo com a documentação oficial do mysql , você pode colocar mais de um arquivo sql no docker-entrypoint-initdb.d, eles são executados em ordem alfabética

phico
fonte
28

A outra maneira simples, use docker-compose com as seguintes linhas:

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

Coloque o esquema do banco de dados em ./database/install_db.sql. Toda vez que você criar seu contêiner, o install_db.sql será executado.

Balázs Zámbó
fonte
26

Eu tentei a resposta de Greg com zero sucesso, devo ter feito algo errado, já que meu banco de dados não tinha dados após todas as etapas: eu estava usando a imagem mais recente do MariaDB, por precaução.

Decidi ler o ponto de entrada da imagem oficial do MariaDB e usei isso para gerar um simples arquivo de composição de encaixe :

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"

Agora eu consigo persistir meus dados E gerar um banco de dados com meu próprio esquema!

mrArias
fonte
1
Oi, estou tentando também fazer o mesmo, mas o script nunca é executado. Existe algum outro parâmetro que precise ser fornecido? Acabei de adicionar este volume para mariadb - ./database/schema.sql:/docker-entrypoint-initdb.d/schema.sql:ro. Graças
Bilak
Nota: o script será executado apenas uma vez, durante a inicialização do contêiner. Se você adicionar mais tarde um novo script, ele não será executado.
ALex_hha 17/05
23

Depois de 4 de agosto de 2015, se você estiver usando a imagem oficial do mysql Docker, poderá simplesmente adicionar / copiar um arquivo no diretório /docker-entrypoint-initdb.d/ e ele será executado com o contêiner inicializado. Consulte o github: https://github.com/docker-library/mysql/commit/14f165596ea8808dfeb2131f092aabe61c967225 se você deseja implementá-lo em outras imagens de contêiner

JDWatkins
fonte
1
Esta é a resposta correta para as versões atuais da imagem oficial do Docker MSQL.
NaN
11

A solução mais fácil é usar tutum / mysql

Passo 1

docker pull tutum/mysql:5.5

Passo 2

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

Etapa 3

Fique acima de CONTAINER_ID e execute o comando docker logspara ver as informações de senha geradas.

docker logs #<CONTAINER_ID>
mainframer
fonte
Você conseguiu que isso fosse executado com êxito? Eu tenho mexido com tutum / mysql por semanas sem sucesso.
Iammesol
2
Sim. Eu uso tutum/mysql:5.5e conseguiu. docker run -d -p 3306:3306 -v /tmp:/tmp -e MYSQL_PASS="admin" -e STARTUP_SQL="/tmp/mysqldump.mysql" tutum/mysql:5.5. Você sempre pode executar o comando docker logs CONTAINER_IDpara ver as mensagens de erro se tiver encontrado problemas.
mainframer
Não consigo descobrir de onde /tmp/to_be_imported.mysqlvem o caminho . esse é um caminho no sistema operacional host? onde está um COPYou ADDpara colocar coisas no sistema de arquivos do contêiner?
Randy L
@ the0ther você não precisa COPY/ ADDporque o diretório de host / tmp está montado em / tmp do contêiner. Observe atentamente o comando que existe -v /tmp:/tmp. Pessoalmente, eu não montaria um volume simplesmente para disponibilizar o arquivo para o contêiner, mas acho que é uma escolha pessoal.
Willa
1
Para ser justo, você não deve usar imagens não oficiais, a menos que queira que isso aconteça novamente.
Dragas
3

Para aqueles que não desejam criar um script de ponto de entrada como eu, você pode realmente iniciar o mysqld no momento da construção e, em seguida, executar os comandos mysql no seu Dockerfile da seguinte maneira:

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';"

A chave aqui é enviar mysqld_safe para segundo plano com o único &sinal.

roothahn
fonte
1

Abaixo está o Dockerfile que usei com êxito para instalar o xampp, criar um MariaDB com o esquema e preencher previamente as informações usadas no 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
fonte
0

Depois de lutar um pouco com isso, dê uma olhada no Dockerfile usando volumes nomeados (db-data). É importante declarar uma vantagem na parte final, onde mencionei que o volume é[external]

Tudo funcionou muito bem dessa maneira!

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
fonte