k8s入门教程五:POD相关知识

简介

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之间能够实现快速的访问和交互。

imgPod概念介绍

如上图所示,Pod的使用方式通常包含两种:

  • Pod中运行一个容器,最经常使用的模式,container封装在pod中调度,两者几乎等同,但k8s不直接管理容器
  • Pod中运行多个容器,多个容器封装在pod中一起调度,适用于容器之间有数据交互和调用的场景,如app+redis,pod内部共享相同的网络命名空间,存储命名空间,进程命名空间等。
    • 共享网络:每个Pod分配一个唯一的IP地址。Pod中的每个容器共享网络命名空间,包括IP地址和网络端口。Pod内的容器可以使用 localhost 互相通信。
    • 共享存储:一个Pod可以指定一组共享存储卷。Pod中的所有容器都可以访问共享卷,允许这些容器共享数据。卷还允许 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[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
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
app: nginx
name: nginx
spec:
containers:
- image: nginx:1.7.9
name: nginx
ports:
- containerPort: 80
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Never
status: {}

这样就可以保存为文件进行修改了。以下是详细的说明。

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1	#与k8s集群版本有关,使用 kubectl api-versions 即可查看当前集群支持的版本
kind: Pod #该配置的类型,我们使用的是 POD
metadata: #译名为元数据,即 POD 的一些基本属性和信息
name: nginx #POD 的名称
labels: #标签,可以灵活定位一个或多个资源,其中key和value均可自定义,可以定义多组
app: nginx #设置key为app,value为nginx的标签
spec: #期望Pod实现的功能(即在pod中部署)
containers: #生成container,与docker中的container是同一种
- name: nginx #container的名称
image: nginx:1.7.9 #使用镜像nginx:1.7.9创建container,该container默认80端口可访问
imagePullPolicy: IfNotPresent #设置container的拉取策略,IfNotPresent存在时不进行拉取。

初学者很难以理解为什么会有这么多个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
2
3
4
5
6
7
8
9
10
11
[root@master ~]# kubectl explain pod.spec.containers.imagePullPolicy
KIND: Pod
VERSION: v1

FIELD: imagePullPolicy <string>

DESCRIPTION:
Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always
if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated.
More info:
https://kubernetes.io/docs/concepts/containers/images#updating-images

关于explain内容解释说明:

1
2
3
<string>        表示后面接一个字符串
<[]Object> 表示后面是一个列表的对象,列表需要以-开始,且可以写多个
<Object> 表示一个对象,对象内部包含多个属性

然后使用kubectl apply或者kubectl create来创建。

1
2
[root@master ~]# kubectl create -f 1.yaml
pod/nginx created

pod查看

查看状态

使用kubectl get pods查看pod的状态。在命令后增加 -A 或 --all-namespaces 可查看所有 名称空间中 的对象,使用参数 -n 可查看指定名称空间的对象

1
2
3
[root@master ~]# kubectl get pods nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 3m20s 10.32.0.4 master <none> <none>

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
[root@master ~]# kubectl describe pods nginx
Name: nginx
Namespace: default
Priority: 0
Node: master/192.168.1.60
Start Time: Tue, 03 Mar 2020 22:20:24 +0800
Labels: app=nginx
Annotations: <none>
Status: Running
IP: 10.32.0.4
IPs:
IP: 10.32.0.4
Containers:
nginx:
Container ID: docker://609a689c9a21e0ce62867f3c6cf541391f7295666b9e19c5a54294030e0bb158
Image: nginx:1.7.9
Image ID: docker-pullable://nginx@sha256:e3456c851a152494c3e4ff5fcc26f240206abac0c9d794affb40e0714846c451
Port: 80/TCP
Host Port: 0/TCP
State: Running
Started: Tue, 03 Mar 2020 22:20:30 +0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-wqbdz (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
default-token-wqbdz:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-wqbdz
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 4m28s default-scheduler Successfully assigned default/nginx to master
Normal Pulled 4m23s kubelet, master Container image "nginx:1.7.9" already present on machine
Normal Created 4m23s kubelet, master Created container nginx
Normal Started 4m22s kubelet, master Started container nginx

可以看到Conditions描述了POD的详细状态;而Events表示了POD的日志。

进入POD

kubectl exec pod_Name -c container_Name -it -- command就可以运行pod上面的名字,当pod只有一个容器时,可以省略-c,如下:

1
2
3
4
[root@master ~]# kubectl exec nginx -it -- /bin/bash
root@nginx:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin selinux srv sys tmp usr var
root@nginx:/#

查看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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: 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; do echo waiting for myservice; sleep 2; done;']
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']

开始创建这个pod,但会一直在Init联合阶段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@master ~]# kubectl get pod myapp-pod
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:0/2 0 96s
[root@master ~]# kubectl logs myapp-pod -c init-myservice
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

waiting for myservice
nslookup: can't resolve 'myservice'
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

waiting for myservice
nslookup: can't resolve 'myservice'
nslookup: can't resolve 'myservice'

查看 myapp-pod 日志,发现myservice是解析不了的。那我们创建一下service:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
kind: Service
apiVersion: v1
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377

创建svc之后,就可以看到pod就在running了。

1
2
3
4
5
6
7
8
9
[root@master ~]# k create -f 1.yaml
service/myservice created
service/mydb created
[root@master ~]# kubectl get pod myapp-pod -w
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:0/2 0 5m34s
myapp-pod 0/1 Init:1/2 0 5m37s
myapp-pod 0/1 PodInitializing 0 5m41s
myapp-pod 1/1 Running 0 5m43s

应用场景

  • 等待其它模块Ready,比如我们有一个应用里面有两个容器化的服务,一个是Web Server,另一个是数据库。其中Web Server需要访问数据库。但是当我们启动这个应用的时候,并不能保证数据库服务先启动起来,所以可能出现在一段时间内Web Server有数据库连接错误。为了解决这个问题,我们可以在运行Web Server服务的Pod里使用一个InitContainer,去检查数据库是否准备好,直到数据库可以连接,Init Container才结束退出,然后Web Server容器被启动,发起正式的数据库连接请求。

  • 第二种场景:初始化配置,比如集群里检测所有已经存在的成员节点,为主容器准备好集群的配置信息,这样主容器起来后就能用这个配置信息加入集群。

  • 其它使用场景:如将pod注册到一个中央数据库、下载应用依赖等。

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
apiVersion: v1
kind: Pod
metadata:
name: javaweb-2
spec:
initContainers:
- image: geektime/sample:v2
name: war
command: ["cp", "/sample.war", "/app"]
volumeMounts:
- mountPath: /app
name: app-volume
containers:
- image: geektime/tomcat:7.0
name: tomcat
command: ["sh","-c","/root/apache-tomcat-7.0.42-v2/bin/start.sh"]
volumeMounts:
- mountPath: /root/apache-tomcat-7.0.42-v2/webapps
name: app-volume
ports:
- containerPort: 8080
hostPort: 8001
volumes:
- name: app-volume
emptyDir: {}

在这个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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
containers:
- name: lifecycle-demo-container
image: nginx
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
preStop:
exec:
command: ["/usr/sbin/nginx","-s","quit"]

这是一个来自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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: v1
kind: Pod
metadata:
name: exec-liveness-probe
annotations:
kubernetes.io/description: "exec-liveness-probe"
spec:
containers:
- name: exec-liveness-probe
image: centos:latest
imagePullPolicy: IfNotPresent
args: #容器启动命令,生命周期为30s
- /bin/sh
- -c
- touch /tmp/liveness-probe.log && sleep 10 && rm -f /tmp/liveness-probe.log && sleep 20
livenessProbe:
exec: #健康检查机制,通过ls -l /tmp/liveness-probe.log返回码判断容器的健康状态
command:
- ls
- l
- /tmp/liveness-probe.log
initialDelaySeconds: 1
periodSeconds: 5
timeoutSeconds: 1

创建PoD后,查看容器的event日志,容器启动后,10s以内容器状态正常,11s开始执行liveness健康检查,检查异常,触发容器重启

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@node-1 demo]# kubectl describe pods exec-liveness-probe | tail
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 28s default-scheduler Successfully assigned default/exec-liveness-probe to node-3
Normal Pulled 27s kubelet, node-3 Container image "centos:latest" already present on machine
Normal Created 27s kubelet, node-3 Created container exec-liveness-probe
Normal Started 27s kubelet, node-3 Started container exec-liveness-probe
#容器已启动
Warning Unhealthy 20s (x2 over 25s) kubelet, node-3 Liveness probe failed: /tmp/liveness-probe.log
ls: cannot access l: No such file or directory #执行健康检查,检查异常
Warning Unhealthy 15s kubelet, node-3 Liveness probe failed: ls: cannot access l: No such file or directory
ls: cannot access /tmp/liveness-probe.log: No such file or directory
Normal Killing 15s kubelet, node-3 Container exec-liveness-probe failed liveness probe, will be restarted
#重启容器

查看容器重启次数,容器不停的执行,重启次数会响应增加,可以看到RESTARTS的次数在持续增加

1
2
3
[root@node-1 demo]# kubectl get pods exec-liveness-probe 
NAME READY STATUS RESTARTS AGE
exec-liveness-probe 1/1 Running 6 5m19s

httpGet健康检查

httpGet probe主要主要用于web场景,通过向容器发送http请求,根据返回码判断容器的健康状态,返回码小于4xx即表示健康,如下定义一个nginx应用,通过探测http://<container>:port/index.html的方式判断健康状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: Pod
metadata:
name: nginx-httpget-livess-readiness-probe
annotations:
kubernetes.io/description: "nginx-httpGet-livess-readiness-probe"
spec:
containers:
- name: nginx-httpget-livess-readiness-probe
image: nginx:latest
ports:
- name: http-80-port
protocol: TCP
containerPort: 80
livenessProbe: #健康检查机制,通过httpGet实现实现检查
httpGet:
port: 80
scheme: HTTP
path: /index.html
initialDelaySeconds: 3
periodSeconds: 10
timeoutSeconds: 3

模拟故障,将pod中的path文件所属文件删除,此时发送http请求时会健康检查异常,会触发容器自动重启

1
2
3
4
5
6
7
8
9
10
#查询pod所属的节点
[root@node-1 demo]# kubectl get pods nginx-httpget-livess-readiness-probe -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-httpget-livess-readiness-probe 1/1 Running 1 3m9s 10.244.2.19 node-3 <none> <none>

#登录到pod中将文件删除
[root@node-1 demo]# kubectl exec -it nginx-httpget-livess-readiness-probe /bin/bash
root@nginx-httpget-livess-readiness-probe:/# ls -l /usr/share/nginx/html/index.html
-rw-r--r-- 1 root root 612 Sep 24 14:49 /usr/share/nginx/html/index.html
root@nginx-httpget-livess-readiness-probe:/# rm -f /usr/share/nginx/html/index.html

再次查看pod的列表,此时会RESTART的次数会增加1,表示重启重启过一次,AGE则多久前重启的时间

1
2
3
[root@node-1 demo]# kubectl get pods nginx-httpget-livess-readiness-probe 
NAME READY STATUS RESTARTS AGE
nginx-httpget-livess-readiness-probe 1/1 Running 1 4m22s

查看pod的详情,观察容器重启的情况,通过Liveness 检查容器出现404错误,触发重启。

1
2
3
4
5
6
7
8
9
10
11
[root@node-1 demo]# kubectl describe pods nginx-httpget-livess-readiness-probe | tail
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 5m45s default-scheduler Successfully assigned default/nginx-httpget-livess-readiness-probe to node-3
Normal Pulling 3m29s (x2 over 5m45s) kubelet, node-3 Pulling image "nginx:latest"
Warning Unhealthy 3m29s (x3 over 3m49s) kubelet, node-3 Liveness probe failed: HTTP probe failed with statuscode: 404
Normal Killing 3m29s kubelet, node-3 Container nginx-httpget-livess-readiness-probe failed liveness probe, will be restarted
Normal Pulled 3m25s (x2 over 5m41s) kubelet, node-3 Successfully pulled image "nginx:latest"
Normal Created 3m25s (x2 over 5m40s) kubelet, node-3 Created container nginx-httpget-livess-readiness-probe
Normal Started 3m25s (x2 over 5m40s) kubelet, node-3 Started container nginx-httpget-

tcpSocket健康检查

tcpsocket健康检查适用于TCP业务,通过向指定容器建立一个tcp连接,可以建立连接则健康检查正常,否则健康检查异常,依旧以nignx为例使用tcp健康检查机制,探测80端口的连通性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: v1
kind: Pod
metadata:
name: nginx-tcp-liveness-probe
annotations:
kubernetes.io/description: "nginx-tcp-liveness-probe"
spec:
containers:
- name: nginx-tcp-liveness-probe
image: nginx:latest
ports:
- name: http-80-port
protocol: TCP
containerPort: 80
livenessProbe: #健康检查为tcpSocket,探测TCP 80端口
tcpSocket:
port: 80
initialDelaySeconds: 3
periodSeconds: 10
timeoutSeconds: 3

应用配置创建容器

1
2
3
4
5
6
[root@node-1 demo]# kubectl apply -f nginx-tcp-liveness.yaml 
pod/nginx-tcp-liveness-probe created

[root@node-1 demo]# kubectl get pods nginx-tcp-liveness-probe
NAME READY STATUS RESTARTS AGE
nginx-tcp-liveness-probe 1/1 Running 0 6s

模拟故障,获取pod所属节点,登录到pod中,安装查看进程工具htop

1
2
3
4
5
6
7
#获取pod所在node
[root@node-1 demo]# kubectl get pods nginx-tcp-liveness-probe -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-tcp-liveness-probe 1/1 Running 0 99s 10.244.2.20 node-3 <none> <none>

#登录到pod中
[root@node-1 demo]# kubectl exec -it nginx-httpget-livess-readiness-probe /bin/bash

kill掉进程观察容器状态,观察RESTART次数重启次数增加

1
2
3
4
5
6
7
root@nginx-httpget-livess-readiness-probe:/# kill 1
root@nginx-httpget-livess-readiness-probe:/# command terminated with exit code 137

#查看pod情况
[root@node-1 demo]# kubectl get pods nginx-tcp-liveness-probe
NAME READY STATUS RESTARTS AGE
nginx-tcp-liveness-probe 1/1 Running 1 13m

查看容器详情,发现容器有重启的记录

1
2
3
4
5
6
7
8
9
10
11
[root@node-1 demo]# kubectl describe pods nginx-tcp-liveness-probe | tail
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 14m default-scheduler Successfully assigned default/nginx-tcp-liveness-probe to node-3
Normal Pulling 44s (x2 over 14m) kubelet, node-3 Pulling image "nginx:latest"
Normal Pulled 40s (x2 over 14m) kubelet, node-3 Successfully pulled image "nginx:latest"
Normal Created 40s (x2 over 14m) kubelet, node-3 Created container nginx-tcp-liveness-probe
Normal Started 40s (x2 over 14m) kubelet, node-3 Started container nginx-tcp-liveness-probe

readiness就绪就绪

就绪检查用于应用接入到service的场景,用于判断应用是否已经就绪完毕,即是否可以接受外部转发的流量,健康检查正常则将pod加入到service的endpoints中,健康检查异常则从service的endpoints中删除,避免影响业务的访问。

创建一个pod,使用httpGet的健康检查机制,定义readiness就绪检查探针检查路径/test.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
apiVersion: v1
kind: Pod
metadata:
name: nginx-tcp-liveness-probe
annotations:
kubernetes.io/description: "nginx-tcp-liveness-probe"
labels: #需要定义labels,后面定义的service需要调用
app: nginx
spec:
containers:
- name: nginx-tcp-liveness-probe
image: nginx:latest
ports:
- name: http-80-port
protocol: TCP
containerPort: 80
livenessProbe: #存活检查探针
httpGet:
port: 80
path: /index.html
scheme: HTTP
initialDelaySeconds: 3
periodSeconds: 10
timeoutSeconds: 3
readinessProbe: #就绪检查探针
httpGet:
port: 80
path: /test.html
scheme: HTTP
initialDelaySeconds: 3
periodSeconds: 10
timeoutSeconds: 3

定义一个service,将上述的pod加入到service中,注意使用上述定义的labels,app=nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@node-1 demo]# cat nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: nginx
name: nginx-service
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: ClusterIP

生成配置

1
2
3
4
[root@node-1 demo]# kubectl apply -f httpget-liveness-readiness-probe.yaml 
pod/nginx-tcp-liveness-probe created
[root@node-1 demo]# kubectl apply -f nginx-service.yaml
service/nginx-service created

此时pod状态正常,此时readiness健康检查异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@node-1 ~]# kubectl get pods nginx-httpget-livess-readiness-probe 
NAME READY STATUS RESTARTS AGE
nginx-httpget-livess-readiness-probe 1/1 Running 2 153m

#readiness健康检查异常,404报错(最后一行)
[root@node-1 demo]# kubectl describe pods nginx-tcp-liveness-probe | tail
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m6s default-scheduler Successfully assigned default/nginx-tcp-liveness-probe to node-3
Normal Pulling 2m5s kubelet, node-3 Pulling image "nginx:latest"
Normal Pulled 2m1s kubelet, node-3 Successfully pulled image "nginx:latest"
Normal Created 2m1s kubelet, node-3 Created container nginx-tcp-liveness-probe
Normal Started 2m1s kubelet, node-3 Started container nginx-tcp-liveness-probe
Warning Unhealthy 2s (x12 over 112s) kubelet, node-3 Readiness probe failed: HTTP probe failed with statuscode: 404

查看services的endpoints,发现此时endpoints为空,因为readiness就绪检查异常,kubelet认为此时pod并未就绪,因此并未将其加入到endpoints中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
[root@node-1 ~]# kubectl describe services nginx-service 
Name: nginx-service
Namespace: default
Labels: app=nginx
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"nginx"},"name":"nginx-service","namespace":"default"},"s...
Selector: app=nginx
Type: ClusterIP
IP: 10.110.54.40
Port: http 80/TCP
TargetPort: 80/TCP
Endpoints: <none> #Endpoints对象为空
Session Affinity: None
Events: <none>

#endpoints状态
[root@node-1 demo]# kubectl describe endpoints nginx-service
Name: nginx-service
Namespace: default
Labels: app=nginx
Annotations: endpoints.kubernetes.io/last-change-trigger-time: 2019-09-30T14:27:37Z
Subsets:
Addresses: <none>
NotReadyAddresses: 10.244.2.22 #pod处于NotReady状态
Ports:
Name Port Protocol
---- ---- --------
http 80 TCP

Events: <none>

进入到pod中手动创建网站文件,使readiness健康检查正常

1
2
[root@node-1 ~]# kubectl exec -it nginx-httpget-livess-readiness-probe /bin/bash
root@nginx-httpget-livess-readiness-probe:/# echo "readiness probe demo" >/usr/share/nginx/html/test.html

此时readiness健康检查正常,kubelet检测到pod就绪会将其加入到endpoints中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#健康检查正常
[root@node-1 demo]# curl http://10.244.2.22/test.html

#查看endpoints情况
readines[root@node-1 demo]# kubectl describe endpoints nginx-service
Name: nginx-service
Namespace: default
Labels: app=nginx
Annotations: endpoints.kubernetes.io/last-change-trigger-time: 2019-09-30T14:33:01Z
Subsets:
Addresses: 10.244.2.22 #就绪地址,已从NotReady中提出,加入到正常的Address列表中
NotReadyAddresses: <none>
Ports:
Name Port Protocol
---- ---- --------
http 80 TCP

查看service状态
[root@node-1 demo]# kubectl describe services nginx-service
Name: nginx-service
Namespace: default
Labels: app=nginx
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"nginx"},"name":"nginx-service","namespace":"default"},"s...
Selector: app=nginx
Type: ClusterIP
IP: 10.110.54.40
Port: http 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.2.22:80 #已和endpoints关联
Session Affinity: None
Events: <none>

同理,如果此时容器的健康检查异常,kubelet会自动将其动endpoint中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#删除站点信息,使健康检查异常
[root@node-1 demo]# kubectl exec -it nginx-tcp-liveness-probe /bin/bash
root@nginx-tcp-liveness-probe:/# rm -f /usr/share/nginx/html/test.html

#查看pod健康检查event日志
[root@node-1 demo]# kubectl get pods nginx-tcp-liveness-probe
NAME READY STATUS RESTARTS AGE
nginx-tcp-liveness-probe 0/1 Running 0 11m
[root@node-1 demo]# kubectl describe pods nginx-tcp-liveness-probe | tail
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 12m default-scheduler Successfully assigned default/nginx-tcp-liveness-probe to node-3
Normal Pulling 12m kubelet, node-3 Pulling image "nginx:latest"
Normal Pulled 11m kubelet, node-3 Successfully pulled image "nginx:latest"
Normal Created 11m kubelet, node-3 Created container nginx-tcp-liveness-probe
Normal Started 11m kubelet, node-3 Started container nginx-tcp-liveness-probe
Warning Unhealthy 119s (x32 over 11m) kubelet, node-3 Readiness probe failed: HTTP probe failed with statuscode: 404

#查看endpoints
[root@node-1 demo]# kubectl describe endpoints nginx-service
Name: nginx-service
Namespace: default
Labels: app=nginx
Annotations: endpoints.kubernetes.io/last-change-trigger-time: 2019-09-30T14:38:01Z
Subsets:
Addresses: <none>
NotReadyAddresses: 10.244.2.22 #健康检查异常,此时加入到NotReady状态
Ports:
Name Port Protocol
---- ---- --------
http 80 TCP

Events: <none>

#查看service状态,此时endpoints为空
[root@node-1 demo]# kubectl describe services nginx-service
Name: nginx-service
Namespace: default
Labels: app=nginx
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"nginx"},"name":"nginx-service","namespace":"default"},"s...
Selector: app=nginx
Type: ClusterIP
IP: 10.110.54.40
Port: http 80/TCP
TargetPort: 80/TCP
Endpoints: #为空
Session Affinity: None
Events: <none>

https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/

https://cloud.tencent.com/developer/article/1517109?s=original-sharing&from=10680

  • 本文作者: wumingx
  • 本文链接: https://www.wumingx.com/k8s/kubernetes-pod.html
  • 本文主题: k8s入门教程五:POD相关知识
  • 版权声明: 本博客所有文章除特别声明外,转载请注明出处!如有侵权,请联系我删除。
0%