面对这种情况,我的反应是只有强制重启服务器,连接上服务器之后再进行问题的排查。重启连接上服务器之后排查了很久都找不到问题在哪里,然后我决定给这台机器做一个监控,就用最简单的python脚本来获取服务器信息。因为我根据云服务后天的监控数据分析得出,服务器卡死的共通前兆就是内存使用率过高。
我们的服务器上是有celery的,可我们的服务都是在容器之中的,考虑到docker中获取宿主机使用参数不太靠谱我就在宿主机中使用crontab来起定时任务。先看一下我的python脚本代码把,我从运维朋友哪里得到查看CPU使用率top10、内存使用率top10的两条命令,用作获取机器使用率的工具。
import subprocess
from WeChat import WeChat
class MonitorServer:
""" 监控服务器 """
def get_memory_used_rate(self):
"""
获取内存使用率
`shell
free
total used free shared buff/cache available
Mem: 4030596 1540296 122456 15912 2367844 2196720
Swap: 0 0 0
`
"""
memory_total_cmd = """free | grep "Mem:" |awk '{print $2}'"""
memory_available_cmd = """free | grep "Mem:" |awk '{print $7}'"""
# 获取内存总数
total_sub = subprocess.Popen(memory_total_cmd, shell=True, stdout=subprocess.PIPE)
total_sub.wait()
total = self.clear_shell_value(total_sub.stdout.read().decode("utf-8"))
total = (float(total) if total.isdigit() else 0) / 1024 / 1024
# 获取内存使用量
available_sub = subprocess.Popen(memory_available_cmd, shell=True, stdout=subprocess.PIPE)
available_sub.wait()
available = self.clear_shell_value(available_sub.stdout.read().decode("utf-8"))
available = (float(available) if available.isdigit() else 0) / 1024 / 1024
used = (total - available)
print("result:", total, used, used / total)
return used / total
def get_memory_used_top_10(self):
"""
获取内存使用前十信息
`shell
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2827 0.7 8.9 1663452 359960 ? Sl 10:08 2:42 python3 manage.py celery worker -A PppsesDgBack.celery -l info
...
`
"""
memory_total_cmd = """ps auxw|head -1;ps auxw|sort -rn -k4|head -10"""
# 获取内存总数
total_sub = subprocess.Popen(memory_total_cmd, shell=True, stdout=subprocess.PIPE)
total_sub.wait()
result = total_sub.stdout.read().decode("utf-8")
return result
def get_cpu_used_top_10(self):
"""
获取cpu使用前十信息
`shell
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2827 0.7 8.9 1663452 359960 ? Sl 10:08 2:42 python3 manage.py celery worker -A PppsesDgBack.celery -l info
...
`
"""
memory_total_cmd = """ps auxw|head -1;ps auxw|sort -rn -k3|head -10"""
# 获取内存总数
total_sub = subprocess.Popen(memory_total_cmd, shell=True, stdout=subprocess.PIPE)
total_sub.wait()
result = total_sub.stdout.read().decode("utf-8")
return result
def get_inner_ip(self):
"""
获取本机ip
`shell
172.18.0.1
172.17.16.11
`
"""
inner_ip_cmd = """
/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d 'addr:'
"""
# 获取内存总数
inner_ip_sub = subprocess.Popen(inner_ip_cmd, shell=True, stdout=subprocess.PIPE)
inner_ip_sub.wait()
result = inner_ip_sub.stdout.read().decode("utf-8")
return result
@staticmethod
def clear_shell_value(target):
return target.replace("\r\n", "").replace("\r", "").replace("\n", "")
def memory_alarm(self, threshold_value=0.9):
"""
内存告警阈
@threshold_value :内存告警阈值
"""
memory_used = self.get_memory_used_rate()
if memory_used >= threshold_value:
inner_ip = self.get_inner_ip()
cpu_info = self.get_cpu_used_top_10()
memory_info = self.get_memory_used_top_10()
content = f"""内存使用率:{"{:.2f}".format(memory_used * 100)}%\n内网ip:\n{inner_ip}CPU使用信息:\n{cpu_info}"""
self.alarm_notice(content=content)
content = f"""内存使用率:{"{:.2f}".format(memory_used * 100)}%\n内网ip:\n{inner_ip}Memory使用信息:\n{memory_info}"""
self.alarm_notice(content=content)
return
def alarm_notice(self, subject="服务告警:", content="忘记内容!"):
""" 告警通知 """
user = 'HuangJiaHui'
# user = 'sorry|HuangJiaHui|MaZhenKai'
wx = WeChat()
wx.send_data(user, subject, content)
return
if __name__ == '__main__':
m_s = MonitorServer()
m_s.memory_alarm(threshold_value=0.9)
上面的脚本的逻辑很清晰了,就是做到检查内存使用率,如果使用率达到某个阈值我就像企业微信发送告警信息。这里WeChat
类可以参考我之前写过的一篇文章:用Python脚本给企业微信发送告警消息,这也是得益于之前在云中心工作的时候帮运维调试脚本的时候接触到了。
怎么获取数据服务器信息的问题得到了解决,那么接下来就考虑怎么用crontab定时来执行这个脚本。我们现在服务器上检查一下crontab服务的状态:systemctl status cron
。确保服务启动之后我们就要开始编写crontab的表达式了,例如我想让我的定时任务每5分钟执行一次我可以这样写:*/5 * * * * /usr/bin/python3 /home/monitor.py
。这里指的注意的是python一定要用绝对路径哦!下面我们就把这条表达式写入到corntab配置文件中,我们敲入命令:crontab -e
。在最下面写入刚刚的表达式,像修改文件一样保存并退出。官方讲编写之后是不用重启进程的,但出于稳健的考虑还是重启一下:systemctl restart cron
。之后我们就可以使用命令查看一些设置后了表达式:crontab -l
,到此监控的脚本就已经部署好了,我们只需要等着服务器再次超载状态发来通知消息就知道是哪个程序在作妖了。
终于,内存负载超过90,并且有逐渐升高的趋势。这个时候我们查看发来的信息,首当其冲的居然是celery worker
。其实我们目前并没有异步或者定时任务,但是我在settings.py之中设置了一个定时任务的空字典,如下。在搜索一番之后,我看有的网友说的是celery一直在fork新的进程而旧的进程又没有销毁掉,我们可以设置CELERYD_MAX_TASKS_PER_CHILD
来限制每个woker最大执行任务的数量。但我最后直接将celery在supervisor中的托管任务给停掉了,这样解决了问题的根源,如果后期我们需要使用celery的时候记得做好限制和监控就可以了。
CELERYBEAT_SCHEDULE = {
# "add": {
# "task": "PppsesDgBack.celery.add", # 定时任务程序
# "schedule": crontab(minute='*/1'), # 每分钟执行一次
# "args": (1, 2), # 定时任务所需参数
# },
}
CELERYD_MAX_TASKS_PER_CHILD = 3 # 每个worker最多执行3个任务就摧毁,避免内存泄漏