Как создать заполненный MySQL Docker Image во время сборки

Я хотел бы создать образ Docker MySQL с уже заполненными данными.

Я хочу создать 3 слоя следующим образом:

        |---------------------|---------------------|
Layer 3 | Customer 1 Database | Customer 2 Database |
        |---------------------|---------------------|
Layer 2 |   Database image with tables but no data  |
        |-------------------------------------------|
Layer 1 |                mysql:5.6.26               |
        |-------------------------------------------|

Вопрос теперь в том, как создать правильный файл Docker для слоев 2 и 3? Где мой файл empty_with_tables.sql загружается в слой 2, а customer1.sql и customer2.sql загружаются в два изображения на уровне 3. Я прочитал кое-что о том, как помещать файлы SQL в '/docker-entrypoint-initdb.d'. Но это приведет к тому, что данные будут, когда изображение будет запущено в первый раз. Это не то, что я хочу. Я хочу, чтобы данные были готовы на изображении (например, чтобы быть быстрым в тестировании).

Я мог бы запустить образ mysql, загрузить данные из командной строки и выполнить команду "commit", но это не воспроизводится, требуя сделать это снова, когда данные в файлах SQL будут изменены.

Как это можно сделать?

С уважением,

  • Мортен Грин Хермансен

Ответы

Ответ 1

Итак, мое решение этой проблемы было просто НЕ для того, чтобы сложить все, кроме создания базового образа, и использовать --volumes-from, чтобы вставлять файлы базы данных из контейнера только для данных.

Ответ 2

На этой неделе я столкнулся с той же проблемой. Я нашел рабочее решение без необходимости --volumes-from

Проблема, о которой уже говорилось, заключается в том, что /var/lib/mysql является томом, и поскольку Docker не будет поддерживать UNVOLUME в нем Dockerfile в ближайшем будущем, вы не сможете использовать это местоположение для своего хранилища баз данных, если хотите для начала с пустой базой данных по умолчанию. (https://github.com/docker/docker/issues/18287). Вот почему я перезаписываю etc/mysqld.my.cnf, предоставляя mysql новый datadir.

Вместе с ответом pwes вы можете создать Dockerile следующим образом:

FROM mysql:5.6

ENV MYSQL_DATABASE db
ENV MYSQL_ROOT_PASSWORD pass
COPY db.sql /docker-entrypoint-initdb.d/db.sql
COPY my.cnf /etc/mysql/my.cnf
RUN /entrypoint.sh mysqld & sleep 30 && killall mysqld
RUN rm /docker-entrypoint-initdb.d/db.sql

Единственное изменение в my.cnf - это расположение элемента данных:

.... 
[mysqld]
skip-host-cache
skip-name-resolve
user        = mysql
pid-file    = /var/run/mysqld/mysqld.pid
socket      = /var/run/mysqld/mysqld.sock
port        = 3306
basedir     = /usr
datadir     = /var/lib/mysql2 <-- can be anything except /var/lib/mysql
tmpdir      = /tmp
lc-messages-dir = /usr/share/mysql
explicit_defaults_for_timestamp
....

Ответ 3

Это не может быть сделано точно так, как вам нужно, по крайней мере, при использовании официального изображения mysql, поскольку вам необходимо связываться с сервером для импорта данных, а сервер не запускается и не инициализируется (из mysql docker-entrypoint.sh), пока контейнер не будет запущен, только когда изображение уже построено.

Нечеткий способ - запустить процесс в контейнере, используя /entrypoint.sh script из образа mysql, но вы должны позаботиться обо всех настройках, необходимых для точки входа (например, $MYSQL_ROOT_PASSWORD) а также чистый способ остановить демона сразу после импорта данных. Что-то вроде:

FROM mysql:5.6

ADD data.sql /docker-entrypoint-initdb.d/00-import-data.sql
ENV MYSQL_ROOT_PASSWORD somepassword
ENV MYSQL_DATABASE db1
RUN /entrypoint.sh mysqld & sleep 30 && killall mysqld

- хакерский способ, который приводит к предварительно инициализированному БД, но... он не работает. Причина в том, что /var/lib/mysql объявляется как тома в mysql Dockerfile, и любые изменения в этом каталоге во время процесса сборки теряются после завершения этапа сборки. Это можно наблюдать в следующем файле Docker:

FROM mysql:5.6

RUN touch /var/lib/mysql/some-file && ls /var/lib/mysql
RUN touch /var/lib/mysql/some-file2 && ls /var/lib/mysql

Поэтому я предлагаю перейти к docker commit, как вы описали. Конечный результат такой же, как тот, который вы хотите достичь, за исключением уровня 2.

UPDATE. Как указано в комментариях к OP, коммит не содержит томов. Таким образом, единственный способ - либо отредактировать файл MySQL Dockerfile, либо удалить VOLUME для хранения данных внутри контейнера или для управления томами отдельно от контейнеров.

Ответ 4

На основе ответа MegaWubs я нашел этот файл Docker достаточным.

FROM mysql:5.6
RUN sed -i 's|/var/lib/mysql|/var/lib/mysql2|g' /etc/mysql/my.cnf

Ответ 5

Ответ MegaWubs хорош, за исключением этого "сна 30", которое заставляет вас угадывать время выполнения initdb. Чтобы избежать этого, я поместил небольшой скрипт оболочки, который будет выполняться после всех остальных, в /docker-entrypoint-initdb.d:

/docker-entrypoint-initdb.d/
  |- 01_my_data1.sql
  |- 02_my_data2.sql
  ...
  |- 99_last_processed_file.sh

С 99_last_processed_file.sh:

#!/usr/bin/env bash
touch /tmp/server_can_shutdown.txt

-

Параллельно в Dockerfile я запускаю другой скрипт вместо Мортенна "sleep && killall":

# Dockerfile
# ...
COPY wait_then_shutdown.sh /tmp/wait_then_shutdown.sh
RUN /entrypoint.sh mysqld & /tmp/wait_then_shutdown.sh  # <-- 
RUN rm /docker-entrypoint-initdb.d/*

С wait_then_shutdown.sh:

#!/usr/bin/env bash
while [ ! -f /tmp/server_can_shutdown.txt ] # <-- created by 99_last_processed_file.sh
do
  sleep 2
done
kill $(pidof mysqld)

-

И теперь mysqld останавливается только тогда, когда все другие файлы обрабатываются в /docker-entrypoint-initdb.d

Ответ 6

Начиная с Docker 17.05 можно использовать функцию многоступенчатой сборки для удаления томов

# Dockerfile
FROM mysql as orig

FROM ubuntu:bionic as image
# care must be taken, this will not preserve fs ownership
COPY --from=orig / / # this will copy all files, without metadata.

ENV ... # include all commands that are not file related

ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 3306
CMD ["mysqld"]

и затем добавьте свои данные как в ответе Фила Сабати.