京东-优惠雷达
无畏Pro 14限量抢购
冷藏饮料节
最高12期免息再送支架
自营热卖

(1) Kubernetes基本概念和术语

白月光 1年前   阅读数 222 0

目录

前言

1、kubernetes架构图

2、kubernetes基本概念和术语

2.1 Master

2.2 Node

 2.3 Pod

2.4 Label(标签)

2.5 Replication Controller(副本控制器,RC)

2.6 Service(服务)

2.6.1 概念

2.6.2 kubernetes的服务发现机制

2.6.3 外部系统访问Service的问题

2.7 Namespace(命名空间)

2.8 Annotation(注解)

3、 Kubernetes工作流程


前言

kubernetes中的大部分概念如Node、Pod、Replication Controller、Service等都可以看作是一种“服务”几乎所有的资源对象都可以通过kubernetes提供的kubectl工具(或API编程调用)执行增、删、改、查操作并将其保存在etcd中持久化存储。从这个角度看kubernetes其实就是一个高度自动化的资源控制系统,它通过跟踪对比etcd库里保存的“资源期望状态”与当前环境中的“实际资源状态”的差异来实现自动控制和自动纠错的高级功能。

1、kubernetes架构图

更加通俗的展示:

2、kubernetes基本概念和术语

2.1 Master

K8S中的Master指的是集群控制节点,每个k8s集群里都需要有一个master节点来负责整个集群的管理和控制,基本上k8s所有的控制命令都是发给它,由它来负责具体的执行过程,我们后面所有执行的命令基本都是在Master节点上运行的。

Master节点通常会占据一个独立的X86服务器/一个虚拟机,主要是因为它太重要了,它是整个集群的“首脑”,如果它宕机或者不能用,那么所有控制命令都将失效。

Master节点上运行着以下一组关键进程:

  • kube-apiserver:提供了HTTP Rest接口的关键服务进程,是k8s中所有资源的增、删、改、查等操作的唯一入口,也是集群控制的入口进程,任何对资源的增删该查都要交给APIServer处理后再交给etcd,如上图,kubectl(kubernetes提供的客户端工具,该工具内部是对kubernetes API的调用)是直接和APIServer交互的。
  • kube-controller-manager如果APIServer做的是前台的工作的话,那么controller manager就是负责后台的。每一个资源都对应一个控制器。而control manager就是负责管理这些控制器的,比如我们通过APIServer创建了一个Pod,当这个Pod创建成功后,APIServer的任务就算完成了。它作为k8s里所有资源对象的自动化控制中心,可理解为资源对象的“大总管”。
  • kube-schedule负责资源调度进程,相当于“调度室”。kube-schedule负责调度Pod到合适的Node上,如果把scheduler看成一个黑匣子,那么它的输入是pod和由多个Node组成的列表,输出是Pod和一个Node的绑定。 kubernetes目前提供了调度算法,同样也保留了接口。用户根据自己的需求定义自己的调度算法。
  • etcdetcd是一个高可用的键值存储系统,kubernetes使用它来存储各个资源的状态数据,从而实现了Restful的API。

2.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数量:

#kubectl get nodes

通过#kubectl describe node <node_name>查看某个Node详细信息:

#kubectl describe node kubernetes-node1

 2.3 Pod

Pod是k8s的最重要也是最基本的概念,它是kubernetes集群中运行部署应用或服务的最小单元,可以支持多容器。它的设计理念是支持多个容器在一个Pod中共享网络地址和文件系统,可以通过进程间通信和文件共享这些简单高效的方式组合完成服务。

如图是Pod的组成示意图,我们看到每个Pod都有一个特殊的被称为“根容器”的Pause容器。Pause容器对应的镜像属于kubernetes平台的一部分,除了Pause容器,每个Pod还包含一个或多个紧密相关的用户业务容器。

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

  • 原因1:一组容器作为一个单元情况下,难以对“整体”简单进行判断及有效地进行行动。

当一个容器死亡时,此时算整体死亡吗?还是算个体死亡?是N/M的死亡率么?引入业务无关并且不易死亡的Pause容器作为Pod的根容器,以它的状态代表整个容器组的状态,简单、巧妙解决这个难题。

  • 原因2:Pod里面,多个业务容器之间如何解决彼此的通信和文件共享问题?

Pod里面多个容器共享Pause容器的IP,共享Pause容器挂接的Volume,这样既简化了密切关联的业务容器之间通信问题,也解决了彼此之间的文件共享问题。

  • k8s为每个Pod都分配了唯一的IP地址,称之为Pod IP。一个Pod里面的多个容器共享Pod IP地址。k8s要求底层网络支持集群r内任意两个Pod之间的TCP/IP直接通信,通常采用虚拟二层网络技术来实现。牢记一点:在kubernetes里,一个Pod里的容器与另外主机上的Pod容器能够直接通信。

Pod的两种类型:普通的Pod、静态Pod(static Pod)

静态Pod并不存放在etcd存储内,而是存放在某个具体额Node上的一个具体文件中,并且只在此Node上启动运行。

普通Pod一旦被创建,就会被放入到etcd存储中,随后会被kubernetes进程实例化成一组相关的Docker容器并启动起来。

在默认情况下,当Pod里某个容器停止时,kubernetes会自动检测到这个问题并且重新启动这个Pod(重启Pod里的所有容器),如果Pod所在的Node宕机,则会将这个Node上的所有Pod重新调度到其他Node节点上。Pod、容器与Node的关系如下:

kubernetes里的所有资源对象都可采用yaml或JSON格式的文件来定义描述:

apiVersion: v1
kind: Pod
metadata:
  name: myweb
  labels:
    name: myweb
spec:
  containers:
  - name: myweb
    image: kubeguide/tomcat-app:v1
    ports:
    - containerPort: 8080
    evn:
    - name: MYSQL_SERVER_HOST
      value: 'mysql'
    - name: MYSQL_SERVER_HOST
      value: '3306'

 解释一下上边各个属性含义:

kind:为Pod表名这是一个Pod的定义

metadata:

name:Pod的名字

labels:资源对象的标签,这里声明myweb拥有一个name=myweb的标签

spec:声明容器组的定义规则

定义了容器的名称、对应镜像名称,该容器注入了环境变量(env关键字),并且在8080端口(containerPort,容器的端口号)上启动容器进程。

Pod的IP+容器端口(containerPort)组成了一个新的概念-------Endpoint,它表示此 Pod里的一个服务进程的对外通信地址。一个Pod也存在多个Endpoint的情况,当我们把Tomcat定义为一个Pod的时候,可以对外暴露管理端口与服务端口这两个Emdpoint。

2.4 Label(标签)

Label是k8s系统的另外一个核心概念。一个Label是一个key=value的键值对,其中key与value由用户自己指定。Label通常在资源对象定义时确定,也可以在对象创建后动态添加或删除。

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

一些常用的Label实例如下:

  • 版本标签:“release”:"stable",“release”:"canary"....
  • 环境标签:“environment”:"dev",“environment”:"qa",“environment”:"production",
  • 架构标签:"tier":"frontend","tier":"backend","tier":"middleware"
  • 分区标签:"position":"customerA","position":"customerB".....
  • 质量管控标签:"track":"daily","track":"weekly"

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

Label Selector可以类比为SQL语句的where查询条件,如name=redis-slave这个Label Selector作用于Pod时,可以类比为select * from pod where pod's name  ='redis-slave'这样的语句。当前有两种Label Selector的表达式:【1】基于等式;【2】基于集合。

前者采用“等式类”的表达式匹配标签。具体栗子:

  • name=redis-slave:匹配所有具有标签name=redis-slave的资源对象;
  • env!=production:匹配所有不具有env!=production的资源对象

后者使用集合操作的表达式匹配标签,举个栗子:

  • name in (redis-master,redis-slave):匹配所有具有标签name=redis-master或者name=redis-slave的资源对象;
  • name not in (php-frontend):匹配所有不具有标签name=php-frontented的资源对象;

可以通过多个Label Selector表达式的组合实现复杂的条件选择,多个表达式之间用“,”进行分隔即可,几个条件之间是“AND”的关系,即同时满足多个条件,栗子:

  • name=redis-slave,env!=production
  • name not in (php-frontend),env!=production

Label Selector在kubernetes中的重要使用场景如下几处:

  • kube-controller进程通过资源对象RC上定义的Label Selector来筛选要监控的Pod副本的数量,从而实现Pod副本的数量始终符合预期设定的全自动控制流程。
  • kube-proxy进程通过Service的Label Selector来选择对应的Pod,自动建立起每个Service到对应Pod的请求转发路由表,从而实现Service的智能负载均衡机制。
  • 通过对某些Node定义特定的Label,并且在Pod定义文件中使用NodeSelector这种标签调度策略,kube-scheduler进程可以实现Pod“定向调度”的特性。

假设为Pod定义了3个Label:release、env、role,不同的Pod定义了不同的Label值,如下图所示,如果我们设置了“role=frontend”的Label Selector,则会选取到Node1和Node2上的Pod。

而设置“rolease=beta”的Label Selector,则会选取到Node2和Node3上的Pod。

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

2.5 Replication Controller(副本控制器,RC)

RC是kubernetes集群中最早的保证Pod高可用的API对象,通过监控运行中的Pod来保证集群中运行指定数目的Pod副本。它其实定义了一个期望的场景,即声明某种Pod的副本数量在任意时刻都符合某个预期值,所以RC的定义包括如下几个部分:

  • Pod期望的副本数量(replicas);
  • 用于筛选目标Pod的Label Selector;
  • 当Pod的副本数量小于预期数量的时候,用于创建新Pod的Pod模板(template);
  • 当Pod的副本多于预期数量时,RC就会杀死多余的Pod副本。

一个完整的RC定义的例子,即确保用于tier=frontend标签的这个Pod在整个kubernetes集群中始终只有一个副本:

apiVersion: v1
kind: ReplicationController
metadata:
  name: frontend
spec:
  replicas: 1           #预期Pod节点副本数量
  selector:             #标签选择器
    tier: frontend
  template:
    metadata:
      labels:
        app: app-demo
        tier: frontend
  spec:
    containers:
    - name: tomcat-demo
      image: tomcat
      imagePullPolicy: IfNotPresent
      evn:
      - name: GET_HOSTS_FROM
        value: dns
      ports:
      - containerPort: 80
  •  我们定义一个RC兵提交到kubernetes集群中后,Master节点上的Controller Manager组件就得到通知,定期巡检系统中存活的目标Pod,并确保目标Pod实例的数量刚好等于此RC的期望数量,如果有过多的Pod副本在运行,系统就会停掉一下Pod,否则系统就会再自动创建一些Pod。通过RC,kubernetes实现了用户应用集群的高可用性,并且大大减少了系统管理员的手工运维工作。

我们以3个node节点的集群为例,说明kubernetes如何通过RC来实现Pod副本数量自动控制的机制。加入我们的RC里定义redis-slave这个Pod需要保持3个副本,系统将可能在其中的两个Node上创建Pod。下图将描述在两个Node上创建redis-slave Pod的情形。

加入Node2上的Pod2意外终止,根据RC定义的replicas数量2,kubernetes将会自动创建并启动一个新的Pod,以保证整个集群中始终有两个redis-slave Pod在运行。

如下图所示,系统可能选择Node1或者Node3来创建一个新的Pod:

此外,在运行时,我们可以通过修改RC的副本数量,来实现Pod的动态缩放(Scaling)功能。

这可以通过执行kubectl scale命令来一键完成:

# kubectl scale rc redis-slave --replicas=3

Scaling的执行结果如下图所示:

注意:删除RC并不会影响通过该RC已创建好的Pod。为了删除所有Pod,可以设置replicas的值为0,然后更新该RC。另外,kubectl提供了stop和delete命令来一次性删除RC和RC控制的全部Pod。

RC实现kubernetes应用升级:

传统升级方式:通过Build一个新的Docker镜像,并用新的镜像版本替代旧版本达到升级目标。

平滑升级方式:当前系统中10个对应的旧版本的Pod,可以让旧版本的Pod每次停止一个,同时创建一个新版本的Pod,在整个升级过程中,此消彼长,而运行的Pod数量始终是10个,当所有的Pod都已是新版本时,升级过程完成。通过RC的机制,kubernetes很容易就实现了高级实用的特性,即“滚动升级(Rolling Update)”

在kubernetes 1.2版本后,RC升级为一个新的概念--------Replica Set,即下一代RC。

它与当前RC的区别:Replica Set支持基于集合的Label Selector(Set-based selector),而RC只支持基于等式的Label Selector(equality-based selector)。

RC的一些特性与作用:

  • 通过定义一个RC实现Pod的创建过程及副本数量的自动控制;
  • RC里包含完整的Pod定义模板;
  • RC通过Label Selector机制实现对Pod副本的自动控制;
  • 通过改变RC中的Pod副本数量,可以实现Pod的扩容或缩容功能;
  • 通过改变RC中Pod模板中的镜像模板,可以实现Pod的滚动升级功能。

2.6 Service(服务)

2.6.1 概念

kubernetes中的每个service就是我们常提起的微服务架构中的一个“微服务”,先前说的Pod、RC等资源对象就是为kubernetes service做“嫁衣”的。如图显示了Pod、RC与service的逻辑关系:

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

Service不是共用一个负载均衡器的IP地址,而是每个Service分配了一个全局唯一的虚拟IP地址,即Cluster IP,这样每个服务就变成了具备唯一IP地址的“通信节点”,服务调用就变成了最基础的TCP网络通信问题。

我们知道Pod的Endpoint地址会随着Pod的销毁和重新创建而发生改变,因为新Pod的IP地址与之前旧Pod的不同。而Service一旦创建,kubernetes就会自动为它分配一个可用的Cluster IP,而在Service的整个生命周期内,它的Cluster IP不会发生改变。关于地址改变问题:只要用Service的name与Cluster IP地址做一个DNS域名映射即可完美解决。关于Endpoint列表,我们可以通过#kubectl get endpoints来查看

2.6.2 kubernetes的服务发现机制

任何分布式系统都会涉及“服务发现”这个基础问题,kubernetes采用了直观朴素的思路去解决这个问题。

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

最早的时候kubernetes采用了Linux环境变量的方式解决这个问题,即每个Service生成一些对应的LInux环境变量(ENV),并在每个Pod的容器启动时,自动注入这些环境变量。

kubernetes通过Add-On增值包的方式引入了DNS系统,把服务名作为DNS域名,程序就可以直接使用服务名来建立通信连接了,目前kubernetes的大部分应用都已经采用了DNS这些新兴的服务发现机制。

2.6.3 外部系统访问Service的问题

kubernetes中存在“三种IP”,分别如下:

  • Node IP:Node节点的IP地址
  • Pod IP:Pod的IP地址
  • Cluster IP:Service的IP地址

首先,Node IP是kubernetes集群中每个节点的物理网卡的IP地址,真实存在的物理网络,所有属于这个网络的服务器之间都能通过这个网络直接通信,不管它们中是否有部分节点不属于这个kubernetes集群。这也表明了kubernetes集群之外的节点访问kubernetes集群之内的某个节点或者TCP/IP服务的时候,必须要通过NodeIP进行通信。

其次,Pod IP是每个Pod的IP地址,它是Docker Engine根据docker()网桥的IP地址段进行分配的,常为虚拟的二层网络。kubernetes要求位于不同Node上的Pod能够彼此直接通信,所有kubernetes里一个Pod里的容器访问另外一个Pod里的容器,就是通过Pod IP所在的虚拟二层网络进行通信的,而真实的TCP/IP流量则是通过Node IP的物理网卡流出的。

最后,关于Service的Cluster IP,它也是一个虚拟的IP,但是更像是一个“伪造”的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的特殊路由规则。

由此可知,Service的Cluster IP属于kubernetes集群内部的地址,无法在集群外部直接使用这个地址。比如web端的服务模块,我们可采用 NodePort来解决,在其Service文件中定义nodeport端口,我们在浏览器里服务http://<nodeport IP>:nodeport/

NodePort的实现方式是在kubernetes集群里的每个Node上为需要外部访问的Service开启一个对应的TCP监听端口,外部系统只需任意一个Node的IP地址+r具体的NodePort端口号即可访问此服务。在任意Node上运行netstat命令,可看大所有NodePort端口被监听:#netstat -tlp | grep 8080

当然,NodePort并没有完全解决外部访问Service的所有问题,比如负载均衡问题。

2.7 Namespace(命名空间)

namespace用于实现多租户的资源隔离。它通过将集群内部的资源对象“分配”到不同namespace中,形成逻辑上分组的不同项目、小组或用户组,便于不同的分组在共享使用整个集群的资源的同时还能被分别管理。

kubernetes集群在启动后,会创建一个名为“default”的Namespace,通过kubectl可以查看到:#kubectl get namespaces

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

apiVersion: v1

kind: Namespace

metadata:

  name: development

2.8 Annotation(注解)

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

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

  • build信息、release信息、Docker镜像信息等,例如时间戳、release id号、PR号、镜像hash值、docker registry地址等
  • 日志库、监控库、分析库等资源库的地址信息
  • 程序调试工具信息
  • 团队的联系信息

3、 Kubernetes工作流程

  • 【1】创建一个包含应用程序的Develpment的yml文件,然后通过kubectl客户端工具发送给ApiServer;
  • 【2】ApiServer接收到客户端的请求并将资源内容存储到数据库etcd中
  • 【3】Controller组件(包含scheduler、replication、endpoint)监控资源变化并作出反应
  • 【4】ReplicaSet检查数据库变化,创建期望数量的Pod实例
  • 【5】Scheduler再次检查数据变化,发现尚未被分配到具体执行节点(Node)的Pod,然后根据一组相关规则将Pod分配到可以运行它们的节点(Node)上,并更新数据库,记录Pod分配情况。
  • 【6】kubelet监控数据库变化,管理后续Pod的生命周期,发现被分配到它所在的Node节点上运行的那些Pod。如果找到新的Pod,则会在该节点上运行这个新的Pod,也就是告诉Doeck Engine运行这个Pod
  • 【7】kuberproxy运行在集群各个主机上,管理网络通信,如服务发现、负载均衡。例如当有数据发送到主机时,将其路由到正确的pod或容器。对于从主机上发出的数据,它可以基于请求地址发现远程服务器,并将数据正确路由,在某些情况下会使用轮训调度算法(Round-robin)将请求发送到集群中的多个实例。

注意:本文归作者所有,未经作者允许,不得转载

全部评论: 0

    我有话说: