K8S Pod

基本概念

Pod是K8S中非常重要的概念之一,是整个K8S架构的基础和核心。Pod是K8S调度的最小单位,是一个不可拆分的独立个体,K8S将多个业务上相关联的容器(Docker容器)合并到一起,组合成一个Pod,这些业务上相关的容器共享Pod中的网络和存储等资源。每个Pod都有一个唯一的IP地址,Pod中的所有容器都共享此IP地址。每个Pod在创建的时候K8S都会为其先创建一个根容器,即Sandbox容器或Pause容器,这个容器非常简单,就是一个主要包含for代码的死循环,起一个占位的作用。K8S先会为Pause容器分配网络命名空间,存储资源等,当在创建其它用户容器时,K8S只需要将这些用户容器加入到Pause容器即可,这样就实现了网络和存储资源的共享。

Pod分为普通Pod和静态Pod,如果没有特别说明,我们通常都使用普通Pod。静态Pod不常使用,其仅存在于特定的Node上,由kubelet进程管理,也无法与Deployment,DaemonSet等进行关联,其作用可能只是为了在某个特定的Node上做一些特殊的事情。

虽然Pod是K8S的最小调度单元,但绝大多数情况我们都不会直接创建一个Pod,因为其不具备副本管理,滚动升级等高级特性。通常都通过控制器来创建,如Deployment,DaemonSet,Job等。

Pod中容器共享Volume

只需要在Pod级别的配置中设置Volumes,可以是hostPath,emptyDir等等,然后在容器级别的配置中为不同的容器mount此Volume即可。

ConfigMap

ConfigMap用于设置Pod内容器的参数信息,如容器中进程监听的端口号,进程的命令行参数,对外依赖的服务名称等,能够实现应用和配置的有效分离。当然,这些配置信息都可以在Pod的yaml的定义中通过环境变量来指定,即给定一个环境变量名,然后写上此环境变量的值。但是有一个问题,如果Pod已经在运行,此时要修改Pod的某个配置参数,这将非常麻烦。一种做法是先把已经运行的Pod删除,再在yaml中修改配置参数,最后再apply此yaml。这个方法很粗暴,因为删除Pod的时候会导致Pod无法提供服务。最好的做法是使用ConfigMap,本质上ConfigMap也是设置环境变量,不同的是该环境变量的值没有hardcode,而是引用了某个ConfigMap。如果ConfigMap发生了改变,K8S会自动修改引用了该ConfigMap的Pod中对应的环境变量的值。

ConfigMap的使用很灵活,可以是key-value的形式,也可以是配置文件的形式,可以在定义ConfigMap的yaml中写上配置文件的全部内容,也可以指定从某个路径引用配置文件。

注意:如果要传递敏感配置,如密码,key等,需要使用Secret,而不是ConfigMap。

Downward API

如果Pod中的容器需要知道自己所属Pod的配置信息,如Pod名称,命名空间,IP地址,注解,Label,CPU Limit,Memory Limit等,可以使用Downward API在Pod的yaml定义中,使用环境变量或Volume挂载的方式传入到Pod的容器中。在很多场景下这些metadata数据都很有用,例如,在记录log的时候,可以带上pod名称用于标识log来源。

生命周期和重启策略

Pod的生命周期包含从Pod创建到Pod消亡的整个过程,通过Pod的状态可以标识不同的生命周期阶段。

Pending:API Server已经创建了Pod,但是Pod中至少还有一个容器没有被完全创建好,例如正在下载image。

Running:Pod中的所有容器都已创建,且正在运行。

Succeed:Pod中的容器都已经成功运行且正常退出,不会再重启。

Failed:Pod中的容器都已退出,但至少有一个的退出状态为失败。

Unknown:Pod状态未知。

Pod的重启策略包括Always,OnFailure和Never。

Always:当容器退出时(可能是正常或失败退出),由kubelet自动创建。

OnFailure:当容器以失败的方式退出时,由kubelet自动创建。

Never:任何情况都不重启。

重启策略还和控制器类别有关,例如Deployment和DaemonSet,重启策略只能是Always,因为这两种控制器都要求容器不能退出,需要一直运行来提供服务。Job和CronJob可以是OnFailure或Never。

健康检查和服务可用性检查

可以使用三类探针来判断某个容器是否正常,LivenessProbe,ReadinessProbe和StartupProbe。

LivenessProbe:用于判断容器是否还存活(Running),如果容器被判定没有存活,则K8S会kill此容器,并尝试重启。注意:这里是容器,不是Pod。

ReadinessProbe:用于判断容器能否正常提供服务,如果不能提供服务,则不会把容器加入到Endpoints列表中。注意和LivenessProbe的区别,如果容器是Running的状态,则可能是Ready或者不是Ready,如果不是Running的状态,肯定就不是Ready了。

StartupProbe :用于在容器创建时可能会占用很长时间的场景,通过StartupProbe能判断容器是否正常启动,属于"有且仅有一次"的长时间等待的检查。

Pod控制器

通过Pod控制器可以实现Pod的自动调度,控制器包括Deployment,ReplicaSet,Replication Controller,DaemonSet,StatefulSet,Operator,Job,CronJob等,参考(Controller部分)https://blog.csdn.net/funnyrand/article/details/135759060

NodeSelector

NodeSelector属于基于Node的定向调度,通过在Pod的yaml中设置nodeSelector,可以将Pod只调度到给定标签的Node上。这是比较旧的一种方式,控制粒度粗,但简单适用。

NodeAffinity/NodeAntiAffinity

在Node层面定义的一些规则,用于替换NodeSelector,可以很精确的控制将Pod调度到哪些Node上或者不调度到哪些Node上,有很灵活的匹配规则。

PodAffinity/PodAntiAffinity

在Pod层面定义的一些规则,用于控制Pod与Pod的亲和性和反亲和性,即希望哪些Pod能调度到一起,哪些Pod不能调度到一起。例如,在进行Server-Client的两节点测试中,我们希望部署在Server上的Pod和部署到Client上的Pod不能部署到一起,而不管它们部署到Node1还是Node2,只要不同时部署到一起即可。

Taints和Tolerations

污点和容忍,如果对某个Node设置了Taints,默认情况下K8S不会把Pod调度到此节点上,可能的原因是该Node的磁盘已满,内存不够,网速很慢,系统出现故障,Node需要维护,系统需要升级等。如果想让某个Pod被自动调度到Taints的Node上,需要在Pod的yaml中设置tolerations配置,标识此Pod能够容忍对应的Taints。

优先级调度

不同的Pod的优先级可能不一样,通过创建PriorityClass资源,并为Pod设置不同的PriorityClass可以控制Pod的优先级,K8S会优先调度高优先级的Pod。如果在创建某个高优先级的Pod时系统资源不够,例如,CPU和内存无法满足Limits的要求,此时K8S可能会驱逐(Evication)已经存在的低优先级的Pod。这里面的规则比较复杂,和QoS等配置也有关系。一般情况,让K8S驱逐已经存在的Pod是高风险操作,需要谨慎。

容灾调度

对于一个很大的K8S集群,集群中的Node可能分布在不同的区域(Zone),为了容灾的考虑,我们在创建某个Pod的时候希望在每个区域都能创建这个Pod,这里可以使用容灾调度。通过设置Pod的topologySpreadConstraints属性,并结合Node的topology.kubenetes.io/zone属性能够方便实现该需求。

自定义调度

如果K8S内置的调度策略无法满足我们的需求,我们可以实现自己的调度器。调度器的作用就是给定某个Pod,通过自定义的调度器的运算能够输出一个Node的名字,然后调用K8S的API(http://$SERVER/aip/v1/namespaces/default/pods/$PODNAME/binding),将Pod绑定到选择的Node上。自定义调度器可以用任何语言来实现。

初始化容器

初始化容器用于业务容器创建前的初始化工作,例如网络连通性检查,存储资源的创建等。通过initContainers可以为Pod配置多个初始化容器,K8S会按顺序执行每个初始化容器,只有所有的初始化容器都成功退出后,K8S才会创建和执行业务容器。

升级/回滚

Pod的升级/回滚通常针对Deployment控制器,如果直接创建Pod,则不具备此功能。

升级:如果某些Pod已经部署且正常运行,现在需要对Pod的Image升级,有三种做法,1)通过kubelet set image命令指定新的Image。2)通过kubelet edit deployment/deployment-xxx命令修改Deployment。3)手动修改yaml文件,然后再apply。升级的基本原理是K8S会创建一个新的ReplicaSet,并将副本数量设置为1,然后将旧的ReplicaSet的数量减1,接着依次进行加1和减1,直到旧的ReplicaSet的数量为0。

回滚 :如果在升级过程中发现Image的版本不正确(错误的版本或不存在的版本等),可以回滚Deployment。通过命令kubelet rollout status deployment/deployment-xxx查看Deployment当前部署状态,然后通过命令kubelet rollout history deployment/deployment-xxx查看这个Deployment的历史记录(这里会输出Deployment的REVISION),最后通过命令kubelet rollout undodeployment/deployment-xxx --to-revision=N来指定回到某个Deployment的版本。

对于多个配置的修改,可以先暂定Deployment的升级执行,等这些配置都全部修改完成后再一次性升级。相关命令:kubelet rollout pause deployment/deployment-xxx,kubelet rollout resumedeployment/deployment-xxx。

DaemonSet和StatefulSet的升级与Deployment的升级稍有不同,其通过不同的策略来完成升级,这里不详细说明。

扩容/缩容

分为手动和自动两种模式,手动扩容/缩容可以通过命令kubelet scal命令完成,自动扩容/缩容通过HPA功能完成。

通过HPA(Horizontal Pod Autoscaler)我们可以方便的使Pod自动扩容/缩容,例如,在业务量很大的时候自动增加Pod数量,在业务量很少的时候自动减少Pod数量。业务量的大小可以通过CPU使用率,内存使用率,或者自定义的某个指标来判断,自定义指标需要容器通过HTTP URL的形式返回给K8S。

相关推荐
码农小卡拉1 小时前
Docker Compose部署EMQX集群详细教程(Ubuntu环境优化版)
mqtt·ubuntu·docker·容器·emqx
WilliamHu.2 小时前
Windows 环境下使用 Docker 成功部署 Dify(完整实战记录)
运维·docker·容器
叫致寒吧2 小时前
Kubernetes 安全机制
安全·容器·kubernetes
Cyber4K3 小时前
【Kubernetes专项】零故障升级之Pod健康探测
云原生·容器·kubernetes
能不能别报错3 小时前
企业级生产级K8s平台
云原生·容器·kubernetes
幼稚园的山代王3 小时前
从 0 到 1,读懂 Kubernetes 核心概念
云原生·容器·kubernetes
秋天枫叶355 小时前
【k8s集群Docker + cri-dockerd】服务器重启或关机后 apiserver/controller/scheduler 无法自动恢复
linux·运维·服务器·容器·kubernetes·bug
不做码农好多年,该何去何从。5 小时前
docker(一)----使用docker安装运行tomcat
docker·容器·tomcat
Curvatureflight5 小时前
Docker容器化部署实战指南:从入门到生产环境
运维·docker·容器
又是进步的一天6 小时前
Kubernetes 证书体系与 OpenSSL 命令学习
学习·容器·kubernetes