【Docker】【MySQL】在容器启动时执行SQL的方法和机制

最初先生

每次启动容器后都要执行SQL真是麻烦啊。所以,我查了一下在Docker启动时自动执行SQL的方法。

方式

目录结构如下。init.sql是在容器启动时要执行的SQL文件。

workspace
├── docker-compose.yml
└── init
    └── init.sql
create database initDB;                               

通过docker-compose.yml文件,将主机PC的./init和容器的/docker-entrypoint-initdb.d两个目录进行绑定挂载,从而实现共享。
这样,在容器启动时会在/docker-entrypoint-initdb.d中创建init.sql文件。

version: '3'
services:
  mysql:
    image: mysql:latest
    container_name: my-mysql-container
    environment:
      MYSQL_ROOT_PASSWORD: your-root-password
      MYSQL_DATABASE: your-database-name
      MYSQL_USER: your-username
      MYSQL_PASSWORD: your-password
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
      - ./init:/docker-entrypoint-initdb.d <- 追加
volumes:
  mysql-data:

这样准备就绪了。
通过docker compose up -d命令创建容器。

$ docker compose up -d
[+] Running 3/3
 ✔ Network mysqltest_default      Created                                                                          0.0s
 ✔ Volume "mysqltest_mysql-data"  Created                                                                          0.0s
 ✔ Container my-mysql-container   Started                                                                          0.0s
$

使用docker exec命令进入容器并登录MySQL。
查看数据库列表,可以看到init.sql中定义的SQL已经执行。

$ docker exec -it my-mysql-container bash
bash-4.4#
bash-4.4#
bash-4.4# mysql -u root -p
Enter password:

mysql>
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| initDB             | <- init.sqlで作成されたデータベース
| mysql              |
| performance_schema |
| sys                |
| your-database-name |
+--------------------+
6 rows in set (0.01 sec)

首次执行的机制

Docker镜像中定义了一个名为docker-entrypoint.sh的shell脚本,该脚本在docker首次启动时被调用。
MySQL的docker-entrypoint.sh脚本似乎包含执行/docker-entrypoint-initdb.d目录中的sql文件的操作。

MySQL 8.0的docker-entrypoint.sh可以在以下链接中找到。
https://github.com/docker-library/mysql/blob/master/8.0/docker-entrypoint.sh

询问的事项

只有无法执行sql文件吗?

结论:可以运行 SQL 文件和 sh 文件
docker-entrypoint.sh 文件中包含如下源代码。

docker_process_init_files() {
	# mysql here for backwards compatibility "${mysql[@]}"
	mysql=( docker_process_sql )

	echo
	local f
	for f; do
		case "$f" in
			*.sh)
				# https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
				# https://github.com/docker-library/postgres/pull/452
				if [ -x "$f" ]; then
					mysql_note "$0: running $f"
					"$f"
				else
					mysql_note "$0: sourcing $f"
					. "$f"
				fi
				;;
			*.sql)     mysql_note "$0: running $f"; docker_process_sql < "$f"; echo ;;
			*.sql.bz2) mysql_note "$0: running $f"; bunzip2 -c "$f" | docker_process_sql; echo ;;
			*.sql.gz)  mysql_note "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
			*.sql.xz)  mysql_note "$0: running $f"; xzcat "$f" | docker_process_sql; echo ;;
			*.sql.zst) mysql_note "$0: running $f"; zstd -dc "$f" | docker_process_sql; echo ;;
			*)         mysql_warn "$0: ignoring $f" ;;
		esac
		echo
	done
}

根据这个来源,对于.sh文件和.sql文件,会执行操作;对于.sql文件的压缩文件,则会先解压缩后再执行操作。因此,只要是.sh文件和.sql文件,都可以被执行。

如果有多个文件,哪个文件会首先被执行?

结论:以文件名升序执行
docker-entrypoint-initdb.d目录中的文件将按照名称的升序顺序执行。
如果要执行依赖关系的文件,则需要在文件名前加上数字或采取其他必要的方法。

$ ls
1_init.sql  2_init.sql  3_init.sql  

最后是

请试着在容器启动时运行文件,这将非常方便,麻烦一定试试看。

请您参考

请为以下链接提供中文的本地化译文,只需一个选项:

https://qiita.com/moaikids/items/f7c0db2c98425094ef10
https://zenn.dev/re24_1986/articles/978801ae092498

https://qiita.com/michikun06/items/24e99618dc59807b98c8

广告
将在 10 秒后关闭
bannerAds