前言
在上一篇精心撰写的文章中,我们详细阐释了pod的整体架构理念。这篇文章将深入探讨Pod在资源管理方面所涉及到的重要内容。在此过程中,首先着重介绍Cgroup——作为隔离容器获取和使用资源的核心技术。
Cgroup
作用
Cgroups的全称是Control Group,它最主要的作用就是限制一个进程组能够使用的资源,包括CPU、内存、磁盘、网络带宽等。
如何限制资源
Cgroup暴露给用户的操作接口是文件系统,即它以文件和目录的方式组织在操作系统的/sys/fs/cgroup路径下。
进入这个目录,看到如下内容:
drwxr-xr-x 5 root root 0 7月 18 11:03 blkio
lrwxrwxrwx 1 root root 11 7月 18 11:03 cpu -> cpu,cpuacct
lrwxrwxrwx 1 root root 11 7月 18 11:03 cpuacct -> cpu,cpuacct
drwxr-xr-x 5 root root 0 7月 18 11:03 cpu,cpuacct
drwxr-xr-x 3 root root 0 7月 18 11:03 cpuset
drwxr-xr-x 5 root root 0 7月 18 11:03 devices
drwxr-xr-x 3 root root 0 7月 18 11:03 freezer
drwxr-xr-x 3 root root 0 7月 18 11:03 hugetlb
drwxr-xr-x 5 root root 0 7月 18 11:03 memory
lrwxrwxrwx 1 root root 16 7月 18 11:03 net_cls -> net_cls,net_prio
drwxr-xr-x 3 root root 0 7月 18 11:03 net_cls,net_prio
lrwxrwxrwx 1 root root 16 7月 18 11:03 net_prio -> net_cls,net_prio
drwxr-xr-x 3 root root 0 7月 18 11:03 perf_event
drwxr-xr-x 5 root root 0 7月 18 11:03 pids
drwxr-xr-x 5 root root 0 7月 18 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调度的全面解读》!