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?

vinnylinux
fonte
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
Kuhess
fonte
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.

Rajiv
fonte
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.

Petru
fonte
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

Saman Shafigh
fonte
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.

rynop
fonte
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