kubernetes的基本概念和术语

2021-09-22  本文已影响0人  乙腾

核心概念

Master:集群的控制和管理

Kubernetes里的Master指的是集群控制节点,在每个Kubernetes集群里都需要有一个Master来负责整个集群的管理和控制,基本上Kubernetes的所有控制命令都发给它,它负责具体的执行过程,我们后面执行的所有命令基本都是在Master上运行的。Master通常会占据一个独立的服务器(高可用部署建议用3台服务器),主要原因是它太重要了,是整个集群的“首脑”,如果它宕机或者不可用,那么对集群内容器应用的管理都将失效。

在Master上运行着以下关键进程

另外,在Master上通常还需要部署etcd服务,因为Kubernetes里的所有资源对象的数据都被保存在etcd中。

Node:接收Master分配的工作负载

除了Master,Kubernetes集群中的其他机器被称为Node,在较早的版本中也被称为Minion。与Master一样,Node可以是一台物理主机,也可以是一台虚拟机。Node是Kubernetes集群中的工作负载节点,每个Node都会被Master分配一些工作负载(Docker容器),当某个Node宕机时,其上的工作负载会被Master自动转移到其他节点上。

在每个Node上都运行着以下关键进程

工作方式

我们可以执行下述命令查看在集群中有多少个Node:


image.png

然后,通过kubectl describe node <node_name>查看某个Node的详细信息:


image.png
image.png
上述命令展示了Node的如下关键信息。

◎ Node的基本信息:名称、标签、创建时间等。
◎ Node当前的运行状态:Node启动后会做一系列的自检工作,比如磁盘空间是否不足(DiskPressure)、内存是否不足(MemoryPressure)、网络是否正常(NetworkUnavailable)、PID资源是否充足(PIDPressure)。在一切正常时设置Node为Ready状态(Ready=True),该状态表示Node处于健康状态,Master将可以在其上调度新的任务了(如启动Pod)。
◎ Node的主机地址与主机名。
◎ Node上的资源数量:描述Node可用的系统资源,包括CPU、内存数量、最大可调度Pod数量等。
◎ Node可分配的资源量:描述Node当前可用于分配的资源量。
◎ 主机系统信息:包括主机ID、系统UUID、Linux kernel版本号、操作系统类型与版本、Docker版本号、kubelet与kube-proxy的版本号等。
◎ 当前运行的Pod列表概要信息。
◎ 已分配的资源使用概要信息,例如资源申请的最低、最大允许使用量占系统总量的百分比。
◎ Node相关的Event信息。

Pod:一组相关的docker容器

Pod是Kubernetes最重要的基本概念,每个Pod都有一个特殊的被称为“根容器”的Pause容器。Pause容器对应的镜像属于Kubernetes平台的一部分,除了Pause容器,每个Pod还包含一个或多个紧密相关的用户业务容器。


image.png

为什么Kubernetes会设计出一个全新的Pod的概念并且Pod有这样特殊的组成结构?

在Kubernetes里,一个Pod里的容器与另外主机上的Pod容器能够直接通信

Kubernetes为每个Pod都分配了唯一的IP地址,称之为Pod IP,一个Pod里的多个容器共享Pod IP地址。Kubernetes要求底层网络支持集群内任意两个Pod之间的TCP/IP直接通信,这通常采用虚拟二层网络技术来实现,例如Flannel、Open vSwitch等。

Pod其实有两种类型

Kubernetes里的所有资源对象都可以采用YAML或者JSON格式的文件来定义或描述

下面是我们在之前的Hello World例子里用到的myweb这个Pod的资源定义文件:


image.png

为每个pod的计算资源设置限额

可以为pod计算资源设置的配额

在Kubernetes里,一个计算资源进行配额限定时需要设定以下两个参数

  通常,我们会把Requests设置为一个较小的数值,符合容器平时的工作负载情况下的资源需求,而把Limit设置为峰值负载情况下资源占用的最大量。
下面这段定义表明MySQL容器申请最少0.25个CPU及64MiB内存,在运行过程中MySQL容器所能使用的资源配额为0.5个CPU及128MiB内存:


image.png

Label:对一组资源通过标签的形式加以聚合

Label(标签)是Kubernetes系统中另外一个核心概念。

我们可以通过给指定的资源对象捆绑一个或多个不同的Label来实现多维度的资源分组管理功能,以便灵活、方便地进行资源分配、调度、配置、部署等管理工作。

例如,部署不同版本的应用到不同的环境中;监控和分析应用(日志记录、监控、告警)等。

一些常用的Label示例如下

Label Selector

Label相当于我们熟悉的“标签”。给某个资源对象定义一个Label,就相当于给它打了一个标签,随后可以通过Label Selector(标签选择器)查询和筛选拥有某些Label的资源对象,Kubernetes通过这种方式实现了类似SQL的简单又通用的对象查询机制。

Label Selector可以被类比为SQL语句中的where查询条件,例如,name=redis-slave这个Label Selector作用于Pod时,可以被类比为select * from pod where pod’s name =‘redis-slave’这样的语句。

两种Label Selector表达式:

例子

以myweb Pod为例,Label被定义在其metadata中:


image.png

管理对象RC和Service则通过Selector字段设置需要关联Pod的Label:


image.png
其他管理对象如Deployment、ReplicaSet、DaemonSet和Job则可以在Selector中使用基于集合的筛选条件定义,例如:
image.png

matchLabels用于定义一组Label,与直接写在Selector中的作用相同;
matchExpressions用于定义一组基于集合的筛选条件,可用的条件运算符包括In、NotIn、Exists和DoesNotExist。

注释

1.如果同时设置了matchLabels和matchExpressions
则两组条件为AND关系,即需要同时满足所有条件才能完成Selector的筛选。
2.Label Selector在Kubernetes中的重要使用场景如下。

复杂的例子

在前面的例子中,我们只使用了一个name=XXX的Label Selector。看一个更复杂的例子:假设为Pod定义了3个Label:release、env和role,不同的Pod定义了不同的Label值,如果设置“role=frontend”的Label Selector,则会选取到Node 1和Node 2上的Pod。


image.png

如果设置“release=beta”的Label Selector,则会选取到Node 2和Node 3上的Pod,如图所示。


image.png
总之,使用Label可以给对象创建多组标签,Label和Label Selector共同构成了Kubernetes系统中核心的应用模型,使得被管理对象能够被精细地分组管理,同时实现了整个集群的高可用性。

Replication Controller:声明pod的副本数并在任意时刻都符合这个预期值

RC是Kubernetes系统中的核心概念之一,简单来说,它其实定义了一个期望的场景,即声明某种Pod的副本数量在任意时刻都符合某个预期值。

RC的定义包括如下几个部分

一个完整的RC例子

下面是一个完整的RC定义的例子,即确保拥有tier=frontend标签的这个Pod(运行Tomcat容器)在整个Kubernetes集群中始终只有一个副本:


image.png

作用

Kubernetes如何通过RC来实现Pod副本数量自动控制的机制

下面以有3个Node的集群为例,说明Kubernetes如何通过RC来实现Pod副本数量自动控制的机制。
假如在我们的RC里定义redis-slave这个Pod需要保持两个副本,系统将可能在其中的两个Node上创建Pod。


image.png

上图描述了在两个Node上创建redis-slave Pod的情形。
假设Node 2上的Pod 2意外终止,则根据RC定义的replicas数量2,Kubernetes将会自动创建并启动一个新的Pod,以保证在整个集群中始终有两个redis-slave Pod运行。
系统可能选择Node 3或者Node 1来创建一个新的Pod


image.png
此外,在运行时,我们可以通过修改RC的副本数量,来实现Pod的动态缩放(Scaling),这可以通过执行kubectl scale命令来一键完成:
image.png
Scaling的执行结果如图
image.png

使用的注意事项

最后总结一下RC(Replica Set)的一些特性与作用

Deployment:更好地解决Pod的编排问题

Deployment是Kubernetes在1.2版本中引入的新概念,用于更好地解决Pod的编排问题。为此,Deployment在内部使用了Replica Set来实现目的,无论从Deployment的作用与目的、YAML定义,还是从它的具体命令行操作来看,我们都可以把它看作RC的一次升级,两者的相似度超过90%。
Deployment相对于RC的一个最大升级是我们可以随时知道当前Pod“部署”的进度。实际上由于一个Pod的创建、调度、绑定节点及在目标Node上启动对应的容器这一完整过程需要一定的时间,所以我们期待系统启动N个Pod副本的目标状态,实际上是一个连续变化的“部署过程”导致的最终状态。

Deployment的典型使用场景有以下几个

Deployment的定义与Replica Set的定义很类似

除了API声明与Kind类型等有所区别,Deployment的定义与Replica Set的定义很类似:


image.png

下面通过运行一些例子来直观地感受Deployment的概念。创建一个名为tomcat-deployment.yaml的Deployment描述文件,内容如下:


image.png

常用命令

1.创建

运行下述命令创建Deployment:


image.png

指定名字tomcat-deploy

2.查看deployments信息

运行下述命令查看Deployment的信息:


注释

对上述输出中涉及的数量解释如下。

3.查看deployments对应的rs

运行下述命令查看对应的Replica Set,我们看到它的命名与Deployment的名称有关系:


image.png

4.查看创建的pod

image.png

我们发现Pod的命名以Deployment对应的Replica Set的名称为前缀,这种命名很清晰地表明了一个Replica Set创建了哪些Pod,对于Pod滚动升级这种复杂的过程来说,很容易排查错误

5.查看水平扩展过程

运行kubectl describe deployments,可以清楚地看到Deployment控制的Pod的水平扩展过程,具体内容后面会说。

pod的管理对象

Pod的管理对象,除了RC和Deployment,还包括ReplicaSet、DaemonSet、StatefulSet、Job等,分别用于不同的应用场景中,将在后面进行详细介绍。

Horizontal Pod Autoscaler:自动触发扩缩容

产生背景

HPA实现原理

当前,HPA有以下两种方式作为Pod负载的度量指标

HPA定义的一个具体例子

下面是HPA定义的一个具体例子:


image.png

根据上面的定义,我们可以知道这个HPA控制的目标对象为一个名为php-apache的Deployment里的Pod副本,当这些Pod副本的CPUUtilizationPercentage的值超过90%时会触发自动动态扩容行为,在扩容或缩容时必须满足的一个约束条件是Pod的副本数为1~10。

除了可以通过直接定义YAML文件并且调用kubectrl create的命令来创建一个HPA资源对象的方式,还可以通过下面的简单命令行直接创建等价的HPA对象:


image.png

后面会给出一个完整的HPA例子来说明其用法和功能

StatefulSet:管理有状态资源对象

在Kubernetes系统中,Pod的管理对象RC、Deployment、DaemonSet和Job都面向无状态的服务。但现实中有很多服务是有状态的,特别是一些复杂的中间件集群,例如MySQL集群、MongoDB集群、Akka集群、ZooKeeper集很多服务是有状态的,特别是一些复杂的中间件集群,例如MySQL集群、MongoDB集群、Akka集群、ZooKeeper集群等,这些应用集群有4个共同点。

(1)每个节点都有固定的身份ID,通过这个ID,集群中的成员可以相互发现并通信。

(2)集群的规模是比较固定的,集群规模不能随意变动。

(3)集群中的每个节点都是有状态的,通常会持久化数据到永久存储中。

(4)如果磁盘损坏,则集群里的某个节点无法正常运行,集群功能受损。

如果通过RC或Deployment控制Pod副本数量来实现上述有状态的集群,就会发现第1点是无法满足的,因为Pod的名称是随机产生的,Pod的IP地址也是在运行期才确定且可能有变动的,我们事先无法为每个Pod都确定唯一不变的ID。另外,为了能够在其他节点上恢复某个失败的节点,这种集群中的Pod需要挂接某种共享存储,为了解决这个问题,Kubernetes从1.4版本开始引入了PetSet这个新的资源对象,并且在1.5版本时更名为StatefulSet,StatefulSet从本质上来说,可以看作Deployment/RC的一个特殊变种。

特性

Service:kubernetes的微服务

概述

Service服务也是Kubernetes里的核心资源对象之一,Kubernetes里的每个Service其实就是我们经常提起的微服务架构中的一个微服务。

Pod、RC与Service的逻辑关系

image.png

Kubernetes的Service定义了一个服务的访问入口地址,前端的应用(Pod)通过这个入口地址访问其背后的一组由Pod副本组成的集群实例,Service与其后端Pod副本集群之间则是通过Label Selector来实现无缝对接的。RC的作用实际上是保证Service的服务能力和服务质量始终符合预期标准。

Kubernetes Service

通过分析、识别并建模系统中的所有服务为微服务—Kubernetes Service,我们的系统最终由多个提供不同业务能力而又彼此独立的微服务单元组成的,服务之间通过TCP/IP进行通信,从而形成了强大而又灵活的弹性网格,拥有强大的分布式能力、弹性扩展能力、容错能力,程序架构也变得简单和直观许多,如图所示


image.png

客户端如何访问服务?

理论上的实现方案:负载均衡器

既然每个Pod都会被分配一个单独的IP地址,而且每个Pod都提供了一个独立的Endpoint(Pod IP+ContainerPort)以被客户端访问,现在多个Pod副本组成了一个集群来提供服务,那么客户端如何来访问它们呢?一般的做法是部署一个负载均衡器(软件或硬件),为这组Pod开启一个对外的服务端口如8000端口,并且将这些Pod的Endpoint列表加入8000端口的转发列表,客户端就可以通过负载均衡器的对外IP地址+服务端口来访问此服务。客户端的请求最后会被转发到哪个Pod,由负载均衡器的算法所决定。

Kubernetes的做法

例子

单端口

image.png

上述内容定义了一个名为tomcat-service的Service,它的服务端口为8080,拥有“tier=frontend”这个Label的所有Pod实例都属于它,运行下面的命令进行创建:


image.png

我们之前在tomcat-deployment.yaml里定义的Tomcat的Pod刚好拥有这个标签,所以刚才创建的tomcat-service已经对应一个Pod实例,运行下面的命令可以查看tomcatservice的Endpoint列表,其中172.17.1.3是Pod的IP地址,端口8080是Container暴露的端口:


image.png
你可能有疑问:“说好的Service的Cluster IP呢?怎么没有看到?”运行下面的命令即可看到tomct-service被分配的Cluster IP及更多的信息:
image.png
在spec.ports的定义中,targetPort属性用来确定提供该服务的容器所暴露(EXPOSE)的端口号,即具体业务进程在容器内的targetPort上提供TCP/IP接入;port属性则定义了Service的虚端口。前面定义Tomcat服务时没有指定targetPort,则默认targetPort与port相同。

多端口

很多服务都存在多个端口的问题,通常一个端口提供业务服务,另外一个端口提供管理服务,比如Mycat、Codis等常见中间件。Kubernetes Service支持多个Endpoint,在存在多个Endpoint的情况下,要求每个Endpoint都定义一个名称来区分。下面是Tomcat多端口的Service定义样例:


image.png

多端口为什么需要给每个端口都命名呢?这就涉及Kubernetes的服务发现机制了,接下来进行讲解。

Kubernetes的服务发现机制

任何分布式系统都会涉及“服务发现”这个基础问题,大部分分布式系统都通过提供特定的API接口来实现服务发现功能,但这样做会导致平台的侵入性比较强,也增加了开发、测试的难度。Kubernetes则采用了直观朴素的思路去解决这个棘手的问题。

首先,每个Kubernetes中的Service都有唯一的Cluster IP及唯一的名称,而名称是由开发者自己定义的,部署时也没必要改变,所以完全可以被固定在配置中。接下来的问题就是如何通过Service的名称找到对应的Cluster IP。

通过Service的名称找到对应的Cluster IP的实现方式

1.每个Service都生成一些对应的Linux环境变量(ENV)

最早时Kubernetes采用了Linux环境变量解决这个问题,即每个Service都生成一些对应的Linux环境变量(ENV),并在每个Pod的容器启动时自动注入这些环境变量。以下是tomcat-service产生的环境变量条目:


image.png

在上述环境变量中,比较重要的是前3个环境变量。可以看到,每个Service的IP地址及端口都有标准的命名规范,遵循这个命名规范,就可以通过代码访问系统环境变量来得到所需的信息,实现服务调用。

2.把服务名作为DNS域名

考虑到通过环境变量获取Service地址的方式仍然不太方便、不够直观,后来Kubernetes通过Add-On增值包引入了DNS系统,把服务名作为DNS域名,这样程序就可以直接使用服务名来建立通信连接了。目前,Kubernetes上的大部分应用都已经采用了DNS这种新兴的服务发现机制,后面会讲解如何部署DNS系统。

外部系统访问Service的问题

Kubernetes里的3种IP

为了更深入地理解和掌握Kubernetes,我们需要弄明白Kubernetes里的3种IP,这3种IP分别如下。

NodePort

根据上面的分析和总结,我们基本明白了:Service的Cluster IP属于Kubernetes集群内部的地址,无法在集群外部直接使用这个地址。那么矛盾来了:实际上在我们开发的业务系统中肯定多少有一部分服务是要提供给Kubernetes集群外部的应用或者用户来使用的,典型的例子就是Web端的服务模块,比如上面的tomcat-service,那么用户怎么访问它?

采用NodePort是解决上述问题的最直接、有效的常见做法。以tomcat-service为例,在Service的定义里做如下扩展即可(见代码中的粗体部分):


image.png

其中,nodePort:31002这个属性表明手动指定tomcat-service的NodePort为31002,否则Kubernetes会自动分配一个可用的端口。接下来在浏览器里访问http://<nodePortIP>:31002/,就可以看到Tomcat的欢迎界面了,如图所示


image.png
NodePort的实现方式是在Kubernetes集群里的每个Node上都为需要外部访问的Service开启一个对应的TCP监听端口,外部系统只要用任意一个Node的IP地址+具体的NodePort端口号即可访问此服务,在任意Node上运行netstat命令,就可以看到有NodePort端口被监听:
image.png
NodePort还没有完全解决外部访问Service的所有问题

但NodePort还没有完全解决外部访问Service的所有问题,比如负载均衡问题。假如在我们的集群中有10个Node,则此时最好有一个负载均衡器,外部的请求只需访问此负载均衡器的IP地址,由负载均衡器负责转发流量到后面某个Node的NodePort上,如图所示


image.png

Load balancer组件独立于Kubernetes集群之外,通常是一个硬件的负载均衡器,或者是以软件方式实现的,例如HAProxy或者Nginx。对于每个Service,我们通常需要配置一个对应的Load balancer实例来转发流量到后端的Node上,这的确增加了工作量及出错的概率。于是Kubernetes提供了自动化的解决方案,如果我们的集群运行在谷歌的公有云GCE上,那么只要把Service的type=NodePort改为type=LoadBalancer,Kubernetes就会自动创建一个对应的Load balancer实例并返回它的IP地址供外部客户端使用。其他公有云提供商只要实现了支持此特性的驱动,则也可以达到上述目的。此外,裸机上的类似机制(Bare Metal Service Load Balancers)也在被开发。

Job:批处理

批处理任务通常并行(或者串行)启动多个计算进程去处理一批工作项(work item),在处理完成后,整个批处理任务结束。从1.2版本开始,Kubernetes支持批处理类型的应用,我们可以通过Kubernetes Job这种新的资源对象定义并启动一个批处理任务Job。与RC、Deployment、ReplicaSet、DaemonSet类似,Job也控制一组Pod容器。从这个角度来看,Job也是一种特殊的Pod副本自动控制器

Job控制Pod副本与RC等控制器的工作机制有以下重要差别:

Volume:Pod中能够被多个容器访问的共享目录

Volume(存储卷)是Pod中能够被多个容器访问的共享目录。

Kubernetes的Volume概念、用途和目的与Docker的Volume比较类似,但两者不能等价。

声明一个Volume

Volume的使用也比较简单,在大多数情况下,我们先在Pod上声明一个Volume,然后在容器里引用该Volume并挂载(Mount)到容器里的某个目录上。举例来说,我们要给之前的Tomcat Pod增加一个名为datavol的Volume,并且挂载到容器的/mydata-data目录上,则只要对Pod的定义文件做如下修正即可(注意代码中的粗体部分):


image.png

除了可以让一个Pod里的多个容器共享文件、让容器的数据写到宿主机的磁盘上或者写文件到网络存储中,Kubernetes的Volume还扩展出了一种非常有实用价值的功能,即容器配置文件集中化定义与管理,这是通过ConfigMap这种新的资源对象来实现的,后面会详细说明。

Kubernetes提供了非常丰富的Volume类型

Persistent Volume:kubernetes集群的网络存储

之前提到的Volume是被定义在Pod上的,属于计算资源的一部分,而实际上,网络存储是相对独立于计算资源而存在的一种实体资源。比如在使用虚拟机的情况下,我们通常会先定义一个网络存储,然后从中划出一个“网盘”并挂接到虚拟机上。Persistent Volume(PV)和与之相关联的Persistent Volume Claim(PVC)也起到了类似的作用。

与Volume的区别

PV可以被理解成Kubernetes集群中的某个网络存储对应的一块存储,它与Volume类似,但有以下区别。

例子

下面给出了NFS类型的PV的一个YAML定义文件,声明了需要5Gi的存储空间:


image.png

比较重要的是PV的accessModes属性,目前有以下类型。

如果某个Pod想申请某种类型的PV,则首先需要定义一个PersistentVolumeClaim对象:


image.png

然后,在Pod的Volume定义中引用上述PVC即可:


image.png

PV的状态

PV是有状态的对象,它的状态有以下几种。

Namespace:多租户的资源隔离

Namespace(命名空间)是Kubernetes系统中的另一个非常重要的概念,Namespace在很多情况下用于实现多租户的资源隔离。Namespace通过将集群内部的资源对象“分配”到不同的Namespace中,形成逻辑上分组的不同项目、小组或用户组,便于不同的分组在共享使用整个集群的资源的同时还能被分别管理。

Kubernetes集群在启动后会创建一个名为default的Namespace,通过kubectl可以查看:


image.png

接下来,如果不特别指明Namespace,则用户创建的Pod、RC、Service都将被系统创建到这个默认的名为default的Namespace中。
Namespace的定义很简单。如下所示的YAML定义了名为development的Namespace。


image.png
一旦创建了Namespace,我们在创建资源对象时就可以指定这个资源对象属于哪个Namespace。比如在下面的例子中定义了一个名为busybox的Pod,并将其放入development这个Namespace里:
image.png

此时使用kubectl get命令查看,将无法显示:


image.png
这是因为如果不加参数,则kubectl get命令将仅显示属于default命名空间的资源对象。
可以在kubectl命令中加入--namespace参数来查看某个命名空间中的对象:
image.png
当给每个租户创建一个Namespace来实现多租户的资源隔离时,还能结合Kubernetes的资源配额管理,限定不同租户能占用的资源,例如CPU使用量、内存使用量等。关于资源配额管理的问题,在后面中会详细介绍。

Annotation:用户任意定义的附加信息,以便于外部工具查找

Annotation(注解)与Label类似,也使用key/value键值对的形式进行定义。不同的是Label具有严格的命名规则,它定义的是Kubernetes对象的元数据(Metadata),并且用于Label Selector。Annotation则是用户任意定义的附加信息,以便于外部工具查找。在很多时候,Kubernetes的模块自身会通过Annotation标记资源对象的一些特殊信息。

通常来说,用Annotation来记录的信息如下。

ConfigMap:动态修改pod配置

为了能够准确和深刻理解Kubernetes ConfigMap的功能和价值,我们需要从Docker说起。我们知道,Docker通过将程序、依赖库、数据及配置文件“打包固化”到一个不变的镜像文件中的做法,解决了应用的部署的难题,但这同时带来了棘手的问题,即配置文件中的参数在运行期如何修改的问题。我们不可能在启动Docker容器后再修改容器里的配置文件,然后用新的配置文件重启容器里的用户主进程。为了解决这个问题,Docker提供了两种方式:

◎ 在运行时通过容器的环境变量来传递参数;

◎ 通过Docker Volume将容器外的配置文件映射到容器内。

这两种方式都有其优势和缺点,在大多数情况下,后一种方式更合适我们的系统,因为大多数应用通常从一个或多个配置文件中读取参数。但这种方式也有明显的缺陷:我们必须在目标主机上先创建好对应的配置文件,然后才能映射到容器里。

上述缺陷在分布式情况下变得更为严重,因为无论采用哪种方式,写入(修改)多台服务器上的某个指定文件,并确保这些文件保持一致,都是一个很难完成的目标。此外,在大多数情况下,我们都希望能集中管理系统的配置参数,而不是管理一堆配置文件。

Kubernetes给出动态修改容器配置参数的方法

针对上述问题,Kubernetes给出了一个很巧妙的设计实现,如下所述。

上一篇 下一篇

猜你喜欢

热点阅读