k8s series 6: Pod

Pod 的共享上下文包括一组 Linux 名字空间、控制组(cgroup)和可能一些其他的隔离 方面,即用来隔离 Docker 容器的技术。 在 Pod 的上下文中,每个独立的应用可能会进一步实施隔离。

就 Docker 概念的术语而言,Pod 类似于共享名字空间和文件系统卷的一组 Docker 容器

yaml格式的pod 定义文件

apiVersion: v1          #必选,版本号,例如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       #必选,容器的镜像名称
    imagePullPolicy: [Always | Never | IfNotPresent]  #获取镜像的策略 Alawys表示下载镜像 IfnotPresent表示优先使用本地镜像,否则下载镜像,Nerver表示仅使用本地镜像
    command: [string]       #容器的启动命令列表,如不指定,使用打包时使用的启动命令
    args: [string]         #容器的启动命令参数列表
    workingDir: string      #容器的工作目录
    volumeMounts:         #挂载到容器内部的存储卷配置
    - name: string         #引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名
      mountPath: string     #存储卷在容器内mount的绝对路径,应少于512字符
      readOnly: boolean     #是否为只读模式
    ports:              #需要暴露的端口库号列表
    - name: string         #端口号名称
      containerPort: int    #容器需要监听的端口号
      hostPort: int        #容器所在主机需要监听的端口号,默认与Container相同
      protocol: string      #端口协议,支持TCP和UDP,默认TCP
    env:              #容器运行前需设置的环境变量列表
    - name: string        #环境变量名称
      value: string       #环境变量的值
    resources:          #资源限制和请求的设置
      limits:           #资源限制的设置
        cpu: string       #Cpu的限制,单位为core数,将用于docker run --cpu-shares参数
        memory: string      #内存限制,单位可以为Mib/Gib,将用于docker run --memory参数
      requests:         #资源请求的设置
        cpu: string       #Cpu请求,容器启动的初始可用数量
        memory: string      #内存清楚,容器启动的初始可用数量
    livenessProbe:        #对Pod内个容器健康检查的设置,当探测无响应几次后将自动重启该容器,检查方法有exec、httpGet和tcpSocket,对一个容器只需设置其中一种方法即可
      exec:             #对Pod容器内检查方式设置为exec方式
        command: [string]   #exec方式需要制定的命令或脚本
      httpGet:            #对Pod内个容器健康检查方法设置为HttpGet,需要制定Path、port
        path: string
        port: number
        host: string
        scheme: string
        HttpHeaders:
        - name: string
          value: string
      tcpSocket:            #对Pod内个容器健康检查方式设置为tcpSocket方式
         port: number
       initialDelaySeconds: 0   #容器启动完成后首次探测的时间,单位为秒
       timeoutSeconds: 0      #对容器健康检查探测等待响应的超时时间,单位秒,默认1秒
       periodSeconds: 0       #对容器监控检查的定期探测时间设置,单位秒,默认10秒一次
       successThreshold: 0
       failureThreshold: 0
       securityContext:
         privileged: false
    restartPolicy: [Always | Never | OnFailure] #Pod的重启策略,Always表示一旦不管以何种方式终止运行,kubelet都将重启,OnFailure表示只有Pod以非0退出码退出才重启,Nerver表示不再重启该Pod
    nodeSelector: obeject     #设置NodeSelector表示将该Pod调度到包含这个label的node上,以key:value的格式指定
    imagePullSecrets:         #Pull镜像时使用的secret名称,以key:secretkey格式指定
    - name: string
    hostNetwork: false        #是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
    volumes:              #在该pod上定义共享存储卷列表
    - name: string          #共享存储卷名称 (volumes类型有很多种)
      emptyDir: {}          #类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。为空值
      hostPath: string        #类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录
        path: string        #Pod所在宿主机的目录,将被用于同期中mount的目录
      secret:             #类型为secret的存储卷,挂载集群与定义的secre对象到容器内部
        scretname: string
        items:   
        - key: string
          path: string
      configMap:          #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
        name: string
        items:
        - key: string
          path: string
复制代码

Pod使用

通常你不需要直接创建 Pod,甚至单实例 Pod。 相反,你会使用诸如 Deployment 和 Statfulset这类工作负载资源 来创建 Pod。如果 Pod 需要跟踪状态, 可以考虑 StatefulSet 资源。

每个 Pod 都旨在运行给定应用程序的单个实例。如果希望横向扩展应用程序(例如,运行多个实例 以提供更多的资源),则应该使用多个 Pod,每个实例使用一个 Pod。 在 Kubernetes 中,这通常被称为副本(Replication)通常使用一种工作负载资源及其 控制器 来创建和管理一组 Pod 副本。

pod创建流程

  1. 通过API Server发送创建pod请求
  2.  API Server处理请求,存储Pod数据到etcd。 
  3.  调度器通过API Server watch机制查看未绑定的Pod。尝试为Pod分配主机。 
  4.  过滤主机 (调度预选):过滤不符合要求的主机,资源不够,label不配匹。 
  5.  主机打分(调度优选):对筛选出符合要求的主机进行打分,在主机打分阶段,调度器会考虑一些整体优化策略
  6.  选择主机:选择最高的主机,进行binding操作,结果存储到etcd中。
  7.  kubelet根据调度结果执行Pod创建操作: 绑定成功后,scheduler会调用APIServer的API在etcd中创建一个boundpod对象,描述在一个工作节点上绑定运行的所有pod信息。运行在每个工作节点上的kubelet也会定期与etcd同步boundpod信息,一旦发现应该在该工作节点上运行的boundpod对象没有更新,则调用Docker API创建并启动pod内的容器。 

Pod 怎样管理多个容器

Pod 被设计成支持形成内聚服务单元的多个协作过程(形式为容器)。 Pod 中的容器被自动安排到集群中的同一物理机或虚拟机上,并可以一起进行调度。 容器之间可以共享资源和依赖、彼此通信、协调何时以及何种方式终止自身。

例如,你可能有一个容器,为共享卷中的文件提供 Web 服务器支持,以及一个单独的 “sidecar(挂斗)”容器负责从远端更新这些文件,如下图所示:

有些 Pod 具有 Init 容器 和 应用容器。 Init 容器会在启动应用容器之前运行并完成。

Pod 天生地为其成员容器提供了两种共享资源:网络和 存储。

Pod 的生命周期

Pod 遵循一个预定义的生命周期,起始于 Pending 阶段,如果至少 其中有一个主要容器正常启动,则进入 Running,之后取决于 Pod 中是否有容器以 失败状态结束而进入 Succeeded 或者 Failed 阶段。

在 Pod 运行期间,kubelet 能够重启容器以处理一些失效场景。 在 Pod 内部,Kubernetes 跟踪不同容器的状态 并确定使 Pod 重新变得健康所需要采取的动作。

在 Kubernetes API 中,Pod 包含规约部分和实际状态部分。 Pod 对象的状态包含了一组 Pod 状况(Conditions)。 如果应用需要的话,你也可以向其中注入自定义的就绪性信息。

Pod 在其生命周期中只会被调度一次。 一旦 Pod 被调度(分派)到某个节点,Pod 会一直在该节点运行,直到 Pod 停止或者 被终止。

Pod 阶段

Pod 的 status 字段是一个 PodStatus 对象,其中包含一个 phase 字段

下面是 phase 可能的值:

如果某节点死掉或者与集群中其他节点失联,Kubernetes 会实施一种策略,将失去的节点上运行的所有 Pod 的 phase 设置为 Failed

容器重启策略

Pod 的 spec 中包含一个 restartPolicy 字段,其可能取值包括 Always、OnFailure 和 Never。默认值是 Always。

restartPolicy 适用于 Pod 中的所有容器。restartPolicy 仅针对同一节点上 kubelet 的容器重启动作。当 Pod 中的容器退出时,kubelet 会按指数回退 方式计算重启的延迟(10s、20s、40s、…),其最长延迟为 5 分钟。 一旦某容器执行了 10 分钟并且没有出现问题,kubelet 对该容器的重启回退计时器执行 重置操作。

Pod 状况

Pod 有一个 PodStatus 对象,其中包含一个 PodConditions 数组。Pod 可能通过也可能未通过其中的一些状况测试。

  • PodScheduled:Pod 已经被调度到某节点;
  • ContainersReady:Pod 中所有容器都已就绪;
  • Initialized:所有的 Init 容器 都已成功启动;
  • Ready:Pod 可以为请求提供服务,并且应该被添加到对应服务的负载均衡池中。

Pod健康检查和服务可用性检查

Probe 是由 kubelet 对容器执行的定期诊断。 要执行诊断,kubelet 调用由容器实现的 Handler (处理程序)。有三种类型的处理程序:

  • ExecAction: 在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。

  • TCPSocketAction: 对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的。

  • HTTPGetAction: 对容器的 IP 地址上指定端口和路径执行 HTTP Get 请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。

每次探测都将获得以下三种结果之一:

  • Success(成功):容器通过了诊断。
  • Failure(失败):容器未通过诊断。
  • Unknown(未知):诊断失败,因此不会采取任何行动。

针对运行中的容器,kubelet 可以选择是否执行以下三种探针,以及如何针对探测结果作出反应:

  • livenessProbe:指示容器是否正在运行。如果存活态探测失败,则 kubelet 会杀死容器, 并且容器将根据其重启策略决定未来。如果容器不提供存活探针, 则默认状态为 Success

  • readinessProbe:指示容器是否准备好为请求提供服务。如果就绪态探测失败, 端点控制器将从与 Pod 匹配的所有服务的端点列表中删除该 Pod 的 IP 地址。 初始延迟之前的就绪态的状态值默认为 Failure。 如果容器不提供就绪态探针,则默认状态为 Success

  • startupProbe: 指示容器中的应用是否已经启动。如果提供了启动探针,则所有其他探针都会被 禁用,直到此探针成功为止。如果启动探测失败,kubelet 将杀死容器,而容器依其 重启策略进行重启。 如果容器没有提供启动探测,则默认状态为 Success

Pod 的终止

由于 Pod 所代表的是在集群中节点上运行的进程,当不再需要这些进程时允许其体面地 终止是很重要的。一般不应武断地使用 KILL 信号终止它们,导致这些进程没有机会 完成清理操作。

设计的目标是令你能够请求删除进程,并且知道进程何时被终止,同时也能够确保删除 操作终将完成。当你请求删除某个 Pod 时,集群会记录并跟踪 Pod 的体面终止周期, 而不是直接强制地杀死 Pod。在存在强制关闭设施的前提下, kubelet 会尝试体面地终止 Pod。

通常情况下,容器运行时会发送一个 TERM 信号到每个容器中的主进程。 很多容器运行时都能够注意到容器镜像中 STOPSIGNAL 的值,并发送该信号而不是 TERM。 一旦超出了体面终止限期,容器运行时会向所有剩余进程发送 KILL 信号,之后 Pod 就会被从 API 服务器 上移除。如果 kubelet 或者容器运行时的管理服务在等待进程终止期间被重启, 集群会从头开始重试,赋予 Pod 完整的体面终止限期。

1、调用apiserver 发送删除pod

2、Pod 对象随着时间的推移更新,在宽限期(默认情况下30秒),pod 被视为“dead”状态 3、将 pod 标记为“Terminating”状态 

4、和3步同时运行,监控到 pod 对象为“Terminating”状态的同时启动 pod 关闭过程 

5、和3步同时进行,endpoints 控制器监控到 pod 对象关闭,将pod与service匹配的 endpoints 列表中删除 

6、如果 pod 中定义了 preStop 钩子处理程序,则 pod 被标记为“Terminating”状态时以同步的方式启动执行;若宽限期结束后,preStop 仍未执行结束,第二步会重新执行并额外获得一个2秒的小宽限期 

7、Pod 内对象的容器收到 TERM 信号 

8、宽限期结束之后,若存在任何一个运行的进程,pod 会收到 SIGKILL 信号 

9、Kubelet 请求 API Server 将此 Pod 资源宽限期设置为0从而完成删除操作

强制终止 Pod

注意: 对于某些工作负载及其 Pod 而言,强制删除很可能会带来某种破坏。

默认情况下,所有的删除操作都会附有 30 秒钟的宽限期限。 kubectl delete 命令支持 --grace-period=<seconds> 选项,允许你重载默认值, 设定自己希望的期限值。

将宽限期限强制设置为 0 意味着立即从 API 服务器删除 Pod。 如果 Pod 仍然运行于某节点上,强制删除操作会触发 kubelet 立即执行清理操作。

说明: 你必须在设置 --grace-period=0 的同时额外设置 --force 参数才能发起强制删除请求。

执行强制删除操作时,API 服务器不再等待来自 kubelet 的、关于 Pod 已经在原来运行的节点上终止执行的确认消息。 API 服务器直接删除 Pod 对象,这样新的与之同名的 Pod 即可以被创建。 在节点侧,被设置为立即终止的 Pod 仍然会在被强行杀死之前获得一点点的宽限时间。

如果你需要强制删除 StatefulSet 的 Pod,请参阅 从 StatefulSet 中删除 Pod 的任务文档

失效 Pod 的垃圾收集

对于已失败的 Pod 而言,对应的 API 对象仍然会保留在集群的 API 服务器上,直到 用户或者控制器进程显式地 将其删除。

控制面组件会在 Pod 个数超出所配置的阈值 (根据 kube-controller-managerterminated-pod-gc-threshold 设置)时 删除已终止的 Pod(阶段值为 SucceededFailed)。 这一行为会避免随着时间演进不断创建和终止 Pod 而引起的资源泄露问题。

pod资源限制

默认情况下, Kubernetes 集群上的容器运行使用的计算资源没有限制。 使用资源配额,集群管理员可以以名字空间为单位,限制其资源的使用与创建。 在命名空间中,一个 Pod 或 Container 最多能够使用命名空间的资源配额所定义的 CPU 和内存用量。 有人担心,一个 Pod 或 Container 会垄断所有可用的资源。 LimitRange 是在命名空间内限制资源分配(给多个 Pod 或 Container)的策略对象。

一个LimitRange(限制范围)

对象提供的限制能够做到:

  • 在一个命名空间中实施对每个 Pod 或 Container 最小和最大的资源使用量的限制。
  • 在一个命名空间中实施对每个 PersistentVolumeClaim 能申请的最小和最大的存储空间大小的限制。
  • 在一个命名空间中实施对一种资源的申请值和限制值的比值的控制。
  • 设置一个命名空间中对计算资源的默认申请/限制值,并且自动的在运行时注入到多个 Container 

LimitRange 支持在很多 Kubernetes 发行版本中也是默认启用的。

配额例子

集群中的 Pod 可取三个优先级类之一,即 “low”、”medium”、”high”。
为每个优先级创建一个配额对象。

apiVersion: v1
kind: List
items:
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-high
  spec:
    hard:
      cpu: "1000"
      memory: 200Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["high"]
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-medium
  spec:
    hard:
      cpu: "10"
      memory: 20Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["medium"]
- apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: pods-low
  spec:
    hard:
      cpu: "5"
      memory: 10Gi
      pods: "10"
    scopeSelector:
      matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["low"]

kubectl create -f ./quota.yml
kubectl describe quota
复制代码

创建优先级为 “high” 的 Pod。 将以下 YAML 保存到文件 high-priority-pod.yml 中。

apiVersion: v1
kind: Pod
metadata:
  name: high-priority
spec:
  containers:
  - name: high-priority
    image: ubuntu
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo hello; sleep 10;done"]
    resources:
      requests:
        memory: "10Gi"
        cpu: "500m"
      limits:
        memory: "10Gi"
        cpu: "500m"
  priorityClassName: high

kubectl create -f ./high-priority-pod.yml
复制代码

确认 “high” 优先级配额 pods-high 的 “Used” 统计信息已更改,并且其他两个配额未更改。

kubectl describe quota
复制代码

参考:

kubernetes.io/zh/docs/con…

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享