本文介绍Kubernetes中的基本概念和术语 — Master、Node、Pod、Label。
1. Master
Kubernetes里的Master指的是集群控制节点,每个Kubernetes集群里需要有一个Master节点来负责整个集群的管理和控制,基本上Kubernetes的所有控制命令都发给它,它来负责具体的执行过程,我们后面执行的所有命令基本都是在Master节点上运行的。Master节点通常会占据一个独立的服务器(高可用部署建议用3台服务器),其主要原因是它太重要了,是整个集群的“首脑”,如果宕机或者不可用,那么对集群内容器应用的管理都将失效。
Master节点上运行着以下一组关键进程:
Kubernetes API Server (kube-apiserver):提供了 HTTP Rest 接口的关键服务进程,是Kubernetes里所有资源的增、删、改、查等操作的唯一入口,也是集群控制的入口进程。
Kubernetes Controller Manager (kube-controller-manager):Kubernetes里所有资源对象的自动化控制中心,可以理解为资源对象的“大总管”。
Kubernetes Scheduler (kube-scheduler):负责资源调度(Pod调度)的进程,相当于公交公司的“调度室”。
另外,在Master节点上还需要启动一个etcd服务,因为Kubernetes里的所有资源对象的数据全部是保存在etcd中的。
2.Node
除了Master,Kubernetes集群中的其他机器被称为Node节点,在较早的版本中也被称为Minion。与Master一样,Node节点可以是一台物理主机,也可以是一台虚拟机。Node节点才是Kubernetes集群中的工作负载节点,每个Node都会被Master分配一些工作负载(Docker容器),当某个Node宕机时,其上的工作负载会被Master自动转移到其他节点上去。
每个Node节点上都运行着以下一组关键进程:
kubelet:负责Pod对应的容器的创建、启停等任务,同时与Master节点密切协作,实现集群管理的基本功能。
kube-proxy:实现Kubernetes Service的通信与负载均衡机制的重要组件。
Docker Engine (docker):Docker引擎,负责本机的容器创建和管理工作。
Node节点可以在运行期间动态增加到Kubernetes集群中,前提是这个节点上已经正确安装、配置和启动了上述关键进程,在默认情况下kubelet会向Master注册自己,这也是Kubernetes推荐的Node管理方式。一旦Node被纳入集群管理范围,kubelet进程就会定时向Master节点汇报自身的情报,例如操作系统、Docker版本、机器的CPU和内存情况,以及当前有哪些Pod在运行等,这样Master可以获知每个Node的资源使用情况,并实现高效均衡等资源调度策略。而某个Node超过指定时间不上报信息时,会被Master判断为“失联”,Node的状态被标记为不可用(Not Ready),随后Master会触发“工作负载大转移”的自动流程。
查看集群中有多少个Node:
1 | kubectl get nodes |
查看某个Node的详细信息:
1 | kubectl describe node 127.0.0.1 |
上述命令展示了Node的如下关键信息:
Node基本信息:名称、标签、创建时间等。
Node当前的运行状态,Node启动以后会做一系列的自检工作,比如磁盘是否满了,如果满了就标注OutOfDisk=True,否则继续检查内存是否不足(如果内存不足,就标注MemoryPressure=True),最后一切正常,就设置为Ready状态(Ready=True),该状态表示Node处于健康状态,Master将可以在其上调度新的任务了(如启动Pod)。
Node的主机地址与主机名。
Node上的资源总量:描述Node可用的系统资源,包括CPU、内存数量、最大可调度Pod数量等,注意到目前Kubernetes已经实验性地支持GPU资源分配了(alpha.kubernetes.io/nvidia-gpu=0)。
Node可分配资源量:描述Node当前可用于分配等资源量。
主机系统信息:包括主机等唯一标识UUID、Linux kernel版本号、操作系统类型与版本、Kubernetes版本号、kubelet与kube-proxy的版本号等。
当前正在运行等Pod列表概要信息。
已分配的资源使用概要信息,例如资源申请的最低、最大允许使用量占系统总量等百分比。
Node相关的Event信息。
3. Pod
此概念下还会引申出以下三个概念:Volume,Endpoint,Event。分别会做出相应的介绍。
Pod是kubernetes的最重要的也最基本的概念,每个Pod都有一个特殊的被称为“根容器”的pause容器。pause容器对应的镜像属于kubernetes平台的一部分,除了Pause容器,每个Pod还包含一个或多个紧密相关的用户业务容器。如下是Pod的组成示意图:
k8s为每个Pod都都分配了唯一的ip,称之为Pod IP,一个Pod里的多个容器共享Pod IP地址。k8s要求底层网络支持集群内任意两个Pod之间的TCP/IP直接通信,这通常采用虚拟二层网络技术来实现,如Flannel、Openswith等。牢记在k8s中,一个Pod容器与另外主机上的Pod容器能够直接通信。
Pod其实有两种类型:普通的Pod及静态的Pod(static Pod),后者比较特殊,并不存放在k8s的etcd里面存储,而是存放在某一个具体的Node上的一个文件中,并且只在此Node上启动运行。而普通的Pod一旦被创建,就会被放入到etcd中存储,随后会被k8s Master调度到某个具体的Node上面进行绑定(Binding),随后该Pod被对应的Node上的kubelet进程实例化成一组的Docker容器并启动起来。默认情况下,当Pod里的某个容器停止时,k8s会自动检测这个问题并重启启动这个Pod(Pod里面的所有容器),如果Pod所在的Node宕机,则会将这个Node上的所有Pod重新调度到其它节点上。Pod、容器与Node的关系如下:
由图可知:一个Node下可以拥有好多Pod,一个Pod下可以拥有好多Containner,Pod里面的业务容器共享Pause容器的IP,共享Pause容器挂载的Volume。以上共享这两点的好处是既简化了密切关联业务容器之间的通信问题,还很好地解决了它们之间文件共享的问题。
Volume
我们经常会说:容器和 Pod 是短暂的。
其含义是它们的生命周期可能很短,会被频繁地销毁和创建。容器销毁时,保存在容器内部文件系统中的数据都会被清除。为了持久化保存容器的数据,可以使用 Kubernetes Volume。Volume 的生命周期独立于容器,Pod 中的容器可能被销毁和重建,但 Volume 会被保留。
本质上,Kubernetes Volume 是一个目录,这一点与 Docker Volume 类似。当 Volume 被 mount 到 Pod,Pod 中的所有容器都可以访问这个 Volume。Kubernetes Volume 也支持多种 backend 类型,包括 emptyDir、hostPath、GCE Persistent Disk、AWS Elastic Block Store、NFS、Ceph 等。
Volume 提供了对各种 backend 的抽象,容器在使用 Volume 读写数据的时候不需要关心数据到底是存放在本地节点的文件系统中呢还是云硬盘上。对它来说,所有类型的 Volume 都只是一个目录。
Endpoint
Pod的IP加上这里的容器端口(containerPort)就组成了一个新的概念Endpoint(端口),它代表Pod的一个服务进程对外的通信地址。一个Pod也存在着具有多个Endpoint的情况,比如tomcat定义pod时候,可以暴露管理端口和服务端口两个Endpoint。
Event
Event是一个事件的记录,记录了时间的最早产生时间、最后重现时间、重复次数、发起者、类型及导致此次事件的原因等总舵信息。Event通常会关联到某个具体的资源对象上,是排查故障的重要信息参考信息。Node描述信息包含Event,而Pod同样有Event记录。当我们发现某个Pod迟迟无法创建时,可以用kubectl describe pod pod_name来查看它的信息,用来定位问题的原因。
一个Pod定义示例
1 | apiSerdion: v1 |
kind为Pod表明这是一个Pod的定义;matadata的name属性为Pod的名字,还能定义资源对象的标签Label,这里声明myweb拥有一个name=myweb
的标签。Pod里面所包含的容器组的定义则在spec中声明,这里定义一个名字为myweb,镜像为kubeguide/tomcat-app:v1
的容器,该容器注入了名为MYSQL_SERVICE_HOST='mysql'
和MYSQL_SERVICE_PORT='3306'
的环境变量(env关键字)。
Pod计算资源限额设置
每个Pod都可以对其能使用的服务器上的计算资源设置限额,当前可以设置的限额的计算资源有CPU和Memory两种,其中CPU的资源单位为CPU(core)核数,一个绝对值。
一个cpu的配额对绝大多数容器来说是一个相当大的资源配额,所以在k8s中,通常以千分之一的cpu配额为最小单位,用m来表示,即一个容器的限额为100-300m,即占用0.1-0.3个cpu限额。由于它是绝对值,所以无论在1个cpu的机器还是多个cpu的机器,这个100m的配额都是一样的。类似的Memory配额也是一个绝对值,单位是内存字节数。
在k8s中,一个计算资源进行配额限定需要设定下面两个参数:
- Request:该资源的最小申请量,系统必须满足要求。
- Limits:该资源允许最大的使用量,不能被突破,当容器试图使用超出这个量的资源时,可能会被k8s杀死并重启。
通常会把Request设置为一个比较小的数值,符合同期平时的工作负载情况下的资源要求,而把Limit设置为峰值负载情况下资源占用的最大量。如下面的设定:
1 | spec: |
即表明mysql容器在申请最少0.25个cpu及64Mib的内存,在运行过程中mysql容器所能使用的最大资源配额为0.5个cpu及128Mib的内存。
以下是Pod及周边对象的示意图:
4. Label
Label是k8s系统中另一个核心的概念。一个Label是一个key=value的键值对,其中key和value由用户自己指定。Label对象可以附加到各种资源对象上面,如Node、Pod、Service、RC等,一个资源对象可以定义任意数量的Label,同一个Label也可以被添加到任意资源对象上去,Label通常在资源对象定义时确定,也可以通过对象创建之后动态添加或删除。
我们可以通过给指定的资源对象捆绑一个或多个不同的Label来实现多维度的资源分组管理功能,以便于灵活、方便地进行资源分配、调度、配置和布署等管理工作。一些常用的label如下:
版本控制:”release”: “stable”, “release”: “canary”
环境标签:”environment”: “dev”, “environment”: “qa”, “environment”: “production”
架构标签:”tier”: “frontend”, “release”: “backend”, “release”: “middkeware”
分区标签:”partition”: “customerA”
质量管控标签:”track”: “daily”, “track”: “weekly”
给某个资源对象定义一个Label,随后可以通过Label Selector(标签选择器)查询和筛选拥有某些Label的资源对象,k8s通过这种方式实现了类似SQL的简单通用的对象查询方式。
当前有两种Label Selector的表达式:基于等式的(Equality-based)和基于集合(Set-based)。
基于等式的表达式匹配标签实例:
- name=redis-slave:匹配所有具有标签name=redis-slave的资源对象。
- env!=production: 匹配不具有标签name=production的资源对象。
基于集合方式的表达式匹配标签实例:
- name in (redis-slave, redis-master):匹配所有具有标签name=redis-slave或name=redis-master的资源对象。
- name not in (php-frontend):匹配不具有标签name=php-frontend的资源对象。
可以通过多个Label Selector表达式的组合实现复杂的条件选择,多个表达式之间用“,”分隔即可,几个条件是and的关系,即同时满足多个条件,如:
- name=redis-slave, env!=production
- name not in (php-frontend), env!=production
Label Selector在k8s中重要使用场景有下面几处:
kube-controller-manage进程通过资源对象RC上定义的Label Selector来筛选要监控的Pod副本的数量。
kube-proxy进程通过Service的Label Selector来选择对应的Pod,自动建立起每个Service到对应Pod的请求转发路由表,从而实现Service的智能负载均衡机制。
通过对某些Node定义特定的Label,并且Pod定义文件中使用NodeSelector这种标签调度策略,kube-scheduler进程可以实现Pod“定向调度”的特性。
总结:使用Label可以给资源对象创建多组标签,Label和Label Selector共同构成了k8s系统中最核心的应用模型,使得被管理对象能够被精细地分组管理,同时实现了整个集群的高可用性。