正确commit mysql容器

前言

接手了两个 Docker 容器,一个是 MySQL 的容器,一个是 Tomcat 容器,里面是一些 Demo 程序和一些测试数据,需要将其 Commit 成镜像方便部署,所以有了下文。

本篇文章会解决以下问题:

  • docker commit 提交后镜像的大小为什么不会变化?

commit tomcat 容器

Tomcat 容器很顺利,使用 docker commit 5a2831dc52c8 tomcat_change:v1 命令直接提交就好了,Tomcat 容器相比 Tomcat 镜像只是拷贝了项目到容器中,并没有进行其他操作,只是省略了通过 Dockerfile 重新构建的过程。

1
2
3
4
5
6
7
b@ubuntu20:~$ docker commit 5a2831dc52c8 tomcat_change:v1
sha256:69e34dbc0a8e2253b1212139ff2dd5330b8847dfe583bcae0cf4f0ae642b8f01
b@ubuntu20:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcat_change v1 69e34dbc0a8e 8 seconds ago 761MB
tomcat latest f796d3d2c195 9 days ago 647MB
mysql latest e1d7dc9731da 2 weeks ago 544MB

可以看到,新的 Tomcat 镜像相比原来的镜像大了好多,多出来的这部分内容就是 Tomcat 中的项目文件所占的大小。

commit MySQL 容器

但是在 commit MySQL 时,出现了问题,不论我怎么 commit,新生成的镜像大小都不会变化,哪怕 1M 的变化都没有。

1
2
3
4
5
6
7
8
b@ubuntu20:~$ docker commit aa3fcc421465 mysql_change:v1
sha256:eccf5478838b964c6def57d255ba81409e5462ba8b55acd891fc272e15a6d1ef
b@ubuntu20:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql_change v1 eccf5478838b 10 seconds ago 544MB
tomcat_change v1 69e34dbc0a8e 3 minutes ago 761MB
tomcat latest f796d3d2c195 9 days ago 647MB
mysql latest e1d7dc9731da 2 weeks ago 544MB

可以看到,新生成的 MySQL 镜像还是原来的大小,按理说 MySQL 在初次启动时会初始化数据库的,而且我也将数据库导入进去了,不可能一点空间都不占用。

我将新生成的镜像 save 后拷贝出来,找到了 MySQL 数据文件默认位置,在 /var/lib/mysql 这个目录,但是这个目录是空的,也就是说数据文件根本没有被保存到这个镜像中,这就奇怪了。

Google 没有查询到类似的问题,我去查询了 docker commit 的官方文档,其中有这么一句话:

The commit operation will not include any data contained in volumes mounted inside the container.

意思是说 commit 命令不会包含挂载在容器中的卷中的数据。我大概明白了什么,去 Docker Hub 上查看了 MySQL 对应版本的 Dockerfile,其中有这么一句:VOLUME /var/lib/mysql ,也就是说 MySQL 容器在启动时自动挂载了一个卷到 /var/lib/mysql 目录中,数据文件全部写入到了这个卷中,通过 docker volume ls 命令查看系统中存在的卷:

1
2
3
b@ubuntu20:~$ docker volume  ls
DRIVER VOLUME NAME
local 54f2937cefd8cda5f91580579fb99e0a87a9b39bebfa0d4b2da3f993917b793e

果真有一个卷,使用 docker volume inspect 54f2937cefd8cda5f91580579fb99e0a87a9b39bebfa0d4b2da3f993917b793e 命令查看卷的详细信息:

1
2
3
4
5
6
7
8
9
10
11
12
b@ubuntu20:~$ docker volume  inspect 54f2937cefd8cda5f91580579fb99e0a87a9b39bebfa0d4b2da3f993917b793e 
[
{
"CreatedAt": "2020-09-25T06:27:58Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/54f2937cefd8cda5f91580579fb99e0a87a9b39bebfa0d4b2da3f993917b793e/_data",
"Name": "54f2937cefd8cda5f91580579fb99e0a87a9b39bebfa0d4b2da3f993917b793e",
"Options": null,
"Scope": "local"
}
]

Mountpoint 字段提示了我们卷在系统中的具体位置,查看文件夹中的内容:

1
2
3
4
5
6
7
root@ubuntu20:/home/b# cd /var/lib/docker/volumes/54f2937cefd8cda5f91580579fb99e0a87a9b39bebfa0d4b2da3f993917b793e/_data
root@ubuntu20:/var/lib/docker/volumes/54f2937cefd8cda5f91580579fb99e0a87a9b39bebfa0d4b2da3f993917b793e/_data# ls
auto.cnf ca.pem ib_buffer_pool '#innodb_temp' public_key.pem undo_002
binlog.000001 client-cert.pem ibdata1 mysql server-cert.pem
binlog.000002 client-key.pem ib_logfile0 mysql.ibd server-key.pem
binlog.index '#ib_16384_0.dblwr' ib_logfile1 performance_schema sys
ca-key.pem '#ib_16384_1.dblwr' ibtmp1 private_key.pem undo_001

熟悉的 MySQL 数据文件找到了。

问题原因

总结下出现问题的原因:因为在 Dockerfile 文件中有一条 VOLUME /var/lib/mysql 命令,指定了卷挂载的位置,所以在每次生成 MySQL 容器时 Dockers 都会新建一个名字为哈希值的卷,挂载到 Volume 命令指定的位置,这样就可以将指定位置的内容全部写入到卷中,防止往 Docker 容器中写入大量内容导致性能问题。再结合官方文档中的 commit 命令不会包含挂载在容器中的卷中的数据,所以在 commit MySQL 容器时,卷中的数据被忽略了,导致我 commit 出来的镜像的数据文件夹是空的。

解决方法

解决方法很简单,新建一个 MySQL 容器,然后更改 MySQL 的配置文件中的数据文件路径,更改为和 /var/lib/mysql 不一样即可,重新启动 MySQL 容器,数据文件就会被写入到新的文件夹中了,然后删除之前新建的卷即可,重新 commit ,问题解决。

不过不推荐使用这种方法,因为我这里是一个 Demo 数据库,基本上只有读没有写,而且对数据可靠性也不敏感,所以选择了最简单的 commit 命令来完成。

总结

由于对 MySQL 镜像和 Docker 的卷不了解,导致了在使用时出现这个问题,看来还是要多学习。

同时官方文档中还提到了:

it is better to use Dockerfiles to manage your images in a documented and maintainable way.

最好通过 Dockerfile 以文档化和可维护的方式来管理镜像。就是说如果有更改最好体现在 Dockerfile 中,然后重新编译,因为可以通过历史提交来追踪镜像的变化,如果直接 commit 的话不便于其他人了解镜像中被更改了什么。

本文章首发于个人博客 LLLibra146’s blog
本文作者:LLLibra146
版权声明:本博客所有文章除特别声明外,均采用 © BY-NC-ND 许可协议。非商用转载请注明出处!严禁商业转载!
本文链接https://blog.d77.xyz/archives/b59689bb.html