🐳 📖

在 Docker 项目中使用 Mysql 数据库

📆 2021-01-14

🏷 DockerMysql

🖍 这篇文章主要记录一下如何在 Docker 项目中集成数据库并初始化数据,示例中使用 Mysql。 为什么使用 Docker 容器技术越来越火, 而 Docke…

🏂 正文 👇

这篇文章主要记录一下如何在 Docker 项目中集成数据库并初始化数据,示例中使用 Mysql。

为什么使用 Docker

容器技术越来越火, 而 Docker 的出现更是极大的降低了使用容器的门槛。 利用容器,我们可以将我们的程序及其环境与机器环境隔离开,方便项目管理。 而且利用编辑器 vs code 提供的基于 Docker 的远程开发插件,可以方便的连接远程服务器,像开发调试本地项目一样开发调试远程项目。

使用 Docker 容器化项目

一般来说,你只需要在项目中添加一个 Dockerfile 文本文件通过定义各种 Docker 配置就可以容器化几乎所有的项目。 但是由于实际项目依赖的复杂性,我们可能还会用到 docker-compose 来定义和管理多容器镜像的项目。 例如一个典型的后端项目,本地开发的时候,除了后端代码本身,你可能还需要连接数据库。 那么,一个基于 eggJs 项目的开发环境项目代码的 Dockerfile.dev 可能会像这样:

1FROM node:12
2
3WORKDIR /usr/src/app
4
5COPY package.json ./
6
7RUN npm install --silent --no-cache --registry=https://registry.npm.taobao.org
8
9COPY ./ ./
10
11CMD ["yarn", "start"]

同时定义和管理多容器的 docker-compose.dev.yml 可能会是这样:

1version: '3.7'
2
3services:
4 mysql:
5 image: 'mysql:8.0.22'
6 networks:
7 - server_net
8 # 同一docker network 下的容器可以通过 容器名 相互引用
9 container_name: 'server_dev_mysql'
10 environment:
11 MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
12 MYSQL_DATABASE: 'server-data'
13 restart: unless-stopped
14 volumes:
15 - server_dev_data_mysql:/var/lib/mysql
16
17 server:
18 build:
19 context: ./
20 dockerfile: Dockerfile.dev
21 ports:
22 - target: 7001
23 published: 7001
24 networks:
25 ivi_server_net:
26 container_name: 'server_dev'
27 restart: on-failure
28 volumes:
29 - ./app:/usr/src/app/app
30 - ./config:/usr/src/app/config
31 - ./database:/usr/src/app/database
32 - ./test:/usr/src/app/test
33
34networks:
35 server_net:
36
37volumes:
38 server_dev_data_mysql:

这里的 docker-compose.dev.yml 定义了两个镜像,那么通过 docker-compose -f docker-compose.dev.yml up 可以启动两个容器,一个 server, 一个 Mysql 数据库。 但是会有一个问题,Mysql 只是初始化了一个空的数据库,没有任何 Table 和数据。实际开发中我们可能需要在数据库初始化的时候就需要一些测试数据。

其实通过 eggJs 集成的 sequelize ORM 框架也可以手动执行一些 Table 初始化和 数据初始化的工作。

这里需要注意的是,我们需要进入到正在运行的容器中执行这些操作:

  1. docker exec -it <server_container_id> sh 进入到 server 容器;
  2. npx sequelize db:migrate 创建项目中提前定义好的 Tables;
  3. npx sequelize-cli db:seed --seed [./database/seeders/seeder_name] 写入一些测试数据;

但是每次创建新镜像都得手动执行这些操作就很麻烦。

所以在初始化数据库的时候就顺便初始化 Table 和测试数据就会很方便。

Mysql 初始化数据

我们将 Mysql 的 Docker 配置提取到一个独立的 mysql 目录,方便管理。

mysqlmysql

其中 Dockerfile 文件如下:

1FROM mysql:8.0.22
2ADD mysqld.cnf /etc/mysql/mysql.conf.d/mysqld.cnf

声明继承自 mysql:8.0.22 并且将 mysqld.cnf 配置文件添加到容器中的特定目录下。

init/init.sql 用来顺序执行其他 sql 目录下的 sql 文件。

1-- 如果此目录放置多个sql文件,它执行时是没有顺序的。因此,这个目录只放一个init.sql,专门用来控制执行sql顺序的。
2source /opt/sql/init.sql;
3use server-data;
4source /opt/sql/authorities.sql;
5source /opt/sql/users.sql;

sql/init.sql, 主要用来创建数据库:

1-- 创建数据库
2CREATE DATABASE `server-data` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
3use server-data;

其他 sql 文件创建表并写入一些初始化数据:

sql/authorities:

1CREATE TABLE `authorities` (
2 `id` int NOT NULL AUTO_INCREMENT,
3 `name` varchar(255) DEFAULT NULL,
4 `product_read` tinyint(1) DEFAULT '0',
5 `product_printer_read` tinyint(1) DEFAULT '0',
6 `product_printer_edit` tinyint(1) DEFAULT '0',
7 `createdAt` datetime DEFAULT NULL,
8 `updatedAt` datetime DEFAULT NULL,
9 PRIMARY KEY (`id`),
10 UNIQUE KEY `name` (`name`)
11) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
12
13INSERT INTO `server-data`.`authorities` (`id`, `name`, `product_read`, `product_printer_read`, `product_printer_edit`,`createdAt`, `updatedAt`) VALUES ('1', 'admin', '1', '1', '1','2020-09-11 06:26:41', '2020-09-11 06:26:41');

sql/users:

1CREATE TABLE `users` (
2 `id` int NOT NULL AUTO_INCREMENT,
3 `authority_id` int DEFAULT NULL,
4 `user_id` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
5 `username` varchar(30) DEFAULT NULL,
6 `password` varchar(255) DEFAULT NULL,
7 `realname` varchar(30) DEFAULT NULL,
8 `gender` int DEFAULT NULL,
9 `age` int DEFAULT NULL,
10 `avatar` text,
11 `email` varchar(255) DEFAULT NULL,
12 `phone` varchar(255) DEFAULT NULL,
13 `signature` varchar(255) DEFAULT 'Nothing',
14 `title` varchar(255) DEFAULT NULL,
15 `group` varchar(255) DEFAULT NULL,
16 `last_sign_in_at` datetime DEFAULT NULL,
17 `country` varchar(255) DEFAULT 'China',
18 `province` varchar(255) DEFAULT NULL,
19 `city` varchar(255) DEFAULT NULL,
20 `address` varchar(255) DEFAULT NULL,
21 `createdAt` datetime DEFAULT NULL,
22 `updatedAt` datetime DEFAULT NULL,
23 PRIMARY KEY (`id`),
24 KEY `authority_id` (`authority_id`),
25 CONSTRAINT `users_ibfk_1` FOREIGN KEY (`authority_id`) REFERENCES `authorities` (`id`)
26) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
27
28INSERT INTO `server-data`.`users` (`id`, `authority_id`, `user_id`, `username`, `password`, `signature`, `createdAt`, `updatedAt`) VALUES ('1', '1', '2f57b536-e809-48bb-9518-2e6b6b95551e', 'admin', '4ae45782fc346d11b85933b388aa4d4d', 'Nothing', '2020-09-11 06:26:41', '2020-12-04 08:10:36');

然后需要调整 docker-compose.dev.yml

1version: '3.7'
2
3services:
4 mysql:
5 build: ./mysql
6 networks:
7 - server_net
8 # 同一docker network 下的容器可以通过 容器名 相互引用
9 container_name: 'server_dev_mysql'
10 environment:
11 MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
12 restart: unless-stopped
13 command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
14 volumes:
15 - server_dev_data_mysql:/var/lib/mysql
16 - ./mysql/init:/docker-entrypoint-initdb.d/
17 #将sql文件放到容器中的 /docker-entrypoint-initdb.d/ 目录,就会在mysql第一次启动时执行。之后重启容器不会重复执行!
18 # 如果此目录放置多个sql文件,它执行时是没有顺序的。因此,这个目录只放一个init.sql,专门用来控制执行sql顺序的。
19 - ./mysql/sql:/opt/sql
20 redis:
21 image: 'redis:alpine'
22 restart: on-failure
23 container_name: 'server_dev_redis'
24 networks:
25 - server_net
26
27 server:
28 build:
29 context: ./
30 dockerfile: Dockerfile.dev
31 ports:
32 - target: 7001
33 published: 7001
34 networks:
35 ivi_server_net:
36 container_name: 'server_dev'
37 restart: on-failure
38 volumes:
39 - ./app:/usr/src/app/app
40 - ./config:/usr/src/app/config
41 - ./database:/usr/src/app/database
42 - ./test:/usr/src/app/test
43
44networks:
45 server_net:
46
47volumes:
48 server_dev_data_mysql:

然后 docker-compose -f docker-compose.dev.yml up 就可以一键构建 server 镜像和 mysql 镜像并启动它们的容器。 而且数据库中也有了初始化数据。

之后在开发过程中,如果需要新建表或者添加新的测试数据,一方面需要通过 ORM 框架实时添加,另一方面也需要在 mysql/sql 文件夹下追加新的 sql 文件用来在项目初始化时初始化数据。

参考:

  1. https://cloud.tencent.com/developer/article/1623549

👐 The End 🎉

上一篇 孤独的故事🏠下一篇基于 Github Actions 和 Docker Hub 的项目开发部署流程 

👇 💬