K8S权威指南之基础概念二

接下来说说与service密切相关的核心资源对象-pod。

pod是kubernetes中最重要的基本概念之一,如图所示是Pod的组成示意图,我们看到每个Pod都有一个特殊的被称为"根容器 "的Pause容器。Pause容器对应的镜像属于Kubernetes平台的一部分,除了Pause容器,每个Pod都还包含一 个或多个紧密相关的用户业务容器。

为什么Kubernetes会设计出一 个全新的Pod橄念并且Pod有这样特殊的组成结构?原因如下。

 

  • 为多进程之间的协作提供一 个抽象模型,使用Pod作为基本的调度、 复制等管理工作的最小单位,让多个应 用进程能一起有效地调度和伸缩。
  • Pod里的多个业务容器共享Pause容器的IP,共享Pause容器挂接的Volume,这样既简化了密切关联的业务容器之间的通信问题,也很好地解决了它们之间的文件共享问题。

    Kubernetes为每个Pod都分配了唯一的IP地址,称之为Pod IP, 一个Pod里的多个容器共享Pod IP地址。Kubernetes要求底层网络支持集群内任意两个Pod之间的TCP/IP直接通信, 这通常采用虚拟二层网络技术实现,例如Flannel、Open vSwitch等,因此我们需要牢记一点:在Kubernetes里,一 个Pod里的容器与另外主机上的Pod 容器能够直接通信。

    Pod其实有两种类型:普通的Pod及静态Pod ( Static Pod )。后者比较特殊,它并没被存放在Kubernetes的etcd中, 而是被存放在某个具体的Node上的一 个具体文件中, 并且 只能在此Nod巳上启动、 运行。而普通的Pod 一旦被创建, 就会被放入etcd中存储,随后被Kubernetes Master调度到某个具体的Node上并绑定(Binding),该Pod被对应的Node 上的kubelet进程实例化成一组相关的Docker容器并启动。在默认情况下,当Pod里的某个容器停止时,Kubernetes会自动检测到这个问题并且重新启动这个Pod(重启Pod里的所有容器),如果Pod所在的Node岩机,就会将这个node上的所有Pod都重新调度到其他节点上。Pod、容器与Node的关系如图所示。

     

下面是我们在之前的hello world例子里用到的myweb这个pod的资源定义文件:请勿复制,需要自行编辑

 

apiVersion: vl 
kind: Pod 
metadata:
  name: myweb
labels:
  name: myweb 
spec:
	containers:
-	name: myweb
image: kubeguideltomcat-app:vl 
ports:
-	containerPort: 8080

 

  

 

 

在以上定义中,kind属性的值为Pod,表明这是一个Pod类型的资游、对象;metadata 里的name属性为Pod的名称,在metadata里还能定义资源对象的标签,这里声明myweb 拥有一个name=myweb标签。在Pod里所包含的容器组的定义则在spec部分中声明,这 里定义了一个名为myweb且对应的镜像为kubeguide/tomcat-app:v1的容器,并在8080端口(containerPort )启动容器进程。Pod的IP加上这里的容器端口(containerPort)组成了一个新的概念一-Endpoint, 代表此Pod里的一 个服务进程的对外通信地址。 一 个Pod也存在具有多个Endpoint的情况, 比如当我们把Tomcat定义为一 个Pod时, 可以对外暴露管理端口与服务端口这两个Endpoint。

我们所熟悉的Docker Volume在Kubernetes里也有对应的概念一-Pod Volume, Pod Volume是被定义在Pod上, 然后被各个容器挂载到自己的文件系统中的。 Volume简单来说就是被挂载到Pod里的文件目录。

这里顺便提一下Kubernet es的Event 概念。 Event是一个事件的记录, 记录了事件的

最早产生时间、 最后重现时间、 重复次数、发起者、 类型, 以及导致此事件的原因等众多信息。 Event通常会被关联到某个具体的资源对象上, 是排查故障的重要参考信息。 之前我们看到在Node的描述信息中包括Event, 而Pod同样有Event 记录,当我们发现某个Pod迟迟无法创建时, 可以用kubectl describe pod xxxx来查看它的描述信息, 以定位问题的成因。 比如下面这个Event记录信息就表明Pod 里的一个容器被探针检测为失败一次:

 

如图所示给出了Pod及pod周边对象的示意图,后面的部分还会涉及这张图里的对象和概念。

在继续说明Service与Pod的关系之前,我们需要先学习理解Kubernetes中重要的一个机制——标签匹配机制。

  1. label与标签选择器

    Label (标签)是Kubernetes系统中的另一个核心概念,相当于我们熟悉的"标签"。一个Lable是一个key=value的键值对,其中的key与value由用户自己指定。Label可以被附加到各种资源对象上,例如Node、Pod、Service、Deployment等,一个资源对象可以定义任意数量的Label,同一个Label也可以被添加到任意数量的资源对象上。Label通常在资源对象定义时确定,也可以在对象创建后动态添加或者删除。我们可以通过给指定的资源对象捆绑一个或多个不同的Label来实现多维度的资源分组管理功能,以便灵活,方便的进行资源分配,调度,配置,部署等管理工作,例如,部署不同版本的应用到不同的环境中,以及监控,分析应用(日志记录,监控,告警)等。一些常用的Label示例如下。

     

     

  • 版本标签:release stable和release : canary。
  • 环境标签: environment: dev、environment: qa和environment : production.
  • 架构标签:tier:frontend、tier : backend和tier : middleware。
  • 分区标签:partition : customerA和partition : customerB。
  • 质量管控标签:track : daily和track : weekly。

     

给某个资源对象定义一个 Label, 就相当于给它打了一个标签 , 随后可以通过Label Selector (标签选择器)查询和筛选拥有某些Label的资源对象 ,Kubernetes通过这种方式实现了类似SQL的简单又通用的对象查询机制。Label Selector可以被类比为SQL语句中的 where查询条件 ,例如,"name=redis-slave" 这个LabelSelector作用于 Pod时,可以被类比为 " select * from pod where pod's name ='redis-slave'" 这样的语句 。 当前有两种Label Selector表达式: 基于等式的(Equality-based) Selector表达式和基于集合的(Set-based ) Selector表达式。

 

基于等式的Selector表达式采用等式类表达式匹配标签, 下面是一些具体的例子。

  • name = redis-slave:匹配所有具有name=redis-slave标签的资源对象。
  • env != production:匹配所有不具有env=production标签的资源对象, 比如" env=test" 就是满足此条件的标签之一。

 

基于集合的Selector表达式则使用集合操作类表达式匹配标签,下面是一些具体的例子。

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

     

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

name=redis-slave,env!=production

name notin (php-frontend),env!=production

 

在前面的留言板例子中只使用了一个" name=XXX" 的Label Selector。看一个更复杂的例子:假设为Pod定义了3个Label: release、 env和role, 不同的Pod定义了不同的Label值, 如图1.8所示, 如果设置 " role=frontend" 的Label Selector, 则会选取到Node1和Node 2上的Pod;如果设置" release=beta" 的Label Selector, 则会选取到Node2和Node3上的Pod,如图所示:

 

 

 

 

 

 

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

Label 也是 Pod 的重要属性之一, 其重要性仅次于 Pod 的端口, 我们几乎见不到没有 Label 的 Poda 以 myweb Pod 为例, 下面给它设定了 app=myweb 标签:

apiVersion: v1
kind: Pod
metadata:
  name: myweb
  labels:
     app: myweb

 

 

 

对应的Service myweb就是通过下面的标签选择器与myweb Pod发生关联的:

spec:
  selector:
     app: myweb

 

所以我们看到,Service很重要的一个属性就是标签选择器,如果我们不小心把标签选择器写错了,就会出现指鹿为马的闹剧。如果恰好匹配到另一种Pod实例,而且对应的容器端口恰好正确,服务可以正常连接,则很难排查问题,特别是在有众多Service的复杂系统中。

 

  1. pod与Deployment

    大部分Service都是无状态的服务,可以由多个Pod副本实例提供服务。通常情况下, 每个Service对应的Pod服务实例数量都是固定的,如果 一个一个地手工创建Pod实例, 就太麻烦了, 最好是用模板的思路, 即提供一个Pod模板( Template ), 然后由程序根据我们指定的模板自动创建指定数量的Pod实例。这就 是Deployment这个资源对象所要完成的事情了。

    先看看之前例子中的Deployment案例(省略部分内容):

     

    这里有几个重要的属性

  • replicas: Pod的副本数量
  • selector: 目标Pod的标签选择器。
  • template: 用于自动创建新Pod副本的模板

有一个Pod副本实例时,我们是否也需要Deployment来自动创建Pod呢?在大多 数情况下,这个答案是 "需要 "。这是因为Deployment除自动创建Pod副本外,还有一个很重要的特性:自动控制。 举个例子,如果Pod所在的节点发生着机事件,Kubernetes就会第一时间观察到这个故障,并自动创建一个新的Pod对象,将其调度到其他合适的节点 上, Kubernetes会实时监控集群中目标Pod的副本数量, 并且尽力与Deployment中声明的replicas数量保持一致。

下面创建一个名为tomcat-deployment.yaml的Deployment描述文件,内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      tier: frontend
    matchExpressions:
      - {key: tier, operator: In, values: [frontend]}
  template:
    metadata:
      labels:
        app: app-demo
        tier: frontend
    spec:
      containers:
      - name: tomcat-demo
        image: tomcat
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080

  

对以上输出中各字段的含义解释如下。

  • DESIRED: Pod副本数量的期望值, 即在Deployment里定义的replicas。
  • CURRENT:当前replicas的值,实际上是Deployment创建的ReplicaSet对象里的replicas值,这个值不断增加,直到达到DESIRED为止,表明整个部署过程完成。
  • UP-TO-DATE:最新版本的 Pod 的副本数量, 用于指示在滚动升级的过程中, 有

    多少个 Pod 副本已经成功升级。

  • AVAILABLE : 当前集群中可用的 Pod 副本数量, 即集群中当前存活的 Pod 数量。Deployment 资源对象其实还与 ReplicaSet 资源对象密切相关, Kubernetes 内部会根据Deployment对象自动创建相关联的ReplicaSet对象,通过以下命令,我们可以看到它的命名与Deployment的名称有对应关系:

    不仅如此, 我们发现 Pod 的命名也是以 Deployment 对应的 ReplicaSet 对象的名称为前缀的,这种命名很清晰地表明了一个 ReplicaSet 对象创建了哪些 Pod , 对于 Pod 滚动升级(Pod Rolling update )这种复杂的操作过程来说, 很容易排查错误:

关于 Deployment 就先说到这里, 最后总结一下它的典型使用场景。

  • 创建一个 Deployment 对象来完成相应 Pod 副本数量的创建。
  • 检查 Deployment 的状态来看部署动作是否完成(Pod 副本数量是否达到预期的

    值)。

  • 更新 Deployment以创建新的 Pod(比如镜像升级) , 如果当前 Deployment 不稳定 ,则回滚到一个早先的 Deployment 版本。
  • 扩展 Deployment以应对高负载。

 

如图显示了pod,Deployment与Service的逻辑关系。

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

 

 

posted @ 2021-11-26 17:29  头发重要  阅读(93)  评论(0编辑  收藏  举报