[工做随笔] 配置 cron 检查 K8s Pod 存活状态以推送钉钉消息

背景

前段时间接手了一份维护老系统的任务。该系统使用了早期的 Spring Cloud 全家桶,其中有一个微服务随着时间运行会出现大量 CLOSE_WAIT 状态的 socket 链接以致于堵塞网关,检查后发现与 HttpClient 相关(可参考 解决:HttpClient致使应用出现过多Close_Wait的问题 这篇博文),可是因为没有完整的源码,没法经过博文里提到的方法解决。所以考虑经过外部手段检测并重启服务来恢复网关与服务的通信,简单的检测手段是经过发起 HTTP 请求看超时状况:html

$ curl --connect-timeout 10 -m 10 <host>:<port>

配置 Kubernetes 存活探测

咱们使用了 Kubernetes 做为部署环境,它使用存活探测器来知道何时要重启容器。存活探测器有三种类型:shell

  • 存活命令;
  • HTTP 存活探测,发起 HTTP GET 请求以探测容器是否存活;
  • TCP 存活探测,发起 socket 链接以探测容器是否存活。

因为咱们须要检测的服务的问题是容器内存在大量的 CLOSE_WAIT 状态链接,此时新的 socket 链接已经没法连通,使用 HTTP 存活探测时其超时检查没法做用于 socket 超时,所以应该使用 TCP 存活探测。json

参照 Kubernetes 官方文档提供的示例便可配置相关探测器:ubuntu

apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: k8s.gcr.io/goproxy:0.1
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20
注意

更多信息参见 定义 TCP 的存活探测api

配置 cron 执行脚本以检查 K8s Pod 存活状态

通过一段的运行后,Kubernetes 自带的存活探测工做良好,可是客户但愿能获悉服务实例重启的信息,即每当服务实例重启时发送消息至群聊。app

一开始考虑使用 BOTKUBE 收集服务实例重启信息,可是有几个小问题:curl

  • BOTKUBE 原生仅支持 Slack、Mattermost、Microsoft Teams、Elastic Search、Webhook 五种方式;
  • BOTKUBE 是被动收集指定负载的相关事件。

可是客户但愿能在钉钉群里中获悉 “N 个实例中重启了 M 个” 信息以进行评估稳定性。socket

若是经过 Webhook 接入 BOTKUBE,则须要在一个短暂的周期内维护服务的总实例数与周期内重启实例数量。相关编码工做量太大,因而咱们经过编写简单的脚本并配置 cron 定时任务来完成该需求。tcp

脚本思路

假定咱们要检查的服务名称为 service
export KUBECONFIG=/path/to/your/kubernetes.yaml
pods=""
total=`/usr/local/bin/kubectl --kubeconfig=$KUBECONFIG get pods -o wide | grep service | sed -n '$='` # 1
for pod in `/usr/local/bin/kubectl --kubeconfig=$KUBECONFIG get pods -o wide | grep service | awk '{print $1 "_" $6}'`    # 2
do
        name=`echo $pod | awk -F_ '{print $1}'`
        ip=`echo $pod | awk -F_ '{print $2}'`
        sname=`echo $name | awk -F- '{print $5}'`
        curl -s --connect-timeout 10 -m 10 $ip:8672 > /dev/null # 3
        if [ $? -ne 0 ]; then # 4
                /usr/local/bin/kubectl delete pod $name
                pods="$pods$sname ×, "
        else
                pods="$pods$sname √, "
        fi
done
pods="${pods%??}" # 5
success=`echo $pods | awk -F"√" '{print NF-1}'` # 6
if [ $success -ne $total ]; then
        # 7
        curl 'https://oapi.dingtalk.com/robot/send?access_token=***' \
                        -H 'Content-Type: application/json' \
                        -d '{ "msgtype": "text", "text": { "content": "检查结果 ['"$success"'/'"$total"'] :\n'"${pods}"'" } }'
fi
  1. 统计当前正在运行的容器实例数量
  2. 咱们须要 pod 的名称以在必要的时候经过 kubectl delete 删除它,还须要 pod 的虚拟 ip 地址以经过 curl 测试链接状况;
  3. 咱们不须要 curl 的链接状态信息和链接成功后的资源下载进度信息,所以经过 -s 参数和重定向到空设备来 静音
  4. curl 因 socket 链接超时返回非 0 值时删除该容器;
  5. 截断多余的 ,<空格>
  6. 统计尚在正常运行的容器数量;
  7. 仅当删除了一个或以上的容器时,发送构造好的报告信息至钉钉机器人。

效果:ide

检查结果 [11/12] :
wdqdd ×, 5xwpz √, rgmc7 √, 8cf4f √, spttn √, dvw2l √, tg9lw √, kzrc2 √, fpk9s √, 9plpt √, dpkpf √, gnhrl √

配置 cron 定时任务

咱们使用的是 Ubuntu Server 18,经过 crontab -e 配置定时任务:

*/10 8-22 * * * /path/to/your/script.sh

使用 cron 执行脚本须要注意几个问题:

  • cron 与脚本的权限问题;
  • 设置脚本的可执行权限(chmod +x script.sh);
  • cron 执行脚本时传递的是最小集环境变量,所以须要指定二进制执行文件的路径,推荐在脚本开头使用 export 指定路径,但咱们内部使用且从简处理,选择直接指定绝对路径 /usr/local/bin/kubectl
  • 系统的时区设置,若是修改了时区,须要重启 cron 服务。

更多关于配置 cron 的注意事项能够参考 Why crontab scripts are not working?

相关文章
相关标签/搜索