我的docker随笔15:MySQL启动时自动创建数据库

一、背景及分析

MySQL容器启动时,会自动创建一些必要的数据库,比如MySQL,这是官方默认的做法。但是,在实际中,还需要让MySQL自动创建我们自定义的数据库。本文就此应用场合进行探究。

一般的做法是,启动容器并挂载数据目录后,使用MySQL客户端连接服务器,再手动输入sql语句创建(或导入.sql文件),当然也可以直接在容器内创建(方法同上)。由于挂载了数据目录,因此可持久化保存。但是,这些方法在部署数据库比较繁琐,不方便,特别是在初测阶段,会频繁地从头开始搭建环境。

如果能在MySQL容器启动时,自动执行我们自己的.sql文件,就不需要那么麻烦了,其实MySQL容器的启动脚本已经考虑了这种场合。通过MySQL镜像构建脚本docker-entrypoint.sh(参考这里
),发现如下语句:

1
2
3
for f in /docker-entrypoint-initdb.d/*; do
process_init_file "$f" "${mysql[@]}"
done

process_init_file函数定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
process_init_file() {
local f="$1"; shift
local mysql=( "$@" )

case "$f" in
*.sh) echo "$0: running $f"; . "$f" ;;
*.sql) echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;;
*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;;
*) echo "$0: ignoring $f" ;;
esac
echo
}

从上面语句看到,脚本将需要执行的文件(如.sh或.sql)放到/docker-entrypoint-initdb.d目录下,那么docker-entrypoint.sh就会自动执行。明白了原理,解决方案就十分简单了。

二、解决方案

2.1 Dockerfile

内容如下:

1
2
FROM mysql:5.7
COPY sql/*.sql /docker-entrypoint-initdb.d/

为了方便起见,所有.sql文件都放到Dockerfile同级的sql目录。

2.2 准备好sql文件

下面是测试用的.sql文件,文件名为test.sql,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 创建db_test数据库
create database `db_test` default character set utf8 collate utf8_general_ci;

use db_test;

-- 创建用户表
DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
`id` bigint(20) NOT NULL,
`email` varchar(255) DEFAULT NULL,
`first_name` varchar(255) DEFAULT NULL,
`last_name` varchar(255) DEFAULT NULL,
`username` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- 插入数据
INSERT INTO `user` (`id`, `email`, `first_name`, `last_name`, `username`)
VALUES(0,'li@latelee.org','Late','Lee','latelee');

主要内容是创建user表,并插入测试的数据。

2.3 创建镜像

1
docker build -t mysqltest .

创建镜像名称为mysqltest,注意最后的点号.,表示使用当前目录的Dockerfile

2.4 容器启动

docker-compose.yml文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
version: '2'
services:
database:
image: mysqltest
#image: singula/mysql
container_name: mysql
ports:
- "3406:3306"
volumes:
- ./mysql_data:/var/lib/mysql
- /home:/home
environment:
MYSQL_ROOT_PASSWORD: 123456

使用下列命令启动:

1
docker-compose up -d

验证数据库表:

1
2
3
4
5
6
7
8
9
10
$ docker exec -it mysql bash
bash-4.2# mysql -uroot -p123456
mysql> use db_test;
mysql> select * from user;
+----+----------------+------------+-----------+----------+
| id | email | first_name | last_name | username |
+----+----------------+------------+-----------+----------+
| 0 | li@latelee.org | Late | Lee | latelee |
+----+----------------+------------+-----------+----------+
1 row in set (0.00 sec)

使用如下语句修改邮箱地址:

1
update user set email='cst@cststudio.com.cn' where username='latelee';

退出容器后,停止容器:

1
docker-compose down

再次重复上述命令启动、查看数据,可以看到邮件地址是最新的,从而验证了数据能永久存储。

三、实践指导

.sql脚本只有第一次启动时创建(或说没有数据库时则会创建),已创建的,不会覆盖掉,否则这个机制就无法正式应用项目中。

.sql脚本中,也可以创建账号、密码,这样就不用在docker-compose文件指定环境变量了。起到了一定的保护作用。

如果要完全迁移数据,需要将数据目录(文中为mysql_data目录)与docker-compose.yml文件一起迁移。