本文介绍了我在 windows 平台和 debian 平台安装 docker 的过程,并将我的 nestjs 服务器使用的 sqlite 数据库迁移到 mysql。最后对 mysql 数据库进行定时备份。
在 win10 安装 docker-desktop
- 进入 bios 开启 svm
- 打开设置-应用和功能-相关设置-程序和功能-
Hyper-V
- 此时安装 docker-desktop 尝试运行,出现 wsl 报错,或者长时间卡在初始化状态
- 以管理员身份运行 cmd,执行命令
wsl --status
查看 wsl 状态,提示执行wsl --update
- 执行
wsl --update
等待更新完毕 - 执行
wsl --install
安装 linux,此时会自动安装 Ubuntu,安装完毕后按提示输入新用户的 username 和 password - 重启,再次运行 docker-desktop,成功运行
docker-desktop 安装 mongoDb 并连接
首先在 Images 中下载最新的 mongodb 镜像,运行镜像到容器,并指定端口映射,此处映射到 27017 端口
下载 MongoDB Compass,一个免费的图形界面 mongodb 管理软件,按照默认连接 mongodb://localhost:27017
即可连接到数据库
本来想用 mongodb 来替代 sqlite,但是 mongodb 不能用 sequelize,为了避免大量代码重写,选择安装 mysql。这篇文章解释了 sequelize 不能用于 mongodb 的原因 https://medium.com/sequelize-to-mongoose-and-vice-versa-a-developers/sequelize-to-mongoose-and-vice-versa-a-developers-guide-906bdf79e81
docker-desktop 安装 mysql 并连接
在镜像中下载最新的 mysql 镜像(8.2),配置 3306 端口映射,添加 Environment variables(环境变量)variable 填写必要环境变量 MYSQL_ROOT_PASSWORD
,value 填写密码,启动镜像。
用于连接 mysql 的客户端的选择了 Mysql Workbench,官方免费跨平台的图形化界面,但目前只支持到 mysql 8.0,连接时会提示部分功能无法适配,选择继续连接即可。
创建 nest 数据库:mysql -u root -p
输入密码,进入 mysql 命令行,create database nest;
创建 nest 数据库,show databases;
显示所有的数据库。
启动服务器,可以连接上数据库了。参考 nestjs 文档教程 https://docs.nestjs.com/recipes/sql-sequelize
查看已创建的表:use nest; show tables;
在 debian 上安装 docker 并运行 mysql 镜像
不能直接 apt install docker,这是错误的做法
阅读官方文档,这篇文档详细描述了怎样在 debian 上安装 docker https://docs.docker.com/engine/install/debian/#os-requirements
鉴于 Mysql Workbench 只支持到 mysql 8.0,而目前主流使用的 mysql 版本分别是 5.7 和 8.0,因此我选择安装目前最新的 8.0 镜像 mysql 8.0.35。在 https://hub.docker.com/ 上可以找到 mysql 的最新镜像。
docker pull mysql:8.0.35-debian
docker image ls // 查看本地镜像
docker run -p 3336:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:8.0.35-debian // 需要在服务器开启 3336 端口,此处可以选择其他端口回避一些对 3306 端口的直接攻击;-d 表示后台运行,mysql:8.0.35-debian 表示指定运行的镜像版本,如果写 mysql 则会重新下载最新版的 mysql 镜像
docker ps // 查看当前运行的容器
docker exec -it mysql bash // 进入容器内部,mysql 是上面给容器起的 neme
mysql -u root -p // 以 root 用户进入 mysql,-p 表示输入密码
show tables; // 列出所有的表
从 sqlite 迁移到 mysql
下载 sqlite3 命令行工具,把数据库 nest.db 转换为 nest.sql
.open nest.db
.output nest.sql
.dump
使用 Mysql Workbench 图形化界面,连接 mysql 数据库,选择菜单栏 server - Data Import。选择 Import from Self-contained File 并选择 nest.sql 文件,在下方 new 按钮可以选择需要把数据导入到哪个 Schema,也就是 database
。点击 Start Import 开始导入,导入过程中可能会遇到各种报错,使用 vscode 打开 nest.sql 修改其中 mysql 不兼容的语法,总结如下
- 移除所有的 [BEGIN TRANSACTION] [COMMIT] 以及 任何包含 [sqlite_sequence] 的(整)行
- 将所有 [autoincrement] 改为 [auto_increment]
- 利用 vscode 正则替换修复时间日期为 mysql 支持的 datetime 格式。我在 sqlite 中使用了 TEXT 类型来储存时间日期,如
YYYY-MM-DD HH:MM:SS.SSS
,需要把所有时间日期替换为替换为 mysql 的 datetime 类型YYYY-MM-DD HH:MM:SS
修改后再次提交,修复报错,直至完成
迁移后启动服务器,发现查不到 users 表的内容。原来 sequelize 使用 Class 创建的 Model,默认是大写开头的,然而旧表中有小写开头的表(早期没有使用 Class 创建表,创建的是 users 表),因此 sequelize 自动帮我创建了大写开头的 Users 表,因此查不到用户。为了解决这个问题,我把 nest.sql 中所有的小写表名改成了大写,删除 nest 数据库,重新导入。
drop database nest; // 删除 nest 数据库
重新导入后,数据恢复正常。
数据库备份
在使用 sqlite 的时候,我通过一个定时任务,在每天零点将数据库文件复制一份,放到指定的位置,并以日期命名。在 mysql 中,有以下几种方式进行备份:
在 mysql workbench 中备份,点击菜单栏 server - Data Export,左侧刷新 schema,选择 nest,下方选择到处目录,点击导出按钮,即可完成数据库备份。
调用 docker 命令备份数据库
// 此处 -u 和 -p 后面没有空格
docker exec mysql sh -c 'exec mysqldump --databases nest -uroot -p{password}' > /root/backup/mysql_20240104.sql
// mysqldump: [Warning] Using a password on the command line interface can be insecure.
如果按照上面这样调用,mysql 会提示你不安全,但仍然备份成功了。接下来要解决账号密码的安全问题
docker exec -it mysql bash // 进入容器内部
cd /etc/mysql
vim my.cnf // 此处可能报错找不到 vim 命令,只要 apt-get update 然后 apt install vim 即可,此处下载非常慢
// 写入以下内容
[client]
host = localhost
user = root
password = 'your_pwd'
// 保存,尝试执行以下命令,注意不要把这个 `-it` 的命令写进 sh 脚本,否则 nodejs 将无法调用
// i 表示交互模式(interactive)t 表示终端模式(terminal),合起来表示交互式终端,然后在 nodejs 调用定时脚本时没有终端,因此会报错,执行失败。
docker exec -it mysql mysqldump --defaults-file=/etc/mysql/my.cnf --databases nest > /root/backup/20240104.sql
注意检查备份出来的文件大小,当文件大小和先前备份的大小差距过大时,要检查命令是否出错。找不到 cnf 文件时文件大小只有几百k,而备份了全部数据库时,文件大小到了 3M 以上,而我的数据库正常备份大小只有 800k
接下来将上述命令写入 sh 脚本:
CONTAINER=mysql
FILENAME=/root/backup/$(date "+%Y%m%d").sql
// 还有一个坑在:单引号里面不能直接使用 ${DB_NAME} 语法,这是因为在单引号内部,变量引用会被保持原样,不会被展开。可以使用双引号来回避这个问题,我在这里直接写 nest。
docker exec ${CONTAINER} sh -c 'exec mysqldump --defaults-file=/etc/mysql/my.cnf --databases nest' > ${FILENAME}
通过 nestjs 定时任务触发,即可完成数据库备份
一些问题
迁移数据后丢失了 AUTO_INCREMENT 自增计数
导入数据库之后,发现能查但不能新增,原来是因为数据库丢失了 AUTO_INCREMENT,以及其自增计数。删除 nest 数据库后,我重新导入备份的 nest.sql
,并修改其中的相关字段,如下是 Account 表的结构。
CREATE TABLE `Accounts` (
`id` int NOT NULL AUTO_INCREMENT,
`expend` int DEFAULT NULL,
`income` int DEFAULT NULL,
`gzhId` varchar(255) DEFAULT NULL,
`reason` varchar(255) DEFAULT NULL,
`date` datetime DEFAULT NULL,
`createdAt` datetime NOT NULL,
`updatedAt` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=677 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
注意不要直接把 mysql workbench 的 DLL 复制过来修改,上面这些就是复制过来的,导致导入一直报错,而且报错很奇怪,可能是字符编码的问题。
最终我设置 sequelize 的 sync: { alter: false,force: true }
强制更改数据库结构,sequelize 把不符合 Model 的表 Accounts 所有行都删除了。我添加了两行之后导出数据库备份 test.sql
来观察和报错的 nest.sql
有什么不一样,最后发现没有不一样,但复制 test.sql
中的 DLL 到 nest.sql
中修改自增计数,再重新导入数据库就成功了。
win10 开启 hyper-v 导致开发服务端口监听失败
有一个本地服务监听 4000 端口,在调试完 docker-desktop 之后无法正常启动
failed to start server. error:
Error: listen EACCES: permission denied 0.0.0.0:4000
原来是因为 4000 是 Hyper-V
的保留端口,只要避开保留端口即可。查看保留端口,在 cmd 执行以下命令
> netsh interface ipv4 show excludedportrange protocol=tcp
开始端口 结束端口
---------- --------
3679 3778
3779 3878
3980 4079
4093 4192
4348 4447
5357 5357
50000 50059 *
* - 管理的端口排除。
可见,4000 端口在保留端口范围内,于是我切换到 6000 端口运行,服务运行起来了,但是浏览器无法访问,提示如下
无法访问此网站
网址为 http://localhost:6000/ 的网页可能暂时无法连接,或者它已永久性地移动到了新网址。
ERR_UNSAFE_PORT
6000 端口是浏览器的不安全端口,因此无法访问。我记得曾经踩过 6666 端口的坑,没想到 6000 端口也是坑。这个问题只要修改端口就可以了