前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >kubernetes应用资源管理

kubernetes应用资源管理

作者头像
heidsoft
发布2022-01-17 16:06:13
7910
发布2022-01-17 16:06:13
举报

QoS(Quality of Service)

Kubernetes针对不同服务质量的预期,通过QoS(Quality of Service)来对pod进行服务质量管理,提供了个采用requestslimits两种类型对资源进行分配和使用限制。对于一个pod来说,服务质量体现在两个为2个具体的指标:CPU与内存。实际过程中,当NODE节点上内存资源紧张时,kubernetes会根据预先设置的不同QoS类别进行相应处理。

资源限制

如果未做过节点 nodeSelector,亲和性(node affinity)或pod亲和、反亲和性(pod affinity/anti-affinity)等Pod高级调度策略设置,我们没有办法指定服务部署到指定机器上,如此可能会造成cpu或内存等密集型的pod同时分配到相同Node,造成资源竞争。另一方面,如果未对资源进行限制,一些关键的服务可能会因为资源竞争因OOM(Out of Memory)等原因被kill掉,或者被限制CPU使用。

资源需求和限制

对于每一个资源,container可以指定具体的资源需求(requests)和限制(limits),requests申请范围是0到node节点的最大配置,而limits申请范围是requests到无限,即0 <= requests <=Node Allocatable, requests <= limits <= Infinity。

对于CPU,如果pod中服务使用CPU超过设置的limits,pod不会被kill掉但会被限制。如果没有设置limits,pod可以使用全部空闲的cpu资源。

对于内存,当一个pod使用内存超过了设置的limits,pod中container的进程会被kernel因OOM kill掉。当container因为OOM被kill掉时,系统倾向于在其原所在的机器上重启该container或本机或其他重新创建一个pod。

分类

Kubelet提供QoS服务质量管理,支持系统级别的OOM控制。在Kubernetes中,pod的QoS级别:Guaranteed, BurstableBest-Effort。下面对各级别分别进行相应说明:

Guaranteed:pod中所有容器都必须统一设置limits,并且设置参数都一致,如果有一个容器要设置requests,那么所有容器都要设置,并设置参数同limits一致,那么这个pod的QoS就是Guaranteed级别。

注:如果一个容器只指明limit而未设定request,则request的值等于limit值。

Guaranteed举例1:容器只指明了limits而未指明requests)。

Guaranteed举例2:requestslimit均指定且值相等。

代码语言:javascript
复制
containers:
name: foo
resources:
  limits:
    cpu: 10m
    memory: 1Gi
  requests:
    cpu: 10m
    memory: 1Gi


name: bar
resources:
  limits:
    cpu: 100m
    memory: 100Mi
  requests:
    cpu: 100m
    memory: 100Mi

Burstable: pod中只要有一个容器的requestslimits的设置不相同,该pod的QoS即为Burstable。举例如下:

Container bar没有指定resources

代码语言:javascript
复制
containers:
name: foo
resources:
  limits:
    cpu: 10m
    memory: 1Gi
  requests:
    cpu: 10m
    memory: 1Gi


name: bar

Burstable举例2:对Container foo与bar不同的resources(foo为memory,而bar为cpu)设置了limits

代码语言:javascript
复制
containers:
name: foo
resources:
  limits:
    memory: 1Gi


name: bar
resources:
  limits:
    cpu: 100m

Burstable举例3:Container foo没有设置limits,而bar requestslimits均未设置。

代码语言:javascript
复制
containers:
name: foo
resources:
  requests:
    cpu: 10m
    memory: 1Gi


name: bar

Best-Effort:如果对于全部的resources来说requestslimits均未设置,该pod的QoS即为Best-Effort。举例如下:

代码语言:javascript
复制
containers:
name: foo
resources:
name: bar
resources:
可压缩资源与不可压缩资源

Kubernetes根据资源能否伸缩进行分类,划分为可压缩资源和不可以压缩资源2种。CPU资源是目前支持的一种可压缩资源,而内存资源和磁盘资源为目前所支持的不可压缩资源。

优先级

3种QoS优先级从有低到高(从左向右):

代码语言:javascript
复制
Best-Effort pods -> Burstable pods -> Guaranteed pods
静态pod

在Kubernetes中有一种DaemonSet类型pod,此类型pod可以常驻某个Node运行,由该Node上kubelet服务直接管理而无需api server介入。静态pod也无需关联任何RC,完全由kubelet服务来监控,当kubelet发现静态pod停止时,kubelet会重新启动静态pod。

资源回收策略

当kubernetes集群中某个节点上可用资源比较小时,kubernetes提供了资源回收策略保证被调度到该节点pod服务正常运行。当节点上的内存或者CPU资源耗尽时,可能会造成该节点上正在运行的pod服务不稳定。Kubernetes通过kubelet来进行回收策略控制,保证节点上pod在节点资源比较小时可以稳定运行。

可压缩资源:CPU

在压缩资源部分已经提到CPU属于可压缩资源,当pod使用超过设置的limits值,pod中进程使用cpu会被限制,但不会被kill。

不可压缩资源:内存

Kubernetes通过cgroup给pod设置QoS级别,当资源不足时先kill优先级低的pod,在实际使用过程中,通过OOM分数值来实现,OOM分数值从0-1000。

OOM分数值根据OOM_ADJ参数计算得出,对于Guaranteed级别的pod,OOM_ADJ参数设置成了-998,对于BestEffort级别的pod,OOM_ADJ参数设置成了1000,对于Burstable级别的POD,OOM_ADJ参数取值从2到999。对于kuberntes保留资源,比如kubelet,docker,OOM_ADJ参数设置成了-999,表示不会被OOM kill掉。OOM_ADJ参数设置的越大,通过OOM_ADJ参数计算出来OOM分数越高,表明该pod优先级就越低,当出现资源竞争时会越早被kill掉,对于OOM_ADJ参数是-999的表示kubernetes永远不会因为OOM而被kill掉。

QoS pods被kill掉场景与顺序

  • Best-Effort 类型的pods:系统用完了全部内存时,该类型pods会最先被kill掉。
  • Burstable类型pods:系统用完了全部内存,且没有Best-Effort container可以被kill时,该类型pods会被kill掉。
  • Guaranteed pods:系统用完了全部内存、且没有Burstable与Best-Effort container可以被kill,该类型的pods会被kill掉。 注:如果pod进程因使用超过预先设定的limites而非Node资源紧张情况,系统倾向于在其原所在的机器上重启该container或本机或其他重新创建一个pod。

使用建议

  • 如果资源充足,可将QoS pods类型均设置为Guaranteed。用计算资源换业务性能和稳定性,减少排查问题时间和成本。
  • 如果想更好的提高资源利用率,业务服务可以设置为Guaranteed,而其他服务根据重要程度可分别设置为BurstableBest-Effort,例如filebeat。
分数设置参数
代码语言:javascript
复制
heidsoft-nginx-7ddf9c5bcf-qqsnk:~# ls -alh /proc/1/oom_*
-rw-r--r--    1 root   root         0 Oct  1 13:28 /proc/1/oom_adj
-r--r--r--    1 root   root         0 Oct  1 13:28 /proc/1/oom_score
-rw-r--r--    1 root   root         0 Oct  1 13:28 /proc/1/oom_score_adj

资源不足处理方式

本页介绍如何使用 kubelet 配置资源不足时的处理方式。

当可用计算资源较少时,kubelet需要保证节点稳定性。这在处理如内存和硬盘之类的不可压缩资源时尤为重要。如果任意一种资源耗尽,节点将会变得不稳定。

驱逐信号

kubelet 支持按照以下表格中描述的信号触发驱逐决定。每个信号的值在 description 列描述,基于 kubelet 摘要 API。

驱逐信号

描述

memory.available

memory.available := node.status.capacity[memory] - node.stats.memory.workingSet

nodefs.available

nodefs.available := node.stats.fs.available

nodefs.inodesFree

nodefs.inodesFree := node.stats.fs.inodesFree

imagefs.available

imagefs.available := node.stats.runtime.imagefs.available

imagefs.inodesFree

imagefs.inodesFree := node.stats.runtime.imagefs.inodesFree

pid.available

pid.available := node.stats.rlimit.maxpid - node.stats.rlimit.curproc

上面的每个信号都支持字面值或百分比的值。基于百分比的值的计算与每个信号对应的总容量相关。

memory.available 的值从 cgroupfs 获取,而不是通过类似 free -m 的工具。这很重要,因为 free -m 不能在容器中工作,并且如果用户使用了 节点可分配资源 特性,资源不足的判定将同时在本地 cgroup 层次结构的终端用户 Pod 部分和根节点做出。这个脚本 复现了与 kubelet 计算 memory.available 相同的步骤。 kubeletinactive_file(意即活动 LRU 列表上基于文件后端的内存字节数)从计算中排除, 因为它假设内存在出现压力时将被回收。

kubelet 只支持两种文件系统分区。

  1. nodefs 文件系统,kubelet 将其用于卷和守护程序日志等。
  2. imagefs 文件系统,容器运行时用于保存镜像和容器可写层。

imagefs 可选。kubelet 使用 cAdvisor 自动发现这些文件系统。 kubelet 不关心其它文件系统。当前不支持配置任何其它类型。例如,在专用 filesytem 中存储卷和日志是 不可以 的。

在将来的发布中,kubelet将废除当前存在的 垃圾回收 机制,这种机制目前支持将驱逐操作作为对磁盘压力的响应。

requests 调度分析
代码语言:javascript
复制
func computePodResourceRequest(pod *v1.Pod) *preFilterState {
	result := &preFilterState{}
	for _, container := range pod.Spec.Containers {
		result.Add(container.Resources.Requests)
	}
	// take max_resource(sum_pod, any_init_container)
	for _, container := range pod.Spec.InitContainers {
		result.SetMaxResource(container.Resources.Requests)
	}
	// If Overhead is being utilized, add to the total requests for the pod
	if pod.Spec.Overhead != nil && utilfeature.DefaultFeatureGate.Enabled(features.PodOverhead) {
		result.Add(pod.Spec.Overhead)
	}
	return result
}
...
func (f *Fit) PreFilter(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod) *framework.Status {
	cycleState.Write(preFilterStateKey, computePodResourceRequest(pod))
	return nil
}
...
func getPreFilterState(cycleState *framework.CycleState) (*preFilterState, error) {
	c, err := cycleState.Read(preFilterStateKey)
	if err != nil {
		// preFilterState doesn't exist, likely PreFilter wasn't invoked.
		return nil, fmt.Errorf("error reading %q from cycleState: %v", preFilterStateKey, err)
	}
	s, ok := c.(*preFilterState)
	if !ok {
		return nil, fmt.Errorf("%+v  convert to NodeResourcesFit.preFilterState error", c)
	}
	return s, nil
}
...
func (f *Fit) Filter(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
	s, err := getPreFilterState(cycleState)
	if err != nil {
		return framework.NewStatus(framework.Error, err.Error())
	}
	insufficientResources := fitsRequest(s, nodeInfo, f.ignoredResources, f.ignoredResourceGroups)
	if len(insufficientResources) != 0 {
		// We will keep all failure reasons.
		failureReasons := make([]string, 0, len(insufficientResources))
		for _, r := range insufficientResources {
			failureReasons = append(failureReasons, r.Reason)
		}
		return framework.NewStatus(framework.Unschedulable, failureReasons...)
	}
	return nil
}
代码语言:javascript
复制
func GetContainerOOMScoreAdjust(pod *v1.Pod, container *v1.Container, memoryCapacity int64) int {
	if types.IsCriticalPod(pod) {
		// Critical pods should be the last to get killed.
		return guaranteedOOMScoreAdj
	}
	switch v1qos.GetPodQOS(pod) {
	case v1.PodQOSGuaranteed:
		// Guaranteed containers should be the last to get killed.
		return guaranteedOOMScoreAdj
	case v1.PodQOSBestEffort:
		return besteffortOOMScoreAdj
	}
	// Burstable containers are a middle tier, between Guaranteed and Best-Effort. Ideally,
	// we want to protect Burstable containers that consume less memory than requested.
	// The formula below is a heuristic. A container requesting for 10% of a system's
	// memory will have an OOM score adjust of 900. If a process in container Y
	// uses over 10% of memory, its OOM score will be 1000. The idea is that containers
	// which use more than their request will have an OOM score of 1000 and will be prime
	// targets for OOM kills.
	// Note that this is a heuristic, it won't work if a container has many small processes.
	memoryRequest := container.Resources.Requests.Memory().Value()
	oomScoreAdjust := 1000 - (1000*memoryRequest)/memoryCapacity
	// A guaranteed pod using 100% of memory can have an OOM score of 10. Ensure
	// that burstable pods have a higher OOM score adjustment.
	if int(oomScoreAdjust) < (1000 + guaranteedOOMScoreAdj) {
		return (1000 + guaranteedOOMScoreAdj)
	}
	// Give burstable pods a higher chance of survival over besteffort pods.
	if int(oomScoreAdjust) == besteffortOOMScoreAdj {
		return int(oomScoreAdjust - 1)
	}
	return int(oomScoreAdjust)
}

https://www.cncf.io/blog/2020/06/10/kubernetes-resources-management-qos-quota-and-limitrangeb/

https://kubesphere.io/zh/blogs/deep-dive-into-the-k8s-request-and-limit/

调度策略

Kubernetes中的调度策略主要分为全局调度与运行时调度2种。其中全局调度策略在调度器启动时配置,而运行时调度策略主要包括选择节点(nodeSelector),节点亲和性(nodeAffinity),pod亲和与反亲和性(podAffinity与podAntiAffinity)。Node Affinity、podAffinity/AntiAffinity以及后文即将介绍的污点(Taints)与容忍(tolerations)等特性,

设置节点label

Label是Kubernetes核心概念之一,其以key/value的形式附加到各种对象上,如Pod、Service、Deployment、Node等,达到识别这些对象,管理关联关系等目的,如Node和Pod的关联。

获取当前集群中的全部节点:

代码语言:javascript
复制
kubectl get nodes

为指定节点设置label:

代码语言:javascript
复制
kubectl label nodes <node-name> <label-key>=<label-value>

确认节点label是否设置成功:

代码语言:javascript
复制
kubectl get nodes -l ‘label_key=label_value’
亲和性(Affinity)与非亲和性(anti-affinity)

亲和性(Affinity)与非亲和性(anti-affinity)则更加灵活的指定pod调度到预期节点上,相比nodeSelector,Affinity与anti-affinity优势体现在:

  • 表述语法更加多样化,不再仅受限于强制约束与匹配。
  • 调度规则不再是强制约束(hard),取而代之的是软限(soft)或偏好(preference)。
  • 指定pod可以和哪些pod部署在同一个/不同拓扑结构下。

亲和性主要分为3种类型:node affinity与inter-pod affinity/anti-affinity,下文会进行详细说明。

节点亲和性(Node affinity)

Node affinity在Kubernetes 1.2做为alpha引入,其涵盖了nodeSelector功能,主要分为requiredDuringSchedulingIgnoredDuringExecution与preferredDuringSchedulingIgnoredDuringExecution 2种类型。前者可认为一种强制限制,如果 Node 的标签发生了变化导致其没有符合 Pod 的调度要求节点,那么pod调度就会失败。而后者可认为理解为软限或偏好,同样如果 Node 的标签发生了变化导致其不再符合 pod 的调度要求,pod 依然会调度运行。

node affinity举例

设置节点label:

代码语言:javascript
复制
$ kubectl label nodes bjo-ep-dep-040.dev.fwmrm.net cpu=high
node "bjo-ep-dep-040.dev.fwmrm.net" labeled


$  kubectl label nodes bjo-ep-svc-017.dev.fwmrm.net cpu=mid
node "bjo-ep-svc-017.dev.fwmrm.net" labeled


$  kubectl label nodes bjo-rpt-re-002.dev.fwmrm.net cpu=low
node "bjo-rpt-re-002.dev.fwmrm.net" labeled

部署pod的预期是到非master节点(role!=master)、且CPU高配的机器上(cpu=high)。

查看满足条件节点:

代码语言:javascript
复制
$ kubectl get nodes -l 'cpu=high, role!=master'
NAME                           STATUS    AGE       VERSION
bjo-ep-dep-040.dev.fwmrm.net   Ready     41d       v1.7.1

pod.yaml文件内容如下:

代码语言:javascript
复制
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
affinity:
nodeAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
    nodeSelectorTerms:
    - matchExpressions:
      - key: role
        operator: NotIn
        values:
        - master
  preferredDuringSchedulingIgnoredDuringExecution:
  - weight: 1
    preference:
      matchExpressions:
      - key: cpu
        operator: In
        values:
        - high
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent

检查结果符合预期,pod nginx成功部署到非master节点且CPU高配的机器上。

代码语言:javascript
复制
$ kubectl get po  nginx -o wide
NAME      READY     STATUS    RESTARTS   AGE       IP             NODE
nginx     1/1       Running   0          32s       10.244.2.185   bjo-ep-dep-040.dev.fwmrm
pod亲和性(Inter-pod affinity)与反亲和性(anti-affinity)

inter-pod affinity与anti-affinity由Kubernetes 1.4引入,当前处于beta阶段,其中podAffinity用于调度pod可以和哪些pod部署在同一拓扑结构之下。而podAntiAffinity相反,其用于规定pod不可以和哪些pod部署在同一拓扑结构下。通过pod affinity与anti-affinity来解决pod和pod之间的关系。

与Node affinity类似,pod affinity与anti-affinity同样分为requiredDuringSchedulingIgnoredDuringExecution and preferredDuringSchedulingIgnoredDuringExecution等2种类型,前者被认为是强制约束,而后者后者可认为理解软限(soft)或偏好(preference)。

pod affinity与anti-affinity举例

本示例中假设部署场景为:期望is服务与oltp服务就近部署,而不希望与solr服务部署同一拓扑结构上。

yaml文件部分内容:

代码语言:javascript
复制
spec:
replicas: 1
template:
metadata:
  labels:
    app: is


spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: NotIn
            values:
            - solr
        topologyKey: kubernetes.io/hostname
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: app
              operator: In
              values:
              - oltp
          topologyKey: beta.kubernetes.io/os

查看部署结果,is服务与oltp部署到了同一台机器,而solr被部署在其他机器上。

代码语言:javascript
复制
$ kubectl get po -o wide
NAME                           READY     STATUS    RESTARTS   AGE       IP             NODE
is-3059482752-5s14t            0/1       Running   1          1m        10.244.1.60    bjo-ep-svc-017.dev.fwmrm.net
oltp-282283610-kdvnp           1/1       Running   0          1m        10.244.1.53    bjo-ep-svc-017.dev.fwmrm.net
solr-908150957-rswlm           1/1       Running   0          1m        10.244.3.5     bjo-rpt-re-002.dev.fwmrm.net

亲和性/反亲和性调度策略比较

代码语言:javascript
复制
调度策略         匹配标签 操作符                拓扑域支持  调度目标
nodeAffinity    主机    In, NotIn, Exists,   否         pod到指定主机
                       DoesNotExist, Gt, Lt  
podAffinity     Pod    In, NotIn, Exists,   是         pod与指定pod同一拓扑域
                       DoesNotExist            
PodAntiAffinity Pod    In, NotIn, Exists,   是         pod与指定pod非同一拓扑域
                       DoesNotExist
污点(Taints)与容忍(tolerations)

对于Node affinity,无论是强制约束(hard)或偏好(preference)方式,都是调度pod到预期节点上,而Taints恰好与之相反,如果一个节点标记为 Taints ,除非 Pod也被标识为可以耐受污点节点,否则该Taints节点不会被调度pod。Taints)与tolerations当前处于beta阶段,

Taints节点应用场景比如用户希望把Kubernetes Master节点保留给 Kubernetes 系统组件使用,或者把一组具有特殊资源预留给某些 pod。

pod不会再被调度到taint标记过的节点。taint标记节点举例如下:

代码语言:javascript
复制
$ kubectl taint nodes bjo-ep-dep-039.dev.fwmrm.net key=value:NoSchedule
node "bjo-ep-dep-039.dev.fwmrm.net" tainted

如果仍然希望某个pod调度到taint节点上,则必须在 Spec 中做出Toleration 定义,才能调度到该节点,举例如下:

代码语言:javascript
复制
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"

effect 共有三个可选项,可按实际需求进行设置:

1. NoSchedule:pod不会被调度到标记为taints节点。

2. PreferNoSchedule:NoSchedule的“preference”或“soft”版本。

3. NoExecute:该选项意味着一旦Taint 生效,如该节点内正在运行的 Pod 没有对应 Tolerate 设置,会直接被逐出。

https://kubernetes.io/blog/2017/03/advanced-scheduling-in-kubernetes/

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-10-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云数智圈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 资源限制
  • 资源需求和限制
  • 分类
  • 可压缩资源与不可压缩资源
  • 优先级
  • 静态pod
  • 资源回收策略
  • 分数设置参数
  • 资源不足处理方式
    • 驱逐信号
      • requests 调度分析
      • 调度策略
        • 设置节点label
          • 亲和性(Affinity)与非亲和性(anti-affinity)
            • 节点亲和性(Node affinity)
              • pod亲和性(Inter-pod affinity)与反亲和性(anti-affinity)
                • 污点(Taints)与容忍(tolerations)
                相关产品与服务
                容器服务
                腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档