在 Dockerfile 中设置 MySQL 并导入转储
Posted
技术标签:
【中文标题】在 Dockerfile 中设置 MySQL 并导入转储【英文标题】:Setting up MySQL and importing dump within Dockerfile 【发布时间】:2014-11-13 05:29:16 【问题描述】:我正在尝试为我的 LAMP 项目设置 Dockerfile,但在启动 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 构建期间设置数据库创建和转储导入有什么想法吗?
【问题讨论】:
这是因为每个RUN
命令都在不同的容器中执行。这里解释得很好:***.com/questions/17891669/…
这只是说明 RUN 命令有不同的上下文。但我依赖的是一个守护进程,而不是上下文。
是的,但这解释了为什么您无法连接到 MySQL。这是因为,它只在您的第一行 RUN
中运行。
要执行你的SQL语句,你需要启动MySQL,并在同一个容器中使用MySQL客户端:一个RUN
,分几步。您可以在此处找到一个多步骤安装软件的示例:***.com/questions/25899912/install-nvm-in-docker/…
您也可以使用 docker-compose 查看服务:docs.docker.com/compose/wordpress/#build-the-project 这样,mysql 可以附加到您的应用中
【参考方案1】:
官方mysql docker image的最新版本允许您在启动时导入数据。这是我的 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
在这里,我在 docker/data
下有我的 data-dump.sql,它与运行 docker-compose 的文件夹相关。我正在将该 sql 文件挂载到容器上的这个目录 /docker-entrypoint-initdb.d
中。
如果您有兴趣了解其工作原理,请查看他们在 GitHub 中的 docker-entrypoint.sh
。他们已添加此块以允许导入数据
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"]
数据容器甚至不必处于启动状态即可持久化。
【讨论】:
恕我直言,这是更好的答案。它允许不自己创建图像。感谢您的提示。 对卷的一个小改动:- ./docker/data:/docker-entrypoint-initdb.d
— 删除了容器目录前面的 .
。
虽然这是正确的过程,但它并不能很好地解决用例。 Docker 的优点之一是您可以非常快速地启动环境。如果在启动期间 Docker 导入 MySQL 数据库时必须等待 3-4 分钟,那么您将失去这一优势。目标是拥有一个已经包含数据库中数据的容器,以便您可以尽快使用它。
这个答案是否仍然适用于最新版本?似乎不再起作用了
请注意这里的 docker-compose 示例是 v2 而不是 v3。 v3 不支持“volumes_from:”。【参考方案2】:
Dockerfile
中的每个 RUN
指令在不同的层中执行(如 documentation of 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
【讨论】:
您知道我如何保存这些信息吗?当我在容器上运行东西时,我在 Dockerfile 构建上创建的数据库和表不可用。 :( 这取决于你所说的坚持。如果您想让数据在多个docker run
执行中保持不变,您需要挂载卷。如果您只想为您的转储提供一个容器,但又不想保留进一步的修改,您可以去掉 Dockerfile
中的 VOLUME
指令。
我在尝试解决方案 1 时收到 ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
。按照日志 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
和 mysqld_safe mysqld from pid file /var/run/mysqld/mysqld.pid ended
我发现在启动时进行脚本检查以查看mysql是否创建了数据库要好得多。如果有,则不要管它,否则运行mysql_init_db
并加载到您的数据库结构中。这允许在测试时自动重置数据库的灵活性,但如果您希望它持久化或测试不同的数据集,您只需使用 docker run -v ...
在 /var/lib/mysql 挂载一个卷。
@trevorgrayson - 这也不起作用。现在它给出错误 ERROR 2003 (HY000): Can't connect to MySQL server on '127.0.0.1' (111)【参考方案3】:
我所做的是将我的 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
时,转储会在数据库中恢复。
【讨论】:
+1,添加一个:指向正在运行的脚本的链接,以便更好地了解实际发生的情况:github.com/docker-library/mariadb/blob/… latestmysql
似乎没有加载转储的 sql 文件,即使使用 5.6 我也遇到了 InnoDb 存储引擎的问题。
这里一样,它有这样的指令,我什至在日志中看到正在加载初始化文件,但 db 是空的!【参考方案4】:
这是使用v3
的docker-compose.yml
的工作版本。关键是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
在我的docker-compose.yml
目录中,我有一个data
目录,其中包含.sql
转储文件。这很好,因为您可以为每个表创建一个 .sql
转储文件。
我只需运行docker-compose up
就可以了。数据在停靠点之间自动保留。如果您想删除数据并“吸入”新的.sql
文件,请运行docker-compose down
,然后运行docker-compose up
。
如果有人知道如何让mysql
docker 重新处理/docker-entrypoint-initdb.d
中的文件而不删除卷,请发表评论,我会更新这个答案。
【讨论】:
这个答案对我有帮助。docker-compose down
上的注释非常重要,因为尽管重新启动了 docker-compose,但我没有看到任何变化。在这种情况下,MYSQL_DATABASE 是必需的变量【参考方案5】:
我使用了 docker-entrypoint-initdb.d 方法(感谢@Kuhess) 但就我而言,我想根据我在 .env 文件中定义的一些参数创建我的数据库,所以我做了这些
1) 首先,我在 docker 根项目目录中定义类似这样的 .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) 然后我定义一个包含 Dockerfile 的 mysql 文件夹。所以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 转储文件,但我在开头添加了 2 行,因此文件看起来像这样
--
-- 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
文件的方法。但是根据this,.env
文件功能仅在您使用docker-compose up
命令时有效,不适用于docker stack deploy
。因此,如果您想在生产中使用.env
文件,您可能需要查看docker 17.06
引入的docker secrets。然后,您可以在开发docker compose up
和生产docker stack deploy
阶段将您的 .env 文件与秘密结合使用
不错的方法,但我建议使用RUN sed -i '1s/^/CREATE DATABASE IF NOT EXISTS
$MYSQL_DATABASE;\nUSE
$MYSQL_DATABASE;\n/' data.sql
而不是您建议的sed
命令。这样您就可以使用您拥有的任何转储文件 - 如果您正在处理大量数据,这将非常有用:)【参考方案6】:
编辑:我误解了这里的问题。我的以下回答解释了如何在 容器创建时间 运行 sql 命令,而不是在 图像创建时间 如 OP 所期望的那样。
我不太喜欢 Kuhess 接受的答案,因为 sleep 5
对我来说似乎有点骇人听闻,因为它假定 mysql db 守护程序已在此时间范围内正确加载。这是假设,不能保证。此外,如果您使用提供的 mysql docker 映像,则该映像本身已经负责启动服务器;我不会用自定义的/usr/bin/mysqld_safe
来干扰这个。
我遵循此处的其他答案并将 bash 和 sql 脚本复制到 docker 容器内的文件夹 /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/
【讨论】:
【参考方案7】:基于Kuhess response,但没有硬睡眠:
RUN /bin/bash -c "/usr/bin/mysqld_safe --skip-grant-tables &" && \
while ! mysqladmin ping --silent; do sleep 1; echo "wait 1 second"; done && \
mysql -u root -e "CREATE DATABASE mydb" && \
mysql -u root mydb < /tmp/dump.sql
【讨论】:
【参考方案8】:我也遇到过同样的问题,但通过分离 MySQL 启动命令设法让它工作:
sudo docker build -t MyDB_img -f Dockerfile.dev
sudo docker run --name SomeDB -e MYSQL_ROOT_PASSWORD="WhatEver" -p 3306:3306 -v $(pwd):/app -d MyDB_img
然后在运行 MySQL 脚本之前睡眠 20 秒,它就可以工作了。
sudo docker exec -it SomeDB sh -c yourscript.sh
我只能假设 MySQL 服务器需要几秒钟才能启动,然后才能接受传入的连接和脚本。
【讨论】:
以上是关于在 Dockerfile 中设置 MySQL 并导入转储的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Dockerfile 中设置 Node 环境变量以运行 node.js 应用程序?
基于 build ARG 有条件地在 Dockerfile 中设置 ENV vars