从资源管理角度认识 K8S

简介: 笔者认为应用开发者为了适应云原生趋势,需要掌握必要的K8S基础知识点,详细介绍在《从应用开发角度认识K8S》: https://developer.aliyun.com/article/778441。

作者个人介绍
刘晨 Lorraine 坐标Fintech,精通持续集成与发布,曾具有全平台100+应用持续部署持续发布实战经验,现在立志于成为K8S玩家。


1.jpeg

前言

大家好,笔者最近学习和整理K8S知识点的时候,一直以多维角度重新K8S,笔者认为应用开发者为了适应云原生趋势,需要掌握必要的K8S基础知识点,详细介绍在https://developer.aliyun.com/article/778441
笔者做为团队的Devops,承担着管理K8S集群的职责,本篇就从资源管理角度重新认识K8S。

资源类型

在物理机或虚拟机集群时代,我们管理的资源主要是以主机为单元的集群。单个主机节点,可以是一台物理机,或者是基于Hypervitor虚拟化中间件的VM,资源类型主要包括CPU,内存,磁盘IO,以及网络带宽。

K8S集群一般由至少一个Master节点和若干个Worker节点组成。Master节点包含了调度器Scheduler,控制器Controller,资源对象接口APIServer以及持久化存储Etcd。Worker节点通过Kubelet跟Master节点通信,其资源以及负载Pod受Master的调度以及控制。K8S本身架构是C/S模式,即worker节点的资源管理与调度都由Master节点控制。

认识K8S集群资源时,不再按照传统方式以节点单元切分,笔者选择工作负载,存储以及网络这三个方面介绍资源类型,是基于K8S提供的核心资源对象Pod,PVC/PV以及Service/Ingress实现了最基本的集群资源管理与容器编排。在K8S集群中,CPU以及内存资源定义包含在Pod内,用户无法从K8S直接管理。PVC/PV模式解耦了存储资源与节点资源。Service提供了自动服务发现,使得K8S集群具备自愈能力。Ingress提供了对集群网络流量的控制,使得K8S可以一体化考虑工作负载与网络负载,达到弹性伸缩。

工作负载

  • K8S通过Pod来管理一组容器组的生命周期,Pod是K8S调度的最小单元。应用服务部署与调度也是以Pod为单元,Pod也可称之为工作负载。对于无状态的应用部署,以ReplicaSet定义与管理;对于有依赖关系,有状态的应用部署,以StatefulSet定义与管理。ReplicaSet与StatefulSet又是一个有意思的话题,笔者会在后续系列详细介绍。应用所需的CPU/内存资源定义在Pod内,因为CPU/内存资源是容器级别的,由Pod的spec->containers->resources定义。
  • K8S提供了requests和limits两个配置参数来定义资源的范围和额度。

requests定义了工作负载的资源下限,是容器启动时K8S资源分配的默认值。

limits定义了工作负载的资源上限,是容器运行时K8S资源预分配的额度。

  • 容器共享所在Pod的存储Volume,网络命名空间以及PID命名空间,容器拥有CPU/内存资源分配与资源额度。在进行资源管理时,要区分CPU/内存资源分配额度和工作负载实际的CPU/内存利用率。K8S集群信息控制台一般展示工作负载定义的CPU/内存分配额度。

2.png

存储

  • PVC全称为PersistentVolumeClaim,持久化卷声明;PV全称为PersistentVolume,持久化卷。顾名思义,持久化卷是K8S描述各种存储资源类型(块存储,NAS,以及对象存储)的资源对象定义。基于各个云厂商提供的存储管理插件,可以直接使用IaaS层的存储资源作为K8S集群存储。
  • 看起来PV抽象就已足够解决K8S存储资源适配各个云厂商的IaaS存储资源,为什么还要再多此一举搞一个PVC资源对象抽象呢?PVC类似OOP中的抽象类。在开发中使用抽象类通常是想解耦对象调用与对象实现。Pod绑定PVC而非PV,是将Pod部署与PV资源分配解耦。

Pod部署一般属于研发团队应用部署的子环节,由开发人员控制。PV存储资源定义与分配则多属于devops团队,由集群管理员控制。

Pod和PVC是命名空间访问控制范围内的资源对象,PV则是集群访问控制范围内的资源对象。

  • PVC/PV模式解耦了存储资源与节点资源,即由PVC/PV定义的存储资源独立于节点资源,通过动态绑定方式随工作负载在节点资源间迁移。具体细节依赖于云厂商提供的存储插件的实现方式。

网络

  • Service实现了服务端自动发现机制,即提供应用服务的一组Pod可以通过同一个服务域名访问地址对外提供服务,Pod的新增或者销毁不会影响该服务整体服务性能。工作负载通过Service绑定了一个虚拟IP以及端口,对于Pod分配的节点资源并不敏感,解耦了Service与节点网络资源,使得整个集群具备负载迁移,故障自愈的能力。
  • Ingress与Service对象不同,是K8S对集群外部提供服务的一种资源对象。在K8S出现之前,我们对于负载与流量控制,多是采用Nginx反向代理和负载均衡,横向扩展服务端处理负载的能力。Ingress本质上就是一个NginxPod,这个Pod也是通过Service对象暴露出来,基于LoadBalancer模式对外提供反向代理与负载均衡。Ingress很像一个集群的路由器和访问入口,通过在ingress配置路由规则绑定工作负载与对应域名解析路径,达到只使用一个外部IP就能将多个集群内部的服务暴露出来,从而节省IP资源。

Pod调度与管理

K8S做为当前最流行的容器编排平台,提供了平台级别的弹性伸缩以及故障自愈等解决方案。K8S集群基于C/S架构,由Master统一控制管理集群资源与负载均衡。Pod是K8S调度的最小单元,我们要掌握集群资源管理与调度就要从Pod调度与管理开始。

健康检查

  • K8S要掌握应用服务运行状态,才有可能进行平台级别的弹性伸缩和故障自愈。最简单的做法就是不断检查容器进程是否处于Running状态。如果检查到容器进程失败,就不断自动尝试重启进程。很多情况下,重启进程就能解决问题,所以这种健康检查虽然简单,但是十分有效和必要。
  • 但是,如果一个java应用运行时抛出了OOM异常或者死锁了,但JVM进程仍然在运行中;Pod仍处于Running状态,但应用进程已经无法提供服务了。这种情况,上述健康检查就无法处理了,K8S提供的
    livenessProbe可以捕获到应用服务级别的异常状态,全面掌握应用服务运行是否健康。

livenessProbe类似进程健康检查,都是对容器进程进行健康检查,当检测到失败时,就重启该进程,以达到自动修复。

不同的是,livenessProbe是通过调用应用服务定义的HTTP GET API来连接Pod的暴露的IP和Port,通过请求的返回码是否属于200~399来判定容器进程运行状态的。

除了HTTP方式,还可以通过TCP Socket是否联通成功判定应用服务运行状态。

这种来自K8S而不是应用服务内部的应用运行状态判定逻辑,使得K8S能够掌握应用服务级别的健康状态。

  • 当负载过大时,即使容器进程一直处于健康状态,仍有可能出现该应用服务无法正常提供服务。这种情况,K8S是通过readinessProbe进行检测的。

ReadinessProbe提供的接入方式与livenessProbe一样,可以通过HTTP GET API或TCP Socket Connection方式由Kubelet探针将Pod工作负载的情况上报给K8S管理节点。

当检测到readinessProbe失败,应用服务进程无法正常处理请求时,该Pod不再被重启,而是从Service端点摘除,不再接收Service的请求负载,类似流量降级,已确保该Pod可以正确处理已接收的请求负载。

自动调度策略

  • 2015年Google开源K8S时,提出了最初始的云原生定义,即应用容器化、面向微服务架构、应用支持容器的编排调度。容器编排调度是K8S最核心的技术。当面对一个成百上千的微服务容器集群时,Pod调度与资源管理就成了一件复杂的事情。Pod内的一组容器在运行时具有相关性,运行在同一节点,共享节点资源。当应用负载发现变化时,容器对节点资源消耗也随之发生变化,节点资源的容量与可用性也会影响应用服务的性能与稳定性。
  • K8S的Scheduler组件基于APIServer定义的Pod资源对象和Kubelet上报的各个节点资源的使用情况,来选择合适的节点进行调度。Scheduler控制Pod的创建,弹性伸缩以及遇到故障时的负载迁移。Scheduler基于容器运行时依赖关系,资源需求设置以及默认调度策略进行决策。默认的调度策略通常会考虑调度的节点可以保证工作负载具有高可用,高性能以及低延迟。所以,除非对于节点选择具有特殊使用用途,否则建议用户一般直接采用默认调度策略。
  • 定制化Scheduler可以操作集群的配置文件,如下所示的JSON文件。其中predicates表示的是调度Pod时只考虑满足上述规则的节点资源。priorities表示的是对于满足predicates规则筛选的节点资源的集合按照priorities进行权重排序,选取权重最高的节点资源进行调度。例如,Scheduler会按照如下示例所示,优先选择资源需求最低的节点进行调度。
{
"kind" : "Policy",
"apiVersion" : "v1",
"predicates" : [ 
        {"name" : "PodFitsHostPorts"},
        {"name" : "PodFitsResources"},
        {"name" : "NoDiskConflict"},
        {"name" : "NoVolumeZoneConflict"},
        {"name" : "MatchNodeSelector"},
        {"name" : "MaxEBSVolumeCount"},
        {"name" : "MaxAzureDiskVolumeCount"},
        {"name" : "checkServiceAffinity"},
        {"name" : "PodToleratesNodeNoExecuteTaints"},
        {"name" : "MaxGCEPDVolumeCount"},
        {"name" : "MatchInterPodAffinity"},
        {"name" : "PodToleratesNodeTaints"},
        {"name" : "HostName"}
        ],
"priorities" : [
        {"name" : "LeastRequestedPriority", "weight" : 2},
        {"name" : "BalancedResourceAllocation", "weight" : 1},
        {"name" : "ServiceSpreadingPriority", "weight" : 1},
        {"name" : "EqualPriority", "weight" : 1}
        ]
}
  • 当Scheduler检测到APIServer生成的Pod资源对象定义时,首先会通过Predicates筛选出符合规则的节点资源集合;然后依据Priority对这些节点资源进行权重排序,选出最优节点;分配节点资源创建Pod。

    3.png

Scheduler调度流程 From Kubernetes-Patterns

资源配置与限制

  • K8S集群通常会提供足够的资源容量供Scheduler进行Pod调度。Pod一般会被调度到资源容量大于Pod资源需求的节点上。一般情况下,节点OS和K8S管理组件会预分配一些节点资源,可分配的资源容量通常小于节点资源总量。Scheduler可以调度的资源配置是指节点可分配资源,也称节点容量。它的计算方式为:
Allocatable Capacity= Node Capacity - Kube-Reserved - System-Reserved

Allocatable Capacity为Scheduler可为应用服务Pod分配的节点资源

Node Capacity为节点资源总量

Kube-Reserved为K8S的后台进程预留资源,例如Kubelet,CRI,CNI等组件

System-Reserved为节点的操作系统后台进程预留资源,例如sshd, udev等

  • 在上文介绍工作负载时,我们提到可以通过requests和limits来确定容器运行资源消耗的用量和额度。因为节点资源并不能全部用来做资源调度,建议在定义Pod模版时,最好对容器的resources->requests和resources->limits都做明确的定义,以防止工作负载与K8S组件竞争资源导致调度失败。
  • Limits定义了容器资源使用的上限,Requests定义了容器资源初始化配置,一般启动容器时按照requests分配资源。容器运行时,资源消耗一般比requests分配的少,如下图所示。
    4.png
  • 这种金字塔型的资源分配方式存在着不小的资源碎片的情况。当工作负载竞争资源时,K8S提供了三种级别的服务质量保证。
    Best-Effort:Pod不设置requests和limits。这种Pod的QOS优先级最低,当节点资源调度不足或者竞争冲突时,优先销毁或迁移该类Pod。

Burstable:Pod设置requests和limits,但是requests小于limits。这种Pod具有最低的资源保证,即当节点资源竞争,不再有Best-Effort这种Pod时,优先销毁或迁移该类Pod。

Guaranteed:Pod设置requests和limits,且requests 等于limits。这种Pod具有最高的资源保证。QOS级别高于Burstable和Best-Effort。所以,建议在定义应用服务的Pod模版时,对于容器资源保证,服务质量要求高的Pod,尽量配置合适的requests和limits。

服务发现

运行在K8S集群的应用服务多是基于微服务架构的分布式系统。服务与服务之间常常具有互相调用关系。当调度应用服务Pod时,Scheduler会选择最佳的节点进行资源分配创建Pod,在启动容器前,随机分配ClusterIP地址给这个Pod。所以,当另一个应用服务Pod想要与该应用服务Pod通信,很难获取到这个随机分配的ClusterIP信息。

客户端自动发现

传统的分布式系统,比如ZooKeeper,常常使用的是客户端发现方式,进行服务间自动发现。客户端服务内置了可以发现服务注册中心以及选择其中某个服务实例进行通信的探针Agent。服务端服务实例将自己的状态上报到服务注册中心,客户端服务通过查询服务注册中心信息,选择并唤醒响应的服务实例进行交互。

5.png

客户端服务发现ByAgent

服务端自动发现

  • K8S实现的服务发现是基于服务端方式,即服务端Pod要主动上报自己的服务容量到服务注册中心。客户端Pod要能访问服务注册中心,并且可以通过服务注册中心提供服务信息,可以访问到响应服务端Pod。客户端Pod通过代理服务,使用一个恒定不变的虚拟IP对同一个服务访问,而不在意提供服务的Pod是哪个。

    6.png

服务端发现ByProxy

  • K8S实现服务端发现逻辑的是Service资源对象。Service通过Pod selector和port number定义,可以给一组Pod绑定一个虚拟IP,也称为clusterIP,示例如下:
apiVersion: v1
kind: Service
metadata:
  name: index-helm
  namespace: bss-dev
spec:
  clusterIP: 172.21.7.94
  ports:
    - name: http
      port: 8081 //service对外提供服务的端口号
      protocol: TCP
      targetPort: 8081 //Pod监听的端口号
  selector:
    app.kubernetes.io/instance: index //spec->selector绑定了对应的Pod
    app.kubernetes.io/name: helm
  sessionAffinity: None
  type: ClusterIP //ClusterIP 是默认service类型
  • 既然ClusterIP都是在Pod启动之后随机分配的,那么其他服务Pod是如何发现这个ClusterIP并与其通信的呢?主要有两种方式:
    环境变量

当Pod被创建后,与之绑定的Service服务对象也随之被创建,并且立即开始监听绑定的端口号。与Service相关的ClusterIP和Port值以环境变量方式自动设置到Pod中,该应用服务就能通过clusterIP和port对外提供服务了。

由于Pod启动以后,无法再将service对应的环境变量注入,所以,基于环境变量绑定clusterIP和port只能发生在Pod启动过程。

DNS查询

K8S提供了一个平台级别的DNS服务,可以配置给所有Pod使用。当一个Service资源对象被创建,DNS服务可以绑定一个DNS访问地址到对应Pod,供访问使用。这个DNS服务管理着应用服务DNS访问地址与Pod启动时分配的ClusterIP和port的对应关系,负责解析从该DNS访问地址过来的流量负载到对应的Pod上。

如果客户端服务已知ServiceName和对应的Namespace,就可以直接通过内部域名地址service-name.namsapce.svc.cluster.local直接访问应用服务Pod。

service-name是service对象定义的名称

namespace是service和pod所在的命名空间名

svc代表这是一个service资源

cluster.local是K8S的coreDNS服务默认集群内部访
问地址域名前缀

结语

笔者最近在整理K8S入门的基础知识点,本篇做为“从应用开发角度认识K8S”的姐妹篇,从资源管理角度重新认识了K8S。K8S平台的用户一般分为应用开发和集群管理员。集群管理员在掌握K8S的时候,更多时候要从集群层面,资源层面,性能层面多维度认识K8S,这时就需要我们掌握和了解最基本的Pod调度与资源管理,存储与网络资源以及服务与流量管理等知识点。

本文介绍的内容算是抛砖引入,如果读者有兴趣继续深入学习相关知识,可以参考学习
https://medium.com/@kumargaurav1247/components-of-kubernetes-architecture-6feea4d5c712

https://medium.com/@yashbindlish1/under-the-hood-an-introduction-to-kubernetes-architecture-bb9d8599f837

https://developers.redhat.com/blog/2020/05/11/top-10-must-know-kubernetes-design-patterns/

相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。     相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
监控 物联网 应用服务中间件
流媒体方案之Nginx——实现物联网视频监控项目
流媒体方案之Nginx——实现物联网视频监控项目
流媒体方案之Nginx——实现物联网视频监控项目
|
7月前
|
安全 数据库 C#
阿里云最新域名注册和续费、云虚拟主机、企业邮箱收费价格表参考
域名,云虚拟主机,企业邮箱是阿里云旗下的基础产品,2025年截止目前阿里云平台注册.com域名的收费标准是85元,新用户首次注册可享受一定的优惠。本文为大家介绍2025年阿里云在域名注册与续费、云虚拟主机、以及企业邮箱方面的最新收费标准与优惠政策,帮助用户更好的了解自己所需产品的收费标准,以供参考。
|
11月前
|
人工智能 网络性能优化 调度
Koordinator v1.6: 支持AI/ML场景的异构资源调度能力
如何高效管理和调度这些资源成为了行业关注的核心问题。在这一背景下,Koordinator积极响应社区诉求,持续深耕异构设备调度能力,并在最新的v1.6版本中推出了一系列创新功能,帮助客户解决异构资源调度难题。
|
传感器 机器学习/深度学习 人工智能
AI视频监控卫士技术介绍:智能化河道管理解决方案
AI视频监控卫士系统,通过高清摄像头、智能传感器和深度学习技术,实现河道、水库、城市水务及生态保护区的全天候、全覆盖智能监控。系统能够自动识别非法行为、水质变化和异常情况,并实时生成警报,提升管理效率和精准度。
1212 13
|
存储 机器学习/深度学习 缓存
MemLong: 基于记忆增强检索的长文本LLM生成方法
本文介绍了一种名为MemLong的创新长文本处理方法,该方法通过整合外部检索器显著增强了大型语言模型处理长上下文的能力。MemLong采用轻量级设计,利用不可训练的外部记忆库存储历史上下文和知识,并通过检索相关的块级键值对增强模型输入。其技术优势包括分布一致性、高效训练策略及扩展的上下文窗口,能够在单个GPU上处理长达80k个token的文本,同时保持计算效率和内存控制。实验结果显示,MemLong在多个长文本基准数据集上表现出色,显著提升了语言建模能力和上下文学习效果。
1177 1
|
机器学习/深度学习 人工智能 API
游戏开发中的图形渲染技术:探索视觉盛宴的背后
【7月更文挑战第23天】游戏开发中的图形渲染技术是一个复杂而庞大的领域,它涵盖了从基础概念到高级应用的各个方面。随着技术的不断进步和创新,未来的游戏图形渲染将呈现出更加真实、生动和沉浸式的视觉效果。对于游戏开发者而言,掌握这些技术并不断创新将是实现成功游戏作品的关键所在。
|
SQL Java 数据库连接
【Java】已解决java.sql.SQLRecoverableException异常
【Java】已解决java.sql.SQLRecoverableException异常
2296 0
|
设计模式 Java
java实现一个简单的监听器
java实现一个简单的监听器
1225 0
|
XML Java 调度
什么是时间轮?
时间轮是一种用于任务调度和时间管理的数据结构,尤其适合处理大量定时任务的场景,如网络服务器或实时系统。它由一个圆形数组构成,每个槽代表固定时间间隔,任务根据执行时间添加到相应槽。时间推进时,指针移动并执行到期任务。时间轮具有高效性和简单性,插入和删除操作接近常数时间复杂度。层级时间轮可扩展处理更大时间范围和精度。在Spring Boot中,可以使用Netty的`HashedWheelTimer`实现高效定时任务管理。
388 0
|
Kubernetes Ubuntu Docker
Kubernetes(K8S v1.1版本) 集群管理Docker容器之部署篇
Kubernetes(K8S v1.1版本) 集群管理Docker容器之部署篇