设置CPU阈值触发Docker容器故障并自动重建的方法
实现Docker Swarm服务在CPU占用过高时自动重启的方案
针对你遇到的Java进程在Swarm服务中CPU持续满载、需要在95%占用时自动故障重建的需求,这里提供几个可行的实现方案,从简单到复杂按需选择:
方案一:利用Docker健康检查(HEALTHCHECK)触发自动重启
这是最轻量化的方案,无需额外监控工具,通过容器内的健康检查脚本判断CPU使用率,一旦超过阈值就让健康检查失败,Swarm会自动重启容器。
步骤1:编写CPU监控的健康检查脚本
在你的Java应用镜像中添加一个shell脚本(比如cpu-healthcheck.sh),内容如下:
#!/bin/bash # 获取容器内Java进程的CPU使用率(过滤掉grep自身进程) JAVA_CPU=$(ps aux | grep java | grep -v grep | awk '{print $3}') # 转换为整数,避免小数比较问题 CPU_THRESHOLD=95 CPU_USAGE=$(printf "%.0f" "$JAVA_CPU") if [ "$CPU_USAGE" -ge "$CPU_THRESHOLD" ]; then echo "[FAILURE] CPU usage is ${CPU_USAGE}%, exceeding threshold ${CPU_THRESHOLD}%" exit 1 # 健康检查失败 else echo "[SUCCESS] CPU usage is ${CPU_USAGE}%, within threshold" exit 0 # 健康检查正常 fi
步骤2:修改Dockerfile集成健康检查
确保镜像中包含ps、grep、awk这些工具(基础镜像如果是Alpine,需要安装procps;Ubuntu/Debian则默认有),然后添加健康检查指令:
# 以Alpine为例,安装依赖工具 RUN apk add --no-cache procps bash # 添加健康检查脚本 COPY cpu-healthcheck.sh /usr/local/bin/ RUN chmod +x /usr/local/bin/cpu-healthcheck.sh # 配置健康检查:每10秒检查一次,超时5秒,启动后30秒开始检查,失败2次标记为不健康 HEALTHCHECK --interval=10s --timeout=5s --start-period=30s --retries=2 \ CMD /usr/local/bin/cpu-healthcheck.sh
步骤3:部署Swarm服务时配置重启策略
创建服务时指定重启条件为on-failure,这样当健康检查失败时Swarm会自动重启容器:
docker service create \ --name your-java-service \ --restart-condition on-failure \ --restart-max-attempts 5 \ # 可选:限制最大重启次数,避免无限循环 --limit-cpu 1 \ your-java-image:latest
方案二:用外部监控栈(Prometheus + Alertmanager)触发重启
如果你的集群已经有监控体系,这个方案更灵活,适合多服务统一管理:
- 采集Swarm容器指标:配置Prometheus采集Docker Daemon的metrics(通过部署
prom/node-exporter或直接启用Docker内置的metrics端点)。 - 配置告警规则:在Prometheus中添加规则,当基于
container_cpu_usage_seconds_total计算出的CPU使用率持续5分钟超过95%时触发告警。 - 配置Alertmanager Webhook:编写一个简单的web服务,接收Alertmanager的告警请求后,调用Docker API执行
docker service update --force your-java-service(或直接删除异常容器),触发Swarm重建服务。
方案三:自定义Swarm监控服务
编写一个独立的监控脚本,部署成Swarm服务,定期检查目标服务的容器CPU使用率,超过阈值就主动删除容器让Swarm重建:
示例监控脚本(shell版)
#!/bin/bash TARGET_SERVICE="your-java-service" CPU_THRESHOLD=95 CHECK_INTERVAL=10 while true; do # 获取目标服务下所有运行中的任务ID docker service ps -q --filter desired-state=running "$TARGET_SERVICE" | while read TASK_ID; do # 获取任务对应的容器ID CONTAINER_ID=$(docker inspect -f '{{.Status.ContainerStatus.ContainerID}}' "$TASK_ID") if [ -n "$CONTAINER_ID" ]; then # 获取容器CPU使用率(去掉%符号并转为整数) CPU_USAGE=$(docker stats --no-stream --format "{{.CPUPerc}}" "$CONTAINER_ID" | sed 's/%//') CPU_USAGE_INT=$(printf "%.0f" "$CPU_USAGE") if [ "$CPU_USAGE_INT" -ge "$CPU_THRESHOLD" ]; then echo "$(date): Container $CONTAINER_ID CPU usage ${CPU_USAGE_INT}%, restarting..." docker container rm -f "$CONTAINER_ID" fi fi done sleep "$CHECK_INTERVAL" done
部署监控服务
将脚本打包成镜像,挂载Docker套接字以获取权限:
docker service create \ --name cpu-monitor \ --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \ your-monitor-image:latest
额外建议
虽然自动重启能临时解决问题,但Java进程持续高CPU通常是代码死循环、内存泄漏、线程阻塞等根源问题导致的。建议结合jstack、jmap等工具分析线程栈和内存快照,从代码层面解决问题,避免频繁重启影响业务稳定性。
内容的提问来源于stack exchange,提问作者Rajesh Rajendran
相关产品推荐
相关产品推荐

