Pod是在K8s集群中运行部署应用或服务的最小单元,以更高的抽象层次为应用部署提供了极大的方便。
Pod中可以运行一个容器,也可以同时运行多个容器协同工作。比如Pod中有一个容器作为web服务器运行,需要用到共享的volume,有另一个“sidecar”容器来从远端获取资源更新这些文件。
Pod中可以共享两种资源:网络和存储。
每个Pod都会被分配一个唯一的IP地址。Pod中的所有容器共享网络空间,包括IP地址和端口。Pod内部的容器可以使用localhost
互相通信。Pod中的容器与外界通信时,必须分配共享网络资源(例如使用宿主机的端口映射)。基于共享网络可以实现流量染色。
可以Pod指定多个共享的Volume。Pod中的所有容器都可以访问共享的volume。Volume也可以用来持久化Pod中的存储资源,以防容器重启后文件丢失。基于共享存储可以实现sidecar模式的日志收集。
Pod总体上分为两大类:
普通pod
普通pod是通过客户端提交请求给Api server创建的,普通pod又分为两种:
裸Pod
又称为自主式Pod,不依赖任何控制器,比如通过kubectl run pod-test --image=nginx:1.8.1
直接创建,这种Pod自愈能力不完善,Pod内的容器挂掉了,会依据restartPolicy来进行重启,但是如果Pod本身被删掉,或者说Pod所在的节点挂掉了,导致本副本不可用了,不会在一个新节点上拉起Pod。
控制器管理的Pod
Kubernetes可以使用更高级的称为Controller的抽象层来管理Pod实例。比如通过kubectl create deployment web --image=nginx:1.14
创建Pod,这种Pod被 Deployment Controller来管理。Controller可以创建和管理多个Pod,提供副本管理、滚动升级和集群级别的自愈能力。例如,如果一个Node故障,Controller就能自动将该节点上的Pod调度到其他健康的Node上。
静态Pod
静态Pod是直接通过配置文件或HTTP的方式交给kubelet创建的pod,相当于一种系统服务,不走API Server。kubelet会定时检测/etc/kubernetes/manifest
目录下的yaml文件定义的静态Pod,yaml文件新增、减少或变动时,kubelet会保证处于yaml文件中定义的状态,静态的pod的状态可以查看到,无法彻底删除静态(删掉就会自动被kubelet拉起,除非删掉它的yaml文件)。
静态pod始终与某个Node节点的kubelet绑定,即始终运行在同一个节点,不会被调度到其他节点上。kubelet无法对静态Pod进行健康检测,但pod崩溃时kubelet会重启该静态pod
restartPolicy的值有三个:Always、OnFailure、Never;默认值为Always。
三种策略的区别在于:
虽然restartPolicy字段是Pod的配置,但是其实是作用于Pod的Container,换句话说,不应该叫Pod的重启策略,而是叫Container的重启策略;Pod中的所有Container都适用于这个策略。
重启策略适用于Pod对象中的所有容器,首次需要重启的容器,将在其需要时立即进行重启,随后再次需要重启的操作将由Kubelet延迟一段时间后进行,且反复的重启操作的延迟时长为10s,20s,40s,80s,160s,300s,300s是最大延迟时长。
所谓Container的退出码,就是Container中进程号为1的进程的退出码。每个进程退出时都有一个退出码,我们常见的提示exit 0表示退出码为0(即成功退出)。举个例子:shell命令cat /tmp/file,如果文件/tmp/file存在,则该命令(进程)的退出码为0。
静态pod
不允许设置restartPolicy
控制器管理的pod
ReplicaSet
、Deployment
、DaemonSet
:此类控制器希望 Pod 一直运行下去,它们的重启策略只能是Always
。job
一次性任务控制器的重启策略只能是OnFailure
或者Never
裸Pod
可以设置三种策略Always
、OnFailure
、Never
Init容器是在containers定义的业务容器启动之前先启动的一些容器,用于完成一些初始化操作。比如:
整个pod只要不重启,init容器就会只执行一次,而且多个init容器是串行运行。Init容器通常无需设置探针,探针通常设置在业务容器中。Init容器运行失败,会依据pod的重启策略进行重启。如果重建pod那必然会运行Init容器。
下面一些情况并不一定会重新运行Init容器:
钩子函数是由kubelet发起调用的,钩子函数有两种:PostStart和PreStop。
PostStart
运行时机:与业务容器是同时起来的,是异步运行,没有先后
PostStart的作用是在容器运行之初完成一些初始化操作,这些初始化工作没有要求必须在业务容器运行前运行,仅仅在容器启动之初做一下就可以。如果PostStart运行失败,容器会被杀死,并根据RestartPolicy决定是否重启。
注意:如果这些初始化工作一定要在业务容器运行前运行,那就用initcontainers来定制。
PreStop
运行时间:一定是容器终止之前执行
PreStop的主要用于资源清理、优雅关闭应用程序、通知其他系统等事项。
当 Pod 开始终止时,Kubernetes 会执行 preStop 钩子。钩子有一个超时时间(默认为 30 秒)。如果钩子在超时时间内没有成功完成,Kubernetes 会终止 Pod,而不再继续等待钩子完成。如果 preStop 运行失败,也会强制删除pod。
补充:
正常删除Pod时会做两件事:
kubectl --force --grace-period=0 delete pod xxxx
的作用。所以如果Pod一直处于Terminating状态,可能是目标节点挂掉,或者目标节点的kubelet挂掉。终止进程的操作无法收到回复,Pod的状态一直是Terminating。
Pod的健康检查是通过探针来实现的,探针就是用来检测容器或容器的服务是否存活的一种机制,主要有三种探针:startupProbe、livenessProbe和readinessProbe。
startupProbe
用于启动场景的检测,通常的应用场景就是启动时间不确定。
该探针检测通过之后才会运行后面的探针,一旦该探针通过则不会重复执行该探针,重复执行的是后两个探针
livenessProbe
周期性检测,检查失败就会重启pod
readinessProbe
周期性检测,检查失败就会把pod从自己的service里下线
探针有三种探测方式:
exec
:执行一段命令(成功与否判定的标准不是命令输出,而是命令的成功与否的状态码)http Get
:检测某个 http 请求,如果响应状态码>=200且<=400,则认为容器健康 tcpSocket
:与目标端口建立tcp连接,建立成功则代表检测功能k8s中常用的资源限制有两种:CPU和内存。
CPU属于可压缩资源,单位是m
,1m
等于千分之一颗CPU。达到limits上限之后只会限频,不会杀掉pod。
内存属于不可压缩资源,单位是Mi
或者Gi
。达到limits上限之后则触发k8s级别的OOM,杀掉Pod。
除了CPU和内存之外,还有其他的资源类型,了解即可:
1 | ephemeral-storage: "2Gi" # 限制最多使用 2 GiB 临时存储 |
当某个节点的资源不足时,k8s会把该节点的pod进行驱逐,(pod此时处于evicted 状态),驱逐时会按照pod的Qos质量等级进行驱逐,质量等级越低的会成为被驱逐的目标,按照优先级从高到低的顺序排列
Guranteed(完全可靠的):
Pod中的所有容器对所有资源类型(cpu与内存)都定义了Limits与requests,且二者值相等、且大于0
Burstable(不稳定的)
设置了requests或limits,但是存在不一致的情况。一个pod既不是Guranteed级也不是BestEffort级,那就是Burstable级。
BestEffort(尽最大努力)
Pod中的所有容器都未定义资源配置(Requests与Limits都未定义),那么该pod的Qos级别就是BestEffort
基于Downward API机制可以把容器所在pod的一些状态信息(pod名字、pod的ip、调度的节点、Label、Annotation)注入到容器中(在容器体现的形式可以是环境变量、也可以某个文件中的文件)
Downward API可以通过两种方式将Pod和容器的元数据注入容器内部
环境变量:将Pod或Container信息设置为容器内的环境变量
1 | # env-pod.yaml |
Volume挂载:将Pod或Container的信息以文件形式挂载到容器内部
1 | # volume-pod.yaml |
注意:
Downward API 能够获取到的信息,一定是 Pod 里的容器进程启动之前就能够确定下来的信息。而如果想要获取 Pod 容器运行后才会出现的信息,比如,容器进程的 PID,那就肯定不能使用 Downward API 了,而应该考虑在 Pod 里定义一个 sidecar 容器来获取了。