Configurando o MySQL e importando o dump no Dockerfile


127

Estou tentando configurar um Dockerfile para o meu projeto LAMP, mas estou tendo alguns problemas ao iniciar o MySQL. Eu tenho as seguintes linhas no meu 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

Mas continuo recebendo este erro:

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

Alguma idéia de como configurar a criação do banco de dados e a importação de despejo durante uma compilação do Dockerfile?


Isso se deve ao fato de que cada RUNcomando é executado em um contêiner diferente. É bem explicado aqui: stackoverflow.com/questions/17891669/…
— Kuhess

Isso apenas explica que os comandos RUN têm contextos diferentes. Mas eu estou dependendo de um daemon, não de contexto.
— vinnylinux 18/09/14

1
Sim, mas isso explica por que você não pode se conectar ao MySQL. É porque, ele está rodando apenas na sua primeira RUNlinha.
— Kuhess

Para executar suas instruções SQL, é necessário iniciar o MySQL e usar o cliente MySQL no mesmo contêiner: um RUNcom várias etapas. Você pode encontrar um exemplo com uma instalação em várias etapas de um software aqui: stackoverflow.com/questions/25899912/install-nvm-in-docker/…
— Kuhess

Você também pode ter um olhar sobre serviço com janela de encaixe-compor: docs.docker.com/compose/wordpress/#build-the-project com isso, mysql pode ser anexado ao seu aplicativo
— aurny2420289

Respostas:


122

Cada RUNinstrução em a Dockerfileé executada em uma camada diferente (conforme explicado na documentação deRUN ).

No seu Dockerfile, você tem três RUNinstruções. O problema é que o servidor MySQL é iniciado apenas no primeiro. Nos outros, nenhum MySQL está sendo executado, é por isso que você obtém seu erro de conexão com o mysqlcliente.

Para resolver este problema, você tem 2 soluções.

Solução 1: use uma linha 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

Solução 2: use um script

Crie um script executável 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

Adicione estas linhas ao seu Dockerfile:

ADD init_db.sh /tmp/init_db.sh
RUN /tmp/init_db.sh

Você tem alguma idéia de como posso manter essa informação? Os bancos de dados e tabelas que estou criando na compilação do Dockerfile não estão disponíveis quando executo coisas em contêineres. :(
— vinnylinux

1
Depende do que você quer dizer com persistir. Se você deseja que os dados persistam em várias docker runexecuções, é necessário montar volumes. Se você quiser apenas ter um contêiner com seu despejo, mas não quiser persistir em outras modificações, poderá se livrar das VOLUMEinstruções contidas no seu arquivo Dockerfile.
— Kuhess 18/09/14

16
Recebo ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)ao tentar a Solução 1. Seguindo os logs mysqld_safe Starting mysqld daemon with databases from /var/lib/mysqlemysqld_safe mysqld from pid file /var/run/mysqld/mysqld.pid ended
— Vituel

1
Eu acho muito melhor ter um script checado na inicialização para ver se o mysql criou um banco de dados. Se houver, deixe em branco, caso contrário, execute mysql_init_dbe carregue na estrutura do banco de dados. Isso permite a flexibilidade de redefinir o banco de dados automaticamente durante o teste, mas se você deseja persistir ou testar conjuntos de dados diferentes, basta montar um volume em / var / lib / mysql usando docker run -v ....
— tu-Restabelece Monica-dor duh

1
@ Trevorgrayson - Isso também não funciona. Agora dá erro ERROR 2003 (HY000): Não é possível conectar ao servidor MySQL no '127.0.0.1' (111)
— Profundo

166

A versão mais recente da imagem oficial do docker mysql permite importar dados na inicialização. Aqui está o meu 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

Aqui, eu tenho meu data-dump.sql sob o docker/dataqual é relativo à pasta da qual o docker-compose está sendo executado. Estou montando esse arquivo sql neste diretório /docker-entrypoint-initdb.dno contêiner.

Se você estiver interessado em ver como isso funciona, dê uma olhada no docker-entrypoint.shGitHub. Eles adicionaram este bloco para permitir a importação de dados

    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

Uma observação adicional: se você deseja que os dados sejam mantidos mesmo após a parada e remoção do contêiner mysql, é necessário ter um contêiner de dados separado, como você vê no docker-compose.yml. O conteúdo do Dockerfile do contêiner de dados é muito simples.

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

VOLUME /var/lib/mysql

CMD ["true"]

O contêiner de dados nem precisa estar no estado inicial para persistência.


1
Esta é a melhor resposta IMHO. Permite NÃO criar uma imagem por conta própria. Obrigado por esta dica.
— Metal3d

5
Uma pequena alteração nos volumes: - - ./docker/data:/docker-entrypoint-initdb.dremoveu o diretório .na frente do contêiner.
— David Sinclair

7
Embora este seja o procedimento correto, ele não trata muito bem o caso de uso. Uma das vantagens do Docker é que você pode criar um ambiente muito rapidamente. Se você precisar aguardar de 3 a 4 minutos enquanto o Docker importa um banco de dados MySQL durante a inicialização, você perde essa vantagem. O objetivo é ter um contêiner que já contenha os dados no banco de dados, para que você possa usá-lo o mais rápido possível.
— Garreth McDaid

3
esta resposta ainda está funcionando com as versões mais recentes? parece não funcionar mais #
— Daniel Daniel

2
Observe que o exemplo do docker-compose aqui é v2 e não v3. "volumes_from:" não é suportado na v3.
— Ernest

37

O que fiz foi baixar o meu sql dump em uma pasta "db-dump" e montá-lo:

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

Quando executo docker-compose uppela primeira vez, o dump é restaurado no banco de dados.


3
+1, com um acréscimo: o link para o script que está sendo executado para uma melhor compreensão do que está realmente acontecendo: github.com/docker-library/mariadb/blob/...
— Tom Imrei

6
O mais recente mysqlparece não carregar o arquivo sql despejado e, mesmo usando o 5.6, tenho problemas com os mecanismos de armazenamento do InnoDb.
— zinking 14/10

1
mesmo aqui, ele tem essas instruções e eu até vi no log que está carregando arquivos init, mas o db está vazio!
— holms

11

Eu usei a abordagem docker-entrypoint-initdb.d (Graças a @Kuhess) Mas, no meu caso, quero criar meu banco de dados com base em alguns parâmetros definidos no arquivo .env, então fiz isso

1) Primeiro, defino o arquivo .env no diretório do projeto raiz do docker

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

2) Em seguida, defino meu arquivo docker-compose.yml. Então, usei a diretiva args para definir minhas variáveis ​​de ambiente e as defini no arquivo .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) Em seguida, defino uma pasta mysql que inclui um Dockerfile. Então o Dockerfile é esse

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) Agora eu uso o mysqldump para despejar meu db e colocar o data.sql dentro da pasta mysql

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

O arquivo é apenas um arquivo de despejo de sql normal, mas adiciono duas linhas no início para que o arquivo fique assim

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

Então, o que está acontecendo é que eu usei o comando "RUN sed -i 's / MYSQL_DATABASE /' $ MYSQL_DATABASE '/ g' /etc/mysql/data.sql" para substituir o MYSQL_DATABASEespaço reservado pelo nome do meu banco de dados em que eu o configurei arquivo .env.

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

Agora você está pronto para criar e executar seu contêiner


Eu gosto da abordagem de usar um .envarquivo. No entanto, de acordo com isso , o .envrecurso de arquivo funciona apenas quando você usa o docker-compose upcomando e não funciona docker stack deploy. Portanto, caso você queira usar o .envarquivo em produção, verifique os segredos da janela de encaixe que foram introduzidos docker 17.06. Então você pode usar o arquivo .env em conjunto com segredos tanto no desenvolvimento docker compose upe produção docker stack deployfases
— ira

Boa abordagem, mas eu sugiro usar RUN sed -i '1s/^/CREATE DATABASE IF NOT EXISTS $ MYSQL_DATABASE ;\nUSE $ MYSQL_DATABASE em ;\n/' data.sqlvez do sedcomando que você sugeriu. Dessa forma, você pode usar qualquer arquivo de despejo que você possui - é útil se você estiver trabalhando com grande quantidade de dados :) #
— Tomasz Kapłoński

9

Aqui está uma versão de trabalho usando v3of docker-compose.yml. A chave é a diretiva volumes :

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

No diretório que eu tenho o meu docker-compose.yml, tenho um datadiretório que contém .sqlarquivos de despejo. Isso é bom porque você pode ter um .sqlarquivo de despejo por tabela.

Eu simplesmente corro docker-compose upe estou pronto para ir. Os dados persistem automaticamente entre as paradas. Se você quiser remover os dados e "sugar" novos .sqlarquivos executado docker-compose downem seguida docker-compose up.

Se alguém souber como fazer com que a mysqljanela de encaixe processe novamente os arquivos /docker-entrypoint-initdb.dsem remover o volume, deixe um comentário e atualizarei esta resposta.


2
esta resposta me ajudou. a nota docker-compose downfoi muito importante, pois eu não estava vendo alterações, apesar de reiniciar o docker-compondo. MYSQL_DATABASE é uma variável necessária neste caso
— nxmohamad 22/02/19
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.