Kubernetes 中, 容器总是以 Pod(容器组)的方式进行调度与运行。因此对 Pod 的理解与掌握是学习 Kubernetes 的基础。

理解 Pod

Pod(容器组)是 Kubernetes 中最小的调度单元,每一个Pod都是某个应用程序的一个运行实例。以前我们的 Web 应用都是以 Tomcat 等 Web 容器进程的形式运行在操作系统中,在 Kubernetes 中,我们需要将 Web 应用打成镜像,以容器的方式运行在 Pod 中。

Kubernetes 不会直接管理容器,而是通过 Pod 来管理。一个Pod包含如下内容:

  1. 一个或多个容器, 一般是一个,除非多个容器紧密耦合共享资源才放在一个 Pod 中;
  2. 共享的存储资源(如数据卷),一个 Pod 中的容器是可以共享存储空间的;
  3. 一个共享的 IP 地址,Pod 中容器之间可以通过 localhost:port 彼此访问;
  4. 定义容器该如何运行的选项。

Pod 中的容器可包括两种类型:

  1. 工作容器:就是我们通常运行服务进程的容器
  2. 初始化容器:完成一些初始化操作的容器,初始化容器在工作容器之前运行,所有的初始化容器成功执行后,才开始启动工作容器

管理 Pod

创建 Pod

在 Kubernetes 中,我们一般不直接创建 Pod,而是通过控制器来调度管理(Deployment,StatefulSet,DaemonSet 等),这里为了便于了解,先通过 yaml 配置文件的方式定义 Pod 来直接创建 Pod。定义配置文件 pod-test.yaml 如下,

apiVersion: v1
kind: Pod
metadata:
name: pod-test # pod 名称
namespace: default # pod 创建的 namespace
spec:
containers: # pod 中容器定义
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
hostPort: 8081
volumeMounts:
- name: workdir
mountPath: /usr/share/nginx/html
restartPolicy: OnFailure # 重启策略
volumes: # 数据卷定义
- name: workdir
hostPath:
path: /tmp
type: Directory

其中 spec 部分的 containers 定义了该 Pod 中运行的容器,从 containers 的复数形式也可以看出一个 Pod 中是可以运行多个容器的。

执行 kubectl createkubectl apply 命令创建 Pod,

[root@kmaster test]# kubectl create -f pod-test.yaml

或

[root@kmaster test]# kubectl apply -f pod-test.yaml

该 Pod 创建后将会拉取一个最新的 nginx 镜像,运行一个 nginx 容器,并将容器的 80 端口映射到宿主机的 8081 端口。

查看 Pod

可使用 kubectl get pods 命令查看当前 namesapce 下的所有 Pod,加 Pod 名称查看具体某个 Pod。 如果需要查看 Pod 调度到了哪个节点,可加 -o wide 选项,如果查看 yaml 文件信息则可加 -o yaml 选项, 如下所示

[root@kmaster test]# kubectl get pods
NAME READY STATUS RESTARTS AGE
pod-test 1/1 Running 0 116s [root@kmaster test]# kubectl get pods pod-test -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-test 1/1 Running 0 2m19s 10.244.1.42 knode2 <none> <none> [root@kmaster test]# kubectl get pods pod-test -o yaml

如果要查看更多的信息,可使用 kubectl describe 命令,

[root@kmaster test]# kubectl describe pod pod-test

该命令输出内容如下图,

各部分说明:

  1. Status: Pending, 表示 Pod 的整体状态,当前处于 Pending 状态;
  2. State: Waiting,Pod 中每个容器都有一个自己的状态 State, 当前容器 nginx 处于 Waiting 状态,Reason: ContainerCreating 表示容器还处于创建中,Ready:False 表明容器还未就绪,还不能对外提供服务;
  3. Conditions, 这部分聚合了一些状态,第一个 Initialized:True,表明已经完成了初始化;而第二个 Ready:False,表明 Pod 还未就绪;ContainersReady:False,表明容器还未就绪; PodScheduled:True,表明 Pod 已经被调度到某个具体的节点上了;
  4. 3中不同的状态之间的转换都会发生相应的事件,事件类型包括 Normal 与 Warning 两种, 从上图可看到一个 Pulling image 的 Normal 事件,表示当前正在拉取 Pod 中容器的镜像。

当 Pod 在调度或运行中出现问题时,我们都可以使用 kubectl describe 命令来进行排查,通过其中的状态及事件来判断问题产生的可能原因。

进入 Pod 容器

通过 kubectl exec 命令可进入 Pod, 类似于 docker exec, 如

# 如果 Pod 中只有一个容器
[root@kmaster test]# kubectl exec -it pod-test bash
root@pod-test:/# # 如果 Pod 中有多个容器
kubectl exec -it pod-name -c container-name /bin/bash

如果一个 Pod 中有多个容器,则需要通过 -c 指定进入哪个容器。

更新/删除 Pod

Kubernetes 对 Pod 的更新做了限制,除了更改 Pod 中容器(包括工作容器与初始化容器)的镜像,以及 activeDeadlineSeconds (对 Job 类型的 Pod 定义失败重试的最大时间), tolerations (Pod 对污点的容忍),修改其它部分将不会产生作用,如我们可以尝试在前面 Pod 定义文档 pod-test.yaml 中将宿主机端口 8081 改为 8082,重新执行 kubectl apply, 将提示如下错误,

[root@kmaster test]# kubectl apply -f pod-test.yaml
The Pod "pod-test" is invalid: spec: Forbidden: pod updates may not change fields other than `spec.containers[*].image`, `spec.initContainers[*].image`, `spec.activeDeadlineSeconds` or `spec.tolerations` (only additions to existing tolerations)

通过 kubectl delete 命令可删除一个 Pod

[root@kmaster test]# kubectl delete pod pod-test

在 Kubernetes 中,一般不直接创建,更新或删除单个 Pod,而是通过 Kubernetes 的 Controller(控制器)来管理 Pod,包括 ReplicSet(一般也不直接用,推荐Deployment方式), Deployment,StatefulSet,DaemonSet 等。

控制器提供如下功能:

  1. 水平伸缩,控制运行 Pod 指定个数的副本
  2. rollout,即版本更新
  3. 故障恢复,当一个节点出现故障,或资源不够,或进入维护中,控制器会自动在另一个合适的节点调度一个一样的 Pod,以保障 Pod 以一定的副本数运行

Pod 状态

Pod状态并不是容器的状态,容器的状态一般包括:

Waiting: 容器的初始状态,处于 Waiting 状态的容器,表示仍然有对应的操作在执行,例如:拉取镜像、应用 Secrets等

Running: 容器处于正常运行的状态

Terminated: 容器处于结束运行的状态

而Pod的状态一般包括:

  • Pending: Kubernetes 已经创建并确认该 Pod,可能两种情况: 1. Pod 还未完成调度(例如没有合适的节点);2. 正在从 docker registry 下载镜像
  • Running: 该 Pod 已经被绑定到一个节点,并且该 Pod 所有的容器都已经成功创建,其中至少有一个容器正在运行,或者正在启动/重启
  • Succeeded:Pod 中的所有容器都已经成功终止,并且不会再被重启
  • Failed:Pod 中的所有容器都已经终止,至少一个容器终止于失败状态:容器的进程退出码不是 0,或者被系统 kill
  • Unknown: 因为某些未知原因,不能确定 Pod 的状态,通常的原因是 master 与 Pod 所在节点之间的通信故障

状态之间的变迁关系如图

Pod 刚开始处于 Pending 的状态,接下来可能会转换到 Running,也可能转换到 Unknown,甚至可能转换到 Failed。然后,当 Running 执行了一段时间之后,它可以转换到类似像 Successded 或者是 Failed。 当出现 Unknown 这个状态时,可能由于一些状态的恢复,它会重新恢复到 Running 或者 Successded 或者是 Failed。

重启策略

定义 Pod 或工作负载时,可以指定 restartPolicy,可选的值有:

  1. Always:默认值,只要退出就重启
  2. OnFailure:失败退出时(exit code 不为 0)才重启
  3. Never: 永远不重启

restartPolicy 作用于 Pod 中的所有容器。kubelete 将在五分钟内,按照递延的时间间隔(10s, 20s, 40s ...)尝试重启已退出的容器,并在十分钟后再次启动这个循环,直到容器成功启动,或者 Pod 被删除。在控制器 Deployment/StatefulSet/DaemonSet 中,只支持 Always 这一个选项,不支持 OnFailure 和 Never 选项。

健康检查

提高应用服务的可用性与稳定性,一般可从两个方面来进行:

  1. 首先是提高应用的可观测性,如对应用的健康状态,资源的使用情况,应用日志等可进行实时的观测
  2. 第二是提高应用的可恢复能力,在应用出现故障时,能通过自动重启等方式进行恢复

Kubernetes 中对 Pod 的健康检查提供了两种方式:

  1. Readiness probe,就绪探测,用来判断一个 Pod 是否处于就绪状态,是否能对外提供相应服务了。当Pod处于就绪状态时,负载均衡器才会将流量打到这个 Pod,否则将把流量从这个 Pod 上面摘除。
  2. Liveness probe,存活探测,用来判断一个 Pod 是否处于存活状态,如果一个 Pod 被探测到不处于存活状态,则由上层判断机制来处理,如果上层配置重启策略为 restart always 的话,Pod 就会被重启。

Liveness probe 适用场景是支持那些可以重新拉起的应用,而 Readiness probe 主要应对的是启动之后无法立即对外提供服务的应用。

就绪探测、存活探测目前支持三种不同的探测方式:

  1. httpGet,通过发送http Get请求来判断,返回状态码在 200-399之间,认为是探测成功
  2. Exec,通过执行容器中的一个命令来判断服务是否正常,如果命令的退出状态码为 0,表示成功
  3. tcpSocket,通过容器的IP,端口来进行TCP连接检查,如果TCP连接能被正常建立,则认为成功

以 httpGet 为例,示例配置文件如下,

apiVersion: v1
kind: Pod
metadata:
name: pod-test
spec:
containers:
- # ... 与前同
- name: workdir
mountPath: /usr/share/nginx/html
livenessProbe:
httpGet:
path: /
port: 80
httpHeaders: # 此处header无意义,仅作示例
- name: purpose
value: for-test
initialDelaySeconds: 2
periodSeconds: 5
# ... 与前同

删除之前的 Pod, 重新创建,使用 kubectl describe 查看,可看到 Events 部分如下图,

Http 存活探测失败,状态码返回 403, 导致容器重启。出现这个错误的原因是前面做目录挂载时将 nginx 的 html 目录挂载到了宿主机的 /tmp 目录, 而 /tmp 目录没有 index.html 文件,导致请求返回403, 在 Pod 调度到的宿主机 /tmp 目录下创建 index.html 文件即可。

echo '<h1>Hello, K8s!</h1>' > /tmp/index.html

其它 Exec,tcpSocket 探测的配置示例如下(配置在 containers 元素下),

# exec
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5 # tcpSocket
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 10
periodSeconds: 10

支持的参数说明:

  • initialDelaySeconds:延迟探测时间,表示 Pod 启动延迟多久后进行一次检查,比如某个应用启动时间如果较长的话,可以设置该值为略大于启动时间;
  • periodSeconds:探测频率,表示探测的时间间隔,正常默认的这个值是 10 秒;
  • timeoutSeconds:超时时间,表示探测的超时时间,当超时时间之内没有检测成功,那会认为失败;
  • successThreshold:健康阈值,表示当这个 Pod 从探测失败到再一次判断探测成功,所需要的阈值次数,默认情况下是 1 次。如果之前探测失败,接下来的一次探测成功了,就会认为这个 Pod 是处在一个正常的状态;
  • failureThreshold: 不健康阈值,与 successThreshold 相对,表示认为探测失败需要重试的次数,默认值是 3。意思是当从一个健康的状态连续探测到 3 次失败,就会认为Pod 的状态处在一个失败的状态。

readinessProbe 配置与 livenessProbe 类似。阿里云上配置就绪检查如图所示:

健康检查的结果分为三种:

  1. Success,表示 container 通过了健康检查,也就是 Liveness probe 或 Readiness probe 是正常的一个状态;
  2. Failure,表示 container 没有通过健康检查。针对 Readiness probe,service 层就会将没有通过 Readiness probe 的 pod 进行摘除,不再分发请求到该 Pod;针对 Liveness probe,就会将这个 pod 进行重新拉起,或者是删除。
  3. Unknown,表示当前的执行机制没有进行完整的一个执行,可能是因为类似像超时或者像一些脚本没有及时返回,此时 Readiness probe 或 Liveness probe 不做任何操作,会等待下一次的机制来进行检查。

健康检查的一些实践建议:

  1. 如果容器中的进程在碰到问题时可以自己 crash,就不需要执行存活探测,因为 kubelet 可以自动的根据 Pod 的 restartPolicy(重启策略)来执行对应的动作;
  2. 如果希望在容器的进程无响应后,将容器重启,则指定一个存活探测 livenessProbe,并同时指定 restartPolicy(重启策略)为 Always 或者 OnFailure;
  3. 如果希望在 Pod 确实就绪之后才向其分发服务请求,就指定一个就绪检查 readinessProbe;
  4. 适当调大 exec 探测的超时阈值,因为在容器里面执行一个 shell 脚本,它的执行时长是非常长的,平时在一台虚机上执行可能 3 秒返回的一个脚本在容器里面可能需要 30 秒。可以适当调大超时阈值,来防止由于容器压力比较大的时候出现偶发的超时;
  5. 调整失败判断的次数,3 次的默认值有时候可能不一定是最佳实践,适当调整一下判断的次数也是一个比较好的方式;
  6. 使用 tcpSocket 方式进行判断的时候,如果遇到了 TLS 的服务,那可能会造成后边 TLS 里面有很多这种未鉴权的 tcp 连接,这时候需要自己针对业务场景判断这种连接是否会对业务造成影响。

总结

本文对 Pod 的概念与基本的管理操作,Pod 的状态变迁机制与重启策略进行了介绍,对 Pod 的健康检查进行了详细的了解。但在 Kubernetes 中,我们一般不直接创建 Pod,而是通过控制器,如Deployment,StatefulSet,DaemonSet, 因为控制器能为我们提供水平伸缩,rollout(版本更新),self-healing(故障恢复)等能力。我们将在接下来的文章了解控制器。


[转载请注明出处]

作者:雨歌

欢迎关注作者公众号:半路雨歌,查看更多技术干货文章

Kubernetes笔记(五):了解Pod(容器组)的更多相关文章

  1. Kubernetes笔记(二):了解k8s的基本组件与概念

    前文 Kubernetes笔记(一):十分钟部署一套K8s环境 介绍了如何快速搭建一个k8s系统.为了继续使用k8s来部署我们的应用,需要先对k8s中的一些基本组件与概念有个了解. Kubernete ...

  2. Kubernetes笔记(六):了解控制器 —— Deployment

    Pod(容器组)是 Kubernetes 中最小的调度单元,可以通过 yaml 定义文件直接创建一个 Pod.但 Pod 本身并不具备自我恢复(self-healing)功能.如果一个 Pod 所在的 ...

  3. Kubernetes基石-pod容器

    引用三个问题来叙述Kubernetes的pod容器 1.为什么不直接在一个Docker容器中运行所有的应用进程. 2.为什么pod这种容器中要同时运行多个Docker容器(可以只有一个) 3.为什么k ...

  4. 《两地书》--Kubernetes(K8s)基础知识(docker容器技术)

    大家都知道历史上有段佳话叫“司马相如和卓文君”.“皑如山上雪,皎若云间月”.卓文君这么美,却也抵不过多情女儿薄情郎. 司马相如因一首<子虚赋>得汉武帝赏识,飞黄腾达之后便要与卓文君“故来相 ...

  5. kubernetes进阶之三:Pod

    一:Pod 是什么 Pod是Kubernetes的最重要最基本的概念.它是能够被创建,调度和管理的最小部署单元.一个Pod代表集群中一个运行的进程. 二:Pod的组成 一个Pod由一个特殊的根容器Pa ...

  6. Kubernetes 笔记 02 demo 初体验

    本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. 从前面的文章我 ...

  7. Kubernetes(K8s)基础知识(docker容器技术)

    今天谈谈K8s基础知识关键词: 一个目标:容器操作:两地三中心:四层服务发现:五种Pod共享资源:六个CNI常用插件:七层负载均衡:八种隔离维度:九个网络模型原则:十类IP地址:百级产品线:千级物理机 ...

  8. Kubernetes — 为什么我们需要Pod?

    不过,我相信你在学习和使用 Kubernetes 项目的过程中,已经不止一次地想要问这样一个问题:为什么我们会需要 Pod? 是啊,我们在前面已经花了很多精力去解读 Linux 容器的原理.分析了 D ...

  9. 5、kubernetes资源清单之Pod应用190709

    一.Pod镜像及端口 获取帮助文档 # kubectl explain pod.spec.containers spec.containers <[]object> pod.spec.co ...

随机推荐

  1. C#LeetCode刷题-排序

    排序篇 # 题名 刷题 通过率 难度 56 合并区间   31.2% 中等 57 插入区间   30.4% 困难 75 颜色分类   48.6% 中等 147 对链表进行插入排序   50.7% 中等 ...

  2. C#LeetCode刷题之#594-最长和谐子序列​​​​​​​​​​​​​​(Longest Harmonious Subsequence)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3800 访问. 和谐数组是指一个数组里元素的最大值和最小值之间的差 ...

  3. 19c新环境安装补丁(三)_推荐

    本次安装Oracle RAC 19.3 版本 Linux red-hat 7.8  DB安装补丁  RUR 20200717. 本次安装Oracle补丁的方法类似于11G RAC打补丁的方法.  [可 ...

  4. 关于dubbo扩展点的一点分析

    扩展点能力 能load class,这个class除了顶层接口class(在ExtensionLoader中对应type字段),还能load各实现类的class. 能创建instance. 能指定这个 ...

  5. JavaScript学习系列博客_26_JavaScript 数组的一些方法

    数组的一些方法 - push() - 用来向数组的末尾添加一个或多个元素,并返回数组新的长度 - 语法:数组.push(元素1,元素2,元素N) - pop() - 用来删除数组的最后一个元素,并返回 ...

  6. 7.hbase shell命令 cmd

    $HADOOP_USER_NAME #创建命名空间create_namespace 'bd1902' #展示所有命名空间 list_namespace #删除命名空间,The namespace mu ...

  7. python 倒序遍历数组

    num = [,,,,,,,,] for i in range(, num.__len__())[::-]: print num[i]

  8. python基础-文件读写'r' 和 'rb'区别

    一.Python文件读写的几种模式: r,rb,w,wb 那么在读写文件时,有无b标识的的主要区别在哪里呢? 1.文件使用方式标识 'r':默认值,表示从文件读取数据.'w':表示要向文件写入数据,并 ...

  9. Build PhantomJS from Source

    Getting the Code To obtain the code using Git from the official repository github.com/ariya/phantomj ...

  10. JAVA虚拟机故障诊断总结

    一.JAVA运行时数据区               1.堆(-Xmx与-Xms):所有线程共享.  目的:用来存放对象实例.所有对象实例和数组都要在堆上分配内存.JAVA堆是垃圾收集器管理的主要区域 ...