1 - 限制范围
默认情况下, Kubernetes 集群上的容器运行使用的计算资源没有限制。 使用资源配额,集群管理员可以以名字空间为单位,限制其资源的使用与创建。 在命名空间中,一个 Pod 或 Container 最多能够使用命名空间的资源配额所定义的 CPU 和内存用量。 有人担心,一个 Pod 或 Container 会垄断所有可用的资源。 LimitRange 是在命名空间内限制资源分配(给多个 Pod 或 Container)的策略对象。
一个 LimitRange(限制范围) 对象提供的限制能够做到:
- 在一个命名空间中实施对每个 Pod 或 Container 最小和最大的资源使用量的限制。
- 在一个命名空间中实施对每个 PersistentVolumeClaim 能申请的最小和最大的存储空间大小的限制。
- 在一个命名空间中实施对一种资源的申请值和限制值的比值的控制。
- 设置一个命名空间中对计算资源的默认申请/限制值,并且自动的在运行时注入到多个 Container 中。
启用 LimitRange
对 LimitRange 的支持自 Kubernetes 1.10 版本默认启用。
LimitRange 支持在很多 Kubernetes 发行版本中也是默认启用的。
LimitRange 的名称必须是合法的 DNS 子域名。
限制范围总览
- 管理员在一个命名空间内创建一个
LimitRange
对象。 - 用户在命名空间内创建 Pod ,Container 和 PersistentVolumeClaim 等资源。
LimitRanger
准入控制器对所有没有设置计算资源需求的 Pod 和 Container 设置默认值与限制值, 并跟踪其使用量以保证没有超出命名空间中存在的任意 LimitRange 对象中的最小、最大资源使用量以及使用量比值。- 若创建或更新资源(Pod、 Container、PersistentVolumeClaim)违反了 LimitRange 的约束,
向 API 服务器的请求会失败,并返回 HTTP 状态码
403 FORBIDDEN
与描述哪一项约束被违反的消息。 - 若命名空间中的 LimitRange 启用了对
cpu
和memory
的限制, 用户必须指定这些值的需求使用量与限制使用量。否则,系统将会拒绝创建 Pod。 - LimitRange 的验证仅在 Pod 准入阶段进行,不对正在运行的 Pod 进行验证。
能够使用限制范围创建的策略示例有:
- 在一个有两个节点,8 GiB 内存与16个核的集群中,限制一个命名空间的 Pod 申请 100m 单位,最大 500m 单位的 CPU,以及申请 200Mi,最大 600Mi 的内存。
- 为 spec 中没有 cpu 和内存需求值的 Container 定义默认 CPU 限制值与需求值 150m,内存默认需求值 300Mi。
在命名空间的总限制值小于 Pod 或 Container 的限制值的总和的情况下,可能会产生资源竞争。 在这种情况下,将不会创建 Container 或 Pod。
竞争和对 LimitRange 的改变都不会影响任何已经创建了的资源。
接下来
参阅 LimitRanger 设计文档获取更多信息。
关于使用限值的例子,可参看
2 - 资源配额
当多个用户或团队共享具有固定节点数目的集群时,人们会担心有人使用超过其基于公平原则所分配到的资源量。
资源配额是帮助管理员解决这一问题的工具。
资源配额,通过 ResourceQuota
对象来定义,对每个命名空间的资源消耗总量提供限制。
它可以限制命名空间中某种类型的对象的总数目上限,也可以限制命令空间中的 Pod 可以使用的计算资源的总上限。
资源配额的工作方式如下:
不同的团队可以在不同的命名空间下工作,目前这是非约束性的,在未来的版本中可能会通过 ACL (Access Control List 访问控制列表) 来实现强制性约束。
集群管理员可以为每个命名空间创建一个或多个 ResourceQuota 对象。
当用户在命名空间下创建资源(如 Pod、Service 等)时,Kubernetes 的配额系统会 跟踪集群的资源使用情况,以确保使用的资源用量不超过 ResourceQuota 中定义的硬性资源限额。
如果资源创建或者更新请求违反了配额约束,那么该请求会报错(HTTP 403 FORBIDDEN), 并在消息中给出有可能违反的约束。
如果命名空间下的计算资源 (如
cpu
和memory
)的配额被启用,则用户必须为 这些资源设定请求值(request)和约束值(limit),否则配额系统将拒绝 Pod 的创建。 提示: 可使用LimitRanger
准入控制器来为没有设置计算资源需求的 Pod 设置默认值。若想避免这类问题,请参考 演练示例。
ResourceQuota 对象的名称必须是合法的 DNS 子域名。
下面是使用命名空间和配额构建策略的示例:
- 在具有 32 GiB 内存和 16 核 CPU 资源的集群中,允许 A 团队使用 20 GiB 内存 和 10 核的 CPU 资源, 允许 B 团队使用 10 GiB 内存和 4 核的 CPU 资源,并且预留 2 GiB 内存和 2 核的 CPU 资源供将来分配。
- 限制 "testing" 命名空间使用 1 核 CPU 资源和 1GiB 内存。允许 "production" 命名空间使用任意数量。
在集群容量小于各命名空间配额总和的情况下,可能存在资源竞争。资源竞争时,Kubernetes 系统会遵循先到先得的原则。
不管是资源竞争还是配额的修改,都不会影响已经创建的资源使用对象。
启用资源配额
资源配额的支持在很多 Kubernetes 版本中是默认启用的。
当 API 服务器
的命令行标志 --enable-admission-plugins=
中包含 ResourceQuota
时,
资源配额会被启用。
当命名空间中存在一个 ResourceQuota 对象时,对于该命名空间而言,资源配额就是开启的。
计算资源配额
用户可以对给定命名空间下的可被请求的 计算资源 总量进行限制。
配额机制所支持的资源类型:
资源名称 | 描述 |
---|---|
limits.cpu | 所有非终止状态的 Pod,其 CPU 限额总量不能超过该值。 |
limits.memory | 所有非终止状态的 Pod,其内存限额总量不能超过该值。 |
requests.cpu | 所有非终止状态的 Pod,其 CPU 需求总量不能超过该值。 |
requests.memory | 所有非终止状态的 Pod,其内存需求总量不能超过该值。 |
hugepages-<size> | 对于所有非终止状态的 Pod,针对指定尺寸的巨页请求总数不能超过此值。 |
cpu | 与 requests.cpu 相同。 |
memory | 与 requests.memory 相同。 |
扩展资源的资源配额
除上述资源外,在 Kubernetes 1.10 版本中,还添加了对 扩展资源 的支持。
由于扩展资源不可超量分配,因此没有必要在配额中为同一扩展资源同时指定 requests
和 limits
。
对于扩展资源而言,目前仅允许使用前缀为 requests.
的配额项。
以 GPU 拓展资源为例,如果资源名称为 nvidia.com/gpu
,并且要将命名空间中请求的 GPU
资源总数限制为 4,则可以如下定义配额:
requests.nvidia.com/gpu: 4
有关更多详细信息,请参阅查看和设置配额。
存储资源配额
用户可以对给定命名空间下的存储资源 总量进行限制。
此外,还可以根据相关的存储类(Storage Class)来限制存储资源的消耗。
资源名称 | 描述 |
---|---|
requests.storage | 所有 PVC,存储资源的需求总量不能超过该值。 |
persistentvolumeclaims | 在该命名空间中所允许的 PVC 总量。 |
<storage-class-name>.storageclass.storage.k8s.io/requests.storage | 在所有与 <storage-class-name> 相关的持久卷申领中,存储请求的总和不能超过该值。 |
<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims | 在与 storage-class-name 相关的所有持久卷申领中,命名空间中可以存在的持久卷申领总数。 |
例如,如果一个操作人员针对 gold
存储类型与 bronze
存储类型设置配额,
操作人员可以定义如下配额:
gold.storageclass.storage.k8s.io/requests.storage: 500Gi
bronze.storageclass.storage.k8s.io/requests.storage: 100Gi
在 Kubernetes 1.8 版本中,本地临时存储的配额支持已经是 Alpha 功能:
资源名称 | 描述 |
---|---|
requests.ephemeral-storage | 在命名空间的所有 Pod 中,本地临时存储请求的总和不能超过此值。 |
limits.ephemeral-storage | 在命名空间的所有 Pod 中,本地临时存储限制值的总和不能超过此值。 |
ephemeral-storage | 与 requests.ephemeral-storage 相同。 |
说明:如果所使用的是 CRI 容器运行时,容器日志会被计入临时存储配额。 这可能会导致存储配额耗尽的 Pods 被意外地驱逐出节点。 参考日志架构 了解详细信息。
对象数量配额
你可以使用以下语法对所有标准的、命名空间域的资源类型进行配额设置:
count/<resource>.<group>
:用于非核心(core)组的资源count/<resource>
:用于核心组的资源
这是用户可能希望利用对象计数配额来管理的一组资源示例。
count/persistentvolumeclaims
count/services
count/secrets
count/configmaps
count/replicationcontrollers
count/deployments.apps
count/replicasets.apps
count/statefulsets.apps
count/jobs.batch
count/cronjobs.batch
相同语法也可用于自定义资源。
例如,要对 example.com
API 组中的自定义资源 widgets
设置配额,请使用
count/widgets.example.com
。
当使用 count/*
资源配额时,如果对象存在于服务器存储中,则会根据配额管理资源。
这些类型的配额有助于防止存储资源耗尽。例如,用户可能想根据服务器的存储能力来对服务器中
Secret 的数量进行配额限制。
集群中存在过多的 Secret 实际上会导致服务器和控制器无法启动。
用户可以选择对 Job 进行配额管理,以防止配置不当的 CronJob 在某命名空间中创建太多
Job 而导致集群拒绝服务。
对有限的一组资源上实施一般性的对象数量配额也是可能的。 此外,还可以进一步按资源的类型设置其配额。
支持以下类型:
资源名称 | 描述 |
---|---|
configmaps | 在该命名空间中允许存在的 ConfigMap 总数上限。 |
persistentvolumeclaims | 在该命名空间中允许存在的 PVC 的总数上限。 |
pods | 在该命名空间中允许存在的非终止状态的 Pod 总数上限。Pod 终止状态等价于 Pod 的 .status.phase in (Failed, Succeeded) 为真。 |
replicationcontrollers | 在该命名空间中允许存在的 ReplicationController 总数上限。 |
resourcequotas | 在该命名空间中允许存在的 ResourceQuota 总数上限。 |
services | 在该命名空间中允许存在的 Service 总数上限。 |
services.loadbalancers | 在该命名空间中允许存在的 LoadBalancer 类型的 Service 总数上限。 |
services.nodeports | 在该命名空间中允许存在的 NodePort 类型的 Service 总数上限。 |
secrets | 在该命名空间中允许存在的 Secret 总数上限。 |
例如,pods
配额统计某个命名空间中所创建的、非终止状态的 Pod
个数并确保其不超过某上限值。
用户可能希望在某命名空间中设置 pods
配额,以避免有用户创建很多小的 Pod,
从而耗尽集群所能提供的 Pod IP 地址。
配额作用域
每个配额都有一组相关的 scope
(作用域),配额只会对作用域内的资源生效。
配额机制仅统计所列举的作用域的交集中的资源用量。
当一个作用域被添加到配额中后,它会对作用域相关的资源数量作限制。 如配额中指定了允许(作用域)集合之外的资源,会导致验证错误。
作用域 | 描述 |
---|---|
Terminating | 匹配所有 spec.activeDeadlineSeconds 不小于 0 的 Pod。 |
NotTerminating | 匹配所有 spec.activeDeadlineSeconds 是 nil 的 Pod。 |
BestEffort | 匹配所有 Qos 是 BestEffort 的 Pod。 |
NotBestEffort | 匹配所有 Qos 不是 BestEffort 的 Pod。 |
PriorityClass | 匹配所有引用了所指定的优先级类的 Pods。 |
CrossNamespacePodAffinity | 匹配那些设置了跨名字空间 (反)亲和性条件的 Pod。 |
BestEffort
作用域限制配额跟踪以下资源:
pods
Terminating
、NotTerminating
、NotBestEffort
和 PriorityClass
这些作用域限制配额跟踪以下资源:
pods
cpu
memory
requests.cpu
requests.memory
limits.cpu
limits.memory
需要注意的是,你不可以在同一个配额对象中同时设置 Terminating
和 NotTerminating
作用域,你也不可以在同一个配额中同时设置 BestEffort
和 NotBestEffort
作用域。
scopeSelector
支持在 operator
字段中使用以下值:
In
NotIn
Exists
DoesNotExist
定义 scopeSelector
时,如果使用以下值之一作为 scopeName
的值,则对应的
operator
只能是 Exists
。
Terminating
NotTerminating
BestEffort
NotBestEffort
如果 operator
是 In
或 NotIn
之一,则 values
字段必须至少包含一个值。
例如:
scopeSelector:
matchExpressions:
- scopeName: PriorityClass
operator: In
values:
- middle
如果 operator
为 Exists
或 DoesNotExist
,则不可以设置 values
字段。
基于优先级类(PriorityClass)来设置资源配额
Kubernetes v1.17 [stable]
Pod 可以创建为特定的优先级。
通过使用配额规约中的 scopeSelector
字段,用户可以根据 Pod 的优先级控制其系统资源消耗。
仅当配额规范中的 scopeSelector
字段选择到某 Pod 时,配额机制才会匹配和计量 Pod 的资源消耗。
如果配额对象通过 scopeSelector
字段设置其作用域为优先级类,则配额对象只能
跟踪以下资源:
pods
cpu
memory
ephemeral-storage
limits.cpu
limits.memory
limits.ephemeral-storage
requests.cpu
requests.memory
requests.ephemeral-storage
本示例创建一个配额对象,并将其与具有特定优先级的 Pod 进行匹配。 该示例的工作方式如下:
- 集群中的 Pod 可取三个优先级类之一,即 "low"、"medium"、"high"。
- 为每个优先级创建一个配额对象。
将以下 YAML 保存到文件 quota.yml
中。
apiVersion: v1
kind: List
items:
- apiVersion: v1
kind: ResourceQuota
metadata:
name: pods-high
spec:
hard:
cpu: "1000"
memory: 200Gi
pods: "10"
scopeSelector:
matchExpressions:
- operator : In
scopeName: PriorityClass
values: ["high"]
- apiVersion: v1
kind: ResourceQuota
metadata:
name: pods-medium
spec:
hard:
cpu: "10"
memory: 20Gi
pods: "10"
scopeSelector:
matchExpressions:
- operator : In
scopeName: PriorityClass
values: ["medium"]
- apiVersion: v1
kind: ResourceQuota
metadata:
name: pods-low
spec:
hard:
cpu: "5"
memory: 10Gi
pods: "10"
scopeSelector:
matchExpressions:
- operator : In
scopeName: PriorityClass
values: ["low"]
使用 kubectl create
命令运行以下操作。
kubectl create -f ./quota.yml
resourcequota/pods-high created
resourcequota/pods-medium created
resourcequota/pods-low created
使用 kubectl describe quota
操作验证配额的 Used
值为 0
。
kubectl describe quota
Name: pods-high
Namespace: default
Resource Used Hard
-------- ---- ----
cpu 0 1k
memory 0 200Gi
pods 0 10
Name: pods-low
Namespace: default
Resource Used Hard
-------- ---- ----
cpu 0 5
memory 0 10Gi
pods 0 10
Name: pods-medium
Namespace: default
Resource Used Hard
-------- ---- ----
cpu 0 10
memory 0 20Gi
pods 0 10
创建优先级为 "high" 的 Pod。
将以下 YAML 保存到文件 high-priority-pod.yml
中。
apiVersion: v1
kind: Pod
metadata:
name: high-priority
spec:
containers:
- name: high-priority
image: ubuntu
command: ["/bin/sh"]
args: ["-c", "while true; do echo hello; sleep 10;done"]
resources:
requests:
memory: "10Gi"
cpu: "500m"
limits:
memory: "10Gi"
cpu: "500m"
priorityClassName: high
使用 kubectl create
运行以下操作。
kubectl create -f ./high-priority-pod.yml
确认 "high" 优先级配额 pods-high
的 "Used" 统计信息已更改,并且其他两个配额未更改。
kubectl describe quota
Name: pods-high
Namespace: default
Resource Used Hard
-------- ---- ----
cpu 500m 1k
memory 10Gi 200Gi
pods 1 10
Name: pods-low
Namespace: default
Resource Used Hard
-------- ---- ----
cpu 0 5
memory 0 10Gi
pods 0 10
Name: pods-medium
Namespace: default
Resource Used Hard
-------- ---- ----
cpu 0 10
memory 0 20Gi
pods 0 10
跨名字空间的 Pod 亲和性配额
Kubernetes v1.21 [alpha]
集群运维人员可以使用 CrossNamespacePodAffinity
配额作用域来
限制哪个名字空间中可以存在包含跨名字空间亲和性规则的 Pod。
更为具体一点,此作用域用来配置哪些 Pod 可以在其 Pod 亲和性规则
中设置 namespaces
或 namespaceSelector
字段。
禁止用户使用跨名字空间的亲和性规则可能是一种被需要的能力,因为带有 反亲和性约束的 Pod 可能会阻止所有其他名字空间的 Pod 被调度到某失效域中。
使用此作用域操作符可以避免某些名字空间(例如下面例子中的 foo-ns
)运行
特别的 Pod,这类 Pod 使用跨名字空间的 Pod 亲和性约束,在该名字空间中创建
了作用域为 CrossNamespaceAffinity
的、硬性约束为 0 的资源配额对象。
apiVersion: v1
kind: ResourceQuota
metadata:
name: disable-cross-namespace-affinity
namespace: foo-ns
spec:
hard:
pods: "0"
scopeSelector:
matchExpressions:
- scopeName: CrossNamespaceAffinity
如果集群运维人员希望默认禁止使用 namespaces
和 namespaceSelector
,而
仅仅允许在特定名字空间中这样做,他们可以将 CrossNamespaceAffinity
作为一个
被约束的资源。方法是为 kube-apiserver
设置标志
--admission-control-config-file
,使之指向如下的配置文件:
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: "ResourceQuota"
configuration:
apiVersion: apiserver.config.k8s.io/v1
kind: ResourceQuotaConfiguration
limitedResources:
- resource: pods
matchScopes:
- scopeName: CrossNamespaceAffinity
基于上面的配置,只有名字空间中包含作用域为 CrossNamespaceAffinity
且
硬性约束大于或等于使用 namespaces
和 namespaceSelector
字段的 Pods
个数时,才可以在该名字空间中继续创建在其 Pod 亲和性规则中设置 namespaces
或 namespaceSelector
的新 Pod。
此功能特性处于 Alpha 阶段,默认被禁用。你可以通过为 kube-apiserver 和
kube-scheduler 设置
特性门控
PodAffinityNamespaceSelector
来启用此特性。
请求与限制的比较
分配计算资源时,每个容器可以为 CPU 或内存指定请求和约束。 配额可以针对二者之一进行设置。
如果配额中指定了 requests.cpu
或 requests.memory
的值,则它要求每个容器都显式给出对这些资源的请求。
同理,如果配额中指定了 limits.cpu
或 limits.memory
的值,那么它要求每个容器都显式设定对应资源的限制。
查看和设置配额
Kubectl 支持创建、更新和查看配额:
kubectl create namespace myspace
cat <<EOF > compute-resources.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-resources
spec:
hard:
requests.cpu: "1"
requests.memory: 1Gi
limits.cpu: "2"
limits.memory: 2Gi
requests.nvidia.com/gpu: 4
EOF
kubectl create -f ./compute-resources.yaml --namespace=myspace
cat <<EOF > object-counts.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: object-counts
spec:
hard:
configmaps: "10"
persistentvolumeclaims: "4"
pods: "4"
replicationcontrollers: "20"
secrets: "10"
services: "10"
services.loadbalancers: "2"
EOF
kubectl create -f ./object-counts.yaml --namespace=myspace
kubectl get quota --namespace=myspace
NAME AGE
compute-resources 30s
object-counts 32s
kubectl describe quota compute-resources --namespace=myspace
Name: compute-resources
Namespace: myspace
Resource Used Hard
-------- ---- ----
limits.cpu 0 2
limits.memory 0 2Gi
requests.cpu 0 1
requests.memory 0 1Gi
requests.nvidia.com/gpu 0 4
kubectl describe quota object-counts --namespace=myspace
Name: object-counts
Namespace: myspace
Resource Used Hard
-------- ---- ----
configmaps 0 10
persistentvolumeclaims 0 4
pods 0 4
replicationcontrollers 0 20
secrets 1 10
services 0 10
services.loadbalancers 0 2
kubectl 还使用语法 count/<resource>.<group>
支持所有标准的、命名空间域的资源的对象计数配额:
kubectl create namespace myspace
kubectl create quota test --hard=count/deployments.apps=2,count/replicasets.apps=4,count/pods=3,count/secrets=4 --namespace=myspace
kubectl create deployment nginx --image=nginx --namespace=myspace --replicas=2
kubectl describe quota --namespace=myspace
Name: test
Namespace: myspace
Resource Used Hard
-------- ---- ----
count/deployments.apps 1 2
count/pods 2 3
count/replicasets.apps 1 4
count/secrets 1 4
配额和集群容量
ResourceQuota 与集群资源总量是完全独立的。它们通过绝对的单位来配置。 所以,为集群添加节点时,资源配额不会自动赋予每个命名空间消耗更多资源的能力。
有时可能需要资源配额支持更复杂的策略,比如:
- 在几个团队中按比例划分总的集群资源。
- 允许每个租户根据需要增加资源使用量,但要有足够的限制以防止资源意外耗尽。
- 探测某个命名空间的需求,添加物理节点并扩大资源配额值。
这些策略可以通过将资源配额作为一个组成模块、手动编写一个控制器来监控资源使用情况, 并结合其他信号调整命名空间上的硬性资源配额来实现。
注意:资源配额对集群资源总体进行划分,但它对节点没有限制:来自不同命名空间的 Pod 可能在同一节点上运行。
默认情况下限制特定优先级的资源消耗
有时候可能希望当且仅当某名字空间中存在匹配的配额对象时,才可以创建特定优先级 (例如 "cluster-services")的 Pod。
通过这种机制,操作人员能够将限制某些高优先级类仅出现在有限数量的命名空间中, 而并非每个命名空间默认情况下都能够使用这些优先级类。
要实现此目的,应设置 kube-apiserver 的标志 --admission-control-config-file
指向如下配置文件:
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: "ResourceQuota"
configuration:
apiVersion: apiserver.config.k8s.io/v1
kind: ResourceQuotaConfiguration
limitedResources:
- resource: pods
matchScopes:
- scopeName: PriorityClass
operator: In
values: ["cluster-services"]
现在在 kube-system
名字空间中创建一个资源配额对象:
apiVersion: v1
kind: ResourceQuota
metadata:
name: pods-cluster-services
spec:
scopeSelector:
matchExpressions:
- operator : In
scopeName: PriorityClass
values: ["cluster-services"]
kubectl apply -f https://k8s.io/examples/policy/priority-class-resourcequota.yaml -n kube-system
resourcequota/pods-cluster-services created
在这里,当以下条件满足时可以创建 Pod:
- Pod 未设置
priorityClassName
- Pod 的
priorityClassName
设置值不是cluster-services
- Pod 的
priorityClassName
设置值为cluster-services
,它将被创建于kube-system
名字空间中,并且它已经通过了资源配额检查。
如果 Pod 的 priorityClassName
设置为 cluster-services
,但要被创建到
kube-system
之外的别的名字空间,则 Pod 创建请求也被拒绝。
接下来
- 查看资源配额设计文档
- 查看如何使用资源配额的详细示例。
- 阅读优先级类配额支持的设计文档。 了解更多信息。
- 参阅 LimitedResources
3 - Pod 安全策略
Kubernetes v1.21 [deprecated]
PodSecurityPolicy 在 Kubernetes v1.21 版本中被弃用,将在 v1.25 中删除。
Pod 安全策略使得对 Pod 创建和更新进行细粒度的权限控制成为可能。
什么是 Pod 安全策略?
Pod 安全策略(Pod Security Policy) 是集群级别的资源,它能够控制 Pod 规约 中与安全性相关的各个方面。 PodSecurityPolicy 对象定义了一组 Pod 运行时必须遵循的条件及相关字段的默认值,只有 Pod 满足这些条件 才会被系统接受。 Pod 安全策略允许管理员控制如下方面:
控制的角度 | 字段名称 |
---|---|
运行特权容器 | privileged |
使用宿主名字空间 | hostPID 、hostIPC |
使用宿主的网络和端口 | hostNetwork , hostPorts |
控制卷类型的使用 | volumes |
使用宿主文件系统 | allowedHostPaths |
允许使用特定的 FlexVolume 驱动 | allowedFlexVolumes |
分配拥有 Pod 卷的 FSGroup 账号 | fsGroup |
以只读方式访问根文件系统 | readOnlyRootFilesystem |
设置容器的用户和组 ID | runAsUser , runAsGroup , supplementalGroups |
限制 root 账号特权级提升 | allowPrivilegeEscalation , defaultAllowPrivilegeEscalation |
Linux 权能字(Capabilities) | defaultAddCapabilities , requiredDropCapabilities , allowedCapabilities |
设置容器的 SELinux 上下文 | seLinux |
指定容器可以挂载的 proc 类型 | allowedProcMountTypes |
指定容器使用的 AppArmor 模版 | annotations |
指定容器使用的 seccomp 模版 | annotations |
指定容器使用的 sysctl 模版 | forbiddenSysctls ,allowedUnsafeSysctls |
Pod 安全策略 由设置和策略组成,它们能够控制 Pod 访问的安全特征。这些设置分为如下三类:
- 基于布尔值控制 :这种类型的字段默认为最严格限制的值。
- 基于被允许的值集合控制 :这种类型的字段会与这组值进行对比,以确认值被允许。
- 基于策略控制 :设置项通过一种策略提供的机制来生成该值,这种机制能够确保指定的值落在被允许的这组值中。
启用 Pod 安全策略
Pod 安全策略实现为一种可选(但是建议启用)的 准入控制器。 启用了准入控制器 即可强制实施 Pod 安全策略,不过如果没有授权认可策略之前即启用 准入控制器 将导致集群中无法创建任何 Pod。
由于 Pod 安全策略 API(policy/v1beta1/podsecuritypolicy
)是独立于准入控制器
来启用的,对于现有集群而言,建议在启用准入控制器之前先添加策略并对其授权。
授权策略
PodSecurityPolicy 资源被创建时,并不执行任何操作。为了使用该资源,需要对
发出请求的用户或者目标 Pod 的
服务账号
授权,通过允许其对策略执行 use
动词允许其使用该策略。
大多数 Kubernetes Pod 不是由用户直接创建的。相反,这些 Pod 是由 Deployment、 ReplicaSet 或者经由控制器管理器模版化的控制器创建。 赋予控制器访问策略的权限意味着对应控制器所创建的 所有 Pod 都可访问策略。 因此,对策略进行授权的优先方案是为 Pod 的服务账号授予访问权限 (参见示例)。
通过 RBAC 授权
RBAC 是一种标准的 Kubernetes 鉴权模式,可以很容易地用来授权策略访问。
首先,某 Role
或 ClusterRole
需要获得使用 use
访问目标策略的权限。
访问授权的规则看起来像这样:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: <Role 名称>
rules:
- apiGroups: ['policy']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames:
- <要授权的策略列表>
接下来将该 Role
(或 ClusterRole
)绑定到授权的用户:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: <绑定名称>
roleRef:
kind: ClusterRole
name: <角色名称>
apiGroup: rbac.authorization.k8s.io
subjects:
# 授权特定的服务账号
- kind: ServiceAccount
name: <要授权的服务账号名称>
namespace: <authorized pod namespace>
# 授权特定的用户(不建议这样操作)
- kind: User
apiGroup: rbac.authorization.k8s.io
name: <要授权的用户名>
如果使用的是 RoleBinding
(而不是 ClusterRoleBinding
),授权仅限于
与该 RoleBinding
处于同一名字空间中的 Pods。
可以考虑将这种授权模式和系统组结合,对名字空间中的所有 Pod 授予访问权限。
# 授权某名字空间中所有服务账号
- kind: Group
apiGroup: rbac.authorization.k8s.io
name: system:serviceaccounts
# 或者与之等价,授权给某名字空间中所有被认证过的用户
- kind: Group
apiGroup: rbac.authorization.k8s.io
name: system:authenticated
参阅角色绑定示例 查看 RBAC 绑定的更多实例。 参阅下文,查看对 PodSecurityPolicy 进行授权的完整示例。
故障排查
- 控制器管理器组件 必须运行在 安全的 API 端口, 并且一定不能具有超级用户权限。 否则其请求会绕过身份认证和鉴权模块控制,从而导致所有 PodSecurityPolicy 对象 都被启用,用户亦能创建特权容器。 关于配置控制器管理器鉴权相关的详细信息,可参阅 控制器角色。
策略顺序
除了限制 Pod 创建与更新,Pod 安全策略也可用来为其所控制的很多字段 设置默认值。当存在多个策略对象时,Pod 安全策略控制器依据以下条件选择 策略:
- 优先考虑允许 Pod 保持原样,不会更改 Pod 字段默认值或其他配置的 PodSecurityPolicy。 这类非更改性质的 PodSecurityPolicy 对象之间的顺序无关紧要。
- 如果必须要为 Pod 设置默认值或者其他配置,(按名称顺序)选择第一个允许 Pod 操作的 PodSecurityPolicy 对象。
说明: 在更新操作期间(这时不允许更改 Pod 规约),仅使用非更改性质的 PodSecurityPolicy 来对 Pod 执行验证操作。
示例
本示例假定你已经有一个启动了 PodSecurityPolicy 准入控制器的集群并且 你拥有集群管理员特权。
配置
为运行此示例,配置一个名字空间和一个服务账号。我们将用这个服务账号来 模拟一个非管理员账号的用户。
kubectl create namespace psp-example
kubectl create serviceaccount -n psp-example fake-user
kubectl create rolebinding -n psp-example fake-editor --clusterrole=edit --serviceaccount=psp-example:fake-user
创建两个别名,以更清晰地展示我们所使用的用户账号,同时减少一些键盘输入:
alias kubectl-admin='kubectl -n psp-example'
alias kubectl-user='kubectl --as=system:serviceaccount:psp-example:fake-user -n psp-example'
创建一个策略和一个 Pod
在一个文件中定义一个示例的 PodSecurityPolicy 对象。 这里的策略只是用来禁止创建有特权要求的 Pods。 PodSecurityPolicy 对象的名称必须是合法的 DNS 子域名。
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: example
spec:
privileged: false # Don't allow privileged pods!
# The rest fills in some required fields.
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
runAsUser:
rule: RunAsAny
fsGroup:
rule: RunAsAny
volumes:
- '*'
使用 kubectl 执行创建操作:
kubectl-admin create -f example-psp.yaml
现在,作为一个非特权用户,尝试创建一个简单的 Pod:
kubectl-user create -f- <<EOF
apiVersion: v1
kind: Pod
metadata:
name: pause
spec:
containers:
- name: pause
image: k8s.gcr.io/pause
EOF
输出类似于:
Error from server (Forbidden): error when creating "STDIN": pods "pause" is forbidden: unable to validate against any pod security policy: []
发生了什么? 尽管 PodSecurityPolicy 被创建,Pod 的服务账号或者
fake-user
用户都没有使用该策略的权限。
kubectl-user auth can-i use podsecuritypolicy/example
no
创建角色绑定,赋予 fake-user
使用 use
访问示例策略的权限:
说明: 不建议使用这种方法! 欲了解优先考虑的方法,请参见下节。
kubectl-admin create role psp:unprivileged \
--verb=use \
--resource=podsecuritypolicy \
--resource-name=example
输出:
role "psp:unprivileged" created
kubectl-admin create rolebinding fake-user:psp:unprivileged \
--role=psp:unprivileged \
--serviceaccount=psp-example:fake-user
输出:
rolebinding "fake-user:psp:unprivileged" created
kubectl-user auth can-i use podsecuritypolicy/example
输出:
yes
现在重试创建 Pod:
kubectl-user create -f- <<EOF
apiVersion: v1
kind: Pod
metadata:
name: pause
spec:
containers:
- name: pause
image: k8s.gcr.io/pause
EOF
输出类似于:
pod "pause" created
此次尝试不出所料地成功了! 不过任何创建特权 Pod 的尝试还是会被拒绝:
kubectl-user create -f- <<EOF
apiVersion: v1
kind: Pod
metadata:
name: privileged
spec:
containers:
- name: pause
image: k8s.gcr.io/pause
securityContext:
privileged: true
EOF
输出类似于:
Error from server (Forbidden): error when creating "STDIN": pods "privileged" is forbidden: unable to validate against any pod security policy: [spec.containers[0].securityContext.privileged: Invalid value: true: Privileged containers are not allowed]
继续此例之前先删除该 Pod:
kubectl-user delete pod pause
运行另一个 Pod
我们再试一次,稍微有些不同:
kubectl-user create deployment pause --image=k8s.gcr.io/pause
输出为:
deployment "pause" created
kubectl-user get pods
输出为:
No resources found.
kubectl-user get events | head -n 2
输出为:
LASTSEEN FIRSTSEEN COUNT NAME KIND SUBOBJECT TYPE REASON SOURCE MESSAGE
1m 2m 15 pause-7774d79b5 ReplicaSet Warning FailedCreate replicaset-controller Error creating: pods "pause-7774d79b5-" is forbidden: no providers available to validate pod request
发生了什么? 我们已经为用户 fake-user
绑定了 psp:unprivileged
角色,
为什么还会收到错误 Error creating: pods "pause-7774d79b5-" is forbidden: no providers available to validate pod request (创建错误:pods "pause-7774d79b5" 被禁止:没有可用来验证 pod 请求的驱动)
?
答案在于源文件 - replicaset-controller
。
fake-user
用户成功地创建了 Deployment,而后者也成功地创建了 ReplicaSet,
不过当 ReplicaSet 创建 Pod 时,发现未被授权使用示例 PodSecurityPolicy 资源。
为了修复这一问题,将 psp:unprivileged
角色绑定到 Pod 的服务账号。
在这里,因为我们没有给出服务账号名称,默认的服务账号是 default
。
kubectl-admin create rolebinding default:psp:unprivileged \
--role=psp:unprivileged \
--serviceaccount=psp-example:default
输出为:
rolebinding "default:psp:unprivileged" created
现在如果你给 ReplicaSet 控制器一分钟的时间来重试,该控制器最终将能够 成功地创建 Pod:
kubectl-user get pods --watch
输出类似于:
NAME READY STATUS RESTARTS AGE
pause-7774d79b5-qrgcb 0/1 Pending 0 1s
pause-7774d79b5-qrgcb 0/1 Pending 0 1s
pause-7774d79b5-qrgcb 0/1 ContainerCreating 0 1s
pause-7774d79b5-qrgcb 1/1 Running 0 2s
清理
删除名字空间即可清理大部分示例资源:
kubectl-admin delete ns psp-example
输出类似于:
namespace "psp-example" deleted
注意 PodSecurityPolicy
资源不是名字空间域的资源,必须单独清理:
kubectl-admin delete psp example
输出类似于:
podsecuritypolicy "example" deleted
示例策略
下面是一个你可以创建的约束性非常弱的策略,其效果等价于没有使用 Pod 安全 策略准入控制器:
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: privileged
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'
spec:
privileged: true
allowPrivilegeEscalation: true
allowedCapabilities:
- '*'
volumes:
- '*'
hostNetwork: true
hostPorts:
- min: 0
max: 65535
hostIPC: true
hostPID: true
runAsUser:
rule: 'RunAsAny'
seLinux:
rule: 'RunAsAny'
supplementalGroups:
rule: 'RunAsAny'
fsGroup:
rule: 'RunAsAny'
下面是一个具有约束性的策略,要求用户以非特权账号运行,禁止可能的向 root 权限 的升级,同时要求使用若干安全机制。
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restricted
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default,runtime/default'
apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
seccomp.security.alpha.kubernetes.io/defaultProfileName: 'runtime/default'
apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default'
spec:
privileged: false
# Required to prevent escalations to root.
allowPrivilegeEscalation: false
# This is redundant with non-root + disallow privilege escalation,
# but we can provide it for defense in depth.
requiredDropCapabilities:
- ALL
# Allow core volume types.
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
# Assume that persistentVolumes set up by the cluster admin are safe to use.
- 'persistentVolumeClaim'
hostNetwork: false
hostIPC: false
hostPID: false
runAsUser:
# Require the container to run without root privileges.
rule: 'MustRunAsNonRoot'
seLinux:
# This policy assumes the nodes are using AppArmor rather than SELinux.
rule: 'RunAsAny'
supplementalGroups:
rule: 'MustRunAs'
ranges:
# Forbid adding the root group.
- min: 1
max: 65535
fsGroup:
rule: 'MustRunAs'
ranges:
# Forbid adding the root group.
- min: 1
max: 65535
readOnlyRootFilesystem: false
更多的示例可参考 Pod 安全标准。
策略参考
Privileged
Privileged - 决定是否 Pod 中的某容器可以启用特权模式。 默认情况下,容器是不可以访问宿主上的任何设备的,不过一个“privileged(特权的)” 容器则被授权访问宿主上所有设备。 这种容器几乎享有宿主上运行的进程的所有访问权限。 对于需要使用 Linux 权能字(如操控网络堆栈和访问设备)的容器而言是有用的。
宿主名字空间
HostPID - 控制 Pod 中容器是否可以共享宿主上的进程 ID 空间。
注意,如果与 ptrace
相结合,这种授权可能被利用,导致向容器外的特权逃逸
(默认情况下 ptrace
是被禁止的)。
HostIPC - 控制 Pod 容器是否可共享宿主上的 IPC 名字空间。
HostNetwork - 控制是否 Pod 可以使用节点的网络名字空间。 此类授权将允许 Pod 访问本地回路(loopback)设备、在本地主机(localhost) 上监听的服务、还可能用来监听同一节点上其他 Pod 的网络活动。
HostPorts -提供可以在宿主网络名字空间中可使用的端口范围列表。
该属性定义为一组 HostPortRange
对象的列表,每个对象中包含
min
(含)与 max
(含)值的设置。
默认不允许访问宿主端口。
卷和文件系统
Volumes - 提供一组被允许的卷类型列表。可被允许的值对应于创建卷时可以
设置的卷来源。卷类型的完整列表可参见
卷类型。
此外, *
可以用来允许所有卷类型。
对于新的 Pod 安全策略设置而言,建议设置的卷类型的最小列表包含:
- configMap
- downwardAPI
- emptyDir
- persistentVolumeClaim
- secret
- projected
警告: PodSecurityPolicy 并不限制可以被PersistentVolumeClaim
所引用的PersistentVolume
对象的类型。 此外hostPath
类型的PersistentVolume
不支持只读访问模式。 应该仅赋予受信用户创建PersistentVolume
对象的访问权限。
FSGroup - 控制应用到某些卷上的附加用户组。
- MustRunAs - 要求至少指定一个
range
。 使用范围中的最小值作为默认值。所有 range 值都会被用来执行验证。 - MayRunAs - 要求至少指定一个
range
。 允许不设置FSGroups
,且无默认值。 如果FSGroup
被设置,则所有 range 值都会被用来执行验证检查。 - RunAsAny - 不提供默认值。允许设置任意
fsGroup
ID 值。
AllowedHostPaths - 设置一组宿主文件目录,这些目录项可以在 hostPath
卷中
使用。列表为空意味着对所使用的宿主目录没有限制。
此选项定义包含一个对象列表,表中对象包含 pathPrefix
字段,用来表示允许
hostPath
卷挂载以所指定前缀开头的路径。
对象中还包含一个 readOnly
字段,用来表示对应的卷必须以只读方式挂载。
例如:
allowedHostPaths:
# 下面的设置允许 "/foo"、"/foo/"、"/foo/bar" 等路径,但禁止
# "/fool"、"/etc/foo" 这些路径。
# "/foo/../" 总会被当作非法路径。
- pathPrefix: "/foo"
readOnly: true # 仅允许只读模式挂载
警告:容器如果对宿主文件系统拥有不受限制的访问权限,就可以有很多种方式提升自己的特权, 包括读取其他容器中的数据、滥用系统服务(如
kubelet
)的凭据信息等。由可写入的目录所构造的
hostPath
卷能够允许容器写入数据到宿主文件系统, 并且在写入时避开pathPrefix
所设置的目录限制。readOnly: true
这一设置在 Kubernetes 1.11 版本之后可用。 必须针对allowedHostPaths
中的 所有 条目设置此属性才能有效地限制容器 只能访问pathPrefix
所指定的目录。
ReadOnlyRootFilesystem - 要求容器必须以只读方式挂载根文件系统来运行 (即不允许存在可写入层)。
FlexVolume 驱动
此配置指定一个可以被 FlexVolume 卷使用的驱动程序的列表。
空的列表或者 nil 值意味着对驱动没有任何限制。
请确保volumes
字段包含了 flexVolume
卷类型,
否则所有 FlexVolume 驱动都被禁止。
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: allow-flex-volumes
spec:
# spec d的其他字段
volumes:
- flexVolume
allowedFlexVolumes:
- driver: example/lvm
- driver: example/cifs
用户和组
RunAsUser - 控制使用哪个用户 ID 来运行容器。
- MustRunAs - 必须配置一个
range
。使用该范围内的第一个值作为默认值。 所有 range 值都被用于验证检查。 - MustRunAsNonRoot - 要求提交的 Pod 具有非零
runAsUser
值,或在镜像中 (使用 UID 数值)定义了USER
环境变量。 如果 Pod 既没有设置runAsNonRoot
,也没有设置runAsUser
,则该 Pod 会被 修改以设置runAsNonRoot=true
,从而要求容器通过USER
指令给出非零的数值形式 的用户 ID。此配置没有默认值。采用此配置时,强烈建议设置allowPrivilegeEscalation=false
。 - RunAsAny - 没有提供默认值。允许指定任何
runAsUser
配置。
RunAsGroup - 控制运行容器时使用的主用户组 ID。
- MustRunAs - 要求至少指定一个
range
值。第一个 range 中的最小值作为默认值。所有 range 值都被用来执行验证检查。 - MayRunAs - 不要求设置
RunAsGroup
。 不过,如果指定了RunAsGroup
被设置,所设置值必须处于所定义的范围内。 - RunAsAny - 未指定默认值。允许
runAsGroup
设置任何值。
SupplementalGroups - 控制容器可以添加的组 ID。
- MustRunAs - 要求至少指定一个
range
值。 第一个 range 中的最小值用作默认值。 所有 range 值都被用来执行验证检查。 - MayRunAs - 要求至少指定一个
range
值。 允许不指定supplementalGroups
且不设置默认值。 如果supplementalGroups
被设置,则所有 range 值都被用来执行验证检查。 - RunAsAny - 未指定默认值。允许为
supplementalGroups
设置任何值。
特权提升
这一组选项控制容器的allowPrivilegeEscalation
属性。该属性直接决定是否为
容器进程设置
no_new_privs
参数。此参数会禁止 setuid
属性的可执行文件更改有效用户 ID(EUID),并且
禁止启用额外权能的文件。例如,no_new_privs
会禁止使用 ping
工具。
如果想有效地实施 MustRunAsNonRoot
控制,需要配置这一选项。
AllowPrivilegeEscalation - 决定是否用户可以将容器的安全上下文设置为
allowPrivilegeEscalation=true
。默认设置下,这样做是允许的,目的是避免
造成现有的 setuid
应用无法运行。将此选项设置为 false
可以确保容器的所有
子进程都无法获得比父进程更多的特权。
DefaultAllowPrivilegeEscalation - 为 allowPrivilegeEscalation
选项设置
默认值。不设置此选项时的默认行为是允许特权提升,以便运行 setuid 程序。
如果不希望运行 setuid 程序,可以使用此字段将选项的默认值设置为禁止,同时
仍然允许 Pod 显式地请求 allowPrivilegeEscalation
。
权能字
Linux 权能字(Capabilities)将传统上与超级用户相关联的特权作了细粒度的分解。 其中某些权能字可以用来提升特权,打破容器边界,可以通过 PodSecurityPolicy 来限制。关于 Linux 权能字的更多细节,可参阅 capabilities(7)。
下列字段都可以配置为权能字的列表。表中的每一项都是 ALL_CAPS
中的一个权能字
名称,只是需要去掉 CAP_
前缀。
AllowedCapabilities - 给出可以被添加到容器的权能字列表。
默认的权能字集合是被隐式允许的那些。空集合意味着只能使用默认权能字集合,
不允许添加额外的权能字。*
可以用来设置允许所有权能字。
RequiredDropCapabilities - 必须从容器中去除的权能字。
所给的权能字会从默认权能字集合中去除,并且一定不可以添加。
RequiredDropCapabilities
中列举的权能字不能出现在
AllowedCapabilities
或 DefaultAddCapabilities
所给的列表中。
DefaultAddCapabilities - 默认添加到容器的权能字集合。 这一集合是作为容器运行时所设值的补充。 关于使用 Docker 容器运行引擎时默认的权能字列表,可参阅 Docker 文档。
SELinux
- MustRunAs - 要求必须配置
seLinuxOptions
。默认使用seLinuxOptions
。 针对seLinuxOptions
所给值执行验证检查。 - RunAsAny - 没有提供默认值。允许任意指定的
seLinuxOptions
选项。
AllowedProcMountTypes
allowedProcMountTypes
是一组可以允许的 proc 挂载类型列表。
空表或者 nil 值表示只能使用 DefaultProcMountType
。
DefaultProcMount
使用容器运行时的默认值设置来决定 /proc
的只读挂载模式
和路径屏蔽。大多数容器运行时都会屏蔽 /proc
下面的某些路径以避免特殊设备或
信息被不小心暴露给容器。这一配置使所有 Default
字符串值来表示。
此外唯一的ProcMountType 是 UnmaskedProcMount
,意味着即将绕过容器运行时的
路径屏蔽行为,确保新创建的 /proc
不会被容器修改。此配置用字符串
Unmasked
来表示。
AppArmor
通过 PodSecurityPolicy 上的注解来控制。 详情请参阅 AppArmor 文档。
Seccomp
Pod 对 seccomp 模版的使用可以通过在 PodSecurityPolicy 上设置注解来控制。 Seccomp 是 Kubernetes 的一项 alpha 阶段特性。
seccomp.security.alpha.kubernetes.io/defaultProfileName - 注解用来 指定为容器配置默认的 seccomp 模版。可选值为:
unconfined
- 如果没有指定其他替代方案,Seccomp 不会被应用到容器进程上 (Kubernets 中的默认设置)。runtime/default
- 使用默认的容器运行时模版。docker/default
- 使用 Docker 的默认 seccomp 模版。自 1.11 版本废弃。 应改为使用runtime/default
。localhost/<路径名>
- 指定节点上路径<seccomp_root>/<路径名>
下的一个 文件作为其模版。其中<seccomp_root>
是通过kubelet
的标志--seccomp-profile-root
来指定的。
seccomp.security.alpha.kubernetes.io/allowedProfileNames - 指定可以为
Pod seccomp 注解配置的值的注解。取值为一个可用值的列表。
表中每项可以是上述各值之一,还可以是 *
,用来表示允许所有的模版。
如果没有设置此注解,意味着默认的 seccomp 模版是不可更改的。
Sysctl
默认情况下,所有的安全的 sysctl 都是被允许的。
forbiddenSysctls
- 用来排除某些特定的 sysctl。 你可以在此列表中禁止一些安全的或者不安全的 sysctl。 此选项设置为*
意味着禁止设置所有 sysctl。allowedUnsafeSysctls
- 用来启用那些被默认列表所禁用的 sysctl, 前提是所启用的 sysctl 没有被列在forbiddenSysctls
中。
参阅 Sysctl 文档。
接下来
- 参阅Pod 安全标准 了解策略建议。
- 阅读 Pod 安全策略参考了解 API 细节。
4 - 进程 ID 约束与预留
Kubernetes v1.20 [stable]
Kubernetes 允许你限制一个 Pod 中可以使用的 进程 ID(PID)数目。你也可以为每个 节点 预留一定数量的可分配的 PID,供操作系统和守护进程(而非 Pod)使用。
进程 ID(PID)是节点上的一种基础资源。很容易就会在尚未超出其它资源约束的时候就 已经触及任务个数上限,进而导致宿主机器不稳定。
集群管理员需要一定的机制来确保集群中运行的 Pod 不会导致 PID 资源枯竭,甚而 造成宿主机上的守护进程(例如 kubelet 或者 kube-proxy 乃至包括容器运行时本身)无法正常运行。 此外,确保 Pod 中 PID 的个数受限对于保证其不会影响到同一节点上其它负载也很重要。
说明:在某些 Linux 安装环境中,操作系统会将 PID 约束设置为一个较低的默认值,例如
32768
。这时可以考虑提升/proc/sys/kernel/pid_max
的设置值。
你可以配置 kubelet 限制给定 Pod 能够使用的 PID 个数。
例如,如果你的节点上的宿主操作系统被设置为最多可使用 262144
个 PID,同时预期
节点上会运行的 Pod 个数不会超过 250
,那么你可以为每个 Pod 设置 1000
个 PID
的预算,避免耗尽该节点上可用 PID 的总量。
如果管理员系统像 CPU 或内存那样允许对 PID 进行过量分配(Overcommit),他们也可以
这样做,只是会有一些额外的风险。不管怎样,任何一个 Pod 都不可以将整个机器的运行
状态破坏。这类资源限制有助于避免简单的派生炸弹(Fork
Bomb)影响到整个集群的运行。
在 Pod 级别设置 PID 限制使得管理员能够保护 Pod 之间不会互相伤害,不过无法 确保所有调度到该宿主机器上的所有 Pod 都不会影响到节点整体。 Pod 级别的限制也无法保护节点代理任务自身不会受到 PID 耗尽的影响。
你也可以预留一定量的 PID,作为节点的额外开销,与分配给 Pod 的 PID 集合独立。 这有点类似于在给操作系统和其它设施预留 CPU、内存或其它资源时所做的操作, 这些任务都在 Pod 及其所包含的容器之外运行。
PID 限制是与计算资源
请求和限制相辅相成的一种机制。不过,你需要用一种不同的方式来设置这一限制:
你需要将其设置到 kubelet 上而不是在 Pod 的 .spec
中为 Pod 设置资源限制。
目前还不支持在 Pod 级别设置 PID 限制。
注意:这意味着,施加在 Pod 之上的限制值可能因为 Pod 运行所在的节点不同而有差别。 为了简化系统,最简单的方法是为所有节点设置相同的 PID 资源限制和预留值。
节点级别 PID 限制
Kubernetes 允许你为系统预留一定量的进程 ID。为了配置预留数量,你可以使用
kubelet 的 --system-reserved
和 --kube-reserved
命令行选项中的参数
pid=<number>
。你所设置的参数值分别用来声明为整个系统和 Kubernetes 系统
守护进程所保留的进程 ID 数目。
说明:在 Kubernetes 1.20 版本之前,在节点级别通过 PID 资源限制预留 PID 的能力 需要启用特性门控
SupportNodePidsLimit
才行。
Pod 级别 PID 限制
Kubernetes 允许你限制 Pod 中运行的进程个数。你可以在节点级别设置这一限制,
而不是为特定的 Pod 来将其设置为资源限制。
每个节点都可以有不同的 PID 限制设置。
要设置限制值,你可以设置 kubelet 的命令行参数 --pod-max-pids
,或者
在 kubelet 的配置文件
中设置 PodPidsLimit
。
说明:在 Kubernetes 1.20 版本之前,为 Pod 设置 PID 资源限制的能力需要启用 特性门控
SupportNodePidsLimit
才行。
基于 PID 的驱逐
你可以配置 kubelet 使之在 Pod 行为不正常或者消耗不正常数量资源的时候将其终止。
这一特性称作驱逐。你可以针对不同的驱逐信号
配置资源不足的处理。
使用 pid.available
驱逐信号来配置 Pod 使用的 PID 个数的阈值。
你可以设置硬性的和软性的驱逐策略。不过,即使使用硬性的驱逐策略,
如果 PID 个数增长过快,节点仍然可能因为触及节点 PID 限制而进入一种不稳定状态。
驱逐信号的取值是周期性计算的,而不是一直能够强制实施约束。
Pod 级别和节点级别的 PID 限制会设置硬性限制。 一旦触及限制值,工作负载会在尝试获得新的 PID 时开始遇到问题。 这可能会也可能不会导致 Pod 被重新调度,取决于工作负载如何应对这类失败 以及 Pod 的存活性和就绪态探测是如何配置的。 可是,如果限制值被正确设置,你可以确保其它 Pod 负载和系统进程不会因为某个 Pod 行为不正常而没有 PID 可用。
接下来
- 参阅 PID 约束改进文档 以了解更多信息。
- 关于历史背景,请阅读 Kubernetes 1.14 中限制进程 ID 以提升稳定性 的博文。
- 请阅读为容器管理资源。
- 学习如何配置资源不足情况的处理。