Настройка MySQL и импорт дампа в Dockerfile

127

Я пытаюсь настроить Dockerfile для своего проекта LAMP, но при запуске MySQL возникают некоторые проблемы. В моем 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

Но я продолжаю получать эту ошибку:

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

Есть идеи о том, как настроить создание базы данных и импорт дампа во время сборки Dockerfile?

vinnylinux
источник
Это связано с тем, что каждая RUNкоманда выполняется в другом контейнере. Это хорошо объяснено здесь: stackoverflow.com/questions/17891669/…
Kuhess
Это просто объясняет, что команды RUN имеют разные контексты. Но я зависим от демона, а не от контекста.
vinnylinux
1
Да, но это объясняет, почему вы не можете подключиться к MySQL. Это потому, что он работает только в вашей первой RUNстроке.
Kuhess
Чтобы выполнить ваши операторы SQL, вам необходимо запустить MySQL и использовать клиент MySQL в том же контейнере: один RUNс несколькими шагами. Вы можете найти пример многоэтапной установки программного обеспечения здесь: stackoverflow.com/questions/25899912/install-nvm-in-docker/…
Kuhess
Вы также можете взглянуть на службу с помощью docker-compose: docs.docker.com/compose/wordpress/#build-the-project с этим, mysql может быть прикреплен к вашему приложению
aurny2420289

Ответы:

121

Каждая RUNинструкция в a Dockerfileвыполняется на отдельном уровне (как описано в документацииRUN ).

В вашем у Dockerfileвас есть три RUNинструкции. Проблема в том, что сервер MySQL запускается только в первый раз. В других MySQL не работает, поэтому вы получаете ошибку соединения с mysqlклиентом.

Для решения этой проблемы у вас есть 2 решения.

Решение 1.Используйте однострочную 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

Решение 2.Используйте сценарий

Создайте исполняемый скрипт 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

Добавьте эти строки в свой Dockerfile:

ADD init_db.sh /tmp/init_db.sh
RUN /tmp/init_db.sh
Kuhess
источник
Вы знаете, как я могу сохранить эту информацию? Базы данных и таблицы, которые я создаю в сборке Dockerfile, недоступны, когда я запускаю что-то в контейнерах. :(
vinnylinux
1
Это зависит от того, что вы имеете в виду под настойчивостью. Если вы хотите, чтобы данные сохранялись во время нескольких docker runзапусков, вам необходимо смонтировать тома. Если вы просто хотите иметь контейнер со своим дампом, но не хотите сохранять дальнейшие модификации, вы можете избавиться от VOLUMEинструкции в вашем Dockerfile.
Kuhess
16
Я получаю ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)при попытке решения 1. Следуя журналам mysqld_safe Starting mysqld daemon with databases from /var/lib/mysqlиmysqld_safe mysqld from pid file /var/run/mysqld/mysqld.pid ended
Витуэль
1
Я считаю, что гораздо лучше проверить сценарий при запуске, чтобы узнать, создал ли mysql базу данных. Если да, то оставьте его в покое, иначе запустите mysql_init_dbи загрузите в структуру своей базы данных. Это обеспечивает гибкость автоматического сброса базы данных при тестировании, но если вы хотите, чтобы она сохраняла или тестировала разные наборы данных, вы просто монтируете том в / var / lib / mysql, используя docker run -v ....
tu-Reinstate Monica-dor duh
1
@trevorgrayson - Это тоже не работает. Теперь он выдает ошибку ERROR 2003 (HY000): не удается подключиться к серверу MySQL на 127.0.0.1 (111)
Deep
166

Последняя версия официального образа докеров mysql позволяет импортировать данные при запуске. Вот мой 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

Здесь у меня есть файл data-dump.sql docker/data, относящийся к папке, из которой выполняется docker-compose. Я монтирую этот sql-файл в этот каталог /docker-entrypoint-initdb.dконтейнера.

Если вам интересно узнать, как это работает, посмотрите их docker-entrypoint.shна GitHub. Они добавили этот блок, чтобы разрешить импорт данных

    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

Дополнительное примечание: если вы хотите, чтобы данные сохранялись даже после остановки и удаления контейнера mysql, вам необходимо иметь отдельный контейнер данных, как вы видите в docker-compose.yml. Содержимое контейнера данных Dockerfile очень простое.

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

VOLUME /var/lib/mysql

CMD ["true"]

Контейнер данных даже не должен находиться в начальном состоянии для сохранения.

Раджив
источник
1
ИМХО, это лучший ответ. Это позволяет НЕ создавать образ самому. Спасибо за этот совет.
Metal3d
5
Одно небольшое изменение для томов: - - ./docker/data:/docker-entrypoint-initdb.dубран .перед каталогом контейнера.
Дэвид Синклер,
7
Хотя это правильная процедура, она не очень хорошо подходит для варианта использования. Одним из преимуществ Docker является то, что вы можете очень быстро развернуть среду. Если вам нужно подождать 3-4 минуты, пока Docker импортирует базу данных MySQL во время запуска, вы теряете это преимущество. Цель состоит в том, чтобы иметь контейнер, который уже содержит данные в базе данных, чтобы вы могли использовать его как можно быстрее.
Гаррет МакДэйд, 06
3
этот ответ все еще работает с последними версиями? похоже, больше не работает
Daniel
2
Обратите внимание, что пример docker-compose здесь v2, а не v3. "volume_from:" не поддерживается в v3.
Эрнест
37

Я загрузил свой sql-дамп в папку "db-dump" и смонтировал его:

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

При docker-compose upпервом запуске дамп восстанавливается в бд.

Петр
источник
3
+1, с одним дополнением: ссылка на запускаемый скрипт для лучшего понимания того, что на самом деле происходит: github.com/docker-library/mariadb/blob/…
Tom Imrei 05
6
latest, mysqlкажется, не загружает сброшенный файл sql, и даже при использовании 5.6 у меня проблемы с механизмами хранения InnoDb.
zinking
1
То же самое здесь, у него есть такая инструкция, и я даже видел в журнале загрузку файлов инициализации, но db пуст!
Holms
11

Я использовал подход docker-entrypoint-initdb.d (спасибо @Kuhess) Но в моем случае я хочу создать свою БД на основе некоторых параметров, которые я определил в файле .env, поэтому я сделал это

1) Сначала я определяю файл .env примерно так в моем корневом каталоге проекта докера.

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

2) Затем я определяю свой файл docker-compose.yml. Поэтому я использовал директиву args для определения переменных среды и установил их из файла .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) Затем я определяю папку mysql, которая включает файл Dockerfile. Итак, Dockerfile - это

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) Теперь я использую mysqldump для дампа моей базы данных и помещаю data.sql в папку mysql.

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

Это обычный файл дампа sql, но я добавляю две строки в начале, чтобы файл выглядел так

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

Итак, что случилось, я использовал команду «RUN sed -i 's / MYSQL_DATABASE /' $ MYSQL_DATABASE '/ g' /etc/mysql/data.sql», чтобы заменить MYSQL_DATABASEзаполнитель на имя моей БД, которое я установил в .env файл.

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

Теперь вы готовы построить и запустить свой контейнер.

Саман Шафиг
источник
Мне нравится подход к использованию .envфайла. Однако в соответствии с этим , то .envфункция файл работает только при использовании docker-compose upкоманды и не работает с docker stack deploy. Таким образом, если вы хотите использовать .envфайл в производстве, вы можете проверить секреты докеров , представленные в docker 17.06. Затем вы можете использовать свой .env файл в конъюнкции с секретами как в области развития docker compose upи производства docker stack deployфаз
IRA
Хороший подход, но я бы предложил использовать RUN sed -i '1s/^/CREATE DATABASE IF NOT EXISTS $ MYSQL_DATABASE ;\nUSE $ MYSQL_DATABASE ;\n/' data.sqlвместо sedпредложенной вами команды. Таким образом, вы можете использовать любой имеющийся у вас файл дампа - это полезно, если вы работаете с большим объемом данных :)
Томаш Каплоньски
9

Вот рабочая версия , использующая v3в docker-compose.yml. Ключевым моментом является директива объемов :

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

В каталоге, который у меня есть, у docker-compose.ymlменя есть каталог data, содержащий .sqlфайлы дампа. Это хорошо, потому что можно.sql файл дампа для каждой таблицы.

Я просто бегу, docker-compose upи мне хорошо. Данные автоматически сохраняются между остановками. Если вы хотите удалить данные и «засосать» новые .sqlфайлы, запустите docker-compose downтогда docker-compose up.

Если кто-нибудь знает, как заставить mysqlдокер повторно обрабатывать файлы /docker-entrypoint-initdb.dбез удаления тома, оставьте комментарий, и я обновлю этот ответ.

rynop
источник
2
этот ответ мне помог. примечание docker-compose downбыло очень важно, поскольку я не видел никаких изменений, несмотря на перезапуск docker-compose. MYSQL_DATABASE - обязательная переменная в этом случае
nxmohamad
0

Мне не очень нравится принятый ответ Кухесса, поскольку он sleep 5кажется мне немного хакерским, поскольку предполагает, что демон mysql db правильно загрузился в течение этого периода времени. Это предположение, никаких гарантий. Также, если вы используете предоставленный образ докера mysql, сам образ уже позаботится о запуске сервера; Я бы не стал вмешиваться в это с кастомом /usr/bin/mysqld_safe.

Я следил за другими ответами здесь и скопировал сценарии bash и sql в папку /docker-entrypoint-initdb.d/в контейнере докеров, так как это явно предполагаемый способ поставщиком изображений mysql. Все в этой папке выполняется, как только демон db готов, поэтому вы можете на него положиться.

В дополнение к другим - поскольку ни один другой ответ явно не упоминает об этом: помимо сценариев sql вы также можете копировать сценарии bash в эту папку что может дать вам больше контроля.

Это то, что мне было нужно, например, поскольку мне также нужно было импортировать дамп, но одного дампа было недостаточно, поскольку он не указывал, в какую базу данных он должен импортировать. Итак, в моем случае у меня есть сценарий db_custom_init.shс таким содержанием:

mysql -u root -p$MYSQL_ROOT_PASSWORD -e 'create database my_database_to_import_into'
mysql -u root -p$MYSQL_ROOT_PASSWORD my_database_to_import_into < /home/db_dump.sql

и этот Dockerfile копирует этот скрипт:

FROM mysql/mysql-server:5.5.62
ENV MYSQL_ROOT_PASSWORD=XXXXX
COPY ./db_dump.sql /home/db_dump.sql
COPY ./db_custom_init.sh /docker-entrypoint-initdb.d/

edit: Обратите внимание, что это будет импортировать дамп sql только во время создания контейнера, а не во время сборки образа!

onoSendai
источник