本节为课程延伸学习内容,对Kubernetes的设计思想、核心概念和机制进行介绍,以便帮助您更好的理解容器集群与编排技术。
Kubernetes 设计思想
Kubernetes 基于API管理一切的思想,采用声明式即“面向结果”的API,围绕 etcd(分布式存储与协调数据库) 构建出来的一套 “面向终态” 的编排体系。
当用户向 Kubernetes 提交了一个 API 对象(Kubernetes Object)的期望状态(Spec)之后,Kubernetes 会负责保证整个集群里各项资源的当前状态(Status),都与 API 对象描述的需求相一致。更重要的是,这个保证是一项 “无条件的”、“没有期限” 的承诺:对于每个保存在 etcd 里的 API 对象,Kubernetes 都通过启动一种叫做 “控制器模式”(Controller Pattern)的无限循环,不断对 etcd 里的 API 对象的变化进行监视(Watch),然后执行控制器(Controller)里定义的编排动作的响应逻辑,进行调谐,最后确保整个集群的状态与 API 对象的描述一致。
为了实现“面向终态”的管理,支持自动化部署、扩缩和管理容器应用,Kubernetes采用了控制平面和计算平面分离的架构。控制平面是整个集群的大脑,负责控制、调度集群资源;计算平面负责运行容器化应用,是控制平面调度的对象,通过增加或减少工作节点实现容器集群处理能力的扩缩。控制平面由至少一个管理节点(Master节点)组成,通常会采用三个管理节点组成高可用集群(一个管理节点提供服务,剩下两个管理节点为备用节点,当管理节点不可用时,从备用节点中自动选举一个出来成为管理节点)。计算平面则由多个工作节点(Node节点)组成。
1.Master节点
Master节点(管理节点)主要负责管理和控制整个Kubernetes集群,对集群做出全局性决策,相当于整个集群的“大脑”。集群所执行的所有控制命令都由Master节点接收并处理。它在集群中主要负责如下任务:
- 集群的“大脑”,负责管理所有Node节点。
- 负责调度Pod在哪些节点上运行。
- 负责控制集群运行过程中的所有状态。
一个管理节点包含四个主要组件:API Server、Controller Manager、Scheduler 及 etcd。
2.Node节点
Node节点(工作节点)是Kubernetes集群中的工作节点,Node节点上的工作由Master节点进行分配,比如当某个Node节点宕机时,Master节点会将其上面的工作转移到其他Node节点上。Node节点在集群中主要负责如下任务:
- 负责管理所有容器(Container)。
- 负责监控/上报所有Pod的运行状态。
一个Node节点主要包含三个组件:kubelet、kube-proxy、Container Runtime。
Controller是什么?
工作负载是一种Controller,在Kubernetes中还有Node Controller等其他多种控制器。每种控制器都是一个智能系统,通过API Server提供的(List-Watch)接口实时监控集群中资源对象的变化,当资源对象因某些原因发生状态变化时,Controller会执行相应逻辑使其最终状态调整到期望状态。比如:某个Node意外宕机时,Node Controller会及时发现此故障并执行自动化修复流程,确保集群始终处于预期的工作状态。
每种Controller都负责一种特定的资源控制器,Controller Manager是Kubernetes中各种Controller的管理者,是集群内部的管理控制中心,也是Kubernetes自动化功能核心。
Kubernetes中如何实现多个环境的隔离?
在Kubernetes容器集群中,同一类型的资源名称是唯一的。在实际中,我们往往需要将不同业务、不同的项目、不同的环境进行隔离管理,这就需要通过命名空间Namespace进行分区管理。
Namespace 是用来做集群内部的逻辑隔离的,它包括鉴权、资源管理等。Kubernetes 的每个资源,比如 Pod、Deployment、Service,都属于一个 Namespace,同一个 Namespace 中的资源命名唯一,不同的 Namespace 中的资源可以重名。在一个Kubernetes集群中可以拥有多个命名空间,它们在逻辑上彼此隔离。
不过,Kubernetes也有一些资源隶属于集群级别的,如Node、Namespace和Persistent Volume等,不属于任何名称空间,所以这些资源对象的名称必须全局唯一。
Kubernetes的原子调度单位Pod
在操作系统中,进程不是"孤苦伶仃"的运行,而是以进程组的方式,"有原则的"组织到一起运行。一个进程组内的进程可以共享文件和资源。Kubernetes借鉴了操作系统的"进程组" 的概念,并抽象出一个逻辑概念Pod。由于Pod是一组紧密关联的容器集合,也叫它容器组。Pod是Kubernetes中操作的基本单元。为了便于理解,我们可以做个类比,Kubernetes就是一个操作系统,就像Linux;Pod就是一个进程组,就像Linux线程组;容器就是一个进程,就像Linux线程。
Kubernetes把Pod作为原子调度单位,主要有两个原因:
- 有的业务需要多个容器一起协作才能完成,如业务容器产生日志,并通过日志采集容器将日志传输到日志系统中进行分析。这两个容器构成一个容器组,密切协作,不能拆分。如果没有Pod,这两个容器就可能被分别调度到不同的Node节点上,导致无法正常工作。需要注意的是,Pod中只会有一个主容器,其他容器只能是辅助容器。
- 若Kubernetes直接管理容器,由于容器销毁就没法跟踪了,难以了解容器的真实状态。通过Pod作为调度单位,因其支持健康检查,可以了解容器存活状况。
在创建无状态工作负载过程中,Kubernetes都做了哪些事情呢?
延伸阅读:无状态Pod的创建流程
我们知道无状态工作负载Deployment创建容器组,是通过控制ReplicaSet来实现的,下面我们了解下ReplicaSet创建Pod 的详细流程。
图中有三个 List-Watch,分别是 Controller Manager(运行在 Master),Scheduler(运行在 Master),kubelet(运行在 Node)。它们在进程一启动就会监听(Watch)API Server 发出来的事件。我们来看下创建的12个步骤。
- 用户通过 kubectl 或其他 API 客户端,提交请求给 API Server 来建立一个 Pod 对象副本(ReplicaSet)。
- API Server 将 Pod 对象的相关元信息存入 etcd 中,待写入操作执行完成,API Server 即会返回确认信息至客户端。
- 当 etcd 接受创建 Pod 信息以后,会发送一个 Create 事件给 API Server。
- 由于 Controller Manager 一直在监听(Watch,通过http的8080端口)API Server 中的事件。此时 API Server 接收到了 Create 事件,又会发送给 Controller Manager。
- Controller Manager 在接到 Create 事件以后,调用其中的 ReplicaSet(简称RS)来保证 Node 上面需要创建的副本数量。一旦副本数量少于 RS 中定义的数量,RS 会自动创建副本。总之它是保证副本数量的 Controller(扩容缩容的担当)。
- 在 Controller Manager 创建 Pod 副本以后,API Server 会在 etcd 中记录这个 Pod 的详细信息。例如 Pod 的副本数,Container 的内容是什么。
- 同样, etcd 会将创建 Pod 的信息通过事件发送给 API Server。
- 由于 Scheduler 在监听(Watch)API Server,并且它在系统中起到了“承上启下”的作用,“承上”是指它负责接收创建的 Pod 事件,为其安排 Node;“启下”是指安置工作完成后,Node 上的 kubelet 进程会接管后继工作,负责 Pod 生命周期中的“后半生”。 换句话说,Scheduler 的作用是将待调度的 Pod 按照调度算法和策略绑定到集群中 Node 上。
- Scheduler 调度完毕以后会更新 Pod 的信息,此时的信息更加丰富了。除了知道 Pod 的副本数量,副本内容。还知道部署到哪个 Node 上面了。并将上面的 Pod 信息更新至 API Server,由 API Server 更新至 etcd 中,保存起来。
- etcd 将更新成功的事件发送给 API Server,API Server 也开始反映此 Pod 对象的调度结果。
- kubelet 是在 Node 上面运行的进程,它也通过 List-Watch 的方式监听(Watch,通过https的6443端口)API Server 发送的 Pod 更新的事件。kubelet 会尝试在当前节点上调用容器运行时启动Pod,并将 Pod 以及容器的结果状态回送至 API Server。
- API Server 将 Pod 状态信息存入 etcd 中。在 etcd 确认写入操作成功完成后,API Server将确认信息发送至相关的 kubelet,事件将通过它被接受。
注意:在完成Pod的创建之后,kubelet 还会一直保持监听,为什么?原因很简单,客户可能有新的请求,如kubectl 发出命令要求扩充 Pod 副本数量,那么上面的流程又会触发一遍,kubelet 会根据最新的 Pod 的部署情况调整 Node 的资源。又或者 Pod 副本数量没有发生变化,但是其中的镜像文件升级了,kubelet 也会自动获取最新的镜像文件并且加载。
Pod内如何实现数据共享?
Volume是Pod中能够被多个容器访问的共享目录,通过Volume可以让容器的数据写到宿主机上或者写文件到网络存储中。
Kubernetes中的Volume与Docker的Volume相似,但不完全相同。
- Kubernetes的Volume定义在Pod上,然后被一个Pod中的多个容器挂载到具体的文件目录下。
- Kubernetes的Volume与Pod生命周期相关,而与容器的生命周期无关,即容器挂掉,数据不会丢失。但是Pod挂掉,数据则会丢失。
Kubernetes的Volume使用方法,是先在Pod上声明一个Volume,然后容器引用该Volume并Mount到容器的某个目录。
Volume有emptyDir和hostPath两种类型。emptyDir是在Pod分配到Node时创建,初始内容为空,无须指定宿主机上对应的目录文件,由Kubernetes自动分配一个目录,当Pod被删除时,对应的emptyDir数据会永久删除。hostPath则是在Pod上挂载宿主机上的文件或目录,与Pod的销毁无关。
Kubernetes的数据如何持久存储?
Kubernetes是通过PV和PVC来实现集群数据的持久化的。
- PV即Persistent Volume ,是集群中由管理员配置的一段网络存储,是集群的全局资源,不属于任何命名空间。PV生命周期独立于使用PV的任何单个Pod,不和Pod有生命周期牵扯,独立存活。
- PVC即Persistent Volume Claim ,是由用户进行存储的请求。也就是说,PVC就是调用PV,访问储存。Pod不能直接使用PV,必须挂载PVC,在PVC绑定PV,Pod才能访问存储。PVC和PV是一一对应的,PVC与挂载它的Pod隶属于同一个命名空间。
工作负载(Workload)是什么?
为了减轻用户的使用负担,通常不需要用户直接管理每个 Pod。 而是使用工作负载资源来替用户管理一组Pod,比如启动指定数量的Pod或实现容器应用的滚动升级。工作负载是在 Kubernetes 上运行的应用程序,是一种控制器。
工作负载是管理Pod的中间层,使用了工作负载之后,只需要告诉它,想要多少个、什么样的Pod就可以了,它就会创建出满足条件的Pod,并确保每一个Pod处于用户期望的状态,如果Pod在运行中出现故障,控制器会基于指定策略重启或者重建Pod。
Kubernetes集群应用的访问
通过工作负载我们创建了一组Pod出来,由于Pod会被销毁后重启,或者会增加新的Pod。用户怎么能访问这些Pod呢? Pod地址是变化的,不能直接访问。能否提供一个固定的访问Pod的入口,不用关心Pod地址的变化呢?
Kubernetes通过Service为一组功能相同的pod提供稳定的访问地址,Service是一个抽象的概念,它定义了Pod的逻辑分组和一种可以访问它们的策略,这组Pod能被Service访问。Service是如何识别这些Pod的呢?通过选择器Selector来识别成员Pod。需要注意的是,Service后端不一定是Pod,可能是Kubernetes集群外的物理机,如MySQL数据库。
Service是通过Selector选择的一组Pods的服务抽象,提供了服务的负载均衡和反向代理的能力。实现 Service 负载均衡和反向代理功能,是通过kube-proxy实现的。kube-proxy 运行在每个节点上,监听 API Server 中服务对象的变化,通过管理 iptables 来实现网络的转发。
Service的负载均衡实现的过程共分为五步,如下:
- 运行在每个Node节点的kube-proxy会实时的watch Service和Endpoints(IP+端口)对象, 当用户在Kubernetes集群中创建了含有Label的Service之后,同时会在集群中创建出一个同名的Endpoints对象,用于存储Service下的Pod IP。
- 当每个Node节点的kube-proxy感知到Service和Endpoints的变化之后,会在各自的Node节点上打开代理端口,并设置相关的iptables或IPVS转发规则。
- 客户端访问Service的ClusterIP。
- 客户端请求会经过iptables/IPVS,会被重定向到kube-proxy的代理端口。IPVS模式的调度由IPVS完成,其他功能仍是iptables实现。
- kube-proxy将请求发送到真实的后端Pod。
Service的访问方式
Service的访问方式有多种,如下:
- ClusterIP: 默认类型,自动分配一个仅Cluster内部可以访问的虚拟IP
- NodePort: 在ClusterIP基础上为Service在每台机器上绑定一个端口,这样就可以通过NodeIP:Port方式来访问该服务
- LoadBalancer: 在NodePort基础上,接触cloud provider创建一个外部负载均衡器,并将请求转发到NodeIP:Port
由于Service只提供了四层服务,若要通过Http/Https的七层访问方式访问服务,则需要通过LoadBalancer的方式,将访问流量转发到Service上来实现。
练习实验:通过ASK搭建企业级网站应用
https://developer.aliyun.com/adc/scenario/ace330daa1d7428cacf3225bea3207e2