lac_agent自愈链路上篇——crontab守护的那些坑与健康检查实战

📅 2026/6/30 23:21:50
lac_agent自愈链路上篇——crontab守护的那些坑与健康检查实战
文章目录crontab守护到底干了啥第一个坑进程僵死crontab以为还活着第二个坑crontab拉起逻辑的陷阱第三个坑多实例环境下crontab打架第四个坑crond自己挂了实战用lac_cron.sh做更精细的控制关于lac_agent stop的小细节一个完整的时间线复盘那怎么办兼容是对前人努力的尊重是确保业务平稳过渡的基石然而这仅仅是故事的起点凌晨三点半手机震了一下是Zabbix告警。我迷迷糊糊摸过手机看了一眼差点没从床上弹起来——三台机器同时报license异常。不是什么网络抖动是lac_agent进程直接没了授权文件过了校验窗口期之后自动失效数据库进入了只读模式。关键是这三台机器的crontab都是配好的按道理说每5分钟就会尝试拉起一次lac_agent。结果呢一个都没拉起来。后来排查了一宿才搞清楚原因今天就把这些坑记录下来也给后面用LAC做集中授权管理的朋友提个醒。crontab守护到底干了啥先说清楚crontab守护的工作机制不然后面有些坑你理解不了。执行lac_agent start的时候这个命令做了两件事第一把lac_agent作为后台常驻进程启动起来第二在当前用户的crontab里面写入一条定时任务每5分钟尝试启动一次lac_agent。对你没看错是写crontab不是systemd不是supervisor就是最朴素的crontab。你可以看看自己的crontab$crontab-l# 下面这行就是lac_agent start自动写入的*/5 * * * * /home/kingbase/Server/bin/lac_agent start/home/kingbase/lac_cron.log21这个设计的初衷是好的——进程挂了不要紧5分钟后crontab会重新拉起。听起来挺美但实际运行中你会发现这个自愈远没有想象中那么可靠。第一个坑进程僵死crontab以为还活着这是最坑的一种情况。lac_agent进程没有完全退出但已经进入了僵死状态zombie或者不可中断睡眠状态D state。这时候你去ps看进程是存在的$psaux|greplac_agent kingbase123450.00.1456782345? Ss Jun200:03 lac_agent看着挺正常的对吧PID还在状态是Ss。但你执行lac_agent status看看$ lac_agent status lac_agent is not responding进程在但是不干活了。crontab每5分钟跑一次lac_agent start这个start命令内部会检查进程是否存在——发现PID在就认为哦lac_agent还活着呢然后退出不会重新拉起。这就是典型的植物人问题。人躺在那儿心跳还有但已经不省人事了。crontab只检查心跳不检查意识。我遇到那次就是这种情况。某次网络抖动导致lac_agent在连接LAC服务端的时候卡住了底层socket调用进入了D状态uninterruptible sleep这种状态连kill -9都杀不掉只能等I/O完成或者重启机器。怎么检测僵死光看PID是不够的得结合多种方式#!/bin/bash# lac_agent健康检查脚本 v1# 用法: bash check_lac_agent.shKB_HOME/home/kingbase/ServerLAC_LOG/home/kingbase/lac/lac_agent.log# 第一层检查进程是否存在PID$(pgrep-f${KB_HOME}/bin/lac_agent)if[-z$PID];thenecho[CRIT] lac_agent进程不存在# 直接拉起${KB_HOME}/bin/lac_agent startexit1fi# 第二层检查进程状态是否为Z僵尸或D不可中断睡眠STATE$(ps-ostate-p$PID2/dev/null)if[[$STATEZ]]||[[$STATED]];thenecho[CRIT] lac_agent进程僵死状态:$STATE# 尝试killkill-9$PID2/dev/nullsleep2# 检查是否真的杀掉了ifps-p$PID/dev/null21;thenecho[ALERT] kill -9无法杀死进程状态为D可能需要重启机器exit2fi# 重新拉起${KB_HOME}/bin/lac_agent startexit1fi# 第三层调用lac_agent status检查实际响应STATUS$(${KB_HOME}/bin/lac_agent status21)ifecho$STATUS|grep-qinot responding\|not running\|error;thenecho[WARN] lac_agent进程在但不响应status输出:$STATUSkill-9$PID2/dev/nullsleep2${KB_HOME}/bin/lac_agent startexit1fi# 第四层检查日志是否太久没更新if[-f$LAC_LOG];thenLAST_MODIFY$(stat-c%Y$LAC_LOG)NOW$(date%s)DIFF$(((NOW-LAST_MODIFY)/60))if[$DIFF-gt30];thenecho[WARN] lac_agent日志超过${DIFF}分钟未更新可能已假死kill$PID2/dev/nullsleep2${KB_HOME}/bin/lac_agent startexit1fifiecho[OK] lac_agent运行正常PID:$PIDexit0这个脚本做了四层检查进程在不在、进程状态对不对、lac_agent自身状态响应、日志最后更新时间。基本上能覆盖大部分假活场景。第二个坑crontab拉起逻辑的陷阱你以为crontab就是简单的5分钟拉一次实际上lac_agent start被重复调用的时候内部有一些判断逻辑。当crontab调用lac_agent start的时候它会检查是不是已经有一个lac_agent在运行。如果检测到了已有的进程它就不会再启动新的。这个检测机制是基于PID文件还是基于进程名匹配官方文档没细说但根据我的实际经验它应该是通过进程名端口来判断的。问题出在哪呢假设你的lac_agent因为某种原因比如OOM被系统kill了但是它的PID文件没来得及清理。下一次crontab触发的时候lac_agent start检测到PID文件存在可能就会认为还在运行然后什么都不做就退出了。这种情况你需要手动清理PID文件# 查找PID文件位置find/home/kingbase/Server-name*.pid|greplac# 确认进程确实不存在后再删psaux|greplac_agent|grep-vgrep# 如果确认没有进程在跑但PID文件还在rm-f/home/kingbase/Server/var/run/lac_agent.pid# 然后重新拉起/home/kingbase/Server/bin/lac_agent start还有一种更隐蔽的情况——crontab拉起成功了但是新的lac_agent启动之后发现端口被占用了旧进程虽然崩了但端口还没释放新进程启动失败就退出了。然后下一个5分钟又拉起还是端口占用……一直循环。所以我在健康检查脚本里加了一步拉起之前先确保端口是干净的# 检查11234端口是否被占用LAC默认端口PORT_OCCUPIED$(ss-tlnp|grep:11234|grep-vlac_agent)if[-n$PORT_OCCUPIED];thenecho[WARN] 端口11234被非lac_agent进程占用echo$PORT_OCCUPIED# 根据实际情况决定是否killfi第三个坑多实例环境下crontab打架这个坑是我在一个双机热备环境里踩到的。那套环境一台物理机上跑了两套KES实例分别属于不同的kingbase用户kingbase1和kingbase2每个实例都有自己的lac_agent。问题来了两个用户各自执行了lac_agent start各自在自己的crontab里写了定时任务。本来相安无事但有一次做系统维护的时候运维同事用root身份修改了其中一台的lac_agent.conf配置导致配置文件的属主变成了root。然后kingbase1的lac_agent重启时就读不到配置文件了权限不对启动失败。但kingbase2的lac_agent正常运行。crontab每5分钟尝试拉起kingbase1的lac_agent每次都因为权限问题失败。关键是这个问题不会在lac_agent.log里面留下很明显的错误提示——因为lac_agent进程根本没启动起来日志都没开始写。你得去看crontab的执行日志# 查看crontab执行记录greplac_agent/var/log/cron|tail-50# 或者直接看crontab的输出日志cat/home/kingbase1/lac_cron.log|tail-100所以我后来把健康检查脚本改了一下在拉起之前先检查配置文件权限LAC_CONF/home/kingbase/Server/share/lac_agent.confif[-f$LAC_CONF];thenCONF_OWNER$(stat-c%U$LAC_CONF)CURRENT_USER$(whoami)if[$CONF_OWNER!$CURRENT_USER];thenecho[WARN] 配置文件属主不匹配:$CONF_OWNERvs$CURRENT_USER# 这里可以选择修复权限或者直接报警# chown $CURRENT_USER:$CURRENT_USER $LAC_CONFfifi第四个坑crond自己挂了这个概率很低但确实遇到过。crond是整个crontab调度的基础如果crond进程本身出了问题僵死、退出那所有依赖crontab的守护机制都会失效。我之前遇到过一次那台机器负载特别高crond进入了D状态所有定时任务都不执行了。lac_agent的crontab守护自然也就废了。检测方法很简单# 检查crond进程状态systemctl status crond# 如果crond僵死了psaux|grepcrond# 看状态是不是D或Z# 尝试重启crondsystemctl restart crond但说实话crond挂了这个事情已经超出了lac_agent自愈的能力范围了。你能做的就是把它纳入到整体的健康检查体系里面去。实战用lac_cron.sh做更精细的控制安装完LAC客户端之后在bin目录下会有一个lac_cron.sh脚本。这个脚本其实就是crontab每次调用的那个入口。你可以看看它的内容cat/home/kingbase/Server/bin/lac_cron.sh这个脚本里面做了些基本的检查但说实话不够用。我的做法是写一个包装脚本把健康检查的逻辑嵌进去然后让crontab调用我的包装脚本而不是直接调用lac_agent start#!/bin/bash# /home/kingbase/lac/lac_watchdog.sh# 增强版lac_agent守护脚本替代crontab直接调用lac_agent startKB_HOME/home/kingbase/ServerLOG_FILE/home/kingbase/lac/watchdog.logALERT_FILE/home/kingbase/lac/alert.flaglog_msg(){echo$(date%Y-%m-%d %H:%M:%S)$1$LOG_FILE}# 检查lac_agent进程check_and_recover(){localPID$(pgrep-u$(whoami)-f${KB_HOME}/bin/lac_agent)# 场景1进程完全不存在if[-z$PID];thenlog_msg[RECOVER] lac_agent进程不存在尝试拉起${KB_HOME}/bin/lac_agent start$LOG_FILE21sleep3# 验证是否成功ifpgrep-u$(whoami)-f${KB_HOME}/bin/lac_agent/dev/null;thenlog_msg[OK] lac_agent已成功拉起rm-f$ALERT_FILEelselog_msg[FAIL] lac_agent拉起失败# 写入告警标记echo$(date)$ALERT_FILEfireturnfi# 场景2进程存在但僵死localSTATE$(ps-ostate-p$PID2/dev/null)if[[$STATEZ]];thenlog_msg[RECOVER] lac_agent僵死(Z状态)清理并重启# 僵尸进程需要先杀父进程localPPID$(ps-oppid-p$PID2/dev/null|tr-d )if[-n$PPID][$PPID!1];thenkill-9$PPID2/dev/nullfikill-9$PID2/dev/nullsleep2${KB_HOME}/bin/lac_agent start$LOG_FILE21returnfi# 场景3进程存在但无响应通过status命令检测localSTATUS$(${KB_HOME}/bin/lac_agent status21)ifecho$STATUS|grep-qinot responding\|not running\|error\|failed;thenlog_msg[RECOVER] lac_agent无响应status:$STATUSkill-9$PID2/dev/nullsleep2# 清理可能残留的PID文件find${KB_HOME}-name*.pid-path*lac*-execrm-f{}\;${KB_HOME}/bin/lac_agent start$LOG_FILE21returnfi# 场景4检查日志新鲜度localLAC_LOG$(grep^log_file${KB_HOME}/Server/share/lac_agent.conf2/dev/null|awk-F {print $2})if[-n$LAC_LOG][-f$LAC_LOG];thenlocalLAST_MOD$(stat-c%Y$LAC_LOG2/dev/null)localNOW$(date%s)localDIFF$(((NOW-LAST_MOD)/60))if[$DIFF-gt20];thenlog_msg[WARN] lac_agent日志${DIFF}分钟未更新疑似假死kill$PID2/dev/nullsleep3# 如果kill不掉尝试kill -9ifps-p$PID/dev/null21;thenkill-9$PID2/dev/nullsleep2fi${KB_HOME}/bin/lac_agent start$LOG_FILE21fifi}# 执行检查check_and_recover# 如果存在告警标记通知运维if[-f$ALERT_FILE];then# 这里可以对接你的告警通道钉钉、企业微信、邮件等# curl -X POST https://oapi.dingtalk.com/robot/send?access_tokenxxx \# -H Content-Type: application/json \# -d {msgtype:text,text:{content:lac_agent自愈失败需人工介入}}log_msg[ALERT] 告警标记存在已触发通知fi然后把crontab改成调用这个脚本# 删掉原来的crontab条目lac_agent stop# 手动编辑crontabcrontab-e# 写入*/5 * * * * /home/kingbase/lac/lac_watchdog.sh注意我用了lac_agent stop先停掉原来的守护这样它会自动清除crontab里面的旧条目。然后手动写入新的crontab指向我的watchdog脚本。关于lac_agent stop的小细节顺便说一下lac_agent stop和lac_agent stop -n的区别。lac_agent stop停止lac_agent进程并且删除crontab中的定时任务lac_agent stop -n只停止进程保留crontab定时任务这个-n参数很多人不知道其实挺有用的。比如你想临时停掉lac_agent做个维护但又不想影响crontab守护等维护完了lac_agent会被crontab自动拉起来。另外还有一个参数lac_agent start -n启动进程但不写crontab。这个参数在你打算用systemd或者其他方式守护的时候很有用避免crontab和systemd两套守护同时存在打架。一个完整的时间线复盘把我那次凌晨故障的排查过程捋一遍可能更有代入感03:17— Zabbix报警3台机器license异常03:19— SSH登录第一台机器执行ps aux | grep lac_agent发现进程确实不在了。奇怪crontab为什么没拉起来03:22— 查看crontabcrontab -l定时任务还在。查看cron日志grep lac_agent /var/log/cron | tail -20发现最近一次执行是02:45之后就没有了。03:25— 检查crond服务systemctl status crond发现crond的状态是active (running)但最后活动时间停在了02:48。进一步查dmesg | tail -50发现02:48左右有一次内存告警OOM killer杀掉了几个进程。03:30— 原来OOM killer不仅杀了lac_agent还顺带把crond给搞崩了进入了某种异常状态。crond虽然显示running但实际上已经不调度任务了。03:32—systemctl restart crond重启crond然后手动拉起lac_agent。03:35— 在其他两台机器上重复同样操作。03:50— 全部恢复数据库从只读模式回到正常读写。事后总结核心问题就一个crontab守护不是真正的自愈。它只解决了进程被杀这种简单场景对于僵死、crond异常、PID残留等问题完全无能为力。那怎么办短期方案就是我上面写的那个watchdog脚本用crontab调用它替代直接调用lac_agent start至少能覆盖大部分常见的假死场景。长期方案那肯定是上systemd。官方其实已经提供了lac_agentd.sh脚本来注册systemd服务但这里面也有不少坑要填。systemd方式需要root权限重启策略要自己配而且跟crontab方式在管理逻辑上有很多不一样的地方。这部分内容比较多放到下篇来详细说。说到这里前阵子我在金仓社区看到有个同行者计划的活动说白了就是推荐商机赢好礼——你手头要是有朋友、客户在做信创选型或者数据库替换的推荐过去成了就有奖励 https://bbs.kingbase.com.cn/forumDetail?articleId1d09d598f414ab764eda4907e8f54758 。像我这种天天跟LAC授权打交道的人身边问金仓方案的朋友还真不少顺手推荐一下两边都落好。回过头来想想crontab守护这套机制就像是你请了个保安跟他说每5分钟巡逻一次看看大门有没有人。但保安可能自己打瞌睡了crond异常可能看到有人躺在地上以为是在睡觉进程僵死也可能巡逻记录本被人撕了几页日志轮转导致误判。真正的自愈不是定时检查而是实时感知自动恢复异常升级。下篇我们聊聊怎么用systemd把这套东西做得更靠谱。