对于mysqld的启停我们可以使用mysqld_safe或者服务来管理, 本案例使用前者. 好处是:mysqld异常挂了之后,会被拉起来.
那么mysqld_safe挂了呢? 谁来拉它呀!
不巧的是, 本次案例就是mysqld_safe挂了.(本案例和mysql无法, 不方便模拟, 所以后续内容仅以文字描述, 并提供相关命令)
前几天给某个mysql实例重启(释放内存)后, 发现mysqld_safe进程没了. mysql.err没有任何信息. /var/log/message也没得任何信息. 仿佛凭空消失了一样(被kill?).
这种通常有3种情况:
dmesg -T
未发现任何信息.首先先看下除了这套环境外, 还有哪些环境的mysqld_safe也没了. 通过巡检记录发现, 还有几套系统的mysqld_safe进程也没了. 有的是半年前的, 有的是最近的. 但有个共同点就是: 使用自动化部署或者重启的.
测试环境使用自动化重启mysqld实例, 成功复现该问题(不到3分钟,mysqldsafe就挂了). ~~故: 这个锅得甩给自动化了(--)~~
但自动化也是调用的脚本来跑的啊.大概流程如下:
这个逻辑也没啥问题啊, 而且自动化切换之类的都使用得好好的. 难道不是自动化的原因, 是mysqld_safe的原因? 还是脚本调用层级太多了?
作为对照, 我们写个脚本来模拟这个多次调用过程. 遗憾的是mysqld_safe一直都活得好好的.(起码把锅给自动化扣牢了)
起码证明了mysqld_safe脚本是没得问题的. 那我们看下自动化的日志吧, 日志在哪? 不知道! 我们使用万能的grep -r
, 找啥关键词,当然是mysqld_safe的pid啊.
# 找到自动化相关的目录
ps -ef | grep xx
# 通过pid找到相关的信息
grep -r 'MYSQLD_SAFE_PID' xxx
果然找到了 kill MYSQLD_SAFE_PID 之类的信息. 那么为啥要kill mysqld_safe进程呢?
我们根据对于的日志信息再使用grep -r KEY
来找到对应的代码(这种自动化的, 很多都是py写的, 所以找起来还是比较简单的).
于是我们找到了如下类似逻辑(如下为伪代码):
for p in process_list:
if p.cwd().startswith('XX_ROOT_DIR') and p.ppid == 1:
p.kill()
看起来的逻辑就是判断 该进程的cwd(current working directory)是否和自动化的目录一致, 并且父进程是否为1, 是的话,直接kill. 用途估计是回收某些zombie进程.
XX_ROOT_DIR 表示某某软件的根目录. 这种设置主要是解决一些路径问题.
从逻辑来讲是没得啥大问题的. 但是我们的mysqld_safe为啥没了呢? 能匹配上这个逻辑? 通常我们启动的Mysqld_safe的cwd是mysql_base之类的, 不太可能是XX_ROOT_DIR之类的
难道我们的Mysqld_safe的cwd路径变为了XX_ROOT_DIR ? 我们修改下 "脚本A", 在调用启动脚本之前, 显示下当前路径, 然后cd到其它目录,再执行启动脚本
......
pwd
cd ${START_SCRIPT_DIR%/*}
pwd
${START_SCRIPT_DIR}
....
然后观察发现: 第一个路径确实是XX_ROOT_DIR, 并且由于使用了cd ${START_SCRIPT_DIR%/}命令, 我们mysqld_safe的cwd路径也确实变为了${START_SCRIPT_DIR%/}. 而且mysqld_safe进程一直都活得好好的!
所以就是自动化调用的时候把我们的cwd目录改了? 我们查看mysqld_safe脚本的时候, 发现如下信息:
# mysql.server works by first doing a cd to the base directory and from there
# executing mysqld_safe
大意是mysqld_safe运行的时候会cd到mysql base目录. 那自动化的为啥还能给我们cd到其它目录去呢?
不对, 自动化是调用的mysqld_safe, 所以是mysqld_safe最后修改, 那应该优先生效才对, 除非没有设置basedir
find_basedir_from_cmdline () {
for arg in "$@"; do
case $arg in
--basedir=*)
MY_BASEDIR_VERSION="`echo "$arg" | sed -e 's;^--[^=]*=;;'`"
# Convert to full path
cd "$MY_BASEDIR_VERSION"
if [ $? -ne 0 ] ; then
log_error "--basedir set to '$MY_BASEDIR_VERSION', however could not access directory"
exit 1
fi
MY_BASEDIR_VERSION="`pwd`"
;;
esac
done
}
oldpwd="`pwd`"
find_basedir_from_cmdline "$@"
于是我们检查了启动mysqld_safe的脚本, 发现: 果然没有设置--basedir
.
于是就使用了自动化软件的XX_ROOT_DIR目录,导致被误以为是自动化软件的目录给干掉了.
所以是谁的锅呢?
原因: mysqld_safe被自动化当作自己的进程给kill了
本次锅不好甩.
本次案例看似是自动化的锅: 手动执行没得问题, 使用自动化执行就有问题, 那不就是自动化的锅么
但: cwd为自己目录也没有问题啊, 把cwd为自己目录的进程干掉以回收资源也没啥问题啊.
又但: 不是你的进程, 你干嘛要kill呢? 你完全可以在某个文件记录你自己进程的pid啊. 你这随便kill也太离谱了.
又又但: 你mysqld_safe不是都支持basedir之类的来指定cwd路径么, 你自己不用, 怪谁呢
又又又但: 有选项又没表示一定要使用, 本来就没必要使用啊, 在哪都不影响我使用啊.
.........
解决方法有2种:
--basedir
(修改数量太多)在kill进程这种操作的时候要稳一点, 尤其是数据库服务器; 如果本次是mysqld进程的话, 不就gg了么. 还是觉得自动kill进程比较离谱(虽然初衷是好的).
参考:
https://dev.mysql.com/doc/refman/8.0/en/mysqld-safe.html
https://www.kernel.org/doc/html/latest/filesystems/proc.html
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。