文章目录
-
- 为什么crontab不够用
- lac_agentd.sh:官方提供的systemd注册脚本
- [systemd unit文件详解](#systemd unit文件详解)
-
- [Type=simple vs Type=forking](#Type=simple vs Type=forking)
- 从crontab迁移到systemd的完整步骤
- systemd方式下的日志管理
- 进阶:搭建完整的自愈体系
- 与外部监控系统的集成
- 完整架构总结
- 一些实践经验
- 最后

兼容 是对前人努力的尊重 是确保业务平稳过渡的基石 然而 这仅仅是故事的起点
上篇说了crontab守护lac_agent的那些坑,说实话写完上篇的时候我自己都有点后怕------在那之前我一直觉得crontab每5分钟拉起一次已经够用了,直到凌晨三点被叫醒才知道这套机制有多脆弱。
这篇文章接着聊,怎么从crontab迁移到systemd托管,以及怎么搭建一套真正靠谱的健康检查和自动恢复体系。
为什么crontab不够用
先简单回顾一下上篇提到的crontab的几个硬伤:
- 进程僵死(Z状态/D状态)时,crontab以为"还活着",不会重新拉起
- PID文件残留导致start命令误判进程仍在运行
- crond自身异常时整个守护机制失效
- 没有失败重试的退避策略(每次都是固定5分钟)
- 没有告警通知机制(拉不起来也不会告诉你)
这些问题归结到一点:crontab是个"定时触发器",不是"服务管理器"。它没有状态感知、没有重启策略、没有依赖管理、没有日志聚合。这些能力,systemd都有。
所以结论很明确:生产环境的lac_agent,应该用systemd来托管。
lac_agentd.sh:官方提供的systemd注册脚本
官方其实已经想到了这一点。在安装完LAC客户端之后,bin目录下会有一个 lac_agentd.sh 脚本,专门用来把lac_agent注册成systemd服务。
先看看这个脚本怎么用:
bash
# 查看帮助
$ /home/kingbase/Server/bin/lac_agentd.sh --help
# 注册为systemd服务(需要root权限)
$ su -
# lac_agentd.sh
# 注册完之后,服务名是lac_agentd
$ systemctl status lac_agentd
$ systemctl start lac_agentd
$ systemctl stop lac_agentd
$ systemctl restart lac_agentd
注意两个要点:
第一,注册systemd服务需要root权限 。这个在官方文档里也写了。原因是systemd的unit文件要写到 /etc/systemd/system/ 目录下,普通用户没有这个权限。
第二,注册之前要确保lac_agent.conf配置正确,因为systemd服务启动时会读取这个配置文件。如果配置不对,服务启动了也会马上挂掉。
那我们先看看 lac_agentd.sh 到底做了什么。打开这个脚本看看:
bash
cat /home/kingbase/Server/bin/lac_agentd.sh
不同版本内容可能不一样,但核心逻辑就是生成一个systemd unit文件,写到 /etc/systemd/system/lac_agentd.service,然后执行 systemctl daemon-reload。
systemd unit文件详解
如果官方脚本生成的unit文件不能满足你的需求(比如你想自定义重启策略、资源限制等),你可以手动编辑这个unit文件。
这里面有几个关键的点我要展开说一下。
Type=simple vs Type=forking
lac_agent start 命令会把进程fork到后台然后退出,这种模式对应 Type=forking。但是这种模式有个问题------systemd只能跟踪主进程的PID,如果lac_agent自己又fork了子进程,systemd就不太容易管住了。
我推荐的做法是用 Type=simple,ExecStart直接执行 lac_agent(不带start参数),让lac_agent作为前台进程运行。这样systemd能完全掌控它的生命周期:进程退出了systemd立刻知道,进程僵死了systemd也能通过watchdog检测到。
但这里有个前提:lac_agent 不加start参数时的行为,根据文档是"手动执行一次授权检查",执行完就退出了。这就尴尬了------我们需要的是常驻进程。
所以下面这个方式更稳妥:
ini
# 用start参数 + Type=forking + PIDFile
Type=forking
ExecStart=/home/kingbase/Server/bin/lac_agent start -n
PIDFile=/home/kingbase/Server/var/run/lac_agent.pid
方案A比较简单,但PIDFile的路径你得确认对,不同版本可能不一样。方案B需要自己写一个wrapper脚本,但控制力更强。
从crontab迁移到systemd的完整步骤
这是很多DBA问我的问题------我现在跑的crontab守护,怎么安全地切到systemd?
关键是顺序,别搞错了,不然可能出现两套守护同时拉起的混乱局面。
bash
#!/bin/bash
# 从crontab迁移lac_agent到systemd的完整流程
# 以kingbase用户操作,部分步骤需要root
KB_HOME="/home/kingbase/Server"
LAC_BIN="${KB_HOME}/bin"
echo "=== Step 1: 停止当前的lac_agent进程和crontab守护 ==="
# 使用stop命令,会同时停止进程并清除crontab
${LAC_BIN}/lac_agent stop
# 确认crontab已经清除
echo "当前crontab内容:"
crontab -l 2>/dev/null || echo "(空)"
# 确认lac_agent进程已经停止
if pgrep -f "${LAC_BIN}/lac_agent" > /dev/null; then
echo "[WARN] lac_agent进程仍在运行,手动停止"
pkill -9 -f "${LAC_BIN}/lac_agent"
sleep 2
fi
echo ""
echo "=== Step 2: 切换到root,注册systemd服务 ==="
echo "请执行: su - 然后运行 lac_agentd.sh"
echo "或者手动创建unit文件(参考上面的配置)"
# 下面是root执行的部分
# su - <<'ROOT_SCRIPT'
# /home/kingbase/Server/bin/lac_agentd.sh
# systemctl daemon-reload
# systemctl enable lac_agentd
# systemctl start lac_agentd
# ROOT_SCRIPT
echo ""
echo "=== Step 3: 验证systemd服务状态 ==="
# systemctl status lac_agentd
# journalctl -u lac_agentd -f # 实时查看日志
echo ""
echo "=== Step 4: 清理旧的crontab残留(如果有)==="
# crontab -l | grep -v lac_agent | crontab -
echo ""
echo "=== Step 5: 确认授权状态正常 ==="
# ${LAC_BIN}/lac_agent status
# ksql -USYSTEM -dTEST -c "SELECT * FROM V\$LICENSE;"
注意Step 1里面用了 lac_agent stop(不带-n),这样会同时清除crontab条目。如果你用了 lac_agent stop -n,crontab还会保留,后续需要手动清理。
systemd方式下的日志管理
用systemd托管之后,lac_agent的日志就自动接入journalctl了。这比看crontab的输出日志方便太多。
bash
# 查看lac_agentd服务的实时日志
journalctl -u lac_agentd -f
# 查看最近1小时的日志
journalctl -u lac_agentd --since "1 hour ago"
# 查看今天的日志
journalctl -u lac_agentd --since today
# 查看指定时间段的日志
journalctl -u lac_agentd --since "2026-06-20 03:00" --until "2026-06-20 04:00"
# 查看所有lac_agent相关的日志(包括手动执行的)
journalctl SYSLOG_IDENTIFIER=lac_agent --since today
但有个问题需要注意:journalctl的日志是二进制格式,而且默认有大小限制(通常几百MB),太老的日志会被自动清理。如果你需要长期保留lac_agent的日志(比如为了审计),最好还是配置log_file,让lac_agent自己写日志文件:
bash
# 在lac_agent.conf中配置
log_file = /home/kingbase/lac/lac_agent.log
log_level = INFO
然后配合logrotate做日志轮转:
bash
# /etc/logrotate.d/lac_agent
/home/kingbase/lac/lac_agent.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
copytruncate
}
这样systemd journal + lac_agent自己的日志文件,双保险。
进阶:搭建完整的自愈体系
systemd解决了"进程挂了自动重启"这个问题,但一个完整的自愈体系还需要:
- 健康检查------进程在不代表工作正常
- 告警通知------恢复了也要知道发生了什么
- 审计日志------事后复盘需要数据支撑
- 升级机制------自动恢复失败时要升级到人工
先说健康检查。systemd有个 ExecStartPost 和 watchdog 机制,但lac_agent本身如果不支持sd_notify协议,systemd就很难知道它是不是真的在正常工作。
我的做法是写一个独立的健康检查脚本,用systemd timer或者单独的crontab来定期执行:
bash
#!/bin/bash
# /home/kingbase/lac/lac_healthcheck.sh
# lac_agent深度健康检查脚本
KB_HOME="/home/kingbase/Server"
LAC_BIN="${KB_HOME}/bin"
HEALTH_LOG="/home/kingbase/lac/healthcheck.log"
ALERT_WEBHOOK="" # 填你的告警webhook地址
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') $1" >> "$HEALTH_LOG"
}
send_alert() {
local level=$1
local msg=$2
log "[ALERT-${level}] ${msg}"
if [ -n "$ALERT_WEBHOOK" ]; then
curl -s -X POST "$ALERT_WEBHOOK" \
-H 'Content-Type: application/json' \
-d "{\"level\":\"${level}\",\"msg\":\"${msg}\",\"host\":\"$(hostname)\"}" \
> /dev/null 2>&1
fi
}
check_health() {
# 1. 检查systemd服务状态
local SVC_STATUS=$(systemctl is-active lac_agentd 2>/dev/null)
if [ "$SVC_STATUS" != "active" ]; then
send_alert "CRITICAL" "lac_agentd服务状态异常: ${SVC_STATUS}"
# 尝试重启
systemctl restart lac_agentd
sleep 5
SVC_STATUS=$(systemctl is-active lac_agentd 2>/dev/null)
if [ "$SVC_STATUS" != "active" ]; then
send_alert "CRITICAL" "lac_agentd重启失败,需人工介入"
return 1
else
send_alert "INFO" "lac_agentd重启成功"
fi
fi
# 2. 检查lac_agent status命令的响应
local AGENT_STATUS=$(${LAC_BIN}/lac_agent status 2>&1)
if echo "$AGENT_STATUS" | grep -qi "not responding\|error\|failed"; then
send_alert "WARNING" "lac_agent状态异常: ${AGENT_STATUS}"
systemctl restart lac_agentd
return 1
fi
# 3. 检查license.dat文件的时效性
local LIC_FILE=$(find ${KB_HOME} -name "license.dat" -type f 2>/dev/null | head -1)
if [ -n "$LIC_FILE" ] && [ -f "$LIC_FILE" ]; then
local LIC_AGE=$(( ( $(date +%s) - $(stat -c %Y "$LIC_FILE") ) / 3600 ))
if [ $LIC_AGE -gt 168 ]; then # 超过7天没更新
send_alert "WARNING" "license.dat已${LIC_AGE}小时未更新,可能授权同步异常"
fi
fi
# 4. 检查lac_agent日志中的错误
local LAC_LOG="${KB_HOME}/share/lac_agent.conf"
# 从配置中获取实际日志路径(简化处理,直接检查常见位置)
local LOG_PATH="/home/kingbase/lac/lac_agent.log"
if [ -f "$LOG_PATH" ]; then
local ERROR_COUNT=$(grep -c "ERROR\|CRIT\|EMERG\|ALERT" "$LOG_PATH" 2>/dev/null || echo 0)
local RECENT_ERRORS=$(tail -100 "$LOG_PATH" | grep -c "ERROR\|CRIT" 2>/dev/null || echo 0)
if [ "$RECENT_ERRORS" -gt 5 ]; then
send_alert "WARNING" "lac_agent日志中最近发现${RECENT_ERRORS}条错误"
fi
fi
log "[OK] 健康检查通过"
return 0
}
check_health
然后用systemd timer来定期执行:
ini
# /etc/systemd/system/lac-healthcheck.timer
[Unit]
Description=LAC Agent Health Check Timer
[Timer]
OnBootSec=2min
OnUnitActiveSec=5min
AccuracySec=30s
[Install]
WantedBy=timers.target
ini
# /etc/systemd/system/lac-healthcheck.service
[Unit]
Description=LAC Agent Health Check
[Service]
Type=oneshot
User=kingbase
ExecStart=/home/kingbase/lac/lac_healthcheck.sh
bash
systemctl enable lac-healthcheck.timer
systemctl start lac-healthcheck.timer
这样每5分钟做一次深度健康检查,比单纯靠systemd的Restart机制要可靠得多。
与外部监控系统的集成
如果你用Zabbix/Prometheus做监控,可以把lac_agent的状态做成监控指标。
Zabbix方式,写一个自定义监控项:
bash
# /etc/zabbix/scripts/check_lac_agent.sh
#!/bin/bash
KB_HOME="/home/kingbase/Server"
STATUS=$(${KB_HOME}/bin/lac_agent status 2>&1)
if echo "$STATUS" | grep -qi "running\|active\|ok"; then
echo 1
else
echo 0
fi
然后在Zabbix Agent配置里加:
UserParameter=lac.agent.status,bash /etc/zabbix/scripts/check_lac_agent.sh
Prometheus方式,可以写一个简单的exporter脚本,或者用textfile collector:
bash
#!/bin/bash
# /opt/prometheus/lac_agent_metrics.sh
KB_HOME="/home/kingbase/Server"
METRICS_FILE="/var/lib/node_exporter/textfile/lac_agent.prom"
STATUS=$(${KB_HOME}/bin/lac_agent status 2>&1)
if echo "$STATUS" | grep -qi "running\|active\|ok"; then
VALUE=1
else
VALUE=0
fi
cat > "$METRICS_FILE" <<EOF
# HELP lac_agent_running Whether lac_agent is running (1=running, 0=not running)
# TYPE lac_agent_running gauge
lac_agent_running ${VALUE}
EOF
这样你就能在监控大盘上看到lac_agent的运行状态了,配合告警规则,能做到比crontab方式更及时的故障发现。
完整架构总结
把上面说的东西串起来,一套完整的lac_agent自愈体系是这样的:
┌──────────────────────────────────────────────────────┐
│ 第一层:systemd托管 │
│ lac_agentd.service - Type=simple/forking │
│ Restart=on-failure, RestartSec=5 │
│ StartLimitBurst=5, StartLimitIntervalSec=60 │
│ → 负责进程崩溃后的快速重启 │
├──────────────────────────────────────────────────────┤
│ 第二层:systemd timer健康检查 │
│ lac-healthcheck.timer - 每5分钟 │
│ → 深度检查:进程状态、日志错误、license时效 │
│ → 发现问题自动重启服务 │
├──────────────────────────────────────────────────────┤
│ 第三层:告警通知 │
│ 健康检查脚本 → webhook → 钉钉/企微/邮件 │
│ → 恢复事件通知、失败升级通知 │
├──────────────────────────────────────────────────────┤
│ 第四层:外部监控 │
│ Zabbix/Prometheus → 指标采集 → 告警规则 │
│ → 独立于lac_agent自身的监控通道 │
└──────────────────────────────────────────────────────┘
有了这四层,基本上lac_agent不管遇到什么问题------进程崩溃、僵死、配置错误、网络中断------都能在一定时间内自动恢复或者至少把告警送到运维手上。
一些实践经验
最后分享几个我这一年多搞下来觉得有用的小经验:
1. lac_agent.conf中的enable_auto_refresh参数
这个参数默认是1,意思是授权失效后lac_agent会自动向LAC服务端重新申请。听起来很好,但在某些场景下它可能是个问题------如果LAC服务端的授权池满了,或者你的激活文件有问题,lac_agent就会不停地去申请,每次失败都写日志,搞不好还会触发服务端的限流。
在排查问题的时候,我会临时把这个参数设为0,避免lac_agent"自作主张"地反复申请授权:
ini
enable_auto_refresh = 0
2. 关于local_ip在容器环境下的问题
这个留到后面容器化的文章里详细说,但这里提一嘴:安装脚本自动获取的local_ip用的是默认路由的网卡IP,在多网卡环境或者容器环境里可能拿到的不是你想要的IP。建议在lac_agent.conf里明确指定:
ini
local_ip = 10.10.12.253
3. 定期检查LAC服务端的授权池状态
不要只盯着客户端,服务端那边的授权池也可能出问题。用LAC管理工具定期检查:
bash
# 查看LAC服务端状态
lac_admin status
# 查看已分配的授权列表
lac_admin list
# 查看某个客户端的授权详情
lac_admin show -ip 10.10.12.253
4. 备份配置文件
lac_agent.conf一定要做备份。我有一次升级LAC客户端的时候用了 -f 参数强制覆盖安装,结果配置文件被覆盖了(虽然会备份成lac_agent.conf.timestamp,但当时手忙脚乱没注意到)。后来花了半小时才把配置恢复回来。
bash
# 每次修改配置前先备份
cp ${KB_HOME}/share/lac_agent.conf ${KB_HOME}/share/lac_agent.conf.bak.$(date +%Y%m%d%H%M%S)
说到这个方向,前两天看到金仓社区在办"2026智能运维工具开发大赛"( https://bbs.kingbase.com.cn/forumDetail?articleId=2394013b19f3ef84a43edb994692b88e 和 https://bbs.kingbase.com.cn/forumDetail?articleId=6152608d769b472397ccfbd29879c0bd ),我上面写的这些脚本其实已经有点运维工具的雏形了------健康检查、自动恢复、告警通知、监控集成,再包装一下没准能交个参赛作品上去。当然这不是重点,重点是这个方向确实值得投入------把踩过的坑沉淀成工具,让其他人少踩一遍。
最后
两篇文章写下来,从crontab的坑到systemd的托管,再到完整的自愈体系,基本把我这一年多在LAC客户端运维上积累的经验都倒出来了。
说真的,lac_agent这个东西说大不大说小不小,平时不显山不露水,但它一挂你就知道厉害了------数据库直接变只读,业务全停。所以把它的守护机制做扎实,绝对是值得花时间的。
别等到凌晨三点被叫醒才想起来搞这些。