kubernetes 核心技术-pod

POD的定义

在Kubernetes集群中,Pod是所有业务类型的基础,也是K8S管理和部署的最小单位,其他的资源对象都是用来支撑或者扩展 Pod 对象功能的,比如控制器是用来管控Pod对象的,Service或者Ingress资源对象是用来暴露Pod对象的,PersistentVolume资源对象是用来为Pod提供存储等等。

k8s 不会直接处理容器,而是pod。pod是容器的载体,所有的容器都是在pod中被管理,一个或多个容器放在pod里作为一个单元方便管理。毕竟docker和kubernetes也不是一家公司的,如果做一个编排部署的工具,你也不可能直接去管理别人公司开发的东西,所以就把docker容器放在了pod里,在kubernetes的集群环境下,我直接管理我的pod,然后对于docker容器的操作,我把它封装在pod里,并不直接操作。

Pod、容器与Node(工作主机)之间的关系如下图所示:

所以,pod是一个或多个容器的组合。这些容器共享存储、网络和命名空间,以及如何运行的规范。在Pod中,所有容器都被统一安排和调度,并运行在共享的上下文中。对于具体应用而言,Pod是它们的逻辑主机,Pod包含业务相关的多个应用容器。

如图,每一个 Pod 都有一个特殊的被称为”根容器“的 Pause容器,加上一个或多个用户自定义的容器构造而成。pause的状态代表了这一组容器的状态。

Kubernetes在每个Pod启动时,会自动创建一个镜像为gcr.io/google_containers/pause:version的容器,所有处于该Pod中的容器在启动时都会添加诸如--net=container:pause --ipc=contianer:pause --pid=container:pause的启动参数,因此pause容器成为Pod内共享命名空间的基础。所有容器共享pause容器的IP地址,也被称为Pod IP。

pod里多个业务容器共享pod的Ip和数据卷,即pod的两个特点。

共享网络:

每一个Pod都会被指派一个唯一的Ip地址,在Pod中的每一个容器共享网络命名空间,包括Ip地址和网络端口。在同一个Pod中的容器可以用locahost(127.0.0.1)进行互相通信,不同容器只要注意不要有端口冲突即可。不同的Pod有不同的IP,不同Pod内的多个容器之前通信,通常情况下使用 Pod的IP进行通信。

共享存储:

一个 Pod 里的多个容器可以共享存储卷,这个存储卷会被定义为 Pod 的一部分,并且可以挂载到该 Pod 里的所有容器的文件系统上。

 

POD的工作方式

1.pod模板

K8s一般不直接创建Pod,而是通过控制器和模版配置来管理和调度。下面是一个完整的yaml格式定义的文件:

apiVersion: v1            //版本
kind: pod                 //类型,pod
metadata:                 //元数据
  name: String            //元数据,pod的名字
  namespace: String       //元数据,pod的命名空间
  labels:                 //元数据,标签列表
    - name: String        //元数据,标签的名字
  annotations:            //元数据,自定义注解列表
    - name: String        //元数据,自定义注解名字
spec:                     //pod中容器的详细定义
  containers:             //pod中的容器列表,可以有多个容器
  - name: String
    image: String         //容器中的镜像
    imagesPullPolicy: [Always|Never|IfNotPresent]//获取镜像的策略
    command: [String]     //容器的启动命令列表(不配置的话使用镜像内部的命令)
    args: [String]        //启动参数列表
    workingDir: String    //容器的工作目录
    volumeMounts:         //挂载到到容器内部的存储卷设置
    - name: String
      mountPath: String
      readOnly: boolean
    ports:                //容器需要暴露的端口号列表
    - name: String
      containerPort: int  //容器要暴露的端口
      hostPort: int       //容器所在主机监听的端口(容器暴露端口映射到宿主机的端口)
      protocol: String
    env:                  //容器运行前要设置的环境列表
    - name: String
      value: String
    resources:            //资源限制
      limits:
        cpu: Srting
        memory: String
      requeste:
        cpu: String
        memory: String
    livenessProbe:         //pod内容器健康检查的设置
      exec:
        command: [String]
      httpGet:             //通过httpget检查健康
        path: String
        port: number
        host: String
        scheme: Srtring
        httpHeaders:
        - name: Stirng
          value: String 
      tcpSocket:           //通过tcpSocket检查健康
        port: number
      initialDelaySeconds: 0//首次检查时间
      timeoutSeconds: 0     //检查超时时间
      periodSeconds: 0      //检查间隔时间
      successThreshold: 0
      failureThreshold: 0
      securityContext:      //安全配置
        privileged: falae
    restartPolicy: [Always|Never|OnFailure]//重启策略
    nodeSelector: object    //节点选择
    imagePullSecrets:
    - name: String
    hostNetwork: false      //是否使用主机网络模式,默认否
  volumes:                  //在该pod上定义共享存储卷
  - name: String
    meptyDir: {}
    hostPath:
      path: string
    secret:                 //类型为secret的存储卷
      secretName: String
      item:
      - key: String
        path: String
    configMap:             //类型为configMap的存储卷
      name: String
      items:
      - key: String
        path: String

2.pod重启

在Pod中的容器可能会由于异常等原因导致其终止退出,Kubernetes提供了重启策略以重启容器。重启策略对同一个Pod的所有容器起作用,容器的重启由Node上的kubelet执行。Pod支持三种重启策略,在配置文件中通过restartPolicy字段设置重启策略:

  • Always:只要退出就会重启。
  • OnFailure:只有在失败退出(exit code不等于0)时,才会重启。
  • Never:只要退出,就不再重启

注意,这里的重启是指在Pod的宿主Node上进行本地重启,而不是调度到其它Node上。

3.资源限制

Kubernetes通过cgroups限制容器的CPU和内存等计算资源,包括requests(请求,调度器保证调度到资源充足的Node上)和limits(上限):

4.健康检查

在Pod部署到Kubernetes集群中以后,为了确保Pod处于健康正常的运行状态,Kubernetes提供了两种探针,用于检测容器的状态:

  • Liveness Probe :检查容器是否处于运行状态(running)。如果检测失败,kubelet将会杀掉容器,并根据重启策略进行下一步的操作。
  • ReadinessProbe :检查容器是否已经处于可接受服务请求的状态(ready)。如果检测失败,端点控制器将会从服务端点(与Pod匹配的)中移除容器的IP地址。

kubelet在容器上周期性的执行探针以检测容器的健康状态,kubelet通过调用被容器实现的处理器来实现检测,在Kubernetes中有三类处理器:

  • Exec:在容器中执行一个指定的命令。如果命令的退出状态为0,则判断认为是成功的;
  • TCPSocket :在容器IP地址的特定端口上执行一个TCP检查,如果端口处于打开状态,则视为成功;
  • HTTPGet:发送一个http Get请求(ip+port+请求路径)如果返回状态吗在200-400之间则表示成功;

健康检测的结果为下面三种情况:

  • Success :表示容器通过检测
  • Failure :表示容器没有通过检测
  • Unknown :表示检测容器失败

 

POD的创建流程

Kubernetes通过watch的机制进行每个组件的协作,每个组件之间的设计实现了解耦。

  1. 用户使用create yaml创建pod,请求给apiserver,apiserver将yaml中的属性信息(metadata)写入etcd。
  2. apiserver触发watch机制准备创建pod,信息转发给scheduler,调度器使用调度算法选择node,调度器将node信息返回给apiserver,apiserver将绑定的node信息写入etcd。
  3. apiserver又通过watch机制,调用kubelet,指定pod信息,触发docker run命令创建容器。
  4. 容器创建完成之后反馈给kubelet,kubelet又将pod的状态信息反馈给apiserver,apiserver又将pod的状态信息写入etcd。
  5. 其中kubectl get pods命令调用的是etcd中的信息。

注意:图中有三次write至etcd:

  1. 元信息
  2. pod分配到哪个node
  3. 记录pod状态

在整个过程中,Pod通常处于以下的五种阶段之一:

  • Pending:Pod定义正确,提交到Master,但其所包含的容器镜像还未完全创建。通常,Master对Pod进行调度需要一些时间,Node进行容器镜像的下载也需要一些时间,启动容器也需要一定时间。(写数据到etcd,调度,pull镜像,启动容器)。
  • Running:Pod已经被分配到某个Node上,并且所有的容器都被创建完毕,至少有一个容器正在运行中,或者有容器正在启动或重启中。
  • Succeeded:Pod中所有的容器都成功运行结束,并且不会被重启。这是Pod的一种最终状态。
  • Failed:Pod中所有的容器都运行结束了,其中至少有一个容器是非正常结束的(exit code不是0)。这也是Pod的一种最终状态。
  • Unknown:无法获得Pod的状态,通常是由于master无法和Pod所在的Node进行通信。

 

POD调度方式

Deployment或RC全自动调度:pod最终运行在哪个节点上, 完全由 Master 的 Scheduler 经过一系列算法计算得 出 ,用户无法干预调度过程和结果。

除此之外,还有以下三种方式去影响pod的调度

  • node节点调度器
  • 亲和性调度
  • 污点容忍度

1.node节点调度

是最直接的调度方式,简单粗暴,所以常用在简单的集群架构中,负载的资源分类和编制不适合这种方式,
解释:先给work节点打上标签,然后在pod的yml文件中去设置nodeSelector绑定节点,这样就能指定pod启动在设置的node节点上。
(1)首先通过 kubectl 给 node1 打上标签

格式:
kubectl label nodes <node-name> <label-key>=<label-value>


[root@master ~]# kubectl get nodes
NAME     STATUS   ROLES    AGE   VERSION
master   Ready    master   23d   v1.18.0
node1    Ready    <none>   21d   v1.18.0
node2    Ready    <none>   21d   v1.18.0
[root@master ~]# kubectl label nodes node1 disk=ssd
node/node1 labeled
[root@master ~]# kubectl get nodes node1 --show-labels
NAME    STATUS   ROLES    AGE   VERSION   LABELS
node1   Ready    <none>   21d   v1.18.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disk=ssd,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux
[root@master ~]# 

(2)通过 nodeSelector 调度 pod 到指定 node

  • Pod的定义中通过nodeSelector指定label标签,pod将会只调度到具有该标签的node之上
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.11
        ports:
        - containerPort: 80
      nodeSelector:
        disk: ssd

这个例子中pod只会调度到具有 disk=ssd 标签的node1上:

[root@master ~]# kubectl apply -f pod-test.yaml 
deployment.apps/nginx-deployment created
[root@master ~]# kubectl get pods -o wide
NAME                                READY   STATUS              RESTARTS   AGE   IP           NODE    NOMINATED NODE   READINESS GATES
nginx-deployment-6b4474784c-ld2ng   0/1     ContainerCreating   0          29s   <none>       node1   <none>           <none>
[root@master ~]# kubectl describe pod nginx-deployment
Name:         nginx-deployment-6b4474784c-ld2ng
Namespace:    default
Priority:     0
Node:         node1/172.31.93.201
Start Time:   Fri, 09 Apr 2021 11:44:47 +0800
Labels:       app=nginx
              pod-template-hash=6b4474784c
Annotations:  <none>
Status:       Running
IP:           10.244.1.4
IPs:
  IP:           10.244.1.4
Controlled By:  ReplicaSet/nginx-deployment-6b4474784c
Containers:
  nginx:
    Container ID:   docker://0669cc442c30510246f12495a796b2968e0b84693b7ae8c378f0d6b0556ed947
    Image:          nginx:1.11
    Image ID:       docker-pullable://nginx@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Fri, 09 Apr 2021 11:45:24 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-tdnv4 (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  default-token-tdnv4:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-tdnv4
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  disk=ssd
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason     Age        From               Message
  ----    ------     ----       ----               -------
  Normal  Scheduled  <unknown>  default-scheduler  Successfully assigned default/nginx-deployment-6b4474784c-ld2ng to node1
  Normal  Pulling    8h         kubelet, node1     Pulling image "nginx:1.11"
  Normal  Pulled     8h         kubelet, node1     Successfully pulled image "nginx:1.11"
  Normal  Created    8h         kubelet, node1     Created container nginx
  Normal  Started    8h         kubelet, node1     Started container nginx

如果给多个node都定义了相同的标签, 则调度器会根据调度算法从这组node中挑选一个可用的node进行pod调度。
如果指定了pod的nodeSelector条件, 且集群中不存在包含响应标签的Node, 则即使在集群中还有其它可供使用的Node,这个pod也无法被成功调度。

2.亲和性调度

较复杂,应用在复杂的多节点归类,资源分类管理的中大型集群中,有硬亲和,软亲和,亲和性和反亲和等概念。

硬亲和:匹配节点上的标签(必须满足,若集群中不存在包含响应标签的Node,无法调度成功)
软亲和:匹配节点上的标签(尝试满足,优先选择包含响应标签的Node,实在没有也能调度运行)

  • 节点亲和性(Node affinity)

上述配置表示,该pod必须不能调度至有标签 role=master 的node节点(必须满足),最好能调度至有标签 cpu=high 的node节点(尝试满足)

支持常用的操作符有:in  Notin  Exists  Gt  Lt  DoesNotExists

  • pod亲和性(pod affinity)与反亲和性(pod anti affinity)

podAffinity用于调度pod可以和哪些pod部署在同一拓扑结构之下。而podAntiAffinity相反,其用于规定pod不可以和哪些pod部署在同一拓扑结构下。主要用来解决pod和pod之间的关系。

上述配置表示,该pod不可以和solr服务的pod部署在同一拓扑结构之下(必须满足),最好不要和oltp服务的pod部署在同一拓扑结构之下(尝试满足)

3.污点和容忍度

无论是nodeSelector还是nodeAffinity,都是通过pod中的属性,在调度的时候实现。

与之前两个调度方式不同,污点是给节点绑定污点(是节点的属性),作用是保护节点,不再让这个节点被scheduler选为pod启动环境。
集群中的master就是设置了污点,所以启动任何pod都不会在master上工作,保证master的工作效率。

查看节点的污点情况:kubectl describe node [node] | grep Taints

[root@master ~]# kubectl describe node master |grep Taint
Taints:             node-role.kubernetes.io/master:NoSchedule

可见,master有一个NoSchedule的污点,污点值有三个:

  • NoSchedule:一定不被调度,对已有pod不影响
  • PreferNoSchedule: 尽量不被调度
  • NoExecute:一定不被调度,并且还会驱逐node上已有pod

设置给node1设置污点:kubectl taint node node1 node-type=prod:NoSchedule

[root@master ~]# kubectl taint node node1 node-type=prod:NoSchedule
node/node1 tainted
[root@master ~]# kubectl describe node node1 |grep Taint
Taints:             node-type=prod:NoSchedule

 创建pod测试:

#创建一个pod
[root@master ~]# kubectl create deployment web --image=nginx
deployment.apps/web created
#复制五个
[root@master ~]# kubectl scale deployment web --replicas=5
deployment.apps/web scaled
[root@master ~]# kubectl get pods -o wide
NAME                                READY   STATUS              RESTARTS   AGE     IP           NODE    NOMINATED NODE   READINESS GATES
web-5dcb957ccc-7qj2f                0/1     ContainerCreating   0          21s     <none>       node2   <none>           <none>
web-5dcb957ccc-9bqz5                1/1     Running             0          89s     10.244.2.4   node2   <none>           <none>
web-5dcb957ccc-d99vb                1/1     Running             0          21s     10.244.2.5   node2   <none>           <none>
web-5dcb957ccc-wd7b4                0/1     ContainerCreating   0          21s     <none>       node2   <none>           <none>
web-5dcb957ccc-xvf74                0/1     ContainerCreating   0          21s     <none>       node2   <none>           <none>

可见,由于node1设置了污点,所有pod都被调度到了node2上。

最后删除污点:kubectl taint node node1 node-type=prod:NoSchedule-  (注意最后面有一个-)

[root@master ~]# kubectl describe node node1|grep Taint
Taints:             node-type=prod:NoSchedule
[root@master ~]# kubectl taint node node1 node-type=prod:NoSchedule-
node/node1 untainted
[root@master ~]# kubectl describe node node1|grep Taint
Taints:             <none>

所有,如果一个节点标记为 Taints ,该Taints节点不会被调度pod,除非要被调度的Pod被标识为可以容忍污点:

就是说,即使一个节点已经被设置污点(假设是NoSchedule),如果一个pod被标识为容忍(容忍度也是NoSchedule),那么这个pod还是有可能会被调度到taint标记过的节点。

 

 

参考文档:

https://blog.csdn.net/weixin_42953006/article/details/106299864

https://www.jianshu.com/p/74f53019a726

https://www.cnblogs.com/mybxy/p/10233682.html

https://zhuanlan.zhihu.com/p/60905652

https://blog.csdn.net/ht9999i/article/details/108037568

posted @ 2021-04-09 17:30  酒红色  阅读(276)  评论(0编辑  收藏  举报