Kubernetes 准入控制

header-how-to-optimize-kubernetes-for-reliability-and-cos

Author:rab



前言

LimitRange 是限制命名空间内可为每个适用的对象类别 (例如 Pod 或 PersistentVolumeClaim) 指定的资源分配量(限制和请求)的策略对象。默认情况下, Kubernetes 集群上的容器运行使用的计算资源没有限制。 使用 Kubernetes 资源配额, 管理员可以在一个指定的命名空间内限制集群资源的使用与创建。 在命名空间中,一个 Pod 最多能够使用命名空间的资源配额所定义的 CPU 和内存用量。

一个 LimitRange(限制范围) 对象提供的限制能够做到:

  • 在一个命名空间中实施对每个 Pod 或 Container 最小和最大的资源使用量的限制。
  • 在一个命名空间中实施对每个 PersistentVolumeClaim 能申请的最小和最大的存储空间大小的限制。
  • 在一个命名空间中实施对一种资源的申请值和限制值的比值的控制。
  • 设置一个命名空间中对计算资源的默认申请/限制值,并且自动的在运行时注入到多个 Container 中。

当某命名空间中有一个 LimitRange 对象时,将在该命名空间中实施 LimitRange 限制。

且 LimitRange 的名称必须是合法的 DNS 子域名,这一要求意味着名称必须满足如下规则:

  • 不能超过 253 个字符
  • 只能包含小写字母、数字,以及 ‘-’ 和 ‘.’
  • 必须以字母数字开头
  • 必须以字母数字结尾

官方参考文档:https://kubernetes.io/zh-cn/docs/concepts/policy/limit-range/

一、限制范围

  1. 管理员在一个命名空间内创建一个 LimitRange 对象。

  2. 用户在此命名空间内创建(或尝试创建) Pod 和 PersistentVolumeClaim 等对象。

  3. 首先,LimitRanger 准入控制器对所有没有设置计算资源需求的所有 Pod(及其容器)设置默认请求值与限制值。

  4. 其次,LimitRange 跟踪其使用量以保证没有超出命名空间中存在的任意 LimitRange 所定义的最小、最大资源使用量以及使用量比值。

  5. 若尝试创建或更新的对象(Pod 和 PersistentVolumeClaim)违反了 LimitRange 的约束, 向 API 服务器的请求会失败,并返回 HTTP 状态码 403 Forbidden 以及描述哪一项约束被违反的消息。

  6. 若你在命名空间中添加 LimitRange 启用了对 cpumemory 等计算相关资源的限制, 你必须指定这些值的请求使用量与限制使用量。否则,系统将会拒绝创建 Pod。

  7. LimitRange 的验证仅在 Pod 准入阶段进行,不对正在运行的 Pod 进行验证。 如果你添加或修改 LimitRange,命名空间中已存在的 Pod 将继续不变。

  8. 如果命名空间中存在两个或更多 LimitRange 对象,应用哪个默认值是不确定的。

二、配置案例

2.1 名称空间 CPU 与内存约束

我们可以通过 LimitRange 对象声明 CPU 的最小和最大值. 如果 Pod 不能满足 LimitRange 的限制,就无法在该命名空间中被创建。

2.1.1 CPU 约束

1、创建命名空间

创建一个命名空间,以便本练习中创建的资源和集群的其余部分相隔离。

kubectl create namespace constraints-cpu-example

2、创建 LimitRange

vim cpu-constraints.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-min-max-demo-lr
spec:
  limits:
  - max:
      cpu: "800m"
    min:
      cpu: "200m"
    type: Container
kubectl apply -f cpu-constraints.yaml -n constraints-cpu-example
kubectl get limitrange cpu-min-max-demo-lr --output=yaml -n constraints-cpu-example

image-20231107143848149

输出结果显示 CPU 的最小和最大限制符合预期。但需要注意的是,尽管你在 LimitRange 的配置文件中你没有声明默认值,默认值也会被自动创建(且默认为你设置的 max 值)。

此时,每当你在 constraints-cpu-example 命名空间中创建 Pod 时,或者某些其他的 Kubernetes API 客户端创建了等价的 Pod 时,Kubernetes 就会执行下面的步骤:

  • 如果 Pod 中的任何容器未声明自己的 CPU 请求和限制,控制面将为该容器设置默认的 CPU 请求和限制。
  • 确保该 Pod 中的每个容器的 CPU 请求至少 200 millicpu(否则创建失败)。
  • 确保该 Pod 中每个容器 CPU 请求不大于 800 millicpu(否则创建失败)。

接下来继续创建 Pod 进行测试。

3、创建 Pod

vim cpu-constraints-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo
spec:
  containers:
  - name: constraints-cpu-demo-ctr
    image: nginx
    resources:
      limits:
        cpu: "800m"
      requests:
        cpu: "500m"
kubectl apply -f cpu-constraints-pod.yaml -n constraints-cpu-example

4、验证 Pod 状态(确保健康)

kubectl get pod constraints-cpu-demo --namespace=constraints-cpu-example

image-20231107144339031

5、查看 Pod 的详情

kubectl get pod constraints-cpu-demo --output=yaml --namespace=constraints-cpu-example

image-20231107144434020

输出结果显示该 Pod 的容器的 CPU 请求为 500 millicpu,CPU 限制为 800 millicpu,这些参数满足 LimitRange 规定的限制范围。

Pod 异常情况分析:

Pod 的 limits 及 requests 的值只要在 LimitRange 的范围内时可正常运行,那如果超过这两个参数值超过 LimitRange 范围(即不再该范围)时又会怎么样呢?这里我们分三种情况来测试一下:超过最大 CPU 限制(limits)不满足最小 CPU 请求(requests)一个没有声明 CPU 请求和 CPU 限制的 Pod

1、超过最大 CPU 限制(limits)的 Pod

这里 Pod 的 limits 值超过 LimitRange 的 max 最大值。

vim cpu-constraints-pod-2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-2
spec:
  containers:
  - name: constraints-cpu-demo-2-ctr
    image: nginx
    resources:
      limits:
        cpu: "1.5"
      requests:
        cpu: "500m"
kubectl apply -f cpu-constraints-pod-2.yaml --namespace=constraints-cpu-example

输出结果:

image-20231107150029113

Error from server (Forbidden): error when creating "cpu-constraints-pod-2.yaml": pods "constraints-cpu-demo-2" is forbidden: maximum cpu usage per Container is 800m, but limit is 1500m.

输出结果表明 Pod 没有创建成功,因为其中定义了一个无法被接受的容器,该容器之所以无法被接受是因为其中设定了过高的 CPU 限制值(即超过了 LimitRange 指定的 CPU 最大值)。

2、满足最小 CPU 请求(requests)的 Pod

这里 Pod 的 limits 值小于 LimitRange 的 min 最小值。

vim cpu-constraints-pod-3.yaml
apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-3
spec:
  containers:
  - name: constraints-cpu-demo-3-ctr
    image: nginx
    resources:
      limits:
        cpu: "800m"
      requests:
        cpu: "100m"
kubectl apply -f cpu-constraints-pod-3.yaml --namespace=constraints-cpu-example

image-20231107150918321

输出结果同样显示 Pod 没有创建成功,因为其中定义了一个无法被接受的容器。 该容器无法被接受的原因是其中所设置的 CPU 请求小于最小值的限制(即不满足 LimitRange 指定的 CPU 最小请求值)。

3、创建一个没有声明 CPU 请求和 CPU 限制的 Pod

这里的 Pod 没有设置 limits。

vim cpu-constraints-pod-4.yaml
apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-4
spec:
  containers:
  - name: constraints-cpu-demo-4-ctr
    image: vish/stress
kubectl apply -f cpu-constraints-pod-4.yaml --namespace=constraints-cpu-example

查看 Pod 的详情:

kubectl get pod constraints-cpu-demo-4 --namespace=constraints-cpu-example --output=yaml

image-20231107152319891

输出结果显示 Pod 的唯一容器的 CPU 请求为 800 millicpu,CPU 限制为 800 millicpu。因此,如果容器没有声明自己的 CPU 请求和限制, 控制面会根据命名空间中配置 LimitRange 设置默认(default)的 CPU 请求和限制。

2.1.2 内存约束

1、创建命名空间

创建一个命名空间,以便本练习中创建的资源和集群的其余部分相隔离。

kubectl create namespace constraints-mem-example

2、创建 LimitRange

vim memory-constraints.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: mem-min-max-demo-lr
spec:
  limits:
  - max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container
kubectl apply -f memory-constraints.yaml --namespace=constraints-mem-example

查看 LimitRange 的详情:

kubectl get limitrange mem-min-max-demo-lr --namespace=constraints-mem-example --output=yaml

image-20231107153154899

输出显示预期的最小和最大内存约束。 但请注意,即使你没有在 LimitRange 的配置文件中指定默认值,默认值也会自动生成(且默认值是你 LimitRange 中的 max 值)。

现在,每当在 constraints-mem-example 命名空间中创建 Pod 时,Kubernetes 就会执行下面的步骤:

  • 如果 Pod 中的任何容器未声明自己的内存请求和限制,控制面将为该容器设置默认的内存请求和限制。
  • 确保该 Pod 中的每个容器的内存请求至少 500 MiB。
  • 确保该 Pod 中每个容器内存请求不大于 1 GiB。

以下为包含一个容器的 Pod 清单。该容器声明了 600 MiB 的内存请求和 800 MiB 的内存限制, 这些满足了 LimitRange 施加的最小和最大内存约束。

案例效果与 CPU 约束基本上是一致的,你就是说你的 Pod 也是不能小于或超过 LimitRange 范围值,否则 Pod 将创建失败,这里就不再演示了,大家可去看官方文档。

2.1.3 默认 CPU 申请约束

前面也提到了,当我们创建 LimitRange 资源并指定 CPU max/min 值时,会自动为我们添加上默认的 CPU 约束值(即 default 和 defaultRequest 值)且是将 max 值作为默认值,如下图:

image-20231107143848149

那这两个值有什么用呢?

我们前面提到,如果创建的 Pod 没有指定约束,那 K8s 控制面板就会将 LimitRange 的 default 和 defaultRequest 值应用到 Pod 上。当然了,前提是你已经配置了 LimitRange,否则默认情况下 K8s 集群对 Pod 的资源(CPU/内存)是没有限制的。

案例也是一样,这里不再重复案例演示。

这里要注意的就是一下几种情况:

1、没有声明容器的限制(limits)和请求(requests)

此时 Pod 的 limits 和 requests 值就是 LimitRange 的 default 和 defaultRequest 值。

2、只声明容器的限制(limits)而不声明请求(requests)

此时 Pod 的 limits 和 requests 值就是你当前声明的值 limits。

3、只声明容器的请求(requests)而不声明限制(limits)

此时 Pod 的 limits 值为你 LimitRange 的 default 值,requests 值就是你当前声明的 requests 值。

因此不难总结出 LimitRange 的 limits 的作用:

  • default:在 Pod 没有指定 limits 约束时的 limits 值;

  • defaultRequest:在 Pod 没有指定 requests 约束时的 requests 值;

  • max:在 Pod 指定了 limits 约束时不能超过的最大值;

  • min:在 Pod 指定了 requests 约束时不能小于的最最值;

而且也要记住,你指定的 Pod 值也是不能超过 LimitRange 默认值范围。

2.1.4 默认内存申请约束

前面也提到了,当我们创建 LimitRange 资源并指定内存 max/min 值时,会自动为我们添加上默认的内存约束值(即 default 和 defaultRequest 值)且是将 max 值作为默认值,如下图:

image-20231107153154899

那这两个值有什么用呢?

同理,当创建的 Pod 没有指定约束时,那 K8s 控制面板就会将 LimitRange 的 default 和 defaultRequest 值应用到 Pod 上。

案例也是一样,这里不再重复案例演示。

这里要注意的就是一下几种情况:

1、没有声明容器的限制(limits)和请求(requests)

此时 Pod 的 limits 和 requests 值就是 LimitRange 的 default 和 defaultRequest 值。

2、只声明容器的限制(limits)而不声明请求(requests)

此时 Pod 的 limits 和 requests 值就是你当前声明的值 limits。

3、只声明容器的请求(requests)而不声明限制(limits)

此时 Pod 的 limits 值为你 LimitRange 的 default 值,requests 值就是你当前声明的 requests 值。

因此不难总结出 LimitRange 的 limits 的作用:

  • default:在 Pod 没有指定 limits 约束时的 limits 值;

  • defaultRequest:在 Pod 没有指定 requests 约束时的 requests 值;

  • max:在 Pod 指定了 limits 约束时不能超过的最大值;

  • min:在 Pod 指定了 requests 约束时不能小于的最最值;

而且也要记住,你指定的 Pod 值也是不能超过 LimitRange 默认值范围。

2.2 名称空间总容量限额约束

以上的案例都是使用 LimitRange 对 namespace 中某个 Pod 的 CPU、内存资源约束,当我们希望控制单个名字空间总的可以消耗多少存储空间、CPU、内存以控制成本时,我们就可以使用名称空间存储容量约束-StorageQuota。

而存储约束无非就这几个方面:

  1. 名字空间中持久卷申领(persistent volume claims)的数量
  2. 每个申领(claim)可以请求的存储量
  3. 名字空间可以具有的累计存储量

1、创建命名空间

创建一个命名空间,以便本练习中创建的资源和集群的其余部分相隔离。

kubectl create namespace quota-mem-cpu-example

2、创建 LimitRange

vim quota-mem-cpu.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: mem-cpu-demo
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi
kubectl apply -f quota-mem-cpu.yaml --namespace=quota-mem-cpu-example

查看 ResourceQuota 详情:

kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml

image-20231107165339685

ResourceQuota 在 quota-mem-cpu-example 命名空间中设置了如下要求:

  • 在该命名空间中的每个 Pod 的所有容器都必须要有内存请求和限制,以及 CPU 请求和限制。
  • 在该命名空间中所有 Pod 的内存请求总和不能超过 1 GiB。
  • 在该命名空间中所有 Pod 的内存限制总和不能超过 2 GiB。
  • 在该命名空间中所有 Pod 的 CPU 请求总和不能超过 1 cpu。
  • 在该命名空间中所有 Pod 的 CPU 限制总和不能超过 2 cpu。

3、创建 Pod

vim quota-mem-cpu-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: quota-mem-cpu-demo
spec:
  containers:
  - name: quota-mem-cpu-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
        cpu: "800m"
      requests:
        memory: "600Mi"
        cpu: "400m"
kubectl apply -f quota-mem-cpu-pod.yaml --namespace=quota-mem-cpu-example

查看 Pod 状态:

kubectl get pod quota-mem-cpu-demo --namespace=quota-mem-cpu-example

image-20231107165734288

再次查看 ResourceQuota 的详情:

kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml

image-20231107165825218

输出结果显示了配额以及有多少配额已经被使用,你可以看到 Pod 的内存和 CPU 请求值及限制值没有超过配额。

如果我们再继续创建一个 Pod,如下:

vim quota-mem-cpu-pod-2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: quota-mem-cpu-demo-2
spec:
  containers:
  - name: quota-mem-cpu-demo-2-ctr
    image: redis
    resources:
      limits:
        memory: "1Gi"
        cpu: "800m"
      requests:
        memory: "700Mi"
        cpu: "400m"
kubectl apply -f quota-mem-cpu-pod-2.yaml --namespace=quota-mem-cpu-example

image-20231107170437885

报错了,为什呢?因为在 quota-mem-cpu-example 名称空间中,内存请求与已经使用的内存请求之和超过了内存请求的配额:600 MiB + 700 MiB > 1 GiB

总结

1、LimitRange

  • 用于对 namespace 中的 Pod 的 CPU、内存约束;
  • 默认情况下 K8s 集群对 Pod 是没有约束的(即没有 LimitRange);
  • 当对 namespace 进行准入控制时,在该 namespace 中的 Pod 需遵循 namespace 中的约束。

2、StorageQuota

  • 用于限制某个 namespace 总的限额(即该 namespace 中的所有的 Pod 资源的 CPU、内存、存储不能超过指定的限额)。

—END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云计算-Security

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值