深入掌握Pod
Pod
Pod可以由1个或多个容器组合而成。当和两个容器应用为紧耦合的关系,并组合成一个整体对外提供服务时,应将这两个容器打包为一个Pod
属于同一个Pod的多个容器应用之间相互访问时仅需通过localhost就可以通信,使得这一组容器被“绑定”在一个环境中。
Pod容器共享Volume
同一个Pod中的多个容器能够共享Pod级别的存储卷Volume。
Volume可以被定义为各种类型,多个容器各自进行挂载操作,将一个Volume挂载为容器内部需要的目录
在下面的例子中,在Pod内包含两个容器:tomcat和busybox,在Pod级别设置Volume“app-logs”,用于tomcat容器向其中写日志文件,busybox容器从中读日志文件。
Pod的配置管理
ConfigMap
ConfigMap供容器使用的典型用法如下。
- 生成容器内的环境变量。
- 设置容器启动命令的启动参数(需设置为环境变量)。
- 以Volume的形式挂载为容器内部的文件或目录。
ConfigMap以一个或多个key:value的形式保存在Kubernetes系统中供应用使用,既可以用于表示一个变量的值(例如apploglevel=info),也可以用于表示一个完整配置文件的内容(例如server.xml=<?xml...>...)。
我们可以通过YAML文件或者直接使用kubectl create configmap命令行的方式来创建ConfigMap。
Pod中使用configMap
环境变量的名称受POSIX命名规范([a-zA-Z_][a-zA-Z0-9_]*)约束,不能以数字开头。如果包含非法字符,则系统将跳
过该条环境变量的创建,并记录一个Event来提示环境变量无法生成,但并不阻止Pod的启动。
在容器内获取Pod信息(Downward API)
Pod的逻辑概念在容器之上,Kubernetes在成功创建Pod
之后,会为Pod和容器设置一些额外的信息,例如Pod级别的Pod名称、Pod IP、Node IP、Label、Annotation、容器级别的资源限制等。
在很多应用场景中,这些信息对容器内的应用来说都很有用,例如使用Pod名称作为日志记录的一个字段用于标识日志来源。为了在容器内获取Pod级别的这些信息,Kubernetes提供了Downward API机制来将Pod和容器的某些元数据信息注入容器环境内,供容器应用方便地使用。
Downward API可以通过以下两种方式将Pod和容器的元数据信息注入容器内部。
- 环境变量:将Pod或Container信息设置为容器内的环境变量。
- Volume挂载:将Pod或Container信息以文件的形式挂载到容器内部。
Pod生命周期和重启策略
Pod状态
状态值 | 描述 |
---|---|
Pending | API Server已经创建该Pod,但在Pod内还有一个或者多个容器镜像没有创建,包括正在下载的镜像过程 |
RUNNING | Pod内所有容器均已创建,且至少有一个容器处于运行状态、正在启动状态或正在重启状态 |
Succeeded | Pod内所有容器均成功执行后退出,且不会再重启 |
Failed | Pod内所有容器均已退出,但至少有一个容器为退出失败状态 |
Unknown | 由于某种原因无法获取该pod的状态,可能由于网络通信不畅导致 |
Pod的重启策略(RestartPolicy)应用于Pod内的所有容器,并且仅在Pod所处的Node上由kubelet进行判断和重启操作。当某个容器异常退出或者健康检查失败时,kubelet将根据RestartPolicy的设置进行相应的操作。
Pod的重启策略包括Always、OnFailure和Never,默认值为Always。
- Always:当容器失效时,由kubelet自动重启该容器。
- OnFailure:当容器终止运行且退出码不为0时,由kubelet自动
重启该容器。 - Never:不论容器运行状态如何,kubelet都不会重启该容器。
kubelet重启失效容器的时间间隔以sync-frequency乘以2n来计算,例如1、2、4、8倍等,最长延时5min,并且在成功重启后的10min后重置该时间。
Pod的重启策略与控制方式息息相关,当前可用于管理Pod的控制器
包括ReplicationController、Job、DaemonSet,还可以通过kubelet管理(静态Pod)。每种控制器对Pod的重启策略要求如下。
- RC和DaemonSet:必须设置为Always,需要保证该容器持续运
行。 - Job:OnFailure或Never,确保容器执行完成后不再重启。
- kubelet:在Pod失效时自动重启它,不论将RestartPolicy设置为什么值,也不会对Pod进行健康检查。
Pod健康检查和服务可用性检查
Kubernetes对Pod的健康状态可以通过三类探针来检查:LivenessProbe、ReadinessProbe及StartupProbe,其中最主要的探针为LivenessProbe与ReadinessProbe,kubelet会定期执行这两类探针来诊断容器的健康状况。
- LivenessProbe探针:用于判断容器是否存活(Running状态),如果LivenessProbe探针探测到容器不健康,则kubelet将“杀掉”该容器,并根据容器的重启策略做相应的处理。如果一个容器不包含LivenessProbe探针,那么kubelet认为该容器的LivenessProbe探针返回的值永远是Success。
- ReadinessProbe探针:用于判断容器服务是否可用(Ready状态),达到Ready状态的Pod才可以接收请求。对于被Service管理的Pod,Service与Pod Endpoint的关联关系也将基于Pod是否Ready进行设置。如果在运行过程中Ready状态变为False,则系统自动将其从Service的后端Endpoint列表中隔离出去,后续再把恢复到Ready状态的Pod加回后端Endpoint列表。这样就能保证客户端在访问Service时不会被转发到服务不可用的Pod实例上。需要注意的是,ReadinessProbe也是定期触发执行的,存在于Pod的整个生命周期中。
- StartupProbe探针:某些应用会遇到启动比较慢的情况,例如应用程序启动时需要与远程服务器建立网络连接,或者遇到网络访问较慢等情况时,会造成容器启动缓慢,此时ReadinessProbe就不适用了,因为这属于“有且仅有一次”的超长延时,可以通过StartupProbe探针解决该问题。
Pod Readiness Gates
Pod Readiness Gates给予了Pod之外的组件控制某个Pod就绪的能力,通过Pod Readiness Gates机制,用户可以设置自定义的Pod可用性探测方式来告诉Kubernetes某个Pod是否可用,具体使用方式是用户提供一个外部的控制器(Controller)来设置相应Pod的可用性状态。Pod的Readiness Gates在Pod定义中的ReadinessGate字段进行设置。
Pod调度
-
不同Pod之间的亲和性(Affinity)。比如MySQL数据库与Redis中间件不能被调度到同一个目标节点上,或者两种不同的Pod必须被调度到同一个Node上,以实现本地文件共享或本地网络通信等特殊需求,这就是PodAffinity要解决的问题。
-
有状态集群的调度。对于ZooKeeper、Elasticsearch、MongoDB、Kafka等有状态集群,虽然集群中的每个Worker节点看起来都是相同的,但每个Worker节点都必须有明确的、不变的唯一ID(主机名或IP地址),这些节点的启动和停止次序通常有严格的顺序。此外,由于集群需要持久化保存状态数据,所以集群中的Worker节点对应的Pod不管在哪个Node上恢复,都需要挂载原来的Volume,因此这些Pod还需要捆绑具体的PV。针对这种复杂的需求,Kubernetes提供了StatefulSet这种特殊的副本控制器来解决问题,在Kubernetes 1.9版本发布后,StatefulSet才可用于正式生产环境中。
-
在每个Node上调度并且仅仅创建一个Pod副本。这种调度通常用于系统监控相关的Pod,比如主机上的日志采集、主机性能采集等进程需要被部署到集群中的每个节点,并且只能部署一个副本,这就是DaemonSet这种特殊Pod副本控制器所解决的问题。
-
对于批处理作业,需要创建多个Pod副本来协同工作,当这些Pod副本都完成自己的任务时,整个批处理作业就结束了。这种Pod运行且仅运行一次的特殊调度,用常规的RC或者Deployment都无法解决,所以Kubernetes引入了新的Pod调度控制器Job来解决问题,并继续延伸了定时作业的调度控制器CronJob。
Deployment或RC:全自动调度
Deployment或RC的主要功能之一就是自动部署一个容器应用的多份副本,以及持续监控副本的数量,在集群内始终维持用户指定的副本数量。
NodeSelector:定向调度
Kubernetes Master上的Scheduler服务(kube-scheduler进程)负责实现Pod的调度,整个调度过程通过执行一系列复杂的算法,最终为每个Pod都计算出一个最佳的目标节点,这一过程是自动完成的,通常我们无法知道Pod最终会被调度到哪个节点上。在实际情况下,也可能需要将Pod调度到指定的一些Node上,可以通过Node的标签(Label)和Pod的nodeSelector属性相匹配,来达到上述目的。
NodeAffinity:Node亲和性调度
NodeAffinity意为Node亲和性的调度策略,是用于替换NodeSelector
的全新调度策略。目前有两种节点亲和性表达。
- RequiredDuringSchedulingIgnoredDuringExecution:必须满足指定的规则才可以调度Pod到Node上(功能与nodeSelector很像,但是使用的是不同的语法),相当于硬限制。
- PreferredDuringSchedulingIgnoredDuringExecution:强调优先满足指定规则,调度器会尝试调度Pod到Node上,但并不强求,相当于软限制。多个优先级规则还可以设置权重(weight)值,以定义执行的先后顺序。
IgnoredDuringExecution的意思是:如果一个Pod所在的节点在Pod运行期间标签发生了变更,不再符合该Pod的节点亲和性需求,则系统将忽略Node上Label的变化,该Pod能继续在该节点上运行。
PodAffinity:Pod亲和与互斥调度策略
在实际的生产环境中有一类特殊的Pod调度需求:存在某些相互依赖、频繁调用的Pod,它们需要被尽可能地部署在同一个Node节点、机架、机房、网段或者区域(Zone)内,这就是Pod之间的亲和性;反之,出于避免竞争或者容错的需求,我们也可能使某些Pod尽可能地远离某些特定的Pod,这就是Pod之间的反亲和性或者互斥性。
Pod间的亲和性与反亲和性调度策略从Kubernetes 1.4版本开始引入。简单地说,就是相关联的两种或多种Pod是否可以在同一个拓扑域中共存或者互斥,前者被称为Pod Affinity,后者被称为Pod AntiAffinity。那么,什么是拓扑域,如何理解这个新概念呢?
一个拓扑域由一些Node节点组成,这些Node节点通常有相同的地理空间坐标,比如在同一个机架、机房或地区,我们一般用region表示机架、机房等的拓扑区域,用Zone表示地区这样跨度更大的拓扑区域。在极端情况下,我们也可以认为一个Node就是一个拓扑区域。
Taints和Tolerations(污点和容忍)
前面介绍的NodeAffinity节点亲和性,是在Pod上定义的一种属性,使得Pod能够被调度到某些Node上运行(优先选择或强制要求)。
Taint则正好相反,它让Node拒绝Pod的运行。简单地说,被标记为Taint的节点就是存在问题的节点,比如磁盘要满、资源不足、存在安全隐患要进行升级维护,希望新的Pod不会被调度过来,但被标记为Taint的节点并非故障节点,仍是有效的工作节点,所以仍需将某些Pod调度到这些节点上时,可以通过使用Toleration属性来实现。
Pod Priority Preemption:Pod优先级调度
对于运行各种负载(如Service、Job)的中等规模或者大规模的集群来说,出于各种原因,我们需要尽可能提高集群的资源利用率。而提高资源利用率的常规做法是采用优先级方案,即不同类型的负载对应不同的优先级,同时允许集群中的所有负载所需的资源总量超过集群可提供的资源,在这种情况下,当发生资源不足的情况时,系统可以选择释放一些不重要的负载(优先级最低的),保障最重要的负载能够获取足够的资源稳定运行。
在Kubernetes 1.8版本之前,当集群的可用资源不足时,在用户提交新的Pod创建请求后,该Pod会一直处于Pending状态,即使这个Pod是一个很重要(很有身份)的Pod,也只能被动等待其他Pod被删除并释放资源,才能有机会被调度成功。Kubernetes 1.8版本引入了基于Pod优先级抢占(Pod Priority Preemption)的调度策略,此时Kubernetes会尝试释放目标节点上低优先级的Pod,以腾出空间(资源)安置高优先级的Pod,这种调度方式被称为“抢占式调度”。在Kubernetes 1.11版本中,该特性升级为Beta版本,默认开启,在后续的Kubernetes 1.14版本中正式Release。如何声明一个负载相对其他负载更重要?我们可以通过以下几个维度来定义:Priority:优先级;QoS:服务质量等级;系统定义的其他度量指标。
优先级抢占调度策略的核心行为分别是驱逐(Eviction)与抢占(Preemption),这两种行为的使用场景不同,效果相同。
Eviction是kubelet进程的行为,即当一个Node资源不足(under resource pressure)时,该节点上的kubelet进程会执行驱逐动作,此时kubelet会综合考虑Pod的优先级、资源申请量与实际使用量等信息来计算哪些Pod需要被驱逐;
当同样优先级的Pod需要被驱逐时,实际使用的资源量超过申请量最大倍数的高耗能Pod会被首先驱逐。对于QoS等级为“Best Effort”的Pod来说,由于没有定义资源申请(CPU/Memory Request),所以它们实际使用的资源可能非常大。
Preemption则是Scheduler执行的行为,当一个新的Pod因为资源无法满足而不能被调度时,Scheduler可能(有权决定)选择驱逐部分低优先级的Pod实例来满足此Pod的调度目标,这就是Preemption机制。
需要注意的是,Scheduler可能会驱逐Node A上的一个Pod以满足Node B上的一个新Pod的调度任务。
使用优先级抢占的调度策略可能会导致某些Pod永远无法被成功调度。因此优先级调度不但增加了系统的复杂性,还可能带来额外不稳定的因素。因此,一旦发生资源紧张的局面,首先要考虑的是集群扩容,如果无法扩容,则再考虑有监管的优先级调度特性,比如结合基于命名空间的资源配额限制来约束任意优先级抢占行为。
DaemonSet:在每个Node上都调度一个Pod
DaemonSet是Kubernetes 1.2 版本新增的一种资源对象,用于管理在集群中的每个Node上仅运行一份Pod的副本实例
这种用法适合有这种需求的应用。
- 在每个Node上都运行一个GlusterFS存储或者Ceph存储的Daemon进程。
- 在每个Node上都运行一个日志采集程序,例如Fluentd或者Logstach。
- 在每个Node上都运行一个性能监控程序,采集该Node的运行性能数据,例如Prometheus Node Exporter、collectd、New Relic agent或者Ganglia gmond等。
Job:批处理调度
Kubernetes从1.2版本开始支持批处理类型的应用,我们可以通过Kubernetes Job资源对象来定义并启动一个批处理任务。批处理任务通常并行(或者串行)启动多个计算进程去处理一批工作项(Work item),处理完成后,整个批处理任务结束。
Cronjob:定时任务
自定义调度器
Pod容灾调度
我们可以将Pod的各种常规调度策略认为是将整个集群视为一个整体,然后进行“打散或聚合”的调度。当我们的集群是为了容灾而建设的跨区域的多中心(多个Zone)集群,即集群中的节点位于不同区域的机房时,比如北京、上海、广州、武汉,要求每个中心的应用相互容灾备份,又能同时提供服务,此时最好的调度策略就是将需要容灾的应用均匀调度到各个中心,当某个中心出现问题时,又自动调度到其他中心均匀分布
Init Container(初始化容器)
在很多应用场景中,应用在启动之前都需要进行如下初始化操作。
- 等待其他关联组件正确运行(例如数据库或某个后台服务)。
- 基于环境变量或配置模板生成配置文件。
- 从远程数据库获取本地所需配置,或者将自身注册到某个中央数据库中。
- 下载相关依赖包,或者对系统进行一些预配置操作。
Kubernetes 1.3版本引入了一个Alpha版本的新特性init container(初始化容器,在Kubernetes 1.5版本时被更新为Beta版本),用于在启动应用容器(app container)之前启动一个或多个初始化容器,完成应用容器所需的预置条件,如图3.10所示。init container与应用容器在本质上是一样的,但它们是仅运行一次就结束的任务,并且必须在成功运行完成后,系统才能继续执行下一个容器。根据Pod的重启策略(RestartPolicy),当init container运行失败而且设置了
RestartPolicy=Never时,Pod将会启动失败;而设置RestartPolicy=Always时,Pod将会被系统自动重启。
Pod的升级和回滚
当集群中的某个服务需要升级时,我们需要停止目前与该服务相关的所有Pod,然后下载新版本镜像并创建新的Pod。如果集群规模比较大,则这个工作变成了一个挑战,而且先全部停止然后逐步升级的方式会导致较长时间的服务不可用。Kubernetes提供了滚动升级功能来解决上述问题。
如果Pod是通过Deployment创建的,则用户可以在运行时修改Deployment的Pod定义(spec.template)或镜像名称,并应用到Deployment对象上,系统即可完成Deployment的rollout动作,rollout可被视为Deployment的自动更新或者自动部署动作。如果在更新过程中发生了错误,则还可以通过回滚操作恢复Pod的版本。
Pod的扩缩容
在实际生产系统中,我们经常会遇到某个服务需要扩容的场景,也可能会遇到由于资源紧张或者工作负载降低而需要减少服务实例数量的场景。此时可以利用Deployment/RC的Scale机制来完成这些工作。
Kubernetes对Pod的扩缩容操作提供了手动和自动两种模式,手动模式通过运行kubectl scale命令或通过RESTful API对一个Deployment/RC进行Pod副本数量的设置,即可一键完成。自动模式则需要用户根据某个性能指标或者自定义业务指标,并指定Pod副本数量的范围,系统将自动在这个范围内根据性能指标的变化进行调整。
标签:Node,容器,Kubernetes,掌握,调度,节点,深入,pod,Pod From: https://www.cnblogs.com/zpf253/p/18200930