风之栖息地

Docker中的Mysql配置问题处理与思考

字数统计: 1.8k阅读时长: 7 min
2019/11/27 Share

记录一下,我在docker容器中配置Mysql时遇到的坑,以及处理过程中学到的知识。

起因

最近作业量多,任务重结果还是被点名出题(哭唧唧),没办法只能硬着头皮上。有了出题思路之后,和@wh1t3Pig交流之后得知需要使用docker-compose来布置出题环境。啥?卧槽?不能手动布置吗?算了,就当做是对我的考验。所以我开开心心的写好源文件之后,开始准备布置环境,那么到底是直接拉个Mysql,还是直接在容器内部装?在内部装又有各种问题。。。这里就把这些坑记录下来造福后人,同时也有一定的分析。

web server+mysql or in one container?

在参考多个知名项目和比赛题目的Dockerfiledocker-compose.ymlstart.sh之后,毅然决定使用把服务全部装进一个容器的思路。

为什么?考虑有以下几点:

  1. 我出的题目是SQLi,需要提前在数据库中插入数据,然而使用直接拉一个Mysql的方式,非常不方便,如果要插入数据必须要再写一个针对Mysql的Dockerfile,那这样还不如直接放在一起。
  2. 这道题的数据库和其他题目不共享,单独拉一个的意义不大。
  3. 如果以后要多节点部署这种题目,数据库独立的话能避免队伍之间搅屎。

注:要是有师傅能有更好的解决方案,欢迎交流。

Mysql的安装交互问题

那么采用了服务装进一个容器里面之后,发现了另外一个问题,apt-get install mysql-server这个安装是需要交互的,途中要求你设置root的密码。这个没有办法用-y选项回避掉。一顿搜索之后,在stackoverflow中看到可以使用一个环境变量DEBIAN_FRONTEND=noninteractive来跳过交互。

这样我们在Dockerfile

1
2
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y mysql-server && apt-get clean

就可以完美解决交互问题。

php中的mysql扩展安装顺序问题

题目使用的是mysql扩展,自带的php没有,这个时候需要docker-php-ext-install mysql。但是事故发生了,我在手动操作的时候是可以成功的,但是自动化的时候连接报错No such file or directory,当时我立即使用Mysql的客户端尝试连接,结果是成功的,排除了用户名和密码问题。那么问题来了,为什么Mysql客户端可以连接成功,php的连接函数就不行。

在网上找到有关分析,使用strace分析之后发现,php连接函数使用的是unixdomain socket,同时访问的是/tmp/mysql.sock,这里没有这个socket文件,导致报出没有文件的错误。结合其他资料,当使用localhost进行连接的时候,会使用unixdomain的方式。这个/tmp/mysql.sock则是由于php没有设置mysql的默认socket,写死在源码中的默认值。

但是这就奇怪了,我在手动操作时,也是通过docker自带的安装程序安装扩展的。思考之后,发现命令的执行顺序不同,在手动配置时,我是先安装Mysql,同时开启服务之后才安装的扩展。那么来对比一下,不同的顺序php的配置会有什么不同。

先安装扩展,后开启服务。

看到这里是没有设置的状态,先开启Mysql服务,再安装扩展。

看到这里的默认socket已经设置了。所以我们在安装服务类型的扩展时,要先开启服务,再安装扩展。

Mysql在docker中的服务开启问题

这又是个诡异的问题,和上个问题一样,在手动操作的时候service mysql start一点毛病没有,在自动化的时候就炸。唯一的区别是中间的交互过程,手动操作是由交互,自动化是无交互。查询日志得到[ERROR] Fatal error: Can't open and lock privilege tables: Got error 140 from storage engine。让人摸不着头脑,只能沿着这条线继续深入。

这个问题发生在很多用Dockerfile来build的容器上,在github的issue上也找到一些问题的回答。找到的其中一个解决方案是在docker-compose.yml文件中加上volumes:(换行) - /var/lib/mysql,我很好奇是什么机制导致必须要手动在容器内部挂载。

有些issue中提起现在docker的内部文件系统已经从aufs变成了overlayoverlay2,Mysql启动出现问题的都是以overlay系列为文件系统的docker。最后找到了docker在overlay2的文档中的一段对打开文件操作的描述。

open(2): OverlayFS only implements a subset of the POSIX standards. This can result in certain
OverlayFS operations breaking POSIX standards. One such operation is the copy-up operation. Suppose that your application calls fd1=open(“foo”, O_RDONLY) and then fd2=open(“foo”, O_RDWR). In this case, your application expects fd1 and fd2 to refer to the same file. However, due to a copy-up operation that occurs after the second calling to open(2), the descriptors refer to different files. The fd1 continues to reference the file in the image (lowerdir) and the fd2 references the file in the container (upperdir). A workaround for this is to touch the files which causes the copy-up operation to happen. All subsequent open(2) operations regardless of read-only or read-write access mode will be referencing the file in the container (upperdir).

yum is known to be affected unless the yum-plugin-ovl package is installed. If the yum-plugin-ovl package is not available in your distribution such as RHEL/CentOS prior to 6.8 or 7.2, you may need to run touch /var/lib/rpm/* before running yum install. This package implements the touch workaround referenced above for yum.

大体的意思就是,新的overlay文件系统仅仅只实现了POSIX标准的一部分,这就导致某些操作会有异常,比如以两种不同的打开标准操作同一个文件,这里就会让其中的一些操作是针对镜像中的文件,另外一些是针对容器中的文件,这样我们的应用本该是只能控制容器内的文件,却对镜像中的这个文件有操作,从而出现异常。里面给出的解决方案是在启动我们的服务应用之前,把需要操作的文件全部touch一遍。

所以第二种解决方案是在启动服务之前全部touch一遍find /var/lib/mysql -type f -exec touch {} \;。但是事情还没完,为什么在docker build的阶段会这样,而我们手动操作的时候不会出现,答案已经很明显了。只有在docker build的阶段才会有可能产生对镜像内的文件操作,但是我们手动执行时已经是确确实实的在容器中了,也就只能操作容器内的文件,类似open(2)的操作对镜像中的文件不会有干扰。

总结

遇到某些奇怪的现象不要停留在表象,要知其所以然,不要想当然。其次,这次的情况就可以看出中文资料非常有限,所以用英文搜索是一种必备技巧。最后,官方文档是一个宝藏,遇到项目问题可能文档中就有答案。

Reference

https://stackoverflow.com/questions/38165407/installing-lightdm-in-dockerfile-raises-interactive-keyboard-layout-menu

https://my.oschina.net/scgywx/blog/1545301

https://github.com/geerlingguy/drupal-vm/issues/1497

https://github.com/docker/for-linux/issues/72

CATALOG
  1. 1. 起因
  2. 2. web server+mysql or in one container?
  3. 3. Mysql的安装交互问题
  4. 4. php中的mysql扩展安装顺序问题
  5. 5. Mysql在docker中的服务开启问题
  6. 6. 总结
  7. 7. Reference