kubernetes 官方文档整理,k8s入门教程笔记整理---6、Pod
kubelet将挑选Pod所映射的主机UID/GID,pod.spec的runAsUser、runAsGroup、fsGroup总是指的是容器内的用户,UID/GID在0~65535内。创建Pod时,默认会使用几个新的命名空间进行隔离:一个网络命名空间来隔离容器网络,一个PID命名空间来隔离进程视图,一个用户命名空间隔离容器中的用户和节点的用户。仅针对于kubelet的容器重启动作,当pod中的容
8 Pod
8.1 Pod
- Pod是k8s中创建和管理得、最小的可部署计算单元
- Pod是一组容器;这些容器共享存储、网路、声明。在共享得上下文中运行(命名空间、控制组),特定于应用得“逻辑主机”。除了应用容器还可以包含init容器。Init 容器会在启动应用容器之前运行并完成。
- Pod通常不直接创建,而是通过使用工作负载资源创建得。对于单个容器得pod,pod可以看作容器的包装器;对于多容器的pod,pod封装由多个紧密耦合且需要共享资源的容器组成
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
- 很少在k8s直接创建pod,一般间接的由控制器创建,被调度在集群的节点上运行,直到pod结束执行、pod对象被删除、因资源不足被驱逐、节点失效为止
- pod操作系统 .spec.os.name,取windows或linux,目前k8s仅支持这两种
- pod和控制器 使用工作负载来创建和管理多个pod。deployment、statefulset、daemonset
- pod模板,通常工作负载使用pod模板(PodTemplate)来创建pod并管理他们
apiVersion: batch/v1
kind: Job
metadata:
name: hello
spec:
template:
# 这里是 Pod 模板
spec:
containers:
- name: hello
image: busybox:1.28
command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
restartPolicy: OnFailure
# 以上为 Pod 模板
- kubelet并不直接检测或管理pod模板相关的细节
- pod共享存储,pod可以设置一组共享的存储卷,pod中所有容器都可访问该共享卷,从而允许容器共享数据
- pod联网,每个pod在每个地址族获得一个唯一的IP地址。pod中每个容器共享网络ns,包括IP地址、网络端口。pod内的容器可以通过localhost通信
在一个pod内,他们也能通过SystemV信号量和POSIX共享内存这类标准的进程间通信方式互相通信。 - 静态pod,直接由kubelet管理,不需要APIServer看到他们,失效时由kubelet重启。静态pod不能引用其他API对象
- 容器探针 Probe,由kubelet对容器进行定期诊断。ExecAction(容器运行时执行)、TCPSocketAction(kubelet执行)、HTTPGetActionkubelet执行()
8.2 Pod的生命周期
Pod起始于Pending阶段,如果至少有一个主要容器正常启动,则进入Running,之后取决于pod中是否由容器以失败状态结束而进入Succeed或Failed阶段
kubelet能够重启容器以处理一些失效场景。在k8s API中,Pod包含规约部分和实际状态部分,还包含了Conditions。
8.2.1 Pod生命周期
- Pod被认为是相对临时性的实体,Pod会被创建、赋予一个唯一的ID—UID,并被调度到节点,在pod终止或删除之前一直运行在该节点
- 如果节点down掉了,调度到该节点的pod也被计划在给定超时期限后被删除
- Pod没有自愈能力。k8s使用一种高级抽象来管理Pod实例–控制器。在通过控制器删除并重建pod时,UID会发生改变,代表是一个全新的pod
- Pod阶段,Pod的status中包含一个phase,代表生命周期的阶段
- Pending—悬决:Pod被k8s系统接收,但是有一个或多个容器尚未创建。此阶段包括等待Pod被调度的时间和下载镜像的时间
- Running—运行中:Pod已经绑定到了某个节点,Pod中所有容器被创建。至少有一个容器处于运行、启动、重启的状态
- Succeeded—成功:Pod中容器已经成功终止,不会重启,常见于job中
- Failed—失败:Pod中所有容器已终止,至少有一个容器因为失败终止。也就是说,容器以非0状态退出或被系统终止
- Unkonwn—未知:因为某些原因无法取得Pod的状态。通常是因为Pod所在主机通信失败
比较特殊的Terminating(终止)。这个并不是Pod阶段之一,他被赋予一个体面终止的期限,默认为30s。可以用–force来强制终止Pod
- 容器状态,k8s会跟踪pod中每个容器的状态,可以用容器生命之前回调来在容器生命周期特定时间点触发事件。一旦调度器将Pod分派给某个节点,kubelet通过容器运行时开始为Pod创建容器,
容器状态有三种,可以通过kubectl describe pod 来查看- Waiting—等待:处于waiting状态的容器仍在运行它完成启动所需的操作,Reason字段给出了等待的原因
- Running—运行中:标识容器正在执行并没有问题发生。如果配置了postStart,那么该回调已完成。
- Terminated—已终止:已经开始正在结束或因为某些原因失败,如果配置了preStop,会在Terminated之前执行
8.2.2 容器重启策略
Pod的spec包含一个restartPolicy字段,取值为Always(默认)、OnFailure、Never。
仅针对于kubelet的容器重启动作,当pod中的容器退出时,kubelet会按指数回退方式计算重启的延迟(10s、20s、40s),最长为5分钟
一旦某容器执行了10分钟没出现问题,kubelet对该容器的重启回退倒计时执行重置操作。
8.2.3 Pod状况
status.conditions数组里包含了一些状况的测试
status:
conditions:
- lastProbeTime: null
lastTransitionTime: "2023-08-10T09:34:14Z"
status: "True"
type: Initialized #所有Init容器已完成
- lastProbeTime: null
lastTransitionTime: "2023-08-17T03:06:02Z"
status: "True"
type: Ready #Pod可以为请求提供服务,并且已经添加到对应服务的负载均衡池中
- lastProbeTime: null
lastTransitionTime: "2023-08-17T03:06:02Z"
status: "True"
type: ContainersReady #Pod中所有容器已就绪
- lastProbeTime: null
lastTransitionTime: "2023-08-10T09:34:14Z"
status: "True"
type: PodScheduled #Pod已被调度到某节点
#还有PodHasNetwork 这个代表Pod沙箱被成功创建并配置了网络(Alpha特性,必须被显示启用)
- lastProbeTime: 上次探测pod状态的时间戳
lastTransitionTime: Pod上次状态转换的时间戳
status: 状态是否适用取True False Unkonwn
reason: 机器可读的、驼峰编码的文字,表述上次状况变化的原因
message: 人类可读的,给出上次状态转换的详细信息
type: PodScheduled #Pod已被调度到某节点
8.2.4 Pod就绪态
你的应用可以向PodStatus中注入外的反馈或信号:Pod Readiness。设置Pod spec的readinessGates列表,为kubelet提供一组额外的状况工期评估Pod就绪态
就绪状态:Pod中所有容器都已就绪、readinessGates中所有状况都为True值
8.2.4.1 Pod网络就绪 PodHasNetwork
在Pod被调度到某节点后–>被kubelet接受并挂载所需的卷—>kubelet和容器运行时(使用CRI)为pod生成运行时沙箱并配置网络—>(启用了PodHasNetworkCondition会修改PodHasNetwork来报告Pod状态)—>kubelet拉取容器镜像和创建容器
PodHasNetwork为false,可能由于pod早期,kubelet还没开始使用容器运行时设置沙箱或在Pod生命周期末期,节点重启Pod未被驱逐。
- 对于带有init容器的pod,kubelet会在init容器完成后更新Initialized为true(发生在运行时成功创建沙箱和配置网络后)
- 对于没有init容器的pod,kubelet会在创建沙箱和配置网络前将Initialized设为true
8.2.4.2 Pod调度就绪态 Pod schedulingGates
通过Pod的.spec.schedulingGates来控制Pod是否进入调度就绪态
schedulingGates,为一个字符串列表,每个字符串文字都被视为Pod在被认可调度之前应满足的标准。
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
schedulingGates:
- name: foo
- name: bar
containers:
- name: pause
image: registry.k8s.io/pause:3.6
8.2.4.3 容器探针
probe是由kubelet对容器执行的定期判断。有四种机制可以用来检查容器
四种机制
- exec 在容内执行指定命令。命令返回码为0则认定成功。特殊的,该种探针涉及创建、复制多个进程,有可能增加CPI负载
- grpc 使用gRPC执行远程调用。目标应该实现gRPC健康检查。响应为SERVING则判定成功
- httpGet 对容器所在IP地址指定端口和路径执行HTTP GET亲贵,响应码为200~400,不包括400,则认定成功
- tcpSocket 对容器的IP地址指定端口执行TCP检查,端口打开,则认定成功
结果
- Success:通过
- Failure:失败
- Unkonwn:未知,不采取行动
三种探针类型
- livenessProbe 存活探针,代表容器是否正在运行。如果检测失败,那么kubelet会杀死容器。
容器种进程在遇到问题或不健康的情况下自行崩溃,那么就不必要提供存活探针,kubelet将根据重启策略自行修复 - readinessProbe 就绪探针,代表容器是否准备好提供服务。如果就绪探针失败,那么端点控制器将从与Pod匹配的所有服务端点列表删除该Pod的IP地址。
如果要尽在探测成功才开始向Pod发送请求流量,那么需要使用就绪探针。希望容器能够自行进入维护状态。对后端服务有严格的依赖性,可以同时实现存活和就绪探针。 - startupProbe 启动探针,代表容器中的应用是否已经启动。在启动探针检测时其他探针会被静默,如果启动探针未通过,则kubelet也会杀死容器
如果你的容器需要在启动期间加载大型数据、配置文件或执行迁移,也就是说需要较长时间才能完成启动的Pod来说,需要使用启动探针 ,启动时间超过intialDelaySeconds + failureThreshold * periodSeconds
8.2.4.4 Pod的终止
pod代表的其实是在节点上运行的进程,当你请求删除Pod时,集群会记录并追踪Pod的体面终止。
通常情况下,容器运行时发送一个TERM信号到每个容器的主进程。一旦超过体面终止期限,容器运行时会向所有剩余进程发送KILL信号
之后Pod将从APIServer上移除
示例
- 1、使用kubelet手动删除pod,pod的体面终止期限默认是30秒
- 2、APIServer种的Pod更新,记录Pod最终终止时间。此时Pod显示为Terminating状态。
- 3、kubelet看到终止时间,那么执行关闭pod。如果定义了preStop,会先回调该逻辑,接下来触发容器运行时发送TERM信号给每个容器中的进程
- 4、在kubelet启动Pod和体面关闭逻辑的同时,会找到Pod关联的Service,并从相应的Endpoint移除
- 5、超出宽限期限,kubelet触发强制关闭过程。容器运行时会向Pod所有容器内仍在运行的进程发送SIGKILL信号。kubelet也会清理隐藏的pause容器
- 6、kubelet将Pod转换到终止阶段,kubelet触发从APIServer删除Pod对象的逻辑,并设置体面终止期限为0
8.2.4.5 强制终止Pod
强制删除可能会带来某种破坏,默认情况所有删除操作都有30秒的宽限期限,kubelet delete支持–grace-period=。如果为0代表宽限时间为0秒
加上–force,代表立即强制删除,如果Pod仍运行于某节点上,强制删除操作将会触发kubelet立即执行清理操作
8.2.4.5 Pod的垃圾收集
对于已失败的Pod来说,API对象仍然会留在APIServer上,Pod的垃圾收集器(PodGC)是控制平面的控制器,
会在Pod个数超出所配置的阈值时删除已终止的Pod(Succeed或Failed状态)
此外PodGC会清理以下Pod
- 1、孤儿Pod - 绑定到不存在的节点
- 2、计划外终止的Pod
- 3、终止过程中的Pod,当启用NodeOutOfServiceVolumnDetach特性门控时,绑定到有node.kubernetes.io/out-of-service污点的未就绪节点
若启用PodDisruptionConditions特性门控,在清理Pod的同时,如果处于非终止状态,PodGC也会将他们标记为失败。在清理孤儿Pod时还会添加Pod干扰状况
8.3 Init 容器
- 一种特殊容器,在Pod内的应用容器启动之前运行,主要特点是总是能运行到完成。可以用于包含一些应用镜像中不存在的实用工具和安装脚本
- init容器可以有多个,如果是多个init容器,那么按照定义的顺序执行,每个都必须在下一个启动之前完成
- 当Init容器失败时,kubelet会不断重启该Init容器直到运行成功。如果restartPolicy为Never,Init容器失败,那么Pod为失败
- 为Pod设置Init容器需要在PodSpec中添加initContainers字段,在statusContainerStatues展示状态
- init容器支持普通应用容器的全部字段和特性,包括资源限制、数据卷、安全特性等,但不支持lifecycle、探针
8.3.1 使用Init容器
特点:
- 1、Init容器可以包含工具或个性化代码,例如sed、awk、python等工具,而不用FROM一个新镜像
- 2、Init容器能以不同的文件系统视图运行,因此可以赋予访问应用容器不能访问的Secret权限
- 3、由于Init容器必须在应用容器启动前运行完成,因此Init容器提供了一种机制来阻塞或延迟应用容器启动。之后应用容器会并行启动
- 4、init容器可以安全的运行实用程序或自定义代码
示例:
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app.kubernetes.io/name: MyApp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
8.3.2 具体行为
在Pod启动过程中,每个Init容器都会在网络和数据卷初始化后由kubelet按顺序启动—>init容器启动或运行失败,会根据restartPolicy策略重试
—>在Init容器全部未成功前,Pod不会变成Ready状态—>Pod重启,init容器重新执行。
8.3.3 容器内的资源共享
- init容器上定义的limit或request的最大值,作为Pod有效初始request/limit。未指定代表无限制
- Pod有效QoS层,和Init容器和应用容器的一样
8.4 干扰(Disruptions)
8.4.1 自愿干扰和非自愿干扰
Pod不会消失,除非有人(控制器或用户)将其销毁,或者出现了不可避免的软硬件错误
非自愿干扰:不可避免的情况,例如:节点硬件故障,删除虚拟机、内核错误、节点消失、节点资源不足导致Pod被驱逐
自愿干扰:手动操作或控制器操作,例如:删除deploy、更新deploy、直接删除pod、drain节点、hpa扩缩
8.4.2 减轻非自愿干扰
确保pod有充足的资源,使用多副本负载,使用反亲和性或跨区域扩展应用。
8.4.3 干扰预算
可以为每个应用创建一个PDB(PodDisruptionBudget),PDB将限制宕机的Pod数量,代表可以容忍的最小副本数量
8.4.4 Pod干扰状况
特性门控:启用PodDisruptionConditions,启用后会给Pod添加一个DisruptionTarget,用来表明Pod因干扰被删除,reason中包括
- PreemptionByScheduler Pod被调度器抢占,目的是接受优先级更高的新Pod
- DeletionByTaintManager Pod不能容忍NoExecute污点而被控制器删除
- EvictionByEvictionAPI Pod已被标记为通过k8s API驱逐
- DeletionByPodGC 绑定到不再存在的Pod被Pod垃圾收集清除
- TerminationByKubelet Pod由于节点压力驱逐或节点体面关闭而被kubelet终止
8.5 临时容器
临时容器是:一种特殊的容器,在现有Pod中临时运行,以便完成用户发起的操作,例如故障排查。
因为Pod是一次性且可替换的,Pod一旦创建就无法将容器加入到Pod中。
临时容器的特点
- 临时容器和其他容器的不同之处在于,缺少对资源或执行的保证,永远不会自动重启。
- 用ContainerSpec描述,许多字段不兼容,端口配置不支持例如port、存活探针、就绪探针等;不支持资源预留和限制等。
- 临时容器是使用API中的一种特殊的ephemeralcontainers处理器进行创建的,而不是直接添加到pod.spec上,因此不能直接kubectl edit来添加临时容器
- 将临时容器添加到Pod后,将不能修改或删除临时容器
使用临时容器来调试 kubectl debug -it nginx --image=busybox:1.28 --target=nginx
–target 参数指定另一个容器的进程命名空间
8.6 Pod QoS 类
服务质量Qos类,k8s根据为Pod中的容器指定的资源约束为每个Pod设置QoS类,
k8s依赖这种分类决定优先驱逐哪些Pod,主要基于资源请求和限制。
可选的QoS类为BestEffort(优先驱逐)、Burstable(其次)、Guaranteed(最后驱逐)
- BestEffort : Pod中所有容器都没内存limit、内存request、CPU limit、CPU request。
- Burstable : Pod中至少有一个容器指定了request或limit
- Guaranteed : Pod中每个容器都必须有内存limit、request,cpu limit request。并且内存limit==内存request。cpu limit == cpu request
独立于k8s的QoS类
- 所有超过limit的容器都会被kubelet杀死并重启,但是不会重启pod,只会重启容器
- 容器超出了request,所在节点面临资源压力,那么该pod会被成为驱逐的候选对象。
- Pod的request等于容器request的和,Pod的limit等于容器limit的和
- kube-scheduler选择要抢占的Pod时不考虑QoS。抢占是当集群没有足够的资源来运行Pod时会发生抢占
8.7 用户命名空间
用户命名空间将容器内运行的用户和主机的用户隔离开来。用户在容器内拥有较高权限,但是在主机没有操作特权,可以用来防止节点被Pod破坏,防止容器逃逸。
用户命名空间是一个linux功能,允许将容器中的用户映射到主机的不通用户,Pod通过pod.spec.hostUsers设置为false来选择使用用户命名空间
kubelet将挑选Pod所映射的主机UID/GID,pod.spec的runAsUser、runAsGroup、fsGroup总是指的是容器内的用户,UID/GID在0~65535内。
创建Pod时,默认会使用几个新的命名空间进行隔离:一个网络命名空间来隔离容器网络,一个PID命名空间来隔离进程视图,一个用户命名空间隔离容器中的用户和节点的用户。
限制:
当Pod使用用户命名空间时,不允许Pod使用其他主机命名空间,如果你设置了hostUsers:false
那么你不能设置hostNetwork: true、hostIPC: true、hostPID: true,
如果使用卷,只能使用以下卷类型:configmap、secret、projected、downwardAPI、emptyDir
示例: 你需要节点的操作系统为Linux、启用UserNamespacesSupport特性门控
apiVersion: v1
kind: Pod
metadata:
name: userns
spec:
hostUsers: false
containers:
- name: shell
command: ["sleep", "infinity"]
image: debian
8.8 Downward API
Downward API 允许容器在不使用k8s客户端或API服务器的情况下获得自己或集群的信息。
例如想获取Pod或容器字段,有两种方法:
- 1、作为环境变量
- 2、在位downwardAPI卷中的文件。这两种方式统称为Downward API
可用字段
只有部分k8s API字段可以通过Downward API使用。
- 使用fieldRef传递Pod级字段的信息。
- 以下字段环境变量,downwardAPI卷都可使用
- metadata.name pod的名称
- metadata.namespace pod的ns
- metadata.uid pod的uid
- metadata.annotations[‘’] pod的某个注解的值 例如 metadata.annotation[‘myanno’]
- metadata.labels[‘’] pod的某个标签的值 例如 metadata.labels[‘myanno’]
- 以下只能使用环境变量,不能作为downwardAPI卷
- spec.serviceAccountName Pod的服务账号名称
- spec.nodeName Pod运行时所处的节点名称
- status.hostIP 所在节点的主IP
- status.hostIPs hostIP的双协议栈版本
- status.podIP Pod的主IP
- status.podIPs 双协议栈版本
- 以下只能使用downwardAPI卷fieldRef获得,不能作为环境变量
- metadata.labels 全部标签
- metadata.annotations 全部注解
- 以下字段环境变量,downwardAPI卷都可使用
- 使用resourceFieldRef传递来自Container级字段的信息。
- resource: limits.cpu 容器的 CPU 限制值。注意 如果没配置将显示节点可使用cpu最大值
- resource: requests.cpu 容器的 CPU 请求值
- resource: limits.memory 容器的内存限制值 注意 如果没配置将显示节点可使用内存最大值
- resource: requests.memory 容器的内存请求值
- resource: limits.hugepages-*容器的巨页限制值
- resource: requests.hugepages-*容器的巨页请求值
- resource: limits.ephemeral-storage 容器的临时存储的限制值
- resource: requests.ephemeral-storage 容器的临时存储的请求值
示例:
#通过环境变量的方式挂载downwardAPI
apiVersion: v1
kind: Pod
metadata:
name: dapi-envars-fieldref
spec:
containers:
- name: test-container
image: registry.k8s.io/busybox
command: [ "sh", "-c"]
args:
- while true; do
echo -en '\n';
printenv MY_NODE_NAME MY_POD_NAME MY_POD_NAMESPACE;
printenv MY_POD_IP MY_POD_SERVICE_ACCOUNT;
sleep 10;
done;
env:
- name: MY_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: MY_POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: MY_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: MY_POD_SERVICE_ACCOUNT
valueFrom:
fieldRef:
fieldPath: spec.serviceAccountName
restartPolicy: Never
#通过卷的方式挂载downwardAPI
apiVersion: v1
kind: Pod
metadata:
name: kubernetes-downwardapi-volume-example
labels:
zone: us-est-coast
cluster: test-cluster1
rack: rack-22
annotations:
build: two
builder: john-doe
spec:
containers:
- name: client-container
image: registry.k8s.io/busybox
command: ["sh", "-c"]
args:
- while true; do
if [[ -e /etc/podinfo/labels ]]; then
echo -en '\n\n'; cat /etc/podinfo/labels; fi;
if [[ -e /etc/podinfo/annotations ]]; then
echo -en '\n\n'; cat /etc/podinfo/annotations; fi;
sleep 5;
done;
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo #通过downwardAPI挂载在/etc/podinfo/ 下有两个文件 labels、annotations
volumes:
- name: podinfo
downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- path: "annotations"
fieldRef:
fieldPath: metadata.annotations
更多推荐
所有评论(0)