Kubernetes 入门之 Pod 详解

Pod 作为 k8s 的基本调度单元,是 k8s 的关键所在。本文从 Pod 的使用、控制、调度、应用配置等方面入手,全方面讲解 k8s 如何发布和管理应用。

pod 基本用法

对长时间运行容器的要求是:其主程序需要一直在前台运行。kubelet 创建包含这个容器的 Pod 之后运行完命令,即认为 Pod 执行结束,接着立即销毁该 Pod ,根据 RS 中定义的 Pod 副本数量,会立即再生成一个新 Pod ,会进入无限循环。

属于一个 Pod 的多个容器应用之间仅需通过 localhost 通信,一组容器被绑定在了一个环境中。

在同一个 Pod 中的容器共享 Pod 级别的 Volume,而每个容器即可各自进行挂载,将 Volume 挂载为容器内部需要的目录。

静态 Pod

静态 pod 是指由 kubelet 管理的仅存在特定 Node 上的 Pod。它们不能通过 APIServer 管理,无法与 RS 等进行关联。

创建静态 Pod 的方式:

静态 pod 的创建方式有两种,分别为配置文件方式和 HTTP 方式。

配置文件方式

设置 kubelet 的启动参数 –config,指定 kubelet 需要监控的配置文件所在目录,kubelet 定期扫描该目录,根据目录中的 .yaml 或 .json 文件创建操作。由于无法通过 APIServer 对静态 pod 进行直接管理,在 Master 节点上尝试删除这个 Pod 将会将其变成 pending 状态,不会删除;若需要删除该 pod 需要在其所在 Node 上,将其定义文件从 /etc/kubelet.d 目录下删除。

HTTP 方式

设置 kubelet 的启动参数 –manifest-url ,kubelet 定期从该 URL 地址下载 Pod 的定义文件,并以 .yaml 或 .json 文件的格式解析,然后创建 Pod。实现方式与配置文件方式一致。

Pod 配置管理 —— ConfigMap

应用部署的一个最佳实践是将应用所需的配置信息与程序分离,好处是可以实现应用程序被更好的服用,通过不同的配置也能实现更灵活的功能。ConfigMap 是 k8s 统一的集群配置管理方案。

用法

生成为容器的环境变量,设置容器启动命令的启动参数,以 Volume 形式挂载为容器内部的文件或目录,以 key:value 的形式保存,既可以表示变量的值,也可以表示一个完整配置文件的内容。

ConfigMap 的创建

通过 yaml 文件的方式:书写好 yaml 文件后, kubectl create -f ***.yaml 命令即可创建 ConfigMap。

直接使用 kubectl create configmap 命令行的方式:可以根据目录、文件或字面值创建 ConfigMap。

1) 可以通过 --from-file 参数从目录中创建

kubectl create configmap <map-name> --from-file=config-files-dir

其中, <map-name> 代表 ConfigMap 的名字,config-files-dir 代表目录。创建出来的 ConfigMap 的 key 即为文件名。

2) 可以通过 --from-file 参数从文件中创建,并自定义 key

kubectl create configmap <map-name> --from-file=<my-key-name>=<path-to-file>

其中, my-key-name 为自定义 key,path-to-file 代表文件。

3) 可以通过 --from-literal 参数从文件中创建,并指定字面值

kubectl create configmap <map-name> --from-literal=<key>=<value>

其中, <key>=<value> 代表指定的键值对。

ConfigMap 的使用方式

下面就是容器中的应用如何使用 ConfigMap 的方法,主要有环境变量方式和挂载 Volume 方式。

环境变量方式

在 Deployment 的 yaml 的 container 中定义 env ,格式如下:

env:
- name: HDLS_KEY
  valueFrom:
    configMapKeyRef:
      name: special-config
      key: hdls

意为:该容器中环境变量 HDLS_KEY 的值取自名为 special-config 的 ConfigMap 中,key 为 hdls 。

volumeMount 模式

需要在 Pod 的 yaml 的 container 中定义 volumeMounts (引用 volume 名和挂载到容器内的目录),且在 volumes 中定义需要挂载的 volume 名和 ConfigMap 等信息。如下:

apiVersion: v1
kind: Pod
metadata:
  name: hdls-pod
spec:
  containers:
  - name: hdls-container
    image: ...
    ports:
      - containerPort: 8080
    volumeMounts:   # 在 container 中定义 volumeMounts
    - name: hdls-server   # 引用的 volume 名
      mountPath: /configfiles   # 挂载到容器中的目录
  volumes:
  - name: hdls-server   # pod 中挂载的 Volume 名
    configMap:
      name: special-config   # 使用 ConfigMap “special-config”
  ...

ConfigMap 的限制条件

  1. 必须在 Pod 之前创建
  2. 只有处于相同 namespace 的 Pod 可以引用
  3. 没有配额管理
  4. 静态 Pod 无法使用 ConfigMap
  5. 在 Pod 对 ConfigMap 进行挂载时,容器内部只能挂载为目录,不能是文件

Pod 生命周期和重启策略

我们在调度、管理 Pod 时,需要熟悉 Pod 在整个生命周期的各个状态,而设置 Pod 的重启策略也是基于对 Pod 的各种状态的了解。

Pod 的状态

Pod 的所有状态总共有 5 种,分别如下:

Pending : APIServer 已经创建该 Pod ,但 Pod 内还有容器的镜像没有创建或正在下载;
Running : Pod 中所有的容器均已创建,且至少有一个容器处于运行、正在启动、正在重启状态;
Succeeded : Pod 中所有容器已成功退出,且不会重启;
Failed : Pod 中所求容器均已退出,但至少有一个容器处于失败状态;
Unknown : 由于某种原因无法获取该 Pod 的状态,可能由于网络不畅所致。

Pod 的重启策略

Pod 的重启策略有 3 种,默认值为 Always。

Always : 容器失效时,kubelet 自动重启该容器;
OnFailure : 容器终止运行且退出码不为0时重启;
Never : 不论状态为何, kubelet 都不重启该容器。

kubelet 重启失效容器的时间间隔以 sync-frequency 乘以 2n 来计算,最长延迟 5 分钟,并在成功重启后的 10 分钟重置该时间。

每种控制器对 Pod 的重启策略

Pod 的重启策略与控制方式有关,每种控制器对 Pod 的重启策略要求如下:

RS 和 DaemonSet:必须设置为 Always
Job:OnFailure 或 Never
kubelet (静态 Pod):Pod 失效时自动重启,且不进行健康检查

健康检查

Pod 的健康检查分别两种:存活检查和就绪检查,分别使用 LivenessProbe 探针和 ReadinessProbe 探针。

LivenessProbe (存活检查)

用于判断容器是否存活,一旦检测到容器不健康, kubelet 即杀掉该容器,并根据重启策略做相应处理,如果容器不包含 LivenessProbe 探针,kubelet 认为其返回值永远是 success。

ReadinessProbe (就绪检查)

用于判断容器是否启动完成,即是否 ready 状态,一旦检测到失败,则 Pod 的状态被改写,并将该 Pod 的 Endpoint 从 Service 的转发 Endpoint 中删除。

探针设置方式

ExecAction :在容器内执行一个命令,如果该命令的返回码为0,表明容器健康;
TCPSocketAction :通过容器的 IP 和端口号执行 TCP 检查,如果能建立 TCP 连接,表明容器健康;
HTTPGetAction :通过容器的 IP 地址、端口号及路径调用 HTTP Get 方法,如果返回码 >=200 ,且 <400,认为容器健康。

以上每种探测方式都需要设置的参数:
initialDelaySeconds:延迟检查的时间,单位为s
timeoutSeconds:健康检查发送后等待响应的超时时间,单位为s
periodSeconds:执行周期

Pod 调度

Pod 实为 k8s 中的最小调度单元,只是容器的载体,其本身无法完成自动调度的功能。 k8s 采用了 RS、Deployment、DaemonSet、Job 等方式实现 Pod 的调度和自动控制。

RS、Deployment 全自动调度

RS 的主要功能之一就是自动部署一个容器应用的多份副本,及持续监控副本的数量。而 RS/RC 在 k8s 中一般很少单独使用,都是在 Deployment 中使用,而 Deployment 是 k8s 引入的一个更好解决 Pod 编排问题的概念,通过 Deployment 我们可以随时知道 Pod 的调度情况。

在 Pod 的定义中,可以采用 NodeSelector 或 NodeAffinity 两种方式进行调度。

NodeSelector 定向调度

Pod 调度是通过 Master 上的 Scheduler 负责实现的,原理是通过 Node 标签和 Pod 的 nodeSelector 属性匹配。

NodeSelector 定向调度的过程为:通过 kubectl label 给目标 Node 打上标签;在 Pod 的定义上加上 nodeSelector 的设置。需要注意的是,一旦 Pod 指定了 nodeSelector ,若集群中不存在匹配的 Node ,即使有空闲的 Node,也不会调度。

通过 kubectl 进行打标签的方法:

kubectl label nodes <node-name> <label-key>=<label_value>

在 Pod 中指定 nodeSelector:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hdls
spec:
  template:
    metadata:
      name: hdls-pod
    spec:
      containers:
      - name: hdls-container
        image: ...
        ports:
        - containerPort: 8080
      nodeSelector:
        <label-key>=<label_value>

NodeAffinity 亲和性调度

NodeAffinity 意为 Node 亲和性的调度策略,是替换 NodeSelector 的下一代调度策略。在 NodeSelector 的基础上增加了 In/NotIn/Exists/DoesNotExsist/Gt/Lt 等操作。

设置亲和性:
requiredDuringSchedulingRequiredDuringExecution:类似于 NodeSelector ,在 Node 不满足条件时,系统从该 Node 上移除之前调度上的 Pod ;
requiredDuringSchedulingIgnoredDuringExecution:在 Node 不满足条件时,系统不一定从该 Node 上移除之前调度上的 Pod;
preferredDuringSchedulingIgnoredDuringExecution:指定在满足条件的 Node 中,哪些 Node 更优先地调度;同时,Node 不满足条件时,不一定从该 Node 上移除之前调度上的 Pod。

DaemonSet 特定场景调度

DaemonSet 是 Kubernetes 1.2 版本中新增的一种资源对象,用于确保全部(或指定的某些)Node 上运行一个 Pod 副本。其 Pod 调度策略与 RC 类似。

使用场景:
1. 在每个 Node 上运行一个 GlusterFS 存储或者 Ceph 存储的 daemon 进程
2. 在每个 Node 上运行一个日志采集程序,如 fluentd 、logstach
3. 在每个 Node 上运行一个健康程序,采集该 Node 的运行性能数据

Job 批处理调度

Job 是 Kubernetes 1.2 版本中新增的支持批处理的资源对象。批处理任务通常并行或串行启动多个计算进程去处理一批工作项(work item),处理完后整个 Job 结束。

Job 的模式:

  1. Job Template Expansion 模式:一个 Job 对应一个 work item 。通常适合 work item 较少、每个 work item 要处理的数据量较大的场景;
  2. Queue with Pod Per Work Item 模式:采用一个任务队列存放 work item,Job 对象去消费这些 work item 。这种模式是一个 Pod 对应一个 work item,一个 Job 启动多个 Pod;
  3. Queue with Variable Pod Count 模式:与上面一种模式相同,唯一不同的是 Job 启动的 Pod 数量时可变的;
  4. Single Job with Static Work Assignment 模式:也是一个 Job 产生多个 Pod 的模式,但采用的是程序静态方式分配任务,而不是队列模式。

Job 的类型

考虑到批处理的并行问题,Job 被分为以下几种类型:

  1. Non-parallel Jobs :一个 Job 启动一个 Pod;
  2. Parallel Jobs with a fixed completion count :并行 Job 启动多个 Pod。需要设置 Job 的 .spec.completions 参数为一个正数,此为正常结束的 Pod 的上限值;.spec.parallelism 参数为并行数,即同时处理 work item 的 Pod 数;
  3. Parallel Jobs with a work queue :并行 Job 有个独立的 queue 来存放 work item ,此时不能设置 .spec.completions 参数。

Pod 扩容和缩容

在实际生产系统中,服务扩容是个不容忽视的场景。在 k8s 中,有两种方式来实现 Pod 的扩容和缩容,分别为 RC 的 Scale 机制和 HPA (Horizontal Pod Autoscaler)。

RC 的 Scale 机制

通过 kebectl 命令可设置 Pod 的副本数:

kubectl scale rc <rc-name> --replicas=3

通过 --replicas=<num> 参数将 Pod 的副本数手动调高,即可完成 Pod 的扩容;相应的,将该参数设置为较低的数,系统将 kill 掉一些运行中的 Pod ,以实现应用集群缩容。

HPA

HPA (Horizontal Pod Autoscaler) 是 Kubernetes v1.1 新增的控制器,用以实现基于 CPU 使用率进行自动 Pod 扩缩容的功能。HPA 基于 Master 的 kube-controller-manager 服务启动参数 –horizontal-pod-autoscaler-sync-period 定义的时长(默认为 30 秒),周期性的检测目标 Pod 的 CPU 使用率,并在满足条件时对 RC 或 Deployment 中的 Pod 副本数进行调整,以符合用户定义的平均 Pod CPU 使用率。PodCPU 使用率来源于 heapster 组件,所以需要预先安装好 heapster。

创建 HPA 时可以使用 kubectlautoscale 命令或使用 yaml 配置文件。在创建 HPA 之前,需要确保已经存在一个 RC 或 Deployment 对象,且该 RC 或 Deployment 中的 Pod 必须定义 resources.requests.cpu 的资源请求值。

滚动升级

在实际生产环境中,应用的升级也是一个很重要的场景。在集群规模较大时,先全部停止再逐步升级的方式会导致较长时间内服务不可用,升级工作就成了一个不小的挑战。 k8s 提供了滚动升级功能来解决这个问题。

滚动升级通过执行 kubectl rolling-update 命令一键完成。整个过程为:

  1. 创建一个新的 RC;
  2. 自动控制旧的 RC 中的 Pod 副本的数量逐渐减少到 0;
  3. 同时新 RC 中的 Pod 副本数从 0 逐步增加到到目标值,旧的 Pod 每减少 1,新的 Pod 就增加 1。

需要注意的是,新旧 RC 必须在同一 namespace 下。

采用 yaml 配置文件

若采用 yaml 配置文件来实现滚动升级,需要先手动创建一个新的 RC yaml,且 yaml 文件中需要注意的是:

  1. RC 的名字不可与旧 RC 名字相同;
  2. 在 selector 在应至少有一个 Label 与旧的 RC 的 Label 不同,以标识为新的 RC ;

再运行 kubectl 命令完成滚动升级:

kubectl rolling-update <RC-name> -f <new-RC-yaml>

不使用 yaml 配置文件

若不使用 yaml 配置文件,可直接用 kubectl rolling-update 命令,加上 --image 参数指定新版镜像名。

kubectl rolling-update <RC-name> --image=<image-name>

与使用配置文件的方式不同,该方法执行的结果是旧的 RC 被删除,新的 RC 仍使用旧的 RC 名,且完成升级后,新 RC 多一个 key 为 “deployment”(这个名字可通过 --deployment-label-key 参数进行修改) 的 Label ,值为 RC 的内容进行 Hash 计算后的值。

最后,若 Pod 需要回滚,可中断更新操作,执行 kubectl rolling-update-rollback 完成 Pod 版本的回滚。

Leave a Reply

Your email address will not be published. Required fields are marked *