本文共 7566 字,大约阅读时间需要 25 分钟。
kubernetes中的Node、Pod、Replication Controller、Service等都可以看作为资源对象,几乎所有的资源对象都可以通过kubectl工具执行增删改查并将其保存在etcd中持久化存储。 kubernetes通过对资源进行监控,并对比etcd库中保存的资源期望状态与当前环境中资源实际状态的差异来实现对容器群的自动控制与自动纠错。
Master和Node都属于物理主机或虚拟机. Master是集群的控制节点, 负责集群的管理和控制, 接收并执行kubernetes的控制命令.
Master之上运行如下关键进程:
● Kubernetes API Server(kube-apiserver): 提供标准的Http Rest 接口,是kubernetes资源增删改查的唯一入口, 也是集群控制的入口进程;
● Kubernetes Controller Manager (kube-controller-manager): kubernetes中所有资源对象的自动化控制中心, 是资源对象的大总管;
● Kubernetes Scheduler(kube-scheduler): 负责资源调度(pod调度)的进程;
● etcd: Master节点上需要启动etcd服务, kubernetes所有的资源对象的数据都会保存在etcd中.
除了Master, kubernetes集群中其他所有机器称之为Node节点. Node节点可以在运行期间动态的增加到kubernetes集群中, 当Node被纳入集群中, kubelet会自动向Master注册自己从而被Master所管理, Master会分配给Node任务, 当Node宕机时, 其工作负载会被Master自动转移到其他Node上.
Node会定时向Master发送自身的信息, 包括操作系统, Docker版本, 机器CPU和内存使用情况, 以及当前有哪些pod在运行等. Master根据这些信息获知每个Node的资源使用情况, 从而实现高效均衡的资源调度策略. 当某个Node超过指定时间没有上报信息时, 会被Master判断为"失联", 该Node状态被标记为"不可用"(Not Ready), 随后Master会触发"工作负载转移"的自动流程.
Node上运行的关键进程如下:
● kubelet: 负责Pod对应容器的创建、启停等任务,同时与Master节点密切协作,实现集群管理的基本功能;
● kube-proxy: 实现kubernetes Service的通信与负载均衡机制的主要组件;
● Docker Engine(docker): Docker引擎,负责本机容器创建以及管理工作, 管理容器的.
pod运行在Node之上, 一个Node可运行多个pod; 容器运行在pod中, 一个pod可对应多个容器.
pod类别:
● 静态pod(Static Pod): 不存放在etcd存储中, 而是存放在某个具体Node上的一个具体文件中, 并且只在该Node上启动运行;
● 普通pod: 普通pod一旦被创建, 会被立即放入etcd中存储, 随后会被kubernetes Master调度到某个具体的Node上并运行绑定(Binding), 随后该pod被对应的Node上的kubelet进程实例化成一组相关的docker容器并启动起来.
容器类别:
● Pause容器: 根容器, 其IP和挂载的Volume被业务容器所共享;
● 业务容器: 用户自定义的容器.
在默认情况下, pod里的某个容器停止运行时, kubernetes会自动检测到这个问题并且重新启动这个pod(重启pod里的所有容器), 如果pod所在的node宕机, 则会将这个node上的所有pod重新调度到其他节点.
Label是用户指定的键值对. 可附加到各种资源对象上, 例如Node, Pod, Service, RC等. 一个资源对象可定义多个Label, 同一个Label可应用到多个资源对象中. Label可在资源对象定义时指定, 也能在运行过程中动态添加和删除.
通过Label Selector(标签选择器)可以筛选拥有某些标签的资源对象, 相当于SQL中的WHERE条件, 例如name=redis-server的Label Selector作用于pod时, 相当于条件WHERE name=redis-server. 我们可以通过为指定的资源对象添加多个Label实现多维度的资源分组管理.
RC用于定义期望值, 例如声明某种pod的副本数量在任意时刻都符合某个预期值, 所以其定义包括如下几个部分:
● Pod期待的副本数;
● 用于筛选目标Pod的Label Selector;
● 当Pod的副本数小于预期数量的时候,用于创建新Pod的Pod模板。
当RC被定义并提交到kubernetes集群中, Master节点的Controller Manage组件就会得到通知, 从而依据RC的定义, 定期巡检系统中存活的目标pod, 确保目标pod实例的数量刚好等于此RC的期望值, 如果有过多pod副本在运行, 系统就会停掉一些pod, 否则自动创建pod. 通过RC, kubernetes实现了用户应用集群的高可用性.
在运行时, 能够通过动态更改RC的副本数量, 来实现pod的动态缩放.
删除RC时, 不会影响通过该RC已创建好的pod, 如果想要删除其创建的pod, 可以通过设置其副本数量为0实现. 另外, kubectl提供了stop和delete命令来一次性删除RC及其控制的全部pod.
此外, RC可以实现滚动升级, 蓝绿部署.
总结RC特性及作用:
● 在大多数情况下,我们通过定义一个RC实现Pod的创建过程以及副本数量的自动控制;
● RC里包括完整的Pod定义模板;
● RC通过Label Selector 机制实现对Pod副本的自动控制;
● 通过改变RC的Pod副本数量,可以实现Pod的扩容和缩容功能;
● 通过改变RC里Pod模板中的镜像版本,可以实现Pod的滚动升级功能.
由于Replication Controller与kubernetes中的模块Replication Controller同名,所以在kubernetes 1.2 ,它升级为Replica Set ,与之前的RC的唯一区别是支持基于集合的Label selector ,而RC只支持基于等式的Label Selector 这使得Replica Set的功能更强。
我们很少直接使用Replica Set, 它主要被Deployment这个更高层的对象使用, 从而形成一整套的pod创建, 删除, 更新的编排机制. 当我们使用Deployment时, 无需关心它是如何创建和维护Replica Set对象的.
Replica Set和Deployment这两个资源对象逐步替代了之前的RC的作用, 是v1.3 pod自动扩容(伸缩)这个告警功能实现的基础.
Deployment为了更好地解决pod的编排问题, 为此, Department在内部使用了Replica Set对象 , Department可以认为是RC的一次升级, 两者相似度超过90%.
相对于RC, Department可以随时知道当前pod部署的进度, 一个pod的创建, 调度, 绑定节点以及在目标Node启动对应的容器这一完整过程需要一定的时间, 就是一个连续变化的部署过程.
典型适用场景:
● 创建一个Deployment对象来生成对应的Replica Set并完成Pod副本的创建过程;
● 检查Deployment的状态来看部署动作是否完成(Pod副本的数量是否达到预期的值);
● 更新Deployment以创建新的Pod(比如镜像升级);
● 如果当前的Depoyment不稳定,则回滚到一个早先的Deployment版本;
● 扩展Department以应对高负载;
● 查看Department状态, 以此作为发布是否成功的指标.
● 清理不需要的旧版Replica Set.
Pod横向自动扩容,通过追踪分析RC控制的所有目标Pod的负载变化情况,来确定是否需要针对性的调整目标Pod的副本数.
HPA有以下两种方式来作为HPA的负载指标:
● CPUUtilizationPercentage 是指Cpu利用率的平均值, 通常是过去1min内的平均值;
● 应用程序自定义的度量标准,比如服务在每秒内的响应的请求数(TPS或QPS)
如果某一指标到达临界点, 例如CPUUtilizationPercentage达到80%, 可以认为是当前pod副本数很可能不足以支撑接下来更多的请求, 此时会触发动态扩容, 当请求高峰期过去后, pod的CPUUtilizationPercentage又会降下来, 此时对应的pod数就会自动减少到一个合理的水平.
在kubernetes中, pod的管理对象RC, Deployment, DaemonSet和Job都是面向无状态的服务, 但是现实中很多服务需要状态, 例如Mysql, Zookeeper等. 这些应用的集群有以下共同点:
● 每个节点都有固定的身份ID, 通过这个ID, 集群中的成员可以相互发现并且通信;
● 集群的规模是比较固定的, 集群规模不能随意改动;
● 集群中每个节点都是有状态的, 通常会持久化数据到永久存储中;
● 如果磁盘损坏, 则集群中某个节点无法正常运行, 集群功能受损.
如果用RC / Deployment控制pod副本数方式来实现有状态的集群, 会发现第一点无法满足, 因为pod名字是随机产生的, 无法为每个pod确定唯一不变的ID. 另外, 为了能够在其他节点上恢复某个失败的节点, 这种集群中的pod需要挂载某种共享的存储. 为了解决这个问题, kubernetes在v1.4引入了PetSet, 并在v1.5更名为StatefulSet.
StatefulSet可以看为Deployment / RC的变种, 有如下特性:
● StatefulSet里的每个pod都有稳定唯一的网络标识, 可以用来发现集群中其他成员. 假设StatefulSet名字为kafka, 那么第一个pod叫kafka-0, 第二个为kafka-1, 以此类推;
● StatefulSet控制的pod副本的启停顺序是受控的, 操作第n个pod时, 前n-1个pod已经是运行且准备好的状态;
● StatefulSet里的pod采用稳定的持久化存储卷, 通过PV / PVC来实现, 删除pod时, 默认不会删除与StatefulSet相关的存储卷.
kubernetes中的Service对应的是微服务架构中的一个微服务, 之前的pod, RC等资源对象都是为Service服务的.
Service定义了一个服务的访问入口地址, 前端应用通过这个入口地址访问其背后的一组由pod副本组成的集群实例. Service与其后端pod副本集群之间则是通过Label Selector来实现对接. 而RC的作用实际上是保证Service的服务能力和服务质量始终保持在预期的标准.
因为pod的IP地址会随着pod的销毁和重新创建而变化,所以访问端不能以写死IP的方式去访问pod提供的服务。而service作为pod路由代理抽象,访问端只需要知道service的地址,由service来提供代理,保证了pod的动态变化对访问端透明。
每个Node上运行的kube-proxy是一个智能的软件负载均衡器,负责将客户端发送到Service的请求依据负载均衡算法转发到后端的某个pod实例上.
Service一旦被创建, kubernetes会为它分配一个可用的Cluster IP, 在Service整个生命周期内都不会发生变化. 于是服务发现的问题被解决了: 使用Service的Name与Cluster IP做一个DNS映射即可.
由此知道, 客户端访问Service地址, 然后Service将请求转发给pod.
Service地址如何得到?
首先看一下Cluster IP特点:
● Cluster IP仅仅作用于Kubernetes Service这个对象,并由kubernetes管理和分配IP地址(来源于Cluster IP地址池);
● Cluster IP无法被ping,因为没有一个实体网络对象来响应;
● Cluster IP只能结合Service Port组成一个具体的通信端口,单独的Cluster IP 不具备TCP/IP通信的基础,并且它们只属于Kubernetes集群这样一个封闭的空间,集群之外的节点要访问这个通信端口,则要做一些额外的工作;
● 在Kubernetes集群之内,Node IP网、Pod IP网与Cluster IP网之间的通信,采用的是Kubernetes自己设计的一种编程方式的特殊的路由规则,与我们熟知的IP路由有很大的不同。
也就是说外网不能直接访问Cluster IP, 那么如何访问Service? 通过NodePort来访问的。在Service定义中指定NodePort, 例如10000, 那么通过NodeIP + NodePort即可访问到Service.
NodePort的实现方式是在kubernetes集群里的每个Node上为需要外部访问的Service开放一个对应的监听端口, 外部系统只要用任意一个Node的IP地址+具体的NodePort端口号即可访问此服务.
Volume是pod中能被多个容器访问的共享目录. 除了可以让多个容器共享文件, 让容器数据写到宿主机的磁盘上或网络存储外, 还能通过ConfigMap做容器配置文件集中化定义与管理.
Volume类型:
● emptyDir: pod被分配到Node时创建的, 初始内容为空, 无需指定宿主机上对应的目录文件, 因为这是kubernetes自动分配的一个目录;
● hostPath: pod挂载在宿主机上的文件和目录;
● NFS: NFS可通过网络将远程NFS服务器分享的目录挂载到本地,使得不同的机器或操作系统共享文件. 使用NFS网络文件系统提供的共享目录存储数据时, 需要在系统中部署一个NFS Server.
PV是集群中的一块网络存储,是Kubernetes集群的一种资源。
Pod的Volume与PV的区别:
Volume从属于pod, 生命周期和Pod相同,Pod被删除时,Volume和保存在Volume中的数据就被删除了;Volume和使用它的Pod之间是一种静态绑定关系,在定义Pod文件时也定义了它使用的Volume。Volume是Pod的附属品,我们无法单独创建一个Volume,因为它不是一个独立的资源对象;
PV是一个独立的资源对象,所以我们可以单独创建一个PV。它不和Pod直接发生关系,而是通过PVC来实现动态绑定。Pod定义里指定的PVC,然后PVC根据Pod的要求自动绑定合适的PV。PV是独立的,即使挂载PV的Pod被删除了,PV和PV上的数据仍然存在。
PV访问模式范围如下:
● ReadWriteOnce:该卷能够以读写模式被加载到一个节点上。
● ReadOnlyMany:该卷能够以只读模式加载到多个节点上。
● ReadWriteMany:该卷能够以读写模式被多个节点同时加载。
PV的状态:
● Available:可用资源,尚未被绑定到 PVC 上;
● Bound:已经绑定到某个PVC上;
● Released:对应的PVC 已经被删除,但该资源尚未被集群回收;
● Failed:该卷的自动回收过程失败。
Namespace在很多情况下用于实现多租户的资源隔离,Namespace通过将集群内部的资源对象分配到不同的namespace中,形成逻辑上分组的不同项目、小组和用户组, 便于不同的分组在共享集群的资源时还能被分别管理.
kubernetes集群在启动后, 默认创建一个名为default的Namespace, 如果不特别指明Namespace, 用户创建的pod, RC, Service都将被系统创建到为default的NameSpace中.
一旦创建了namespace, 在创建资源对象时就可以指定哪些资源属于哪个namespace.
结合kubernetes的资源配额管理, 能够限定不同租户能占用的资源, 例如CPU / 内存使用量等.
Annotation与Label类似, 同样使用键值对定义. 但Label定义的是对象的元数据, 可用于Label Selector. 而Annotation是用户任意定义的附加信息, 以便于外部工具查找, 很多时候, kubernetes的模块自身会通过Annotation方式标记资源对象的一些特殊信息.
注: 本文基于Kubernete权威指南进行总结.
转载地址:http://zxgfl.baihongyu.com/