
一从零搭建守护环境1在 root 目录下新建 all-n1-install.sh 文件内容如下#!/bin/bashset-eecho N1盒子自愈全套【无jq版·重新生成守护环境】 # 不再安装jq依赖echo[1/7] 跳过jq依赖使用pm2 id返回码判断状态# 2.生成 /root/n1.shecho[2/7] 生成 /root/n1.shcat/root/n1.shINNER_EOF #!/bin/bash # N1盒子服务自愈脚本【无jq最终版】 # 宿主机服务: go-cqhttp sillyGirl xdd # 青龙容器内服务默认关闭巡检 QL_APPS() QL_CONTAINERqinglong LOCK_DIR/root/.n1-deploy LOG_FILE/root/ql/logs/selfheal.log CRON_LOG/root/ql/logs/cron-n1.log PM2_LOG_DIR/root/.pm2/logs mkdir -p ${LOCK_DIR} mkdir -p /root/ql/logs mkdir -p ${PM2_LOG_DIR} # 配置参数 log_max_size$((20*1024*1024)) # 单文件上限 20MB log_keep_days3 # 碎片日志保留天数 docker_exec_timeout8 # docker exec超时秒数 # ## ① 主日志超过阈值清空 for logfile in ${LOG_FILE} ${CRON_LOG};do if [ -f ${logfile} ];then filesize$(stat -c%s ${logfile}) if [ ${filesize} -gt ${log_max_size} ];then ${logfile} fi fi done ## ② 碎片日志过期删除 find /root/ql/logs -type f -name *.log ! -name selfheal.log ! -name cron-n1.log -mtime ${log_keep_days} -delete ## ③ 宿主机pm2日志处理 for logfile in ${PM2_LOG_DIR}/*.log;do if [ -f ${logfile} ];then filesize$(stat -c%s ${logfile}) if [ ${filesize} -gt ${log_max_size} ];then ${logfile} fi fi done find ${PM2_LOG_DIR} -type f -name *.log -mtime ${log_keep_days} -delete ## ④ docker容器日志截断 docker_log_path$(docker inspect --format{{.LogPath}} ${QL_CONTAINER} 2/dev/null || true) if [ -n ${docker_log_path} ] [ -f ${docker_log_path} ];then docker_log_size$(stat -c%s ${docker_log_path}) if [ ${docker_log_size} -gt ${log_max_size} ];then truncate -s 0 ${docker_log_path} fi fi # 互斥锁防止脚本并发运行 exec 9${LOCK_DIR}/n1.lock if ! flock -n 9 ; then echo $(date %Y-%m-%d %H:%M:%S) n1.sh 已在运行放弃本次执行 ${LOG_FILE} exit 0 fi log(){ echo $(date %Y-%m-%d %H:%M:%S) $* ${LOG_FILE} echo $* } # ---------------- pm2 id方式检测无jq ---------------- host_is_online(){ local app$1 pm2 id ${app} /dev/null 21 return $? } ql_is_online(){ local app$1 docker exec --timeout ${docker_exec_timeout} ${QL_CONTAINER} pm2 id ${app} /dev/null 21 return $? } HOST_APPS(go-cqhttp sillyGirl xdd) QL_APPS() log N1 夜维 $(date %Y-%m-%d_%H:%M) # 宿主机服务巡检 host_ok0 host_total${#HOST_APPS[]} for app in ${HOST_APPS[]};do if host_is_online ${app};then log ✅ 腿[${app}] online host_ok$((host_ok1)) else log ⚠️ 腿[${app}] 异常执行重启 log 重启宿主机 ${app} pm2 restart ${app} ${LOG_FILE} 21 fi done log ${host_ok}/${host_total} # 容器服务巡检QL_APPS为空数组时不会循环执行 ql_ok0 ql_total${#QL_APPS[]} for app in ${QL_APPS[]};do if ql_is_online ${app};then log ✅ ql[${app}] online ql_ok$((ql_ok1)) else log ⚠️ ql[${app}] 异常执行重启 log 重启容器 ${app} docker exec --timeout ${docker_exec_timeout} ${QL_CONTAINER} pm2 restart ${app} ${LOG_FILE} 21 fi done log ${ql_ok}/${ql_total} disk_used$(df -P / | awk NR2{print $5} | tr -d %) log ✅ 磁盘 ${disk_used}% log 巡检结束 exit 0 INNER_EOFchmodx /root/n1.sh# 3.生成systemd开机恢复服务宿主机pm2 resurrect 容器pm2 resurrectecho[3/7] 生成 systemd 开机延时恢复服务cat/etc/systemd/system/n1-ql-restore.serviceUNIT_EOF [Unit] DescriptionN1开机延时恢复宿主机青龙容器pm2 Afterdocker.service Requiresdocker.service [Service] Typeoneshot ExecStart/bin/sh -c sleep 40; pm2 resurrect /dev/null 21; pm2 save /dev/null 21; docker exec qinglong pm2 resurrect /dev/null 21; docker exec qinglong pm2 save /dev/null 21; [Install] WantedBymulti-user.target UNIT_EOFsystemctl daemon-reload systemctlenablen1-ql-restore.service# 4.配置crontab覆盖式写入干净定时避免重复任务echo[4/7] 设置crontab定时任务覆盖旧定时cat/root/tmp_cron.txtCRON_EOF 05 3 * * * /root/ql-scrub.sh /root/ql/logs/night.log 21 15 3 * * * /root/n1.sh */5 * * * * /bin/bash /root/n1.sh CRON_EOFcrontab/root/tmp_cron.txtrm-f/root/tmp_cron.txt# 5.生成日志清理脚本 ql‑scrub.shecho[5/7] 生成日志裁剪脚本 ql‑scrub.shcat/root/ql-scrub.shSCRUB_EOF #!/bin/bash LOGDIR/root/ql/logs MAX_SIZE$((1024*1024*20)) #20MB mkdir -p ${LOGDIR} for logfile in ${LOGDIR}/*.log;do if [ -f ${logfile} ];then filesize$(stat -c%s ${logfile}) if [ ${filesize} -gt ${MAX_SIZE} ];then mv -f ${logfile} ${logfile}.old touch ${logfile} fi fi done SCRUB_EOFchmodx /root/ql-scrub.sh# 6.清理旧锁、创建目录echo[6/7] 清理旧锁文件创建目录rm-f/root/.n1-deploy/n1.lockmkdir-p/root/ql/logsmkdir-p/root/.pm2/logs# 7.首次执行测试echo[7/7] 执行首次巡检测试bash/root/n1.shechoecho 部署完成 echo✅ 完全无jq依赖pm2 id返回码检测进程echo✅ QL_APPS() 默认关闭容器内pm2巡检无需手动修改echo✅ systemd开机40秒后恢复宿主机青龙容器pm2echo✅ crontab5分钟巡检凌晨3:05日志清理3:15额外巡检echo✅ 日志策略单文件20MB轮转碎片日志保留3天echo✅ 巡检对象宿主机go‑cqhttp/sillyGirl/xddecho✅ flock防并发、docker exec超时8秒保护echoecho手动巡检命令bash /root/n1.shecho查看自愈日志tail -f /root/ql/logs/selfheal.logecho查看开机恢复服务状态systemctl status n1-ql-restore.serviceecho查看定时任务crontab -lechoecho 青龙容器内部建议执行pm2 resurrect pm2 saveecho 整机重启测试reboot2脚本跑完校验crontab-lsystemctl status n1-ql-restore.servicebash/root/n1.sh只会输出宿主机三条 ✅ xxx online容器部分不会输出任何内容使用场景N1 彻底重装 Armbian 从零部署守护环境就运行这个脚本二备份守护环境1确认全部 5 个文件都存在ls-l\/root/n1.sh\/root/ql-scrub.sh\/etc/systemd/system/n1-ql-restore.service\/root/cron_backup.txt\/root/pm2_ecosystem_backup.json上面命令不报错列出全部文件就可以打包crontab-l/root/cron_backup.txt pm2 ecosystem/root/pm2_ecosystem_backup.jsontar-zcvf/root/n1_backup.tar.gz\/root/n1.sh\/root/ql-scrub.sh\/etc/systemd/system/n1-ql-restore.service\/root/cron_backup.txt\/root/pm2_ecosystem_backup.json2查看包里文件清单确认tar-ztvf/root/n1_backup.tar.gz3能看到 5 个文件就全部正常root/n1.sh root/ql-scrub.sh etc/systemd/system/n1-ql-restore.service root/cron_backup.txt root/pm2_ecosystem_backup.json4看备份包大小ls-lh/root/n1_backup.tar.gz现在这个备份包是基于无‑jq 一键脚本生成出来的环境打包出来的n1.shQL_APPS()默认关闭容器巡检、pm2 id 判断、无 jqql‑scrub.sh日志轮转脚本systemd 服务开机 40s 同时恢复宿主机 pm2 青龙容器 pm2cron_backup.txt保存当前干净的 3 条定时pm2_ecosystem_backup.jsonpm2 进程清单备份三N1 重装 Armbian 之后完整恢复整套守护环境这个备份只保存自愈守护相关巡检脚本、日志清理、systemd 开机服务、定时任务、pm2 进程清单✅ n1.sh 自愈脚本使用pm2 id判断进程彻底避开 pm2 jq 解析 buggo‑cqhttp/sillyGirl/xdd 识别 online不会误重启✅ ql‑scrub.sh 日志裁剪脚本重建完成防止日志打爆磁盘✅ systemd n1‑ql‑restore.service开机延时恢复 pm2✅ crontab 定时已精简无重复任务无宝塔旧垃圾定时✅ flock 锁防止脚本并发重复运行✅ 备份包包含全部守护逻辑脚本、systemd 服务、定时导出、pm2 进程清单1N1 重装 Armbian 点我2安装好宝塔、docker、pm2、青龙容器、go‑cqhttp、sillyGirl、xdd3把备份的n1_backup.tar.gz上传到宝塔 root 目录下然后依次执行tar-zxvf/root/n1_backup.tar.gz-C/chmodx /root/n1.shchmodx /root/ql-scrub.shcrontab/root/cron_backup.txt systemctl daemon-reload systemctlenablen1-ql-restore.service pm2 start /root/pm2_ecosystem_backup.json pm2 savemkdir-p/root/ql/logsbash/root/n1.sh执行完最后一条 bash /root/n1.sh输出三条 ✅ xxx online代表整套自愈恢复成功4恢复完成后校验#1. 确认定时任务加载正确crontab-l#2. 确认开机自启服务已经启用systemctl status n1-ql-restore.service#3. 再手动跑一次自愈脚本确认状态bash/root/n1.sh✅预期输出N1 夜维 xxxx✅ 腿[go-cqhttp]online ✅ 腿[sillyGirl]online ✅ 腿[xdd]online3/30/0 ✅ 磁盘 xx%巡检结束#4. 看实时日志是否正常写入tail-n20/root/ql/logs/selfheal.log5可选整机重启测试最重要的验证reboot盒子重启SSH 重新登录进来#重启登录后检查pm2 listbash/root/n1.sh重启后 pm2 三个进程能自动拉起来、自愈脚本识别全部 online代表完整恢复成功后台自动工作①每 5 分钟自动运行 /root/n1.sh进程挂掉自动重启②凌晨 3:05 运行日志清理脚本防止日志占磁盘③开机 systemd 服务自动恢复 pm2 进程6日常排查命令crontab-l# 核对定时任务systemctl status n1-ql-restore.service# 核对开机恢复服务已启用bash/root/n1.sh# 手动跑一遍自愈脚本确认全部onlinetail-n20/root/ql/logs/selfheal.log# 查看最近20行日志确认日志写入正常