Docker,如何在镜像中运行.sql文件?

24

这是我第一次使用Docker,对于自己的操作是否正确感到不确定。

我的Rails应用程序依赖于一个Mysql数据库,所以我已经按照以下方式配置了docker-compose.yml文件:

db:
  image: library/mysql:5.6
  environment:
    MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
  expose:
    - "3306"
  ports:
    - "3306:3306"

rails-app:
  build: .
  dockerfile: Dockerfile
  environment:
    RAILS_ENV: development
  links:
    - db
  volumes:
    - ".:/home/app"
  volumes_from:
    - bundle

  ... omitted lines ...

那么,如果我运行下面的代码:

$ docker-compose run db mysql --host=$DOCKER_LOCALHOST --port=3306 --protocol=tcp -u root < shared/create_common_tables.sql

我遇到了这个错误:
ERROR 2003 (HY000): Can't connect to MySQL server on '192.168.99.100' (111)

这很正常,因为我怀疑在一些链接到db的容器之前,我必须要先build
我知道这是因为如果我按照以下顺序运行:
$ docker-compose build rails-app
$ docker-compose run -e RAILS_ENV=development rails-app bundle
$ docker-compose run -e RAILS_ENV=development rails-app bundle exec rake db:create
$ docker-compose run db mysql --host=$DOCKER_LOCALHOST --port=3306 --protocol=tcp -u root < shared/create_common_tables.sql

它运行得很好。

但是,在创建任何容器之前,我该如何执行此sql语句?

1个回答

25

您可以在镜像的build阶段加载sql文件。为此,您需要为db服务创建一个Dockerfile,其内容类似于:

FROM mysql:5.6
COPY setup.sh /mysql/setup.sh
COPY setup.sql /mysql/setup.sql
RUN /mysql/setup.sh

setup.sh 大致如下:

#!/bin/bash
set -e
service mysql start
mysql < /mysql/setup.sql
service mysql stop

在你的 docker-compose.yml 文件中,你需要将 image 修改为 build: ./db 或者你放置文件的路径。

如果你的所有 SQL 语句都存储在原生的 .sql 文件中,那么这将起作用。但是如果你正在使用 Rails 或类似的框架,SQL 实际上是存储在代码中的,那么就有两个选择。

  1. 你可以使用 FROM your_app_image_that_has_the_code_in_itapt-get install mysql ... 替代 FROM mysql:5.6。这样会得到一个更大的包含 MySQL 和应用程序的镜像,使你能够运行上面的 Ruby 命令。你需要用 rails-app bundle exec rake db:create 代替 mysql < /mysql/setup/sql。此外,还需要提供一个应用程序配置,以连接到 localhost:3306 上的数据库,而不是 db:3306

  2. 我更喜欢的方法是创建一个脚本,将 SQL 导出到一个 .sql 文件中,然后使用该文件构建数据库容器。这需要更多的工作,但更好。这意味着你只需要运行该脚本来加载数据库,而不是运行 rails-app bundle exec rake db:create

这样的脚本看起来像这样:

#!/bin/bash
set -e
docker-compose build rails-app
docker run -d --name mysql_empty mysql:5.6
docker run --link mysql_empty:db -v $PWD:/output project_rails-app export.sh

其中 export.sh 类似于这样:

#!/bin/bash
set -e
RAILS_ENV=development
rails-app bundle exec rake db:create
mysqldump > /output/setup.sql

如果您想的话,也可以使用第二个Compose文件替换docker run脚本。


10
MySQL的Docker镜像已经有了初始化新容器数据的机制。它会从/docker-entrypoint-initdb.d目录加载所有.sh和.sql文件,你只需要将这些文件从主机COPY或挂载到该目录即可。请参见 https://hub.docker.com/_/mysql/ 中的“Initializing a fresh instance”部分。 - diwo
4
是的,但是这种机制不符合问题中“我该如何在创建任何容器之前执行此sql”的要求。个人认为将装置作为入口点的一部分并不是一个好的解决方案。它需要你每次创建容器时重复这个过程,这会显著减慢启动时间。 - dnephin
1
就像dnephin所说的那样。我喜欢这个解决方案,即图像执行脚本,这样用户只需要获取最新版本的图像以节省容器启动时间。对于mysql docker镜像,你不能这样做,因为每次容器启动时都会执行docker-entrypoint.sh。查看脚本,你会发现数据库目录是在其中创建的。你尝试在镜像中使用mysql的所有操作都将被删除。在我看来,唯一的方法是不使用mysql docker镜像并自己处理。 - Qri

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接