附录1:在k8s中限制pod的pid数

使用 Cgroups 限制 Kubernetes Pod 进程数

Kubernetes 里面的 Pod 资源是最小的计算单元,抽象了一组(一个或多个)容器。容器也是 Linux 系统上的进程,但基于 Namespace 和 Cgroups(Control groups) 等技术实现了不同程度的隔离。 简单来说 Namespace 可以让每个进程有独立的 PID, IPC 和网络空间。Cgroups 可以控制进程的资源占用,比如 CPU ,内存和允许的最大进程数等等。

今天主要介绍如何通过 Cgroups 里面的 pids 控制器限制 Kubernetes Pod 容器的最大进程数量。

场景介绍

之前遇到过这样一个问题,我们的服务会调用执行外部的命令,每调用一次外部命令就会 fork 产生子进程。但是由于代码上的 bug ,没有及时对子进程回收,然后这个容器不断 fork 产生子进程,耗尽了宿主机的进程表空间,最终导致整个系统不响应,影响了其它的服务。

这种问题除了让开发人员修复 bug 外,也需要在系统层面对进程数量进行限制。所以,如果一个容器里面运行的服务会 fork 产生子进程,就很有必要使用 Cgroups 的 pids 控制器限制这个容器能运行的最大进程数量。

解决方法

Kubelet 开启 PodPidsLimit 功能

Kubernetes 里面的每个节点都会运行一个叫做 Kubelet 的服务,负责节点上容器的状态和生命周期,比如创建和删除容器。根据 Kubernetes 的官方文档 Process ID Limits And Reservations 内容,可以设置 Kubelet 服务的 –pod-max-pids 配置选项,之后在该节点上创建的容器,最终都会使用 Cgroups pid 控制器限制容器的进程数量。

我们 Kubernetes 是在 CentOS 7 上使用 kubeadm 部署的 v1.15.9 版本,需要额外设置 SupportPodPidsLimit 的 feature-gate,对应操作如下(其它发行版应该也类似):

# kubelet 使用 systemd 启动的,可以通过编辑 /etc/sysconfig/kubelet
# 添加额外的启动参数,设置 pod 最大进程数为 1024
$ vim /etc/sysconfig/kubelet
KUBELET_EXTRA_ARGS="--pod-max-pids=1024 --feature-gates=\"SupportPodPidsLimit=true\""

# 重启 kubelet 服务
$ systemctl restart kubelet

# 查看参数是否生效
$ ps faux | grep kubelet | grep pod-max-pids
root     104865 10.5  0.6 1731392 107368 ?      Ssl  11:56   0:30 /usr/bin/kubelet ... --pod-max-pids=10 --feature-gates=SupportPodPidsLimit=true

验证 PodPidsLimit

通过配置 Kubelet 的 –pod-max-pids=1024 选项,限制了一个容器内允许的最大进程数为 1024 个。现在来测试下如果容器内不断 fork 子进程,数目到达 1024 个时会触发什么行为。

参考 Fork bomb 的内容,可以创建一个 pod,不断 fork 子进程。

# 创建普通的 nginx pod yaml
$ cat <<EOF > test-nginx.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-nginx
spec:
  containers:
  - name: nginx
    image: nginx
EOF

# 创建到 Kubernetes 集群
$ kubectl apply -f test-nginx.yaml

# 进入 nginx 容器模拟 fork bomb 
$ kubectl exec -ti test-nginx bash
root@test-nginx:/# bash -c "fork() { fork | fork &  }; fork"
environment: fork: retry: Resource temporarily unavailable
environment: fork: retry: Resource temporarily unavailable
environment: fork: retry: Resource temporarily unavailable

通过进入一个 nginx 容器里面使用 bash 运行 fork bomb 命令,我们会发现当 fork 的子进程达到限制的上限数目后,会报 retry: Resource temporarily unavailable 的错误,这个时候再看下宿主机的 fork 进程数目。

# 通过在外部宿主机执行下面的命令,会发现 fork 的进程数目接近 1024 个
$ ps faux | grep fork | wc -l
1019

通过以上的实验,发现能够通过设置 Kubelet 的 –pod-max-pids 选项,限制容器类的进程数,避免容器进程数不断上升最终耗尽宿主机资源,拖垮整个宿主机系统。

原理实现

通过之前描述的解决方法,已经能够限制容器的进程数了。

现在从代码的层面看下 Kubelet 如何设置 Cgroups pids 控制器。

Kubelet 代码调用

首先来看下 Kubelet 代码里面 –pod-max-pids 是怎么生效的,Kubernetes 的版本为 v1.15.9。

联系管理员微信tutu19192010,注册账号

上一篇
下一篇
Copyright © 2022 Egon的技术星球 egonlin.com 版权所有 帮助IT小伙伴学到真正的技术