【云原生】K8S 相关知识点整理 -- Pod
【1】简介
Pod 是 Kubernetes 中的最小调度单元,K8S 通过定义一个 Pod 资源,然后在 Pod 里面运行容器(容器需要指定一个镜像),从而运行具体的服务;一个 Pod 封装一个/多个容器,Pod 里的容器共享存储、网络等资源;即可以将整个 Pod 看作虚拟机,每个容器相当于运行在虚拟机的进程
- Pod 网络
每个 Pod 都被分配唯一的 IP 地址 (IP 地址是靠网络插件 calico、flannel、weave 等分配),Pod 中的容器共享网络名称空间,包括 IP 地址和网络端口;Pod 内部的容器可以使用 localhost 相互通信,Pod 中的容器也可以通过网络插件 calico 与其他节点的 Pod 通信
- Pod 存储
创建 Pod 时可以指定挂载的存储卷,Pod 中的所有容器都可以访问共享卷,允许这些容器共享数据,Pod 只要挂载持久化数据卷,Pod 重启之后数据就会存储
【2】命名空间
Kubernetes 支持多个虚拟集群,它们底层依赖于同一个物理集群,这些虚拟集群被称为命名空间;
命名空间 namespace 是 K8S 集群级别的资源,可以给不同的用户、租户、环境或项目创建对应的命名空间,例如,可以为 test、devlopment、production 环境分别创建各自的命名空间;
K8S 集群默认提供了几个命名空间用于特定目的
- kube-system 主要用于运行系统级资源,存放 K8S 的一些组件
- default 为未指定命名空间的资源操作提供一个默认值
K8S 命名空间相关的常用命令
kubectl get namespace,查看 namespace 资源
kubectl describe namespace $NAME,查看特定的命名空间的详细信息
kubectl create namespace $NAME,创建命名空间
namespace 资源的名称仅能由字母、数字、下划线、连接线等字符组成
删除 namespace 资源会级联删除其包含的所有其他资源对象
kubectl config set-context --current --namespace=kube-system,切换命名空间
切换命名空间后,kubectl get pods 如果不指定 -n,查看的就是 kube-system 命名空间的资源
kubectl api-resources --namespaced=true,查看属于命名空间级别的资源信息
namespace 资源限额
apiVersion: v1
kind: ResourceQuota
metadata:
name: mem-cpu-quota
namespace: test
spec:
hard:
requests.cpu: "2"
requests.memory: 2Gi
limits.cpu: "4"
limits.memory: 4Gi
针对创建的 ResourceQuota 对象将在 test 命名空间中添加以下限制
每个容器必须设置
内存请求 (memory request),
内存限额 (memory limit),
cpu 请求 (cpu request)
cpu 限额 (cpu limit)
所有容器的内存请求总额不得超过 2GB
所有容器的内存限额总额不得超过 4GB
所有容器的 CPU 请求总额不得超过 2CPU
所有容器的 CPU 限额总额不得超过 4CPU
【3】标签
标签即 key/value 键值对,被关联到对象上,比如 Pod;
使用标签可以标识对象的特殊特点,可以用来划分特定的对象 (比如版本,服务类型等),可以方便对资源进行分组管理
标签可以在创建一个对象的时候直接定义,也可以在后期随时修改
每一个对象可以拥有多个标签,但是,key 值必须是唯一的
K8S 标签相关的常用命令
// 对已经存在的 pod 标记标签
kubectl label pods pod-first(pod 名称) release=v1
// 查看标签是否标记成功
kubectl get pods pod-first(pod 名称) --show-labels
// 显示如下则标签标记成功
NAME READY STATUS RESTARTS AGE LABELS
pod-first 1/1 Running 1 21h release=v1, app=tomcat-pod-first
// 查看默认命名空间下所有 pod 资源的标签
kubectl get pods --show-labels
// 查看默认命名空间下指定 pod 具有的所有标签
kubectl get pods pod-first --show-labels
// 列出默认命名空间下标签 key 是 release 的 pod,不显示标签
kubectl get pods -l release
// 列出默认命名空间下标签 key 是 release、值是 v1 的 pod,不显示标签
kubectl get pods -l release=v1
// 列出默认命名空间下标签 key 是 release 的所有 pod,并打印对应的标签值
kubectl get pods -L release
// 查看所有命名空间下的所有 pod 的标签
kubectl get pods --all-namespace --show-labels
【4】节点选择器
在创建 Pod 资源时,Pod 会经过 schduler 进行调度,默认会调度到随机的一个工作节点,若想要 Pod 调度到指定节点或者调度到一些具有相同特点的 node 节点,可以使用 Pod 中的 nodeName 或者 nodeSelector 字段指定要调度到的 node 节点;
【5】亲和性、污点、容忍度
【5.1】Node 节点亲和性
- prefered (软亲和性),表示节点尽量满足这个位置定义的亲和性,不是一个必须的条件
- 软策略适用于满不满足条件都能工作,但是满足条件更好的情况,比如服务最好运行在某个区域,减少网络传输等;
- require(硬亲和性),表示节点必须满足这个位置定义的亲和性,是硬性条件
- 硬策略适用于 Pod 必须运行在某种节点,否则会出现问题的情况,比如集群中节点的架构不同,而运行的服务必须依赖某种架构提供的功能;
- requiredDuringSchedulingIgnoredDuringExecution
- 表示 Pod 必须部署到满足条件的节点上,如果没有满足条件的节点,就不停重试;其中 IgnoreDuringExecution 表示 Pod 部署之后运行的时候,如果节点标签发生了变化,不再满足 Pod 指定的条件,Pod 也会继续运行
- requiredDuringSchedulingRequiredDuringExecution
- 表示 Pod 必须部署到满足条件的节点上,如果没有满足条件的节点,就不停重试;其中 RequiredDuringExecution 表示 Pod 部署之后运行的时候,如果节点标签发生了变化,不再满足 Pod 指定的条件,则重新选择符合要求的节点
- preferredDuringSchedulingIgnoredDuringExecution
- 表示优先部署到满足条件的节点上,如果没有满足条件的节点,就忽略这些条件,按照正常逻辑部署
- preferredDuringSchedulingRequiredDuringExecution
- 表示优先部署到满足条件的节点上,如果没有满足条件的节点,就忽略这些条件,按照正常逻辑部署;其中 RequiredDuringExecution 表示如果后面节点标签发生了变化,满足了条件,则重新调度到满足条件的节点
【5.2】Pod 亲和性
- Pod 自身的亲和性调度有两种表示形式
- podaffinity : Pod 和 Pod 更倾向于在一起,把相近的 Pod 结合到相近的位置,如同一区域,同一机架,从而 Pod 和 Pod 之间能够更好的通信
- podunaffinity : Pod 和 Pod 更倾向不在一起,比如部署两套程序,那么这两套程序更倾向于反亲和性,这样相互之间不会有影响
【5.3】污点与容忍度
污点和容忍度属性给了节点选则的主动权,当给节点打一个污点,那么无法容忍该污点的 Pod 就无法运行在该节点,污点是定义在节点上的键值属性数据,可以定决定拒绝哪些 Pod
- taints 是键值数据,用在节点上,定义污点
- tolerations 是键值数据,用在 Pod 上,定义容忍度,能容忍哪些污点
【6】Pod 常见状态和重启策略
【6.1】Pod 常见状态
Pod 的 status 定义在 PodStatus 对象中,其中有一个 phase 字段,简单描述了 Pod 在其生命周期的阶段;
- 挂起 (Pending) : 在请求创建 Pod 时,条件不满足,调度没有完成,没有任何一个节点能满足调度条件,都会处于挂起状态,处于 pending 状态会持续一段时间,包括调度 Pod 的时间和通过网络下载镜像的时间
- 运行中 (Running) : Pod 已经绑定到了一个节点上,Pod 中所有的容器都已被创建,至少有一个容器正在运行,或者正处于启动或重启状态
- 成功 (Succeeded) : Pod 中的所有容器都被成功终止,并且不会再重启
- 失败 (Failed) : Pod 中的所有容器都已终止了,并且至少有一个容器是因为失败终止,即容器以非 0 状态退出或者被系统终止
- 未知 (Unknown) : 未知状态,所谓 Pod 是什么状态是 apiserver 和运行在 Pod 节点的 kubelet 进行通信获取状态信息的,如果节点之上的 kubelet 本身出故障,那么 apiserver 就连不上 kubelet,便得不到信息,就会看 Unknown
- 驱逐 (Evicted) : 该状态多见于系统内存或硬盘资源不足,可 df-h 查看 docker 存储所在目录的资源使用情况,如果百分比大于 85%,就要及时清理下资源,尤其是一些大文件、docker 镜像
- CrashLoopBackOff : 容器曾经启动了,但可能异常退出
- Error 状态 : Pod 启动过程中发生了错误
【6.2】Pod 的重启策略 (RestartPolicy)
Pod 的重启策略应用于 Pod 内的所有容器,并且仅在 Pod 所处的 Node 上由 kubelet 进行判断和重启操作,当某个容器异常退出或者健康检查失败时,kubelet 将根据 RestartPolicy 的设置来进行相应的操作
- Always : 当容器失败时,由 kubelet 自动重启该容器
- OnFailure : 当容器终止运行且退出码不为 0 时,由 kubelet 自动重启该容器
- Never : 不论容器运行状态如何,kubelet 都不会重启该容器
【7】Pod 的生命周期
【7.0】Pod 生命周期各阶段总体图示
【7.1】Init 容器
Pod 里面可以有一个或者多个容器,部署应用的容器称为主容器,在创建 Pod 时,Pod 中可以有一个或多个先于主容器启动的 Init 容器,init 容器即初始化容器,初始化容器从启动开始到初始化代码执行完便退出,不会一直存在,用于在主容器启动之前执行初始化,初始化容器可以有多个,多个初始化容器是串行执行的,先执行初始化容器 1,再执行初始化容器 2 等,等初始化容器执行完初始化阶段便结束了,然后执行主容器,主容器一旦退出,Pod 就结束了,主容器退出的时间点就是 Pod 的结束时间点
注意,由于一个 Pod 里的存储卷是共享的,因此 Init Container 中产生的数据可以被主容器使用
Init 容器与普通的容器区别
- 1、Init 容器不支持 Readiness,因为它们必须在 Pod 就绪之前运行完成
- 2、每个 Init 容器必须运行成功,下一个才能够运行
- 3、如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止,然而对于普通容器,若 Pod 对应的 restartPolicy 值为 Never 则普通容器不会重新启动
【7.2】容器钩子
初始化容器启动之后,开始启动主容器,在主容器启动之前有一个 post start hook (容器启动后钩子) 和 pre stop hook (容器结束前钩子),可以通过这两个 hook 处理开场前的预设,结束前的清理工作
- postStart : 该钩子在容器被创建后立刻触发,通知容器已经被创建事件,若该钩子对应的 hook handler 执行失败,则该容器会被杀死,并根据该容器的重启策略决定是否要重启该容器
- preStop : 该钩子在容器被删除前触发,其所对应的 hook handler 必须在删除该容器的请求发送给 Docker daemon 之前完成,在该钩子对应的 hook handler 完成后不论执行的结果如何,Docker daemon 会发送一个 SGTERN 信号量给 Docker daemon 来删除该容器
【7.3】Pod 探针
- 探针简介
- LivenessProbe (Pod 存活性探针),存活探针主要用指定的方式检测 Pod 中的容器应用是否正常运行,如果检测失败,则认为容器不健康,那么 Kubelet 将根据 Pod 中设置的 restartPolicy 来判断 Pod 是否要进行重启操作,如果容器配置中没有配置 LivenessProbe,Kubelet 将认为存活探针探测一直为成功状态;
- ReadinessProbe (Pod 就绪性探针),用于判断容器中的应用是否启动完成,探测容器内的程序是否健康,容器是否准备好服务请求,当探测成功后才会使能 Pod 对外提供网络访问,设置容器 Ready 状态为 true,如果探测失败,则 endpoint 将从与 Pod 匹配的所有 Service 的端点中删除该 Pod 的 IP 地址并设置容器的 Ready 状态为 false;如果容器不提供就绪探针,则默认状态为 Success;
- StartupProbe,探测容器中的应用是否已经启动;如果提供了启动探测 (startup probe),则禁用所有其他探测,直到它成功为止;如果启动探测失败,kubelet 将杀死容器,容器服从其重启策略进行重启;如果容器没有提供启动探测,则默认状态为成功 Success
可以自定义在 Pod 启动时是否执行这些检测,如果不设置,则检测结果均默认为通过,如果设置,则顺序为 StartupProbe > ReadinessProbe > LivenessProbe;
- LivenessProbe 和 ReadinessProbe 探针支持的探测方法
- 1、ExecAction,在容器中执行指定的命令,如果执行成功,退出码为 0 则探测成功
- 2、TCPSocketAction,通过容器的 IP 地址和端口号执行 TCP 检查,如果能够建立 TCP 连接,则表明容器健康
- 3、HTTPGetAction,通过容器的 IP 地址、端口号及路径调用 HTTP Get 方法,如果响应的状态码大于等于 200 且小于 400,则认为容器健康
- 探针探测结果的值
- 1、Success,表示通过检测
- 2、Failure,表示未通过检测
- 3、Unknown,表示检测没有正常进行
- Pod 探针相关的属性
- 探针 (Probe) 有许多可选字段,可以用来更加精确的控制 Liveness 和 Readiness 两种探针的行为
- initialDelaySeconds,Pod 启动后首次进行检查的等待时间,单位 “秒”
- periodSeconds,检查的间隔时间,默认为 10s,单位 “秒”
- timeoutSeconds,探针执行检测请求后,等待响应的超时时间,默认为 1s,单位 “秒”
- successThreshold,连续探测几次成功,才认为探测成功,默认为 1,在 Liveness 探针中必须为 1,最小值为 1
- failureThreshold,探测失败的重试次数,重试一定次数后将认为失败,在 readiness 探针中,Pod 会被标记为未就绪,默认为 3,最小值为 1
- 两种探针区别
- ReadinessProbe 和 LivenessProbe 可以使用相同探测方式,只是对 Pod 的处置方式不同
- readinessProbe 当检测失败后,将 Pod 的 IP:Port 从对应的 EndPoint 列表中删除
- livenessProbe 当检测失败后,将杀死容器并根据 Pod 的重启策略来决定作出对应的措施
附录
【附录 1】Pod 对应的 yaml 文件简析
参考博客,【云原生】K8S 相关知识点整理 -- 典型的 Yaml 文件示例
【附录 2】Pod 的创建过程
- 第一步
- 客户端提交创建 Pod 的请求
- 通过调用 API Server 的 Rest API 接口
- 通过 kubectl 命令行工具
- 第二步
- apiserver 接收到 Pod 创建请求后,会将 yaml 中的属性信息 (metadata) 写入 etcd
- 第三步
- apiserver 触发 watch 机制准备创建 Pod,信息转发给调度器 scheduler,调度器使用调度算法选择 node,调度器将 node 信息给 apiserver,apiserver 将绑定的 node 信息写入 etcd
- 第四步
- apiserver 通过 watch 机制,调用 kubelet,指定 Pod 信息,调用 Docker API 创建并启动 Pod 内的容器
- 第五步
- 创建完成之后反馈给 kubelet,kubelet 将 Pod 的状态信息给 apiserver,apiserver 将 Pod 的状态信息写入 etcd
参考与致谢
本博客为博主学习笔记,同时参考了网上众博主的博文以及相关专业书籍,在此表示感谢,本文若存在不足之处,请批评指正。
【1】K8S调度之节点亲和性
【2】污点和容忍度
【3】Pod 的生命周期
【4】Kubernetes学习之路(十一)之Pod状态和生命周期管理
【5】Kubernetes中pod创建流程