Loading

「Bug」Jenkins Slave 卡顿与僵尸进程

问题描述

出问题的是我们的主 Jenkins Slave,是在 Ubuntu 虚拟机里面,使用 Docker 跑了四个不同环境的 Jenkins Slave,提供 c#/golang/flutter/python 等的构建/测试环境。

而且这台服务器是不关机的,24h 提供服务。

一段时间后,这台 Jenkins Slave 虚拟机的内存就居高不下。这大概是某些构建任务会维护守护进程以加快下一次构建的速度,所以内存没释放。

大概过了一个月左右,问题就出现了:明明虚拟机提示还有 2G 的空闲内存(不包含 cached),可 ssh 登录却要花将近两三分钟,登入后 top/htop 也要黑屏将近一分钟。。
神奇的是其他的命令(ls/cd/df/du/docker)却都很正常,没有什么明显的卡顿现象。

快速解决方案

对比其他用于加速构建的 Jenkins Slave,它们每天晚上都会关机(节约用电),就一直没出过问题。

于是将其重启后,暂时解决了问题。

原因分析

首先列出卡顿的通用排查流程:

  1. 性能问题:CPU/RAM/IO/Network
  2. 僵尸进程导致 Pid 资源不足。
  3. 使用 ICMP-ping、tcp-ping 检查网卡
  4. 系统问题,检查 kernel 和 syslog

既然 CPU/RAM 都没有问题,而且每晚关机的 Slave 也没问题,这说明卡顿是在长期运行时才会发生。

排查:

  1. 系统负载很正常,排除性能问题。
  2. 内核参数:之前图方便,给所有的服务器都设了 vm.max_map_count=262144(elasticsearch 6.8+ 需要)
    • 之前遇到过因为这个参数,导致 redis 长期运行后响应缓慢的问题。可能和它有关。
  3. Jenkins Slave 虚拟机中有 66248 个进程,但是其中 66009 个都是僵尸进程

Jenkins Slave 最明显的问题,就是僵尸进程过多了。

僵尸进程

man ps 中对 僵尸进程(状态 Z)的解释如下:

Z defunct ("zombie") process, terminated but not reaped by its parent

在子进程已经终止,但是父进程却没有通过 wait 系统调用来收割 (reap) 子进程时,这个子进程会成为一个僵尸进程。top 命令的第二行会显示僵尸进程的个数。

查看僵尸进程的命令:ps -ef| grep defunc,defunc 意为“死者”。

僵尸进程无法通过 kill 命令杀死(你无法杀死一个已经死掉的进程hhh),只能通过干掉它的父进程来间接的杀死它们。

系统中存在过多的僵尸进程将占用大量的操作系统进程表资源,导致系统卡顿并最终崩溃!

通过前述的 ps 命令,我们发现这些 zombies 的父进程基本都是 jenkins-agent 进程,
重启服务器时该进程也被重启,因此服务器恢复正常。但是这显然只是治标的方法,过一段时间僵尸进程又会堆积,导致服务器卡顿。

尝试寻找治根的解决方法,搜索 Jenkins zombie 发现如下 Issues、PR:

  1. Handling of zombie processes would be useful
  2. defunct sh and sleep processes when running on slave-jnlp
  3. Wrapper process leaves zombie when no init process present
  4. Start long running containers with --init

最简单的解决方案,应该就是在通过 docker 启动 jenkins-agent 时添加 --init 参数,对应的 docker-compose 参数为 docker-compose reference - init

于是在 docker-compose.yml 中添加上这个参数,解决了该问题,点击查看示例 docker-compose.yaml

posted @ 2020-01-05 20:38  於清樂  阅读(330)  评论(0编辑  收藏