深入掌握Pod
- 6、在容器内获取Pod信息(Downward API)
- 6.1、环境变量方式:将Pod信息注入为环境变量
- 6.2、环境变量方式:将容器资源信息注入为环境变量
- 6.3、Volume挂载方式
- 7、Pod生命周期和重启策略
- 8、Pod健康检查和服务可用性检查
6、在容器内获取Pod信息(Downward API)
我们知道,每个Pod在被成功创建出来之后,都会被系统分配唯一的名字、IP地址,并且处于某个Namespace中,那么我们如何在Pod的容器内获取Pod的这些重要信息呢?答案就是使用Downward API。
Downward API可以通过以下两种方式将Pod信息注入容器内部。
- 环境变量:用于单个变量,可以将Pod信息和Container信息注入容器内部。
- Volume挂载:将数组类信息生成为文件并挂载到容器内部。
下面通过几个例子对Downward API的用法进行说明。
6.1、环境变量方式:将Pod信息注入为环境变量
下面的例子通过Downward API将Pod的IP、名称和所在Namespace注入容器的环境变量中,容器应用使用env命令将全部环境变量打印到标准输出中:
# dapi-test-pod.yamlapiVersion:v1kind:Podmetadata:name:dapi-test-podspec:containers:-name:test-containerimage:busyboxcommand:["/bin/sh","-c","env"]env:-name:MY_POD_NAMEvalueFrom:fieldRef:fieldPath:metadata.name-name:MY_POD_NAMESPACEvalueFrom:fieldRef:fieldPath:metadata.namespace-name:MY_POD_IPvalueFrom:fieldRef:fieldPath:status.podIPrestartPolicy:Never注意到上面valueFrom这种特殊的语法是Downward API的写法。目前Downward API提供了以下变量。
- metadata.name:Pod的名称,当Pod通过RC生成时,其名称是RC随机产生的唯一名称。
- status.podIP:Pod的IP地址,之所以叫作status.podIP而非metadata.IP,是因为Pod的IP属于状态数据,而非元数据。
- metadata.namespace:Pod所在的Namespace。
运行kubectl create命令创建Pod:
# kubectl create -f dapi-test-pod.yamlpod"dapi-test-pod"created查看dapi-test-pod的日志:
# kubectl logs dapi-test-pod......MY_POD_NAMESPACE=defaultMY_POD_IP=172.17.1.2MY_POD_NAME=dapi-test-pod......从日志中我们可以看到Pod的IP、Name及Namespace等信息都被正确保存到了Pod的环境变量中。
6.2、环境变量方式:将容器资源信息注入为环境变量
下面的例子通过Downward API将Container的资源请求和限制信息注入容器的环境变量中,容器应用使用printenv命令将设置的资源请求和资源限制环境变量打印到标准输出中:
dapi-test-pod-container-vars.yamlapiVersion:v1kind:Podmetadata:name:dapi-test-pod-container-varsspec:containers:-name:test-containerimage:busyboximagePullPolicy:Nevercommand:["sh","-c"]args:-while true; do echo-en '\n'; printenv MY_CPU_REQUEST MY_CPU_LIMIT; printenv MY_MEM_REQUEST MY_MEM_LIMIT; sleep 3600; done;resources:requests:memory:"32Mi"cpu:"125m"limits:memory:"64Mi"cpu:"250m"env:-name:MY_CPU_REQUESTvalueFrom:resourceFieldRef:containerName:test-containerresource:requests.cpu-name:MY_CPU_LIMITvalueFrom:resourceFieldRef:containerName:test-containerresource:limits.cpu-name:MY_MEM_REQUESTvalueFrom:resourceFieldRef:containerName:test-containerresource:requests.memory-name:MY_MEM_LIMITvalueFrom:resourceFieldRef:containerName:test-containerresource:limits.memoryrestartPolicy:Never注意valueFrom这种特殊的Downward API语法,目前resourceFieldRef可以将容器的资源请求和资源限制等配置设置为容器内部的环境变量。
- requests.cpu:容器的CPU请求值。
- limits.cpu:容器的CPU限制值。
- requests.memory:容器的内存请求值。
- limits.memory:容器的内存限制值。
运行kubectl create命令来创建Pod:
# kubectl create -f dapi-test-pod-container-vars.yamlpod"dapi-test-pod-container-vars"created# kubectl get podsNAME READY STATUS RESTARTS AGE dapi-test-pod-container-vars1/1 Running036s查看dapi-test-pod-container-vars的日志:
# kubectl logs dapi-test-pod-container-vars113355443267108864从日志中我们可以看到Container的requests.cpu 、limits.cpu 、 requests.memory 、limits.memory等信息都被正确保存到了Pod的环境变量中。
6.3、Volume挂载方式
下面的例子通过Downward API将Pod的Label、Annotation列表通过Volume挂载为容器中的一个文件,容器应用使用echo命令将文件的内容打印到标准输出中:
# dapi-test-pod-volume.yamlapiVersion:v1kind:Podmetadata:name:dapi-test-pod-volumelabels:zone:us-est-coastcluster:test-cluster1rack:rack-22annotations:build:twobuilder:john-doespec:containers:-name:test-containerimage:busyboximagePullPolicy:Nevercommand:["sh","-c"]args:-while true; do if[[-e /etc/labels]]; then echo-en '\n\n'; cat /etc/labels; fi; if[[-e /etc/annotations]]; then echo-en '\n\n'; cat /etc/annotations; fi; sleep 3600; done;volumeMounts:-name:podinfomountPath:/etcreadOnly:falsevolumes:-name:podinfodownwardAPI:items:-path:"labels"fieldRef:fieldPath:metadata.labels-path:"annotations"fieldRef:fieldPath:metadata.annotations这里要注意“volumes”字段中downwardAPI的特殊语法,通过items的设置,系统会根据path的名称生成文件。根据上例的设置,系统将在容器内生成/etc/labels和/etc/annotations两个文件。在/etc/labels文件中将包含metadata.labels的全部Label列表,在/etc/annotations文件中将包含metadata.annotations的全部Label列表。
运行kubectl create命令创建Pod:
# kubectl create -f dapi-test-pod-volume.yamlpod"dapi-test-pod-volume"created# kubectl get podsNAME READY STATUS RESTARTS AGE dapi-test-pod-volume1/1 Running01m查看dapi-test-pod-volume的日志:
# k logs dapi-test-pod-volumecluster="test-cluster1"rack="rack-22"zone="us-est-coast"build="two"builder="john-doe"从日志中我们看到Pod的Label和Annotation信息都被保存到了容器内的/etc/labels和/etc/annotations文件中。
那么,Downward API有什么价值呢?
在某些集群中,集群中的每个节点都需要将自身的标识(ID)及进程绑定的IP地址等信息事先写入配置文件中,进程在启动时会读取这些信息,然后将这些信息发布到某个类似服务注册中心的地方,以实现集群节点的自动发现功能。此时Downward API就可以派上用场了,具体做法是先编写一个预启动脚本或Init Container,通过环境变量或文件方式获取Pod自身的名称、IP地址等信息,然后将这些信息写入主程序的配置文件中,最后启动主程序。
7、Pod生命周期和重启策略
Pod在整个生命周期中被系统定义为各种状态,熟悉Pod的各种状态对于理解如何设置Pod的调度策略、重启策略是很有必要的。
Pod的状态如表所示。
Pod的重启策略(RestartPolicy)应用于Pod内的所有容器,并且仅在Pod所处的Node上由kubelet进行判断和重启操作。当某个容器异常退出或者健康检查(详见下节)失败时,kubelet将根据RestartPolicy的设置来进行相应的操作。
Pod的重启策略包括Always、OnFailure和Never,默认值为Always。
- Always:当容器失效时,由kubelet自动重启该容器。
- OnFailure:当容器终止运行且退出码不为0时,由kubelet自动重启该容器。
- Never:不论容器运行状态如何,kubelet都不会重启该容器。
kubelet重启失效容器的时间间隔以sync-frequency乘以2n来计算,例如1、2、4、8倍等,最长延时5min,并且在成功重启后的10min后重置该时间。
Pod的重启策略与控制方式息息相关,当前可用于管理Pod的控制器包括ReplicationController、Job、DaemonSet及直接通过kubelet管理(静态Pod)。每种控制器对Pod的重启策略要求如下。
- RC和DaemonSet:必须设置为Always,需要保证该容器持续运行。
- Job:OnFailure或Never,确保容器执行完成后不再重启。
- kubelet:在Pod失效时自动重启它,不论将RestartPolicy设置为什么值,也不会对Pod进行健康检查。
结合Pod的状态和重启策略,下表列出一些常见的状态转换场景。
8、Pod健康检查和服务可用性检查
Kubernetes对Pod的健康状态可以通过两类探针来检查: LivenessProbe和ReadinessProbe,kubelet定期执行这两类探针来诊断容器的健康状况。
- (1)
LivenessProbe探针:用于判断容器是否存活(Running状态),如果LivenessProbe探针探测到容器不健康,则kubelet将杀掉该容器,并根据容器的重启策略做相应的处理。如果一个容器不包含LivenessProbe探针,那么kubelet认为该容器的LivenessProbe探针返回的值永远是Success。 - (2)
ReadinessProbe探针:用于判断容器服务是否可用(Ready状态),达到Ready状态的Pod才可以接收请求。对于被Service管理的Pod,Service与Pod Endpoint的关联关系也将基于Pod是否Ready进行设置。如果在运行过程中Ready状态变为False,则系统自动将其从Service的后端Endpoint列表中隔离出去,后续再把恢复到Ready状态的Pod加回后端Endpoint列表。这样就能保证客户端在访问Service时不会被转发到服务不可用的Pod实例上。
LivenessProbe和ReadinessProbe均可配置以下三种实现方式。
- (1)ExecAction:在容器内部执行一个命令,如果该命令的返回码为0,则表明容器健康。
在下面的例子中,通过执行“cat /tmp/health”命令来判断一个容器运行是否正常。在该Pod运行后,将在创建/tmp/health文件10s后删除该文件,而LivenessProbe健康检查的初始探测时间(initialDelaySeconds)为15s,探测结果是Fail,将导致kubelet杀掉该容器并重启它:apiVersion:v1kind:Podmetadata:labels:test:livenessname:liveness-execspec:containers:-name:livenessimage:gcr.io/google_containers/busyboxargs:-/bin/sh--c-echo ok>/tmp/health; sleep 10; rm-rf /tmp/health; sleep 600livenessProbe:exec:command:-cat-/tmp/healthinitialDelaySeconds:15timeoutSeconds:1 - (2)TCPSocketAction:通过容器的IP地址和端口号执行TCP检查,如果能够建立TCP连接,则表明容器健康。
在下面的例子中,通过与容器内的localhost:80建立TCP连接进行健康检查:
apiVersion:v1kind:Podmetadata:name:pod-with-healthcheckspec:containers:-name:nginximage:nginxports:-containerPort:80livenessProbe:tcpSocket:port:80initialDelaySeconds:30timeoutSeconds:1- (3)HTTPGetAction:通过容器的IP地址、端口号及路径调用HTTP Get方法,如果响应的状态码大于等于200且小于400,则认为容器健康。
在下面的例子中,kubelet定时发送HTTP请求到localhost:80/_status/healthz来进行容器应用的健康检查:apiVersion:v1kind:Podmetadata:name:pod-with-healthcheckspec:containers:-name:nginximage:nginxports:-containerPort:80livenessProbe:httpGet:path:/_status/healthzport:80initialDelaySeconds:30timeoutSeconds:1
对于每种探测方式,都需要设置initialDelaySeconds和timeoutSeconds两个参数,它们的含义分别如下。
initialDelaySeconds:启动容器后进行首次健康检查的等待时间,单位为s。timeoutSeconds:健康检查发送请求后等待响应的超时时间,单位为s。当超时发生时,kubelet会认为容器已经无法提供服务,将会重启该容器。
Kubernetes的ReadinessProbe机制可能无法满足某些复杂应用对容器内服务可用状态的判断,所以Kubernetes从1.11版本开始,引入Pod Ready++特性对Readiness探测机制进行扩展,在1.14版本时达到GA稳定版,称其为Pod Readiness Gates。
通过Pod Readiness Gates机制,用户可以将自定义的ReadinessProbe探测方式设置在Pod上,辅助Kubernetes设置Pod何时达到服务可用状态(Ready)。为了使自定义的ReadinessProbe生效,用户需要提供一个外部的控制器(Controller)来设置相应的Condition状态。
Pod的Readiness Gates在Pod定义中的ReadinessGate字段进行设置。下面的例子设置了一个类型为www.example.com/feature-1的新Readiness Gate:
Kind:Pod......spec:readinessGates:-conditionType:"www.example.com/feature-1"status:conditions:-type:Ready# Kubernetes系统内置的名为Ready的Conditionstatus:"True"lastProbeTime:nulllastTransitionTime:2018-01-01T00:00:00Z-type:"www.example.com/feature-1"# 用户自定义Conditionstatus:"False"lastProbeTime:nulllastTransitionTime:2019-03-01T00:00:00ZcontainerStatuses:-containerID:docker://abcd...ready:true......新增的自定义Condition的状态(status)将由用户自定义的外部控制器设置,默认值为False。Kubernetes将在判断全部readinessGates条件都为True时,才设置Pod为服务可用状态(Ready为True)。