Dockerfile实现MySQL定时备份
本文利用自定制Dockerfile实现在mysql容器内自动定时备份,避免了在宿主机设置cron定时任务所带来的高耦合操作,更易于部署和搬迁.项目最新版已上传到github和DockerHub,建议直接到DockerHub查看使用.
网上其他教程大多都是利用宿主机crontab定期执行docker exec实现,很简单但每次部署mysql都需要设置,本教程则是将其封装起来,避免重复性工作.踩坑不易,请大家多多支持,未经本人允许禁止转载.
一. 文件结构
二. 文件介绍
cron-shell文件夹
放置cron任务表文件crontab.bak和相关任务脚本,对应容器内文件夹为**/cron-shell**- backup.sh
实现备份功能,用户可替换掉该文件以实现自己的备份逻辑 - crontab.bak
crontab任务表,要求结尾必须换行,且文件为Unix格式
- backup.sh
start.sh
功能:
- 将mysql用户的环境变量写入/etc/default/locale文件,使得这些环境变量对cron运行的脚本(如backup.sh)可见
- 启动cron
- 载入/cron-shell/crontab.bak定时任务列表文件
- init-sql文件夹
inti-sql文件夹下的sql脚本(.sql)或者sql的压缩文件(.sql.gz)在容器启动后由msql运行,通常用来实现数据库初始化,需要注意的是这些sql的执行顺序不固定,故如果对sql脚本执行顺序有要求的话应自己使用shell脚本实现- data_20180909.sql.gz
数据库初始化sql的压缩包
- data_20180909.sql.gz
- Dockerfile
将文件复制进镜像文件,并安装sudo,cron工具,同时授予mysql组用户免密码使用sudo的权限.
三. 实现思路
通过Dockerfile安装所需cron,利用docker-entrypoint.sh中自动执行**/docker-entrypoint-initdb.d**文件夹下shell和sql脚本的功能完成cron自启动和任务制定
四. 文件实现
1. baskup.sh
1 |
|
2. crontab.bak
1 |
|
注意结尾要换行,并且文件格式要是Unix(其他所有sh脚本也是)
3. start.sh
1 |
|
4. Dockerfile
1 |
|
五. 问题
(1) 在什么时候启动cron(在哪里执行service cron start
)
我本来是想在Dockerfile里完成启动cron工作的,Dockerfile里执行语句无外乎RUN
,CMD
和ENTRYPOINT
三种指令,但很可惜这三种指令都不行,最后只能利用docker-entrypoint.sh,下面进行分析说明:
1. RUN
RUN的特点是可以执行多条,每执行一条docker镜像就会多构建一层.
需要注意的是RUN通常只用来构建镜像,而镜像是不带有运行状态的.故RUN可完成安装软件,修改文件等操作,但使用service start是无意义的.
而CMD和ENTRYPOINT则是等到容器启动后才运行.
2. CMD
CMD每个Dockerfile文件可以有多条,但一个镜像只有最后一条CMD有效
MySQL官方镜像已经有用到CMD和ENTRYPOINT指令了,分别是CMD ["mysqld"]
和ENTRYPOINT ["docker-entrypoint.sh"]
,当ENTRYPOINT和CMD同时存在时,CMD通常是作为ENTRYPOINT的参数,其内容视ENTRYPOINT的处理而执行.
而docker-entrypoint.sh中对cmd内容的分类有如下:
- 开头是
-
, 认为是参数的情况
可携带参数,如**–character-set-server=utf8mb4和–collation-server=utf8mb4_unicode_ci** - 开头是 mysqld, 且用户 id 为0 (root 用户) 的情况
- 开头是 mysqld 的情况
- 其他情况
由docker-entrypoint.sh最后一句的exec "$@"
负责执行用户的自定义语句
特别注意:前3种情况在docker-entrypoint.sh都有对应的处理,最终都会启动mysql,而最后一种情况不会 !
在前3种情况中CMD无法携带service cron start
,会被视为参数忽略或报错,而最后一种启动cron的语句如下CMD service cron start && tail -n 1 -f somefile
,其中tail语句是为了让容器在前台运行而不会自动退出.但是使用docker exec 进入容器就可以发现,mysql根本没有运行.而如果想在CMD里面同时启动cron和mysql就更麻烦了,几乎不可能(因为有很多mysql的初始化工作要做,这些本来是docker-entrypoint帮你做好的),而且这样就失去了使用docker-entrypoint.sh的意义,得不偿失.
3. ENTRYPOINT
ENTRYPOINT每个Dockerfile文件可以有多条,但一个镜像只有最后一条ENTRYPOINT有效
MySQL官方镜像的ENTRYPOINT是执行docker-entrypoint.sh,所以这一句是改不了的,要改不如直接改docker-entrypoint.sh.
以上,可以看出来,想要在Dockerfile里执行service cron start是不可能的,唯一的办法在docker-entrypoint.sh文件.
4. docker-entrypoint.sh
该文件的详细解读网上有比较多,可以直接把执行语句加在这里,毕竟这个文件就是来做这种事的,这也确实是个实现思路.但这么做修改了原来的文件,很不优雅,不符合开闭原则,还好MySQL镜像的设计者考虑到了这点,留下了个/docker-entrypoint-initdb.d
文件夹
2021年更新:/docker-entrypoint-initdb.d
只有在数据库空白(即第一次启动且挂载目录为空)的时候才会执行初始化。如果将执行语句放这里会导致容器重启后cron执行语句失效,参考 https://github.com/linshenkx/mysql-cron/issues/1 。现已更改为使用自定义ENTRYPOINT脚本,再在脚本的最后调用docker-entrypoint.sh。
docker-entrypoint中相关语句如下
1 |
|
如上,会遍历/docker-entrypoint-initdb.d
中的每个文件作为参数调用process_init_file
方法,该方法如下
1 |
|
如上,需要执行的shell脚本或者sql脚本,可放到/docker-entrypoint-initdb.d/目录下,如果是sql.gz的压缩包,容器会自动解压再执行.
需要注意的是,只能保证遍历全部执行而不保证顺序,所以有顺序需求的需要利用脚本自己实现.
很明显,cron的启动脚本应该放在这个文件夹下
(2) sh脚本文件须为Unix格式(Windows下默认为dos),同时注意提供可执行权限
dos转unix格式通常有以下几种方式:
- windows:使用notepad++
依次点击”编辑”->”文档格式转换” ->”转换为UNIX格式” - linux:使用vim
在命令模式输入set ff=unix回车即可,set ff可以查看格式 - linux:使用dos2unix
dos2unix后直接加文件名即可