ccw 的 Shell CookBook 系列第一篇,本系列主要是记录 ccw 的一些 shell 使用 case,并非各种命令的教学。
本篇为大家带来一条 Watchdog,主要功能是定时监控系统运行是否正常,其本体是一段 Bash Script。另外 Watchdog 这个词是一念之间造出来的(也可能是潜意识的记忆,但不知道出处了),因为它有问题就会叫(报警),以看门狗来形容还算贴切。
前言
时代在前进,工具在进步,这年头能熟练使用 Shell 的人,我感觉大概不多了。我也不熟,所以需要用文字记录来确保将来想起来的时候能找到。
就我来说,有一些场景我是不会用 Shell 的,尤其是处理字符串,比如处理 JSON 我会直接上 JavaScript(这很合理,它能直接读 JSON),统计去重后条数我会用原生支持 set 的语言临时整个然后取长度(这很经典,但我确实不能保证能打对sort
和uniq
的各种参数组合,也因此被人吐槽过),还有根据规则做替换(sed 和 awk 啥的很有名,但是确实不是首选。关于 sed,主体的语法在 vim 里大概会用,然而在外面带上参数就不熟了;如果字符串或文件不算大,直接贴到 VSCode 里操作更快)
以我目前的认识,Shell 的精髓在于组合各种程序,是比 Python 更胶水的胶水;此外,操作文件也非常地方便快捷。如果这两个痛点都中(即你需要用各种程序来处理文件?),使用 Shell 来解决问题是当仁不让的。
背景
某内网环境,不希望投入部署成本搞一套监控系统,需要搞一个土制的。同时监控的要求也不算高,能捕捉到小时级的状态异常就可以接受。
有限开放对公网的访问,得通过一个 http 代理。可以通过跳板机访问该环境。
被监控系统部署在 Kubernetes 上。跳板机可以操作其集群,当然是通过kubectl
。
系统确定不再更新,因此也不存在内嵌逻辑来做报警的可能。
有 hdfs,可以在系统内使用命令来读写,但在跳板机上不行。
经过在系统上进行一定的配置后,可以每小时在 check 某个条件通过后,向 hdfs 中的某个文件追加内容,具体是上个小时的时间戳和实际执行时间的时间戳。可以认为如果该小时这个 hdfs 内的文件被追加了正确的内容,那么系统端到端都是正常的。
思路
铺垫已经铺好了,其实就是需要在外部定时检测那个被写的 hdfs 文件。定时执行我们选择信任crontab
,它可以帮我们定时拉起一个程序或脚本。于是问题就变成了这个脚本怎么写。
我们选择在每个小时的55分拉起脚本来做检测,如果检测不通过就往外部的通讯工具发消息。这里为什么是55分,因为系统追加 hdfs 文件内容的行为并非是每个小时准点做的,会等待某个条件达成后才开始。我们可以接受这个条件的达成时间比小时准点晚一会,当然不会晚太多;到了这个小时快要结束的时候,一定是达成了的。
往外部的通讯工具发消息,是通过一个在公网的 api 接口,因此这里会用curl
并通过代理去请求。这个是在脚本开发前先要验证通过的。
而具体的检测逻辑,一方面是算出当前所处小时上一小时的时间戳。date
有相关参数可以完成。另一方面是需要去获取 hdfs 文件内容的末尾,拿到系统打印的那个时间戳。在该环境下,需要kubectl
和hdfs
命令结合来做。之后两个比较,如果不一致,就该报警了。比如这个小时系统没有把前一小时的时间戳正常写 hdfs,最后一条就会是前两个小时的时间戳,这就产生了不一致。
实施
首先先把最基本的单元搞起,比如先把 crontab 配好。为了方便观察,这边还打了日志。
1 | $ crontab -e |
然后是通过代理请求公网的curl
命令。这个代理使用了用户名和密码来做认证。具体参数请参考 cURL man page。不过主要部分都是从 Postman 拷的就是了,只加了下 proxy 相关的参数。
1 | curl -x http://proxy:1234 --proxy-user usr:pwd --location --request POST 'https://api/' --header 'Content-Type: application/json' --data-raw '{"text": "汪汪汪"}' |
使用date
算出当前所在小时的时间戳。有点绕 but it works,大概的意思是先拼出上一个0点,之后再换成时间戳。
1 | date -d "$(date "+%Y-%m-%d %H:00:00" -d last-hour)" +%s |
使用kubectl
通过系统的 pod 去访问hdfs
,这里需要分三步:
- 从 K8s 集群里捞出目标系统的 pod
- 先列出所有的 pod,但是需要筛掉额外的信息
kubectl get pod | grep target-system | head -n 1 | awk '{print $1}'
- 在该容器上执行
hdfs
命令,并把结果拉回- 直接在跳板机上输出,而不是在 pod 里外拷来拷去
- 需要用
--
使kubectl
意识到后面的参数都要带到 pod 里面去 kubectl exec pod-xxxxx -- hdfs dfs -tail /user/me/checktime
- 拉回来的结果处理一下
- 拿最后一行的第一列
kubectl blabla | tail -n 1 | awk '{print $1}'
1 | kubectl exec `kubectl get pod | grep target-system | head -n 1 | awk '{print $1}'` -- hdfs dfs -tail /user/me/checktime | tail -n 1 | awk '{print $1}' |
最终
关键代码都在上面了,最后当然是要把逻辑和过程套上了。
话说每次写 Bash 都要查一下 if 咋写,这个也得反省一下 TvT。
冷知识(?): [ xxx ]
是test xxx
命令的语法糖(这样就能理解为什么中括号前后得有空格了)。
1 |
|
这样就完成了一个土制的监控系统 watchdog。