Kubernetes概念与术语
综述
学习Kubernetes时,发现它的概念和术语还是比较多的,光靠啃官方文档比较晦涩。所以边学习边整理,对主要的概念和术语做一下分类及简要说明。感觉把重要概念都理解了,对Kubernetes的设计思想,整体框架也就有了初步的认识。
从功能上来说,我把Kubernetes的概念或术语大概分为以下三类:
- 组件:指实际在集群中运行的Kubernetes程序集,分为Master组件、Node组件及附加组件。
- 对象:对象是一个抽象的概念实体,Kubernetes把应用、运行场景、功能、集群状态等都抽象成一个个对象,并通过API对这些对象进行管理,从而管理整个集群的状态。
- 标识:对组件和对象的各种表示方式,例如命名、标签、注释等。标识是KubernetesAPI与操作对象间的纽带。
组件
Master组件
Master组件提供了k8s管理集群的核心功能,k8s通过Master组件实现整个集群的调度管理,它就像集群的大脑,会根据集群当前的状态,不断进行调整,使集群一直保持在期望的状态。Master组件包括以下几个进程:
- kube-apiserver
apiserver进程对外暴露REST API接口给外部客户端及内部组件调用,它封装了对各个对象的增、删、改、查操作,可以说是整个集群管理的总入口。 - kube-scheduler
kube-scheduler做的事情说来简单--监控新pod的创建,并把pod分配到合适的node来运行。但实际上,选择“合适的Node”是一个很复杂的过程,需要考虑的因素包括单node及整个集群的资源需求、软硬件资源的限制策略、数据存储、工作负载间的干扰等等。 kube-controllers-manager
controllers-manager进程从字面上理解,是用来做控制器管理的。controllers-manager其实是一组守护进程,通过监控集群各组件的状态,并不断调整集群实际状态,使的集群向期望状态调整,它由一组子进程构成:- replication controller
- endpoints controller
- namespace controller
- serviceaccounts controller
Etcd
Etcd是Kubernetes集群的基础组件,用于保存集群所有的元数据信息、配置信息、对象状态等。
Node组件
运行Node组件的节点称为Node节点,是k8s集群实际的工作负载节点。Node节点可以是物理机、虚拟机或任何可以运行Node组件的设备。所有的业务应用都运行在Node节点中。Node组件包括以下几个部分:
- Kubelet
kubelet负责实际的容器管理,Kubernetes通过apiserver给kubelet发送Pod管理请求,同时把Pod及Node的运行状态汇报给apiserver。 - kube-proxy
kube-proxy负责集群内部的请求转发及负载均衡工作。它通过Service对象的配置信息,为Pod创建代理服务,实现请求从Service到Pod的路由转发。 - 容器运行时
实际的容器运行引擎。Kubernetes其实支持其他的容器技术比如rkt,但是Docker相比与它们处于绝对的优势地位。所以,Kubernetes生态中的容器也基本特指指Docker容器。
对象
在说明对象之前,先区分下Kubernetes中的三个名词:对象(objects)、资源(resources)和工作负载(workload).
- 对象:指的是KubernetesAPI中定义好的操作对象,是抽象概念。
- 资源:抽象API对象被创建后,对象就被实例化了。实例化后的对象被称为资源。
- 工作负载:特指有运行容器的资源,例如Deployment资源就是工作负载,而service资源不是。
Kubernetes集群为每个对象维护三类信息:对象元数据(ObjectMeta)、期望状态(Desired State)与实际状态(Autual State),元数据指对象的基本信息,比如命名、标签、注释等等,用于识别对象;期望状态一般由用户配置spec来描述的;实际状态是由集群各个组件上报的集群实际的运行情况。Kubernetes努力使每个对象的实际状态与期望状态相匹配,从而使整个集群的状态与用户配置的期望状态一致。
Kubernetes对象一般用yaml文件来描述,有几个个字段是必须的:
- apiVersion: 创建该对象所使用的 Kubernetes API 的版本
- kind: 想要创建的对象的类型
- metadata: 帮助识别对象唯一性的数据,包括一个 name 字符串、UID 和可选的 namespace
- spec: 对象期望状态的描述
用户通过命令行工具kubectl来操作这些对象,Kubectl吧yaml转换成JSON格式来执行API请求。下面是一个POD对象的描述文件示例及注释:
apiVersion: v1 kind: Pod # 对象名称 metadata: # 对象的基本信息 name: pod-example # 对象唯一标示 spec: # 期望状态 containers: # 指定pod中运行的容器镜像及运行参数(类似Dockerfile) - name: ubuntu image: ubuntu:trusty command: ["echo"] args: ["Hello World"]
其中,kind字段描述对象类型"Pod",spec之前是对象的ObjectMeta,spec开始,就是对象期望状态的描述。
根据官方API对象参考文档的分类,下面我们把对象分为几大类来分别简要说明。计划以后的文章,会结合实际操作,对各个重要对象做更深入的说明。
Workloads 资源对象
- workloads类的对象用于管理运行容器实例
- Pod:
Pod是Kubernetes里最重要的对象,也是Kubernetes集群中运行部署应用或服务的最小单位。Pod对象描述一个Pod由哪些容器镜像构成,以及这些容器应该怎么运行。类比传统的业务架构,Pod更接近于虚拟机的角色而不是应用进程的角色。 - ReplicaSet:
ReplicaSet对象是对Pod的更高一层抽象,它用来管理一组相同POD副本,并确保这组相同Pod的数量始终与用户期望一致,并实现Pod的高可用。即如果有容器异常退出,ReplicaSet会自动创建新的Pod来替代,保证Pod数量永远与用户配置一致。 - Deployment:
Deployment对象是Kubernetes中最常用的对象之一,用于部署无状态服务。它在ReplicaSet对象的基础上,又做了一层抽象。通过管理多组ReplicaSet对象,来实现POD的滚动升级、回滚、扩容缩容等。 - StatefulSet:
不同于Deployment,StatefulSet对象顾名思义是对有状态服务的一种抽象。所以,StatefulSet对象在定义时,相比与Deployment多了一些与状态相关的内容,比如持久化存储、服务对外的不变的唯一标示、部署、扩容、滚动升级时确保有序等等。 - DaemonSet:
DaemonSet对象是Kubernetes对守护进程的抽象。当我们需要集群中的每个Node都跑一些如日志收集、监控等守护进程时,就可以把这类型进程包装成Pod,并用调用DaemonSet来部署了。DaemonSet做的事情其实和Deployment差不多,只不过Deployment对象部署的Pod,会根据集群情况,在不同Node间自动调度。而DaemonSet会指定的NODE上(默认是集群所有Node),都部署定义Pod,确保这些NODE都有跑着守护进程Pod。 - Job、CronJob:
除了上面几个对象所抽象描述的应用工作场景外,实际业务场景中,还有一类应用或服务并不需要一直运行,比如一些一次性任务,或需要定时执行的任务。这种不需要长时间运行的任务,完成了就可以退出的服务,就可以用Job对象来定义。CronJob和Job对象的关系,和Deployment与ReplicaSet的关系很像,可以类比来理解。首先,Job也是直接用于管理Pod的,同时可以定义这个一次性任务Pod的执行时间、超时时间、错误处理(重启还是生成新Pod)等任务属性。而CronJob管理是用来管理Job,类似crobtab,它可以通过yaml文件中的schedule字段配置具体时间,来控制指定Job定时执行,从而实现定时执行特定的一次性任务。
Discovery & LB 资源对象
- 该类对象用于服务发现和负载均衡的对象
Service:
上面提到的对象都是用于管理POD及容器实例的。但是,业务系统光有POD实例还不够,还必须对外提供服务。这里就会衍生出两个问题:- Pod的IP地址并不是固定,而是随着编排不断变化的,外面的请求怎么找到对应的POD,这个也就是大多数分布式业务都会遇到的服务发现问题
- 多个相同Pod一起提供服务的时候,它们的负载均衡怎么实现
而Service对象就是用来解决这两个问题的。它用固定的VIP或DNS名称和指定标识的一组Pod对应起来,不管Pod IP怎么变,Service对外的VIP都不会变化,并且,自动的将请求在这组Pod中负载均衡。
Config & Storage 资源对象
- 该类对象用于初始化容器配置及持久化容器存储的对象
- Volume:
前面说到,Pod里的不同容器可以共享存储,这个共享存储就靠Volume来配置的。要注意的是,这里Volume与Docker中的定义不用,Kubernetes中Volume跟Pod生命周期一致,Pod终止了,该Pod挂载的Volume就失效了(根据挂载volume的类型不同,数据可能丢失也可能不丢失)。 - PV,PVC:
Volume可以支持多种类型的存储,除了直接在Volume字段中去声明所有存储细节,K8S还抽象出了PV和PVC对象。简单来说PV是对具体存储资源的描述,比如存储类型、地址、访问账户等;而PVC,是对怎么使用资源的方法的描述,比如只读只写,需要的空间等;而Pod通过Volume字段挂载具体要使用的PVC。PV和PVC是独立于Pod单独定义的,这样,就把共享存储的配置、分割、挂载等操作都解耦了。比如,一个NFS迁移后ip地址变了,存储管理员只需要修改PV中的配置,而不用关心具体哪个业务POD在使用这个NFS。 - ConfigMap,Securet
K8S中除了常见的存储,还有一类特殊的Volume,不是为了Pod存放数据,而是为了给Pod提供数据的。ConfigMap和Secret对象就是用来定义这类型的Volume。简单来说,可以将它们理解为一份Key:Value格式的数据,Pod可以通过Volume挂载它们,将这份数据保存成文件随时调用。唯一的区别是,ConfigMap对象保存的数据是明文,一般作为应用配置文件;而Secret对象保存的对象要求是经过Base64转码的,用于提供数据库密码等对安全要求比较高的配置。不过这些配置,直接在做容器镜像时就配置不就好了,为啥要多此一举呢?原因和上面PV和PVC一样,都是为了尽可能解耦业务核心与经常可能变化的依赖配置。比如数据库更换了账号,只需要修改Secret对象,不用重新去构建容器镜像。
Cluster resources objects
- 该类对象定义整个K8S集群运行方式的对象
- Namespace
Kubernetes作为一个集群管理平台,为不同用户划分不同权限管理,例如多租户等,是必备的功能。而不同用户作用域的隔离,就是靠Namespace对象实现的。Namespace是Kubernetes项目中的一个逻辑管理单位,不同Namespace的API对象,在调用API进行操作是,是相互隔离开的。通过不同用户角色与Namespace关联,从而实现权限管理。
Metadata resources
- 除了上述分类外的其他对象都归为这一类,一般用来管理集群的特殊功能比如弹性伸缩等
- Horizontal Pod Autoscaling
容器作为一个轻量级的承载应用的技术,快速启动和快速部署是它的优点之一。自然的,根据业务负载自动扩缩容等需求,在容器集群的可行性和效率就可以变得很高。而Kubernetes中 Horizontal Pod Autoscaling对象,就是用于进行POD自动水平缩放的,这也是Kubernetes最能体现运维优势的特性之一。
Horizontal Pod Autoscaling具体的操作对象是Deployment和ReplicaSet,通过不同循环获取每个Pod的资源情况,比如CPU利用率,并根据设定的目标利率来计算目前需要的Pod副本缩放的比例,然后通过访问相应的Deployment或ReplicaSet对象,来对Pod数量进行自动缩放,从而提高了集群资源整体利用率。
标识
标识是Kubernetes中最重要的概念,因为它是所有API的操作与操作对象间的纽带。Kubernetes中的标识主要有以下几种:
- Label
Label可以说是Kubernetes中最最重要的核心概念,一个Lable是一个KEY=Value的键值对。任何对象资源都可以有一个或多个Label,而同个Label,也可以配置在多个对象上。然后重点来了,大多数API对象的yaml字段中,都有Label Selector字段,可以实现对Label中的key或value的多维度选择,例如in、not in、and、or等等。这样,Kubernetes对API作用对象就能进行多维度,细粒度的选择,从而实现比较精细化的容器编排调度工作,比如,对所有"ENV:release"执行扩容操作,或者把部分请求灰度到有"ver:v1.14.0"的pod上等等。Label把资源对象和应用、基础设施都解耦了,你不用关心这个Pod具体在那个Node上运行,也不用关心具体是什么应用用了哪个容器镜像,只要Label符合Label Selector,就可以通过API调用统一进行操作。 - Names
除了Label,所有资源对象肯定还必须有一个全局唯一的标识,即Names字段。 - Annotation
Annotation很好理解,与通常意义的注释一样,用于描述作者、版本、配置说明等等任何需要的信息,需要说明的是,Annotation也必须是Key:Value格式的。
总结
通过上面对Kubernetes重要概念的说明,我们可以大致梳理出Kubernetes的一些设计理念:
- Kubernetes对容器应用进行了抽象,例如把一个容器或强关联的一组容器抽象为Pod,把各类存储都抽象成Volume,把一组Pod抽象成Service等等基础对象。
- 在基础对象的层面上,Kubernetes又对应用使用场景,做了一层抽象。极客时间的Kubernetes课程有一张图画的很好:可以看到,Kubernetes中各个对象实际就是对生产业务场景的各类需求的抽象。
- 抽象出各类型对象以后,用户可以通过yaml文件(或直接命令行调用API)来描述这些对象的期望状态,确认了各对象些期望状态的集合,也就确认了整个集群的期望状态。这些所有的操作,都是声明式的,而不是命令集群要怎么做
- Kubernetes通过控制器循环不断将各组件收集来的集群实际状态与各对象的期望状态对比,并自动化的将集群实际状态向期望状态转移,这个过程,也就是Kubernetes最核心的概念:编排。
本文对Kubernetes中的重要概念做了分类和简要,后面的文章会结合集群的实际操作,对每个概念做更详细的说明。