要定位具体是哪个脚本导致卡死,可以按照以下步骤进行排查:
一、快速定位卡死的脚本
1. 查看长时间运行的进程
# 在容器内执行,按CPU时间排序
ps -eo pid,user,etime,args | sort -k3 -r | head -n 10
- 关键点:关注
ETIME
(进程运行时间)较长且状态为R
(运行中)或D
(不可中断睡眠)的脚本。
2. 检查僵尸进程
ps aux | grep Z # Z状态表示僵尸进程
- 僵尸进程:子进程结束但父进程未回收其状态,可能导致资源泄漏。
3. 使用strace
跟踪系统调用
# 找到疑似卡死的进程ID(PID)
strace -p <PID> # 实时跟踪进程的系统调用
- 输出分析:
- 若长时间停留在
read()
、write()
或select()
等调用,可能是IO阻塞。 - 若显示
futex()
相关调用,可能是线程死锁。
- 若长时间停留在
二、日志与监控定位
1. 添加脚本执行日志
在所有关键脚本的开头和结尾添加日志记录:
#!/bin/sh
echo "[$(date)] START: $0" >> /var/log/script_tracker.log
# 原有脚本逻辑
...
echo "[$(date)] END: $0" >> /var/log/script_tracker.log
2. 监控脚本执行时间
# 创建监控脚本
cat > /opt/monitor_scripts.sh << EOF
#!/bin/sh
while true; do
ps -eo pid,etime,args | grep -E '/bin/sh|bash' | grep -v grep | awk '{print \$0}' >> /var/log/long_running_scripts.log
sleep 300 # 每5分钟记录一次
done
EOF
# 后台运行
nohup /opt/monitor_scripts.sh &
三、容器层面排查
1. 使用docker exec
交互式排查
docker exec -it <容器ID> /bin/sh # 进入容器
- 在容器内执行
top
、htop
或ps
命令,查看资源占用情况。
2. 容器健康检查
修改Dockerfile
添加健康检查,定期执行关键脚本:
HEALTHCHECK --interval=30s --timeout=10s \
CMD /path/to/your/script.sh || exit 1
3. 资源限制触发监控
# 查看容器OOM事件
dmesg | grep -i -e oom -e docker -e <容器ID>
四、自动化定位工具
1. 使用timeout
命令包装脚本
# 修改启动脚本,为每个关键脚本添加超时
timeout 300s /path/to/script.sh & # 超时5分钟自动终止
2. 脚本执行追踪
# 使用auditd记录所有脚本执行
apt-get install auditd -y
auditctl -w /bin/sh -p x -k script_execution
# 查看日志
ausearch -k script_execution | grep -i "success=yes"
五、卡死现场保存
当发现脚本卡死时,立即执行以下操作:
# 1. 保存进程堆栈信息
gdb -p <PID> -ex "thread apply all bt" -ex "quit" > /tmp/script_stacktrace.txt
# 2. 保存容器状态
docker inspect <容器ID> > /tmp/container_state.json
# 3. 导出系统资源快照
tar czvf /tmp/system_snapshot_$(date +%s).tar.gz /proc /sys /var/log
六、示例场景分析
场景1:脚本因死循环卡死
# 定位到CPU占用高的脚本
top -H -p $(docker inspect -f '{{.State.Pid}}' <容器ID>)
# 使用pstack查看线程堆栈(需要安装gdb)
pstack <PID>
场景2:脚本因外部依赖卡死
# 使用netstat查看网络连接
netstat -tulpn | grep <PID>
# 使用lsof查看文件打开情况
lsof -p <PID>
关键排查指标
指标 | 工具/命令 | 异常表现 |
---|---|---|
CPU使用率 | top , docker stats | 长时间100%占用 |
内存使用率 | free , ps aux | 持续增长或达到限制 |
文件描述符 | lsof -p <PID> | 接近或达到ulimit -n 限制 |
网络连接 | netstat , ss | 大量CLOSE_WAIT或ESTABLISHED |
磁盘IO | iostat , iotop | 读写请求堆积 |
通过以上方法,通常可以定位到具体卡死的脚本及其原因。如果问题仍然难以复现,可以考虑在开发环境中模拟生产环境条件,逐步缩小问题范围。