k8s 准入控制器之LimitRanger

发布时间 2023-08-08 15:17:48作者: 小吉猫

LimitRanger概述

尽管用户可以为容器或Pod资源指定资源需求及资源限制,但这并非强制性要求,那些未明确定义资源限制的容器应用很可能会因程序Bug或真实需求而吞掉本地工作节点上的所有可用计算资源。因此妥当的做法是,使用LimitRange资源在每个名称空间中限制每个容器的最小及最大计算资源用量,以及为未显式定义计算资源使用区间的容器设置默认的计算资源需求和计算资源限制。一旦在名称空间上定义了LimitRange对象,客户端创建或修改资源对象的操作必将受到LimitRange控制器的“验证”,任何违反LimitRange对象定义的资源最大用量的请求都将被直接拒绝。
LimitRange支持在Pod级别与容器级别分别设置CPU和内存两种计算资源的可用范围,它们对应的资源范围限制类型分别为Pod和Container。一旦在名称空间上启用LimitRange,该名称空间中的Pod或容器的requests和limits的各项属性值必须在对应的可用资源范围内,否则将会被拒绝,这是验证型准入控制器的功能。而未显式指定request和limit属性的容器,将会从LimitRange资源上分别自动继承相应的默认设置,这是变异型准入控制器的功能。
另外,LimitRange也支持在PersistentVolumeClaim资源级别设定存储空间的范围限制,它用于限制相应名称空间中创建的PVC对象请求使用的存储空间不能逾越指定的范围。未指定requests和limits属性的PVC规范,将在创建时自动继承LimitRange上配置的默认值。
当前,默认的 LimitRanger 对 default 名字空间中的所有 Pod 都设置 0.1 CPU 的需求。

限制范围

默认情况下, Kubernetes 集群上的容器运行使用的计算资源没有限制。 使用 Kubernetes 资源配额, 管理员(也称为 集群操作者)可以在一个指定的命名空间内限制集群资源的使用与创建。 在命名空间中,一个 Pod 最多能够使用命名空间的资源配额所定义的 CPU 和内存用量。 作为集群操作者或命名空间级的管理员,你可能也会担心如何确保一个 Pod 不会垄断命名空间内所有可用的资源。

LimitRange 是限制命名空间内可为每个适用的对象类别 (例如 Pod 或 PersistentVolumeClaim) 指定的资源分配量(限制和请求)的策略对象。
一个 LimitRange(限制范围) 对象提供的限制能够做到:

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

LimitRange 的名称必须是合法的 DNS 子域名。

资源限制和请求的约束

管理员在一个命名空间内创建一个 LimitRange 对象。
用户在此命名空间内创建(或尝试创建) Pod 和 PersistentVolumeClaim 等对象。
首先,LimitRanger 准入控制器对所有没有设置计算资源需求的所有 Pod(及其容器)设置默认请求值与限制值。
其次,LimitRange 跟踪其使用量以保证没有超出命名空间中存在的任意 LimitRange 所定义的最小、最大资源使用量以及使用量比值。
若尝试创建或更新的对象(Pod 和 PersistentVolumeClaim)违反了 LimitRange 的约束, 向 API 服务器的请求会失败,并返回 HTTP 状态码 403 Forbidden 以及描述哪一项约束被违反的消息。 # kubectl get deployments deployment-name -n ns -o json
若你在命名空间中添加 LimitRange 启用了对 cpu 和 memory 等计算相关资源的限制, 你必须指定这些值的请求使用量与限制使用量。否则,系统将会拒绝创建 Pod。
LimitRange 的验证仅在 Pod 准入阶段进行,不对正在运行的 Pod 进行验证。 如果你添加或修改 LimitRange,命名空间中已存在的 Pod 将继续不变。
如果命名空间中存在两个或更多 LimitRange 对象,应用哪个默认值是不确定的。

创建LimitRange

demoapp-ns.yaml

apiVersion: v1
kind: Namespace
metadata:
    name: demoapp

limitrange.yaml

需要注意的是,LimitRange生效于名称空间级别,它需要定义在每个名称空间之上;另外,定义的限制仅对该资源创建后的Pod和PVC资源创建请求有效,对之前已然存在的资源无效;再者,不建议在生效于同一名称空间的多个LimitRange资源上,对同一个计算资源限制进行分别定义,以免产生歧义或导致冲突。
apiVersion: v1
kind: LimitRange
metadata:
  name: limitrange-demoapp
  namespace: demoapp
spec:
  limits:
  - type: Container    #限制的资源类型
    max:
      cpu: "2"         #限制单个容器的最大CPU
      memory: "2Gi"    #限制单个容器的最大内存
    min:
      cpu: "500m"      #限制单个容器的最小CPU
      memory: "512Mi"  #限制单个容器的最小内存
    default:
      cpu: "500m"      #默认单个容器的CPU限制
      memory: "512Mi"  #默认单个容器的内存限制
    defaultRequest:
      cpu: "500m"      #默认单个容器的CPU创建请求
      memory: "512Mi"  #默认单个容器的内存创建请求
    maxLimitRequestRatio:
      cpu: 2           #限制CPU Limit/request比值最大为2
      memory: 2        #限制内存 Limit/request比值最大为2
  - type: Pod
    max:
      cpu: "500m"         #限制单个Pod的最大CPU
      memory: "512Mi"     #限制单个Pod最大内存
    min:
      cpu: "300m"         #限制单个Pod的最小CPU
      memory: "128Mi"    #限制单个Pod最小内存
  - type: PersistentVolumeClaim
    max:
      storage: "60Gi"    #限制 PVC最大的 requests.storage
    min:
      storage: "30Gi"    #限制 PVC最小的 requests.storage
    default:
      storage: "30Gi"    #限制 PVC默认的 requests.storage      
    defaultRequest:
      storage: "30Gi"    
    maxLimitRequestRatio:
      storage: "2"
LimitRange仅在Container资源类型上可为CPU与内存设置default(limits属性的默认值)和defaultrequest(requests属性的默认值),Pod资源类型不支持。

创建LimitRange资源

# kubectl apply -f demoapp-ns.yaml -f limitrange.yaml 
namespace/demoapp created
limitrange/limitrange-demoapp created

查看LimitRange资源

# kubectl describe limitranges/limitrange-demoapp -n demoapp
Name:                  limitrange-demoapp
Namespace:             demoapp
Type                   Resource  Min    Max    Default Request  Default Limit  Max Limit/Request Ratio
----                   --------  ---    ---    ---------------  -------------  -----------------------
Container              memory    512Mi  2Gi    512Mi            512Mi          2
Container              cpu       500m   2      500m             500m           2
Pod                    cpu       300m   500m   -                -              -
Pod                    memory    128Mi  512Mi  -                -              -
PersistentVolumeClaim  storage   30Gi   70Gi   30Gi             30Gi           2

示例 

没有声明容器限制

depoly-demoapp-v10.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: demoappv10
    version: v1.0
  name: demoappv10
  namespace: demoapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demoapp
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: demoapp
        version: v1.0
    spec:
      containers:
      - image: ikubernetes/demoapp:v1.0
        name: demoapp
        env:
        - name: PORT
          value: "8080"
        resources: {}

创建Deployment资源

# kubectl apply -f depoly-demoapp-v10.yaml
deployment.apps/demoappv10 created

查看deployment资源详情

# kubectl get pod `kubectl get pods -n demoapp -o jsonpath={".items[0].metadata.name"}` -n demoapp -o jsonpath={".spec.containers[0].resources"} | jq
{
  "limits": {
    "cpu": "500m",
    "memory": "512Mi"
  },
  "requests": {
    "cpu": "500m",
    "memory": "512Mi"
  }
}
输出内容显示该 Pod 的容器资源限制。 这些都是 LimitRange 设置的默认值。

声明容器限制超出LimitRange限制

depoly-demoapp-v10.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: demoappv10
    version: v1.0
  name: demoappv10
  namespace: demoapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demoapp
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: demoapp
        version: v1.0
    spec:
      containers:
      - image: ikubernetes/demoapp:v1.0
        name: demoapp
        env:
        - name: PORT
          value: "8080"
        resources: 
          limits:
            memory: "1Gi"
            cpu: "3"
          requests:
            memory: "1Gi"
            cpu: "3"

创建Deployment资源

# kubectl apply -f depoly-demoapp-v10.yaml
deployment.apps/demoappv10 created

查看deployment资源

# kubectl get deployment/demoappv10 -n demoapp
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
demoappv10   0/1     0            0           2m42s
deployment正常创建,pod没有正常创建。

查看deployment报错信息

# kubectl get deployment/demoappv10 -n demoapp -o jsonpath={".status.conditions"} | jq
[
  {
    "lastTransitionTime": "2023-08-08T06:38:34Z",
    "lastUpdateTime": "2023-08-08T06:38:34Z",
    "message": "Created new replica set \"demoappv10-8644b5fff4\"",
    "reason": "NewReplicaSetCreated",
    "status": "True",
    "type": "Progressing"
  },
  {
    "lastTransitionTime": "2023-08-08T06:38:34Z",
    "lastUpdateTime": "2023-08-08T06:38:34Z",
    "message": "Deployment does not have minimum availability.",
    "reason": "MinimumReplicasUnavailable",
    "status": "False",
    "type": "Available"
  },
  {
    "lastTransitionTime": "2023-08-08T06:38:34Z",
    "lastUpdateTime": "2023-08-08T06:38:34Z",
    "message": "pods \"demoappv10-8644b5fff4-tcctm\" is forbidden: [maximum cpu usage per Container is 2, but limit is 3, maximum cpu usage per Pod is 500m, but limit is 3, maximum memory usage per Pod is 512Mi, but limit is 1073741824]",   # 报错信息
    "reason": "FailedCreate",
    "status": "True",
    "type": "ReplicaFailure"
  }
]
maximum cpu usage per Container is 2, but limit is 3。 该错误信息是type: Container 的limits信息。
maximum cpu usage per Pod is 500m, but limit is 3。    该错误信息是type: pod 的limits信息。
由此可见对同一个计算资源限制进行分别定义,都需要满足要求后pod才可创建成功。

注意事项

需要注意的是,LimitRange生效于名称空间级别,它需要定义在每个名称空间之上;另外,定义的限制仅对该资源创建后的Pod和PVC资源创建请求有效,对之前已然存在的资源无效;再者,不建议在生效于同一名称空间的多个LimitRange资源上,对同一个计算资源限制进行分别定义,以免产生歧义或导致冲突。

参考文档

https://kubernetes.io/zh-cn/docs/concepts/policy/limit-range/