Ответ 1
Итак, мое решение этой проблемы было просто НЕ для того, чтобы сложить все, кроме создания базового образа, и использовать --volumes-from, чтобы вставлять файлы базы данных из контейнера только для данных.
Я хотел бы создать образ 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 будут изменены.
Как это можно сделать?
С уважением,
Итак, мое решение этой проблемы было просто НЕ для того, чтобы сложить все, кроме создания базового образа, и использовать --volumes-from, чтобы вставлять файлы базы данных из контейнера только для данных.
На этой неделе я столкнулся с той же проблемой. Я нашел рабочее решение без необходимости --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
....
Это не может быть сделано точно так, как вам нужно, по крайней мере, при использовании официального изображения 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
для хранения данных внутри контейнера или для управления томами отдельно от контейнеров.
На основе ответа MegaWubs я нашел этот файл Docker достаточным.
FROM mysql:5.6
RUN sed -i 's|/var/lib/mysql|/var/lib/mysql2|g' /etc/mysql/my.cnf
Ответ 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
Начиная с 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"]
и затем добавьте свои данные как в ответе Фила Сабати.