用Docker启动MySQL

这段时间,一直用docker部署内网,踩了很多坑,尤其是数据库,有时候感觉很棘手,有时候问题解决了,却还是很疑惑。先来总结一下如何用docker启动mysql。

step1 制作mysql镜像

一般我们都需要数据库里面的数据支持中文,但是mysql默认字符集是latain,而它是不支持中文的,mysql在 5.5.3 之后增加了 utf8mb4 字符编码,mb4即 most bytes 4。简单来说,utf8mb4 是 utf8 的超集并完全兼容utf8,能够用四个字节存储更多的字符。我们可以用官方的docker mysql image制作一个支持utf8mb4的mysql镜像。

首先,创建mysql的配置文件utf8mb4.cnf,如下:

1
2
3
4
5
6
7
8
9
10
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4
[mysqld]
character-set-client-handshake = FALSE # 忽略客户端的字符集,使用服务器的设置
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

然后,创建Dockerfile文件,基于mysql官方的docker镜像,将utf8mb4.cnf 复制到容器的/etc/mysql/conf.d/目录下,构建新镜像:

1
2
FROM mysql
COPY utf8mb4.cnf /etc/mysql/conf.d/

在当前文件夹构建镜像,添加标签:

1
docker build . -t <image-tag>

最后启动一个容器:

1
docker run --detach --name=<mysql-name> --env="MYSQL_ROOT_PASSWORD=mypassword" --publish 6603:3306 <image-tag>

docker run 会启动一个mysql容器在后台运行,并设置其名字,把相应的参数设为mypassword,这个参数用于登录这个mysql容器,mysql容器默认开放3306端口,3306时容器对主机的端口,6603是主机对外界的端口,设置这个参数可以直接通过外界访问到mysql容器的内部。

step2 在mysql容器中创建数据库

1
docker inspect test-mysql | grep IPAddress

获取主机中mysql容器的地址

1
mysql -uroot -pmypassword -h IPAddress -P 3306

进入容器,这时可以查看mysql容器的字符集情况

1
show variables like 'character_set_%';

可以看到:

它们的含义如下

1
2
3
4
5
6
client 为客户端使用的字符集。
connection 为连接数据库的字符集设置类型,如果程序没有指明连接数据库使用的字符集类型则按照服务器端默认的字符集设置。
database 为数据库服务器中某个库使用的字符集设定,如果建库时没有指明,将使用服务器安装时指定的字符集设置。
results 为数据库给客户端返回时使用的字符集设定,如果没有指明,使用服务器默认的字符集。
server 为服务器安装时指定的默认字符集设定。
system 为数据库系统使用的字符集设定。

说明容器支持utf8mb4 。
创建数据库:

1
create database <database-name>;

step3 配置docker-compose.yml 和 .env 文件

在docker-compose.yml文件中加入

1
2
external_links:
- <mysql-name>:mysql

意为外连一个名为的mysql容器

在.env 文件中设置数据库的URL,使用刚才在mysql容器中创建的数据库 ,形如:

1
WEBSITE_SQL=mysql://root:mypassword@IPAddress:3306/<database-name>

注意这里要使用IPAddress,IPAddress是mysql容器的IP地址,否则它会提示找不到localhost,之后会出现无法迁移数据库的情况。

step4 初始化数据库

使用

1
docker exec <container-id> + 命令

能操作容器。
所以,可以对运行在容器的内的服务进行如下操作:

1
2
3
4
docker exec <container-id> python manage.py db init
docker exec <container-id> python manage.py db migrate
docker exec <container-id> python manage.py db upgrade
docker exec <container-id> python manage.py test

在这个过程中,我碰到了一些问题,关于migrations文件。运行docker exec python manage.py db init 有时候会在本机生成migrations,有时候又没有,并且两者的docker-compose.yml和Dockerfile文件并无差别。
比如这样:


按理来说migrations不应该在本机生成。虽然生不生成migrations不影响数据库迁移,但是这很奇怪。

后续如果数据库中的模型有所变更,应使用如下命令迁移数据库:

1
2
docker exec <container-id> python manage.py db migrate
docker exec <container-id> python manage.py db upgrade

step5 解决中文字符

如果直接使用官方的mysql docker image,则无法支持中文字符,要修改mysql容器的字符设置,利用以下命令进入mysql容器:

1
docker exec -ti <container-id> /bin/bash

进入mysql容器之后,登录mysql:

1
mysql -u root -pmypassword

查看当前mysql字符集情况 可以看到:

database和service的字符集设定是Latin1,不支持中文,如果要支持中文,就要把要数据库编码修改为utf8mb4,但是我把这个mysql的配置和数据库的配置称utf8mb4,依旧不支持中文(这就很气
所以这是一个未解之谜,以后再解决吧。

step6 数据库迁移

之前我迁移数据库,由于各种原因不成功的时候,总是非常粗暴地直接进到服务的容器里面,直接把migrations删了重新,重新建一个。然而,运行到

1
docker exec <container-id> python manage.py db migrate

的时候,总是有一个报错:

这是因为Alembic在数据库里保存了版本记录,虽然migrations已经删掉了,但是alembic_version还保留着它的版本,所以要把它删掉。
进入mysql容器,登录mysql,查找创建的数据库,果然发现里面有一个alembic_version:

查看alembic_version:

把alembic_version删除 :

1
drop table alembic_version;

这样就不会报错了。

最后

还是有很多很奇怪的地方,但好在问题都有了解决办法,,所以先不管它。