简介
Pod 是 Kubernetes 的一个最小调度以及资源单元。用户可以通过 Kubernetes 的 Pod API 生产一个 Pod,让 Kubernetes 对这个 Pod 进行调度,也就是把它放在某一个 Kubernetes 管理的节点上运行起来。一个 Pod 简单来说是对一组容器的抽象,它里面会包含一个或多个容器。
Deployment 是在 Pod 这个抽象上更为上层的一个抽象,它可以定义一组 Pod 的副本数目、以及这个 Pod 的版本。一般大家用 Deployment 这个抽象来做应用的真正的管理,而 Pod 是组成 Deployment 最小的单元。
Pods是kubernetes中最小的调度单位,Pods内运行一个或者多个container,container之间共享pod的网络ip资源,存储volume资源,计算等资源,方便pod内部的container之间能够实现快速的访问和交互。
Pod概念介绍
如上图所示,Pod的使用方式通常包含两种:
- Pod中运行一个容器,最经常使用的模式,container封装在pod中调度,两者几乎等同,但k8s不直接管理容器
- Pod中运行多个容器,多个容器封装在pod中一起调度,适用于容器之间有数据交互和调用的场景,如app+redis,pod内部共享相同的网络命名空间,存储命名空间,进程命名空间等。
- 共享网络:每个Pod分配一个唯一的IP地址。Pod中的每个容器共享网络命名空间,包括IP地址和网络端口。Pod内的容器可以使用
localhost互相通信。 - 共享存储:一个Pod可以指定一组共享存储卷。Pod中的所有容器都可以访问共享卷,允许这些容器共享数据。卷还允许 Pod 中的持久数据保留下来,以防其中的容器需要重新启动。
- 共享网络:每个Pod分配一个唯一的IP地址。Pod中的每个容器共享网络命名空间,包括IP地址和网络端口。Pod内的容器可以使用
POD的简单用法
POD简介
kubernetes交互的方式通常分为四种:
- 命令行,kubectl和kubernetes交互,完成资源的管理,命令行入门简单,但只能支持部分资源创建
- API,通过resfulAPI以http的方式和kubernetes交互,适用于基于API做二次开发
- SDK,提供各种语言原生的SDK,实现各种语言编程接入
- YAML,通过易于理解的YAML文件格式,描述资源的定义,功能最丰富,最终转换为json格式
kubernetes中通过定义生申明式的方式定义资源,即通过在yaml文件中定义所需的资源,kubernetes通过controller-manager按照yaml文件中定义的资源去生成所需的资源(match the current state to desired state)。通常在kubernetes中通过yaml文件的方式定义资源,然后通过kubectl create -f 文件.yaml的方式应用配置,如下演示创建一个nginx应用的操作。
POD创建
比较快速的方法可以使用,kubectl create apps -o yaml --dry-run的方式生成,—dry-run仅仅是试运行,并不实际在k8s集群中运行,通过指定-o yaml输出yaml格式文件,生成后给基于模版修改即可,如下:
1 | [root@master ~]# kubectl run nginx --image=nginx:1.7.9 --generator=run-pod/v1 --labels=app=nginx -o yaml --dry-run --port=80 --restart=Never |
这样就可以保存为文件进行修改了。以下是详细的说明。
1 | apiVersion: v1 #与k8s集群版本有关,使用 kubectl api-versions 即可查看当前集群支持的版本 |
初学者很难以理解为什么会有这么多个name。metadata里面的name是POD的名字,在使用kubectl get pods时显示的名字就是这个名字;而spec.containers.name这个名字是容器的名字,为什么容器还需要名字呢?因为一个POD是可以包括很多个容器的。
imagePullPolicy有三种选项,IfNotPresent、Always、Never,分别表示以下意思:
- IfNotPresent:仅当镜像在本地不存在时镜像才被拉取。
- Always:每次启动 pod 的时候都会拉取镜像
- Never:镜像被假设存在于本地。 没有尝试拉取镜像。
如果不设置imagePullPolicy,那默认值分为2个情况:
- 镜像标签为
:latest或被省略,那imagePullPolicy的默认值为Always。 - 镜像的标签被指定且不是
:latest,那imagePullPolicy的默认值为IfNotPresent。
我们可以通过kubectl explain查到每个字段的含义,使用说明和使用方式。
1 | [root@master ~]# kubectl explain pod.spec.containers.imagePullPolicy |
关于explain内容解释说明:
1 | <string> 表示后面接一个字符串 |
然后使用kubectl apply或者kubectl create来创建。
1 | [root@master ~]# kubectl create -f 1.yaml |
pod查看
查看状态
使用kubectl get pods查看pod的状态。在命令后增加 -A 或 --all-namespaces 可查看所有 名称空间中 的对象,使用参数 -n 可查看指定名称空间的对象
1 | [root@master ~]# kubectl get pods nginx -o wide |
Pod 的 status 定义在 PodStatus 对象中,其中有一个 phase 字段。Pod 的运行阶段(phase)是 Pod 在其生命周期中的简单宏观概述。该阶段并不是对容器或 Pod 的综合汇总,也不是为了做为综合状态机。
pod从创建到最后的创建成功会分别处于不同的阶段,在源码中用PodPhase来表示不同的阶段。
| Phase | 描述 |
|---|---|
| Pending | Kubernetes 已经创建并确认该 Pod。此时可能有两种情况:Pod 还未完成调度(例如没有合适的节点)或者正在从 docker registry 下载镜像 |
| Running | 该 Pod 已经被绑定到一个节点,并且该 Pod 所有的容器都已经成功创建。其中至少有一个容器正在运行,或者正在启动/重启 |
| Succeeded | Pod 中的所有容器都已经成功终止,并且k8s永远不会自动重启这些容器,一般会是在部署job的时候会出现。 |
| Failed | Pod 中的所有容器都已经终止,至少一个容器终止于失败状态:容器的进程退出码不是 0,或者被系统 kill |
| Unknown | 因为某些未知原因,不能确定 Pod 的状态,通常的原因是 master 与 Pod 所在节点之间的通信故障 |
以上说到的pod的状态是粗略的状态,还有更佳详细的状态信息:
- PodScheduled:pod正处于调度中,刚开始调度的时候,hostip还没绑定上,持续调度之后,有合适的节点就会绑定hostip,然后更新etcd数据
- Initialized:pod中的所有初始化容器已经初启动完毕
- Ready:pod中的容器可以提供服务了
- Unschedulable:不能调度,没有合适的节点
当STATUS为Running时,不一定代表POD就可以正常提供服务了,仅表示POD正常运行起来了,还需要看READY的状态。kubelet是通过readinessProbe 以及 livenessProbe来确认READY的状态。
- 就绪检查 readinessProbe: 确定容器是否已经就绪并接收服务请求。如果就绪检查失败,kubernetes 将该 Pod 的 IP 地址从所有匹配的 Service 的资源池中移除掉。
- 健康检查 livenessProbe: 确定容器是否正在运行。如果健康检查失败,kubelete 将结束该容器,并根据 restart policy(重启策略)确定是否重启该容器。
查看详细信息
如果POD STATUS异常,可以使用kubectl describe pods查看是什么原因导致出现的问题。
1 | [root@master ~]# kubectl describe pods nginx |
可以看到Conditions描述了POD的详细状态;而Events表示了POD的日志。
进入POD
kubectl exec pod_Name -c container_Name -it -- command就可以运行pod上面的名字,当pod只有一个容器时,可以省略-c,如下:
1 | [root@master ~]# kubectl exec nginx -it -- /bin/bash |
查看POD输出日志
使用 kubectl logs Pod名称 可以查看到pod的输出日志。
Pod生命周期
POD生命周期是指一个POD从创建到消亡有哪些周期,先后顺序为:
- 先保证Init容器成功运行,可以有多个init容器,每个init容器都必须运行成功之后才会运行下一个init容器。如果init容器启动失败,会不断重启Pod,直到init容器成功为止。
- 主容器的运行期间,有start、stop、readiness、readiness的检测。

init容器
POD可以包含多个容器,应用运行在这些容器里面,同时 Pod 也可以有一个或多个先于应用容器启动的 Init 容器。
Init 容器与普通的容器非常像,除了如下两点:
- 它们总是运行到完成。
- 每个都必须在下一个启动之前成功完成。
如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止。然而,如果 Pod 对应的 restartPolicy 值为 Never,它不会重新启动。
有了初始化容器,就可以完成一些初始化的事情。下面的例子定义了一个具有 2 个 Init 容器的简单 Pod。 第一个等待 myservice 启动,第二个等待 mydb 启动。 一旦这两个 Init容器 都启动完成,Pod 将启动spec区域中的应用容器。
1 | apiVersion: v1 |
开始创建这个pod,但会一直在Init联合阶段。
1 | [root@master ~]# kubectl get pod myapp-pod |
查看 myapp-pod 日志,发现myservice是解析不了的。那我们创建一下service:
1 | kind: Service |
创建svc之后,就可以看到pod就在running了。
1 | [root@master ~]# k create -f 1.yaml |
应用场景
等待其它模块Ready,比如我们有一个应用里面有两个容器化的服务,一个是Web Server,另一个是数据库。其中Web Server需要访问数据库。但是当我们启动这个应用的时候,并不能保证数据库服务先启动起来,所以可能出现在一段时间内Web Server有数据库连接错误。为了解决这个问题,我们可以在运行Web Server服务的Pod里使用一个InitContainer,去检查数据库是否准备好,直到数据库可以连接,Init Container才结束退出,然后Web Server容器被启动,发起正式的数据库连接请求。
第二种场景:初始化配置,比如集群里检测所有已经存在的成员节点,为主容器准备好集群的配置信息,这样主容器起来后就能用这个配置信息加入集群。
其它使用场景:如将pod注册到一个中央数据库、下载应用依赖等。
实例
1 | apiVersion: v1 |
在这个Pod中,我们定义了两个容器,第一个容器使用的镜像是geektime/sample:v2,这个镜像里只有一个WAR包(sample.war)放在根目录下。而第二个容器则使用的是一个标准的Tomcat镜像。
不过,你可能已经注意到,WAR包容器的类型不再是一个普通容器,而是一个Init Container类型的容器。
在Pod中,所有Init Container定义的容器,都会比spec.containers定义的用户容器先启动。并且,Init Container容器会按顺序逐一启动,而直到它们都启动并且退出了,用户容器才会启动。
所以,这个Init Container类型的WAR包容器启动后,我执行了一句”cp /sample.war /app”,把应用的WAR包拷贝到/app目录下,然后退出。
而后这个/app目录,就挂载了一个名叫app-volume的Volume。
接下来就很关键了。Tomcat容器,同样声明了挂载app-volume到自己的webapps目录下。
所以,等Tomcat容器启动时,它的webapps目录下就一定会存在sample.war文件:这个文件正是WAR包容器启动时拷贝到这个Volume里面的,而这个Volume是被这两个容器共享的。
像这样,我们就用一种“组合”方式,解决了WAR包与Tomcat容器之间耦合关系的问题。
实际上,这个所谓的“组合”操作,正是容器设计模式里最常用的一种模式,它的名字叫:sidecar。
顾名思义,sidecar指的就是我们可以在一个Pod中,启动一个辅助容器,来完成一些独立于主进程(主容器)之外的工作。
比如,在我们的这个应用Pod中,Tomcat容器是我们要使用的主容器,而WAR包容器的存在,只是为了给它提供一个WAR包而已。所以,我们用Init Container的方式优先运行WAR包容器,扮演了一个sidecar的角色。
https://kubernetes.io/docs/concepts/workloads/pods/init-containers/
Lifecycle字段
定义的是Container Lifecycle Hooks。顾名思义,Container Lifecycle Hooks的作用,是在容器状态发生变化时触发一系列“钩子”。我们来看这样一个例子:
1 | apiVersion: v1 |
这是一个来自Kubernetes官方文档的Pod的YAML文件。它其实非常简单,只是定义了一个nginx镜像的容器。不过,在这个YAML文件的容器(Containers)部分,你会看到这个容器分别设置了一个postStart和preStop参数。这是什么意思呢?
- postStart:
它指的是,在容器启动后,立刻执行一个指定的操作。需要明确的是,postStart定义的操作,虽然是在Docker容器ENTRYPOINT执行之后,但它并不严格保证顺序。也就是说,在postStart启动时,ENTRYPOINT有可能还没有结束。当然,如果postStart执行超时或者错误,Kubernetes会在该Pod的Events中报出该容器启动失败的错误信息,导致Pod也处于失败的状态。 - preStop:容器被杀死之前(比如,收到了SIGKILL信号)操作的事项。而需要明确的是,preStop操作的执行,是同步的。所以,它会阻塞当前的容器杀死流程,直到这个Hook定义操作完成之后,才允许容器被杀死,这跟postStart不一样。
所以,在这个例子中,我们在容器成功启动之后,在/usr/share/message里写入了一句“欢迎信息”(即postStart定义的操作)。而在这个容器被删除之前,我们则先调用了nginx的退出指令(即preStop定义的操作),从而实现了容器的“优雅退出”。
https://kubernetes.io/docs/tasks/configure-pod-container/attach-handler-lifecycle-event/
pod健康检查
健康检查概述
应用在运行过程中难免会出现错误,如程序异常,软件异常,硬件故障,网络故障等,kubernetes提供Health Check健康检查机制,当发现应用异常时会自动重启容器,将应用从service服务中剔除,保障应用的高可用性。k8s定义了有以下探针Probe:
- readiness probes:就绪型探测。
用于判断容器服务是否可用(Ready状态),达到Ready状态的Pod才可以接收请求。对于被Service管理的Pod,Service与Pod Endpoint的关联关系也将基于Pod是否Ready进行设置。Pod对象启动后,容器应用通常需要一段时间才能完成其初始化的过程,例如加载配置或数据,甚至有些程序需要运行某类的预热过程,若在此阶段完成之前即接入客户端的请求,势必会因为等待太久而影响用户体验。因此应该避免于Pod对象启动后立即让其处理客户端请求。而是等待容器初始化工作执行完成并转为Ready状态。尤其是存在其他提供相同服务的Pod对象的场景更是如此。如果在运行过程中Ready状态变为False,则系统自动将其从Service的后端Endpoint列表中隔离出去,后续再把恢复到Ready状态的Pod加回后端Endpoint列表。这样就能保证客户端在访问Service时不会被转发到服务不可用的Pod示例上。 - liveness probes:存活性探测。
用于判断容器是否健康(Running状态)并反馈给kubelet。有不少应用程序长时间持续运行后会逐渐转为不可用的状态,并且仅能通过重启操作恢复,kubernetes的容器存活性探测机制可发现诸如此类问题,并依据探测结果结合重启策略触发后的行为。存活性探测是隶属于容器级别的配置,kubelet可基于它判定何时需要重启一个容器。如果一个容器不包含LivenessProbe探针,那么kubelet认为该容器的LivenessProbe探针返回的值永远是Success。
每种探测机制支持三种健康检查方法,分别是命令行exec,httpGet和tcpSocket,其中exec通用性最强,适用与大部分场景,tcpSocket适用于TCP业务,httpGet适用于web业务。
- exec 提供命令或shell的检测,在容器中执行命令检查,返回码为0健康,非0异常
- httpGet http协议探测,在容器中发送http请求,根据http返回码判断业务健康情况
- tcpSocket tcp协议探测,向容器发送tcp建立连接,能建立则说明正常
每种探测方法能支持几个相同的检查参数,用于设置控制检查时间:
initialDelaySeconds:容器启动后要等待多少秒后存活和就绪探测器才被初始化,默认是 0 秒,最小值是 0。periodSeconds:执行探测的时间间隔(单位是秒)。默认是 10 秒。最小值是 1。timeoutSeconds:探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。successThreshold:探测器在失败后,被视为成功的最小连续成功数。默认值是 1。存活探测的这个值必须是 1。最小值是 1。failureThreshold:当 Pod 启动了并且探测到失败,Kubernetes 的重试次数。存活探测情况下的放弃就意味着重新启动容器。就绪探测情况下的放弃 Pod 会被打上未就绪的标签。默认值是 3。最小值是 1。
exec检测
许多应用程序运行过程中无法检测到内部故障,如死锁,出现故障时通过重启业务可以恢复,kubernetes提供liveness在线健康检查机制,我们以exec为例,创建一个容器启动过程中创建一个文件/tmp/liveness-probe.log,10s后将其删除,定义liveness健康检查机制在容器中执行命令ls -l /tmp/liveness-probe.log,通过文件的返回码判断健康状态,如果返回码非0,暂停20s后kubelet会自动将该容器重启。
1 | apiVersion: v1 |
创建PoD后,查看容器的event日志,容器启动后,10s以内容器状态正常,11s开始执行liveness健康检查,检查异常,触发容器重启
1 | [root@node-1 demo]# kubectl describe pods exec-liveness-probe | tail |
查看容器重启次数,容器不停的执行,重启次数会响应增加,可以看到RESTARTS的次数在持续增加。
1 | [root@node-1 demo]# kubectl get pods exec-liveness-probe |
httpGet健康检查
httpGet probe主要主要用于web场景,通过向容器发送http请求,根据返回码判断容器的健康状态,返回码小于4xx即表示健康,如下定义一个nginx应用,通过探测http://<container>:port/index.html的方式判断健康状态。
1 | apiVersion: v1 |
模拟故障,将pod中的path文件所属文件删除,此时发送http请求时会健康检查异常,会触发容器自动重启
1 | #查询pod所属的节点 |
再次查看pod的列表,此时会RESTART的次数会增加1,表示重启重启过一次,AGE则多久前重启的时间
1 | [root@node-1 demo]# kubectl get pods nginx-httpget-livess-readiness-probe |
查看pod的详情,观察容器重启的情况,通过Liveness 检查容器出现404错误,触发重启。
1 | [root@node-1 demo]# kubectl describe pods nginx-httpget-livess-readiness-probe | tail |
tcpSocket健康检查
tcpsocket健康检查适用于TCP业务,通过向指定容器建立一个tcp连接,可以建立连接则健康检查正常,否则健康检查异常,依旧以nignx为例使用tcp健康检查机制,探测80端口的连通性
1 | apiVersion: v1 |
应用配置创建容器
1 | [root@node-1 demo]# kubectl apply -f nginx-tcp-liveness.yaml |
模拟故障,获取pod所属节点,登录到pod中,安装查看进程工具htop
1 | #获取pod所在node |
kill掉进程观察容器状态,观察RESTART次数重启次数增加
1 | root@nginx-httpget-livess-readiness-probe:/# kill 1 |
查看容器详情,发现容器有重启的记录
1 | [root@node-1 demo]# kubectl describe pods nginx-tcp-liveness-probe | tail |
readiness就绪就绪
就绪检查用于应用接入到service的场景,用于判断应用是否已经就绪完毕,即是否可以接受外部转发的流量,健康检查正常则将pod加入到service的endpoints中,健康检查异常则从service的endpoints中删除,避免影响业务的访问。
创建一个pod,使用httpGet的健康检查机制,定义readiness就绪检查探针检查路径/test.html
1 | apiVersion: v1 |
定义一个service,将上述的pod加入到service中,注意使用上述定义的labels,app=nginx
1 | [root@node-1 demo]# cat nginx-service.yaml |
生成配置
1 | [root@node-1 demo]# kubectl apply -f httpget-liveness-readiness-probe.yaml |
此时pod状态正常,此时readiness健康检查异常
1 | [root@node-1 ~]# kubectl get pods nginx-httpget-livess-readiness-probe |
查看services的endpoints,发现此时endpoints为空,因为readiness就绪检查异常,kubelet认为此时pod并未就绪,因此并未将其加入到endpoints中。
1 | [root@node-1 ~]# kubectl describe services nginx-service |
进入到pod中手动创建网站文件,使readiness健康检查正常
1 | [root@node-1 ~]# kubectl exec -it nginx-httpget-livess-readiness-probe /bin/bash |
此时readiness健康检查正常,kubelet检测到pod就绪会将其加入到endpoints中
1 | #健康检查正常 |
同理,如果此时容器的健康检查异常,kubelet会自动将其动endpoint中
1 | #删除站点信息,使健康检查异常 |
https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/
https://cloud.tencent.com/developer/article/1517109?s=original-sharing&from=10680