云原生探索系列(七):Pod的资源管理

117 阅读11分钟

前言

在上一篇精心撰写的文章中,我们详细阐释了pod的整体架构理念。这篇文章将深入探讨Pod在资源管理方面所涉及到的重要内容。在此过程中,首先着重介绍Cgroup——作为隔离容器获取和使用资源的核心技术。

Cgroup

作用

Cgroups的全称是Control Group,它最主要的作用就是限制一个进程组能够使用的资源,包括CPU、内存、磁盘、网络带宽等。

如何限制资源

Cgroup暴露给用户的操作接口是文件系统,即它以文件和目录的方式组织在操作系统的/sys/fs/cgroup路径下。
进入这个目录,看到如下内容:

drwxr-xr-x 5 root root  0 718 11:03 blkio
lrwxrwxrwx 1 root root 11 718 11:03 cpu -> cpu,cpuacct
lrwxrwxrwx 1 root root 11 718 11:03 cpuacct -> cpu,cpuacct
drwxr-xr-x 5 root root  0 718 11:03 cpu,cpuacct
drwxr-xr-x 3 root root  0 718 11:03 cpuset
drwxr-xr-x 5 root root  0 718 11:03 devices
drwxr-xr-x 3 root root  0 718 11:03 freezer
drwxr-xr-x 3 root root  0 718 11:03 hugetlb
drwxr-xr-x 5 root root  0 718 11:03 memory
lrwxrwxrwx 1 root root 16 718 11:03 net_cls -> net_cls,net_prio
drwxr-xr-x 3 root root  0 718 11:03 net_cls,net_prio
lrwxrwxrwx 1 root root 16 718 11:03 net_prio -> net_cls,net_prio
drwxr-xr-x 3 root root  0 718 11:03 perf_event
drwxr-xr-x 5 root root  0 718 11:03 pids
drwxr-xr-x 5 root root  0 718 11:03 systemd
  • blkio: 这个子系统设置限制每个块设备的输入输出控制。例如:磁盘,光盘以及USB等等。
  • cpu: 这个子系统使用调度程序为cgroup任务提供CPU的访问。
  • cpuacct: 产生 cgroup 任务的 CPU 资源报告。
  • cpuset: 如果是多核心的 CPU,这个子系统会为 cgroup 任务分配单独的 CPU 和内存。
  • devices: 允许或拒绝 cgroup 任务对设备的访问。
  • freezer: 暂停和恢复cgroup任务。
  • memory: 设置每个 cgroup 的内存限制以及产生内存资源报告。
  • net_cls: 标记每个网络包以供cgroup方便使用。
  • ns: 名称空间子系统。
  • pid: 进程标识子系统。

cpu子系统

进入cpu目录,查看目录内容:

-rw-r--r--  1 root root 0 7月  18 11:03 cgroup.clone_children
--w--w--w-  1 root root 0 7月  18 11:03 cgroup.event_control
-rw-r--r--  1 root root 0 7月  18 11:03 cgroup.procs
-r--r--r--  1 root root 0 7月  18 11:03 cgroup.sane_behavior
-r--r--r--  1 root root 0 7月  18 11:03 cpuacct.stat
-rw-r--r--  1 root root 0 7月  18 11:03 cpuacct.usage
-r--r--r--  1 root root 0 7月  18 11:03 cpuacct.usage_percpu
-rw-r--r--  1 root root 0 7月  18 11:03 cpu.cfs_period_us
-rw-r--r--  1 root root 0 7月  18 11:03 cpu.cfs_quota_us
-rw-r--r--  1 root root 0 7月  18 11:03 cpu.rt_period_us
-rw-r--r--  1 root root 0 7月  18 11:03 cpu.rt_runtime_us
-rw-r--r--  1 root root 0 7月  18 11:03 cpu.shares
-r--r--r--  1 root root 0 7月  18 11:03 cpu.stat
drwxr-xr-x  4 root root 0 7月  18 11:04 kubepods.slice
-rw-r--r--  1 root root 0 7月  18 11:03 notify_on_release
-rw-r--r--  1 root root 0 7月  18 11:03 release_agent
drwxr-xr-x 88 root root 0 7月  18 11:19 system.slice
-rw-r--r--  1 root root 0 7月  18 11:03 tasks
drwxr-xr-x  2 root root 0 7月  18 11:04 user.slice
  • cpu.shares: 可出让的能获得 CPU 使用时间的相对值。
  • cpu.cfs_period_us: cfs_period_us 用来配置时间周期长度,单位为 us(微秒).
  • cpu.cfs_quota_us: cfs_quota_us 用来配置当前 Cgroup 在 cfs_period_us 时间内最多能使用的 CPU 时间数,单位为 us(微秒)。
  • cpu.stat: Cgroup 内的进程使用的 CPU 时间统计。
  • nr_periods: 经过 cpu.cfs_period_us 的时间周期数量。
  • nr_throttled: 在经过的周期内,有多少次因为进程在指定的时间周期内用光了配额时间而受到限制。
  • throttled_time: Cgroup 中的进程被限制使用 CPU 的总用时,单位是 ns(纳秒)。

这一堆概念看着头疼,很难理解,别急,我们从一个小案例入手来理解是如何限制进程的cpu:
在cpu目录下,新建一个文件夹:

mkdir container

进入container目录,查看目录内容如下

-rw-r--r-- 1 root root 0 7月  18 11:46 cgroup.clone_children
--w--w--w- 1 root root 0 7月  18 11:46 cgroup.event_control
-rw-r--r-- 1 root root 0 7月  18 11:46 cgroup.procs
-r--r--r-- 1 root root 0 7月  18 11:46 cpuacct.stat
-rw-r--r-- 1 root root 0 7月  18 11:46 cpuacct.usage
-r--r--r-- 1 root root 0 7月  18 11:46 cpuacct.usage_percpu
-rw-r--r-- 1 root root 0 7月  18 11:46 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 7月  18 11:46 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 7月  18 11:46 cpu.rt_period_us
-rw-r--r-- 1 root root 0 7月  18 11:46 cpu.rt_runtime_us
-rw-r--r-- 1 root root 0 7月  18 11:46 cpu.shares
-r--r--r-- 1 root root 0 7月  18 11:46 cpu.stat
-rw-r--r-- 1 root root 0 7月  18 11:46 notify_on_release
-rw-r--r-- 1 root root 0 7月  18 11:46 tasks

看到没有,创建目录后,操作系统自动在目录下生成了资源限制文件。
现在,我们运行一个死循环脚本,

while :; do :; done &

这条命令是 Bash 脚本中的一个无限循环,执行后输出一个PID为:38292。
使用top命令,查看cpu使用率,结果如下

PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
38292 root      20   0  120216   5204    192 R 100.0  0.1   3:27.92 bash

可以看到,cpu使用率达到了100%
接下来,我们就通过修改文件来cpu资源使用,先来看看默认值

cat cpu.cfs_quota_us
cat cpu.cfs_period_us

输出结果如下:

-1
100000

先不解释,先跟着操作,此时将cpu.cfs_quota_us 改为20000us(20ms)

echo 20000 > cpu.cfs_quota_us

最后把被限制的进程PID写入tasks文件中,设置就会生效

echo 38292 > tasks

再使用top查看,

PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
38292 root      20   0  120216   5204    192 R  20.0  0.1  81:12.03 -bash

发现cpu使用率变为20%左右了。
cpu.cfs_quota_us 的默认值为 -1 表示没有 CPU 时间限制,可以尽可能使用 CPU 资源。这里我们设置为 20000,表示在每个 cpu.cfs_period_us(默认为 100000 微秒)内最多可以使用 20 毫秒的 CPU 时间。也就是20/100
结合案例是不是容易理解了。

kubernetes容器资源配置

先来看个yaml文件,nginx.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
          resources:
            limits:
              memory: 1000Mi
              cpu: 800m
            requests:
              memory: 256Mi
              cpu: 100m

resource request 是你告诉 Kubernetes 集群的一个值,指定了 Pod 中每个容器所需的最小资源量(CPU 和内存)。Kubernetes 使用这个值来决定在调度 Pod 时需要为其分配多少资源。如果没有足够的资源可用,Pod 可能会被拒绝调度。
resource limit 指定了容器在节点上可以使用的最大资源量(CPU 和内存)。这个限制是 Kubernetes 使用来保护节点免受资源耗尽的影响的一种方式。如果容器尝试使用超过其限制的资源量,Kubernetes 将会限制其使用,并可能触发一系列的资源管理策略,如终止容器或限制其进一步的资源请求。

limits memory: 1000Mi 这定义了Pod中的容器可以使用的最大内存量为1000兆字节(Megabytes),其中“Mi”代表兆字节(Mebibytes)。在Kubernetes中,资源的单位通常使用二进制前缀(如Ki、Mi、Gi等),而不是传统的十进制前缀(如K、M、G等)。 cpu: 800m 这定义了Pod中的容器可以使用的最大CPU为800毫核(millicores)。在Kubernetes中,CPU资源通常以毫核(m)为单位进行分配,其中1000m等于1个CPU核心。所以,这里指定的800m表示容器可以使用相当于0.8个CPU核心的资源。

requests 这些字段定义了Kubernetes调度器在调度Pod时应考虑的最低资源需求。 memory: 256Mi 这表示Pod请求的最低内存量为256兆字节。调度器会寻找具有足够可用内存的节点来运行这个Pod,并且会确保该节点上运行的所有Pod的内存请求总和不超过其可用内存。 cpu: 100m 这表示Pod请求的最低CPU资源为100毫核,即相当于0.1个CPU核心。调度器会寻找具有足够CPU资源的节点来运行这个Pod,并且会确保该节点上运行的所有Pod的CPU请求总和不超过其可用CPU资源。

划重点:kubernetes调度时是使用requests字段计算资源的

执行kubectl apply -f nginx.yaml ,pod创建成功

NAME                               READY   STATUS    RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
nginx-deployment-d7cb576db-6gwbp   1/1     Running   0          25s   10.244.2.75   k8s-node02   <none>           <none>

使用如下命令,找到Container ID

kubectl describe po -n nginx nginx-deployment-76789565d-gxjxq

找到Container ID为

Container ID:   docker://1673cd0a19324c2e091667b77ddc2eddb165f4712eeb62ee6a78dfc8ae5a83f0

进入k8s-node02节点机器,使用如下命令,查找cgroup

docker inspect 1673cd0a19324c2e091667b77ddc2eddb165f4712eeb62ee6a78dfc8ae5a83f0

找到如下内容:

......
            "CpuShares": 102,
            "Memory": 1048576000,
            "NanoCpus": 0,
            "CgroupParent": "kubepods-burstable-pod37fb83e4_ee0a_4f91_baab_19243d0b31e4.slice",
            "BlkioWeight": 0,
            "BlkioWeightDevice": null,
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 100000,
            "CpuQuota": 80000,
...

我们进入CgroupParent目录

cd /sys/fs/cgroup/cpu/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod37fb83e4_ee0a_4f91_baab_19243d0b31e4.slice

然后我们查看一下cpu.shares、cpu.cfs_period_us、cpu.cfs_quota_us的值,其实上面docker inspect也可以看到相关信息

cat cpu.shares
cat cpu.cfs_period_us
cat cpu.cfs_quota_us

这三个值分别为102、100000、80000

创建容器时我们设置requests cpu: 100m,也就是0.1个cpu,而1个cpu对应的cpu.shares为1024,那0.1个cpu就是1024/10
那cpu.cfs_period_us和cpu.cfs_quota_us表示在100毫秒的cpu时间片里进程能用80毫秒,也就是0.8个cpu,这和limits cpu: 800m是相符的。

最后

这篇内容核心中的核心就是对cgroup的理解,动手实践更便于理解。敬请期待下期精彩内容——《关于pod调度的全面解读》!