基本概念

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 undo deployment/deployment-xxx --to-revision=N来指定回到某个Deployment的版本。

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

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

扩容/缩容

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

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

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐