Docker容器中,Shell脚本运行一段时间后卡死是一个复杂的问题,可能由多种因素导致。以下是详细的排查思路和解决方法:
一、可能的原因分析
-
资源耗尽
- 容器内存或CPU配额不足,触发OOM(Out of Memory) killer。
- 文件描述符达到上限,导致脚本无法创建新连接或打开文件。
-
系统配置问题
- 麒麟系统对ARM64架构的Docker支持不完善,存在内核兼容性问题。
- SELinux/AppArmor等安全模块过度限制容器权限。
-
脚本自身问题
- 脚本中存在死循环、资源泄漏(如未关闭文件句柄)或阻塞调用。
- 脚本依赖的外部命令或服务响应超时。
-
Docker daemon问题
- Docker版本与麒麟系统不兼容,导致容器运行时异常。
- 容器存储驱动(如overlay2)出现文件系统损坏。
二、排查步骤
1. 监控资源使用情况
# 在宿主机上监控容器资源
docker stats <容器ID> # 实时查看CPU、内存、网络IO
top -H -p $(docker inspect -f '{{.State.Pid}}' <容器ID>) # 查看容器内进程
# 在容器内监控
df -h # 检查磁盘空间
ulimit -n # 查看文件描述符限制
ps auxf # 查看进程树,是否存在僵尸进程
2. 检查系统日志
# 宿主机日志
journalctl -u docker.service # Docker daemon日志
dmesg | grep -i docker # 内核中与Docker相关的错误
# 容器内日志
tail -f /var/log/syslog # 系统日志
3. 验证脚本逻辑
# 添加调试信息到脚本
set -x # 开启详细输出
set -e # 遇到错误立即退出
# 使用timeout命令测试脚本
timeout 300s /bin/sh your_script.sh # 限制脚本运行时间,观察是否超时
4. 检查Docker配置
# 查看容器配置
docker inspect <容器ID> | grep -i -e memory -e cpu -e ulimits
# 检查Docker版本兼容性
docker version
cat /etc/os-release # 确认麒麟系统版本
三、解决方法
1. 调整容器资源限制
# 重启容器并增加资源配额
docker run -it --memory=2g --cpus=2 --ulimit nofile=10240:10240 your_image /bin/sh
2. 优化脚本
# 添加超时控制
timeout 60s command_that_might_hang # 为可能阻塞的命令设置超时
# 使用后台任务+等待机制
command_in_background &
wait $! # 等待后台任务完成,并获取退出状态
3. 更新Docker和内核
# 更新麒麟系统软件包
sudo apt update && sudo apt upgrade -y
# 更新Docker到最新稳定版
curl -fsSL https://get.docker.com | sh
4. 调整安全模块配置
# 临时禁用SELinux(测试用,生产环境需谨慎)
sudo setenforce 0
# 修改容器安全选项
docker run --security-opt seccomp:unconfined your_image
5. 更换存储驱动
# 修改Docker daemon配置
sudo vi /etc/docker/daemon.json
{
"storage-driver": "aufs" # 尝试使用aufs替代overlay2
}
sudo systemctl restart docker
四、预防措施
-
脚本健壮性
- 使用
set -euo pipefail
避免未定义变量和管道错误。 - 添加资源清理逻辑(如
tempfile
自动删除)。
- 使用
-
容器健康检查
# 在Dockerfile中添加健康检查 HEALTHCHECK --interval=30s --timeout=5s CMD /bin/sh -c 'exit 0'
-
监控与告警
- 集成Prometheus+Grafana监控容器状态。
- 设置脚本运行超时自动重启机制。
如果问题仍然存在,建议提供以下信息以便进一步分析:
- 脚本的关键部分代码。
- Dockerfile和容器启动命令。
- 系统日志中的错误信息。
- 资源监控数据(CPU、内存、磁盘IO)。