这篇文章主要记录一下如何在 Docker 项目中集成数据库并初始化数据,示例中使用 Mysql。
为什么使用 Docker
容器技术越来越火, 而 Docker 的出现更是极大的降低了使用容器的门槛。 利用容器,我们可以将我们的程序及其环境与机器环境隔离开,方便项目管理。 而且利用编辑器 vs code 提供的基于 Docker 的远程开发插件,可以方便的连接远程服务器,像开发调试本地项目一样开发调试远程项目。
使用 Docker 容器化项目
一般来说,你只需要在项目中添加一个 Dockerfile
文本文件通过定义各种 Docker 配置就可以容器化几乎所有的项目。
但是由于实际项目依赖的复杂性,我们可能还会用到 docker-compose
来定义和管理多容器镜像的项目。
例如一个典型的后端项目,本地开发的时候,除了后端代码本身,你可能还需要连接数据库。
那么,一个基于 eggJs 项目的开发环境项目代码的 Dockerfile.dev
可能会像这样:
1FROM node:1223WORKDIR /usr/src/app45COPY package.json ./67RUN npm install --silent --no-cache --registry=https://registry.npm.taobao.org89COPY ./ ./1011CMD ["yarn", "start"]
同时定义和管理多容器的 docker-compose.dev.yml
可能会是这样:
1version: '3.7'23services:4 mysql:5 image: 'mysql:8.0.22'6 networks:7 - server_net8 # 同一docker network 下的容器可以通过 容器名 相互引用9 container_name: 'server_dev_mysql'10 environment:11 MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'12 MYSQL_DATABASE: 'server-data'13 restart: unless-stopped14 volumes:15 - server_dev_data_mysql:/var/lib/mysql1617 server:18 build:19 context: ./20 dockerfile: Dockerfile.dev21 ports:22 - target: 700123 published: 700124 networks:25 ivi_server_net:26 container_name: 'server_dev'27 restart: on-failure28 volumes:29 - ./app:/usr/src/app/app30 - ./config:/usr/src/app/config31 - ./database:/usr/src/app/database32 - ./test:/usr/src/app/test3334networks:35 server_net:3637volumes: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 初始化和 数据初始化的工作。
这里需要注意的是,我们需要进入到正在运行的容器中执行这些操作:
docker exec -it <server_container_id> sh
进入到 server 容器;npx sequelize db:migrate
创建项目中提前定义好的 Tables;npx sequelize-cli db:seed --seed [./database/seeders/seeder_name]
写入一些测试数据;
但是每次创建新镜像都得手动执行这些操作就很麻烦。
所以在初始化数据库的时候就顺便初始化 Table 和测试数据就会很方便。
Mysql 初始化数据
我们将 Mysql 的 Docker 配置提取到一个独立的 mysql
目录,方便管理。
其中 Dockerfile
文件如下:
1FROM mysql:8.0.222ADD 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;1213INSERT 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;2728INSERT 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'23services:4 mysql:5 build: ./mysql6 networks:7 - server_net8 # 同一docker network 下的容器可以通过 容器名 相互引用9 container_name: 'server_dev_mysql'10 environment:11 MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'12 restart: unless-stopped13 command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci14 volumes:15 - server_dev_data_mysql:/var/lib/mysql16 - ./mysql/init:/docker-entrypoint-initdb.d/17 #将sql文件放到容器中的 /docker-entrypoint-initdb.d/ 目录,就会在mysql第一次启动时执行。之后重启容器不会重复执行!18 # 如果此目录放置多个sql文件,它执行时是没有顺序的。因此,这个目录只放一个init.sql,专门用来控制执行sql顺序的。19 - ./mysql/sql:/opt/sql20 redis:21 image: 'redis:alpine'22 restart: on-failure23 container_name: 'server_dev_redis'24 networks:25 - server_net2627 server:28 build:29 context: ./30 dockerfile: Dockerfile.dev31 ports:32 - target: 700133 published: 700134 networks:35 ivi_server_net:36 container_name: 'server_dev'37 restart: on-failure38 volumes:39 - ./app:/usr/src/app/app40 - ./config:/usr/src/app/config41 - ./database:/usr/src/app/database42 - ./test:/usr/src/app/test4344networks:45 server_net:4647volumes:48 server_dev_data_mysql:
然后 docker-compose -f docker-compose.dev.yml up
就可以一键构建 server 镜像和 mysql 镜像并启动它们的容器。
而且数据库中也有了初始化数据。
之后在开发过程中,如果需要新建表或者添加新的测试数据,一方面需要通过 ORM 框架实时添加,另一方面也需要在 mysql/sql
文件夹下追加新的 sql 文件用来在项目初始化时初始化数据。
参考: