K8S--资源控制器&Pod详解&探针
一、资源控制器
K8S中内建了很多的控制器,这些控制器都是用来空指Pod的具体状态和行为。从控制器的角度来说,Pod可以分为自主式Pod和控制器管理的Pod。自主式的Pod一旦退出,该类型的Pod就不会被重新创建,而控制器管理的Pod,在控制器的生命周期里,始终要维持Pod的副本数量。
常用的控制器,在K8S--架构及基本概念中已经说过,主要有ReplicationController(旧版本)、ReplaSet、Deployment、DaemonSet、Job、CronJob,具体的概念就不再说多,直接上代码示例。
对于RC、RS、Deployment的配置可以参见K8S--实战
(一)DaemonSet
DaemonSet确保全部Node上运行一个Pod副本,当有Node加入集群时,也会为他们新增一个pod,当有Node从集群中被移除时,这些pod也会被回收,删除DaemonSet将会删除其创建的所有pod。最典型的场景就是每个Pod里面都有服务在运行,需要收集服务运行日志,但是Pod是由K8S自动创建或删除的,因此需要使用DaemonSet来设定在每一个Pod中进行日志收集。
#确保只运行一个副本,运行在集群中每一个节点上。(也可以部分节点上只运行一个且只有一个pod副本,如监控ssd硬盘) # kubectl explain ds # vim filebeat.yaml apiVersion: apps/v1 kind: DaemonSet metadata: name: my-deamon namespace: default labels: app: daemonset spec: selector: matchLabels: app: my-daemonset template: metadata: labels: app: my-daemonset spec: containers: - name: daemon-app image: nginx:1.16
使用:kubectl apply -f xxx.yaml执行该配置文件即可,就会在每个节点中创建一个名为my-deamon的pod。
然后可以使用:kubectl get daemonset查看在运行的daemonset
实际上,K8S自己就在用deamonSet在运行组件系统。
(二)Job
Job负责处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个Pod成功结束。而CronJob则就是在Job上加上了时间调度。
# 我们用Job这个资源对象来创建一个任务,我们定一个Job来执行一个倒计时的任务,定义YAML文件: apiVersion: batch/v1 kind: Job metadata: name: job-demo spec: template: metadata: name: job-demo spec: restartPolicy: Never containers: - name: counter image: busybox command: - "bin/sh" - "-c" - "for i in 9 8 7 6 5 4 3 2 1; do echo $i; done"
上面的配置文件是创建了一个job,并从9输出到1。
使用命令执行配置文件: kubectl apply -f job.yaml
查看pod:
可以看到job已经执行完成,然后查看输出日志:
可以看到已经正常打印。
(三)CronJob
CronJob其实就是在Job的基础上加上了时间调度,我们可以:在给定的时间点运行一个任务,也可以周期性地在给定时间点运行。这个实际上和我们Linux中的crontab就非常类似了。
apiVersion: batch/v1beta1 kind: CronJob metadata: name: cronjob-demo spec: schedule: "*/1 * * * *" jobTemplate: spec: template: spec: restartPolicy: OnFailure containers: - name: hello image: busybox args: - "bin/sh" - "-c" - "for i in 9 8 7 6 5 4 3 2 1; do echo $i; done"
执行该配置文件: kubectl apply -f cronjob.yaml
可以使用以下命令查看:
# 查询cronjob kubectl get cronjob # 查询jon ,cronjon会循环多个job kubectl get job # 实时监控查询job kubectl get job -w
二、Pod
(一)Pod生命周期
Pod的status属性描述了Pod处于生命周期的阶段。
阶段 | 描述 |
Pending | Pod 已被 Kubernetes 接受,但尚未创建一个或多个容器镜像。这包括被调度之前的时间以及通过网络下载镜像所花费的时间,执行需要一段时间。 |
Running | Pod 已经被绑定到了一个节点,所有容器已被创建。至少一个容器正在运行,或者正在启动或重新启动。 |
Succeeded | 所有容器成功终止,也不会重启。 |
Failed | 所有容器终止,至少有一个容器以失败方式终止。也就是说,这个容器要么已非 0 状态退出,要么被系统终止。 |
Unknown | 由于一些原因,Pod 的状态无法获取,通常是与 Pod 通信时出错导致的。 |
重启策略:
对于Pod的重启策略,有Always、OnFailure、Never三种,默认为Always,其中Always表示容器失败时,kubectl会自动重启该容器,Onfailure表示容器终止运行且退出码不为0时重启,Never表示无论什么状态,kubectl都不会重启该容器。
失败的容器由kubectl以五分钟为上限的指数延迟重新启动(10秒、20秒、40秒),并在成功执行十分钟后重置。
Pod的生命周期如下图所示:
流程说明:
(1)初始化容器阶段,初始化Pod中的每一个容器,他们是串行执行的,执行完就退出了。
(2)启动主容器main container
(3)在main container刚刚启动之后可以执行post start命令(勾子程序)
(4)在整个main container执行的过程中可以做两类探测:liveness probe(存活探测)和readiness probe(就绪探测)
(5)在main container结束前可以执行pre stop命令(勾子程序)
配置启动后勾子(post start)和终止前勾子(pre stop):
post start:容器创建之后立即执行,如果失败了就会按照重启策略重启容器
pre stop:容器终止前立即执行,执行完成后容器将成功终止
可以使用以下命令查看post start和pre stop的设置格式:
kubectl explain pod.spec.containers.lifecycle.preStop
kubectl explain pod.spec.containers.lifecycle.postStart
(二)Pod init
1、init容器
Pod能够持有多个容器,应用运行在容器里面,但是它也可能有一个或多个先于应用程序启动的init容器,init容器与普通容器非常相似,只有以下两点区别:
(1)init容器总是运行到成功完成为止
(2)每个init容器都必须在下一个init容器启动之前成功完成
如果pod的init容器失败,K8S会不断的重启该Pod,直到init容器成功为止,不过也有特里,就是如果将Pod的重启策略restartPolicy设置为Never,他就不会重启了。
2、init的作用
因为init容器与应用程序容器的镜像是分离的,所以他们启动相关代码具有以下优势:
(1)它们可以包含并运行实用工具,但是处于安全考虑,一般不建议在应用程序容器镜像中包含这些实用工具
(2)他们可以包含实用工具和定制化代码来安装,但是不能出现在现有应用程序镜像中,例如创建镜像没有必要FROM另一个镜像,只需要在安装过程中使用类似sed、awk、python或dig这样的工具
(3)应用程序镜像可以分离出创建和部署的角色,而没有必要联合他们构建一个单独的镜像。
(4)init容器使用LinuxNamespace,所以相对应用程序来说,其具有不同文件系统视图,因此,他们能够具有访问Secret的权限,而应用程序容器则不能。
(5)他们必须在应用程序启动之前运行完成,而应用程序是并行运行的,所以init容器能够提供一种简单的阻塞或延迟应用容器启动的方法,直到满足了一组先决条件。
3、特殊说明
(1)在pod启动过程中,init容器会按照顺序在网络和数据卷初始化之后启动(网络和数据卷初始化是在pause容器中),每个容器必须在下一个容器启动前成功退出
(2)如果由于运行时或失败退出,将导致容器启动失败,它会根据Pod的restartPolicy指定的重启策略来进行处理
(3)在所有的init容器没有成功之前,Pod将不会变成Ready状态,init容器的端口将不会在Service中进行聚集,正在初始化的Pod处于Pending状态,但应该会将Initializing状态设置为true
(4)如果Pod重启,所有的Init容器必须重新执行
(5)对init容器spec的修改被限制在容器的image字段,修改其他字段都不会生效,更改init容器的image字段,等价于重启了Pod
(6)Init容器具有应用容器的所有字段,除了readinessProbe(就绪检测),因为init容器无法定义不同于完成(Completion)的就绪(readiness)之外的其他状态,这会在验证过程中强制执行。
(7)在Pod中的每个app和init容器的名称必须唯一,与任何其他容器共享一个名称,会在验证时抛出错误。
4、演示
创建一个init容器的配置文件,在该配置文件中,创建了一个initPod叫init-mydb,该initpod需要执行一个连接inint-db的pod
#init-pod.yaml apiVersion: v1 kind: Pod metadata: name: init-pod labels: app: nginx spec: containers: - name: myapp image: nginx command: ['sh', '-c', 'echo -n "running at " && date +%T && sleep 600'] initContainers: - name: init-mydb image: nginx command: ['sh', '-c', 'until nslookup init-db; do echo waiting for init-db;date +%T; sleep 2;echo; done;']
创建:
#创建 kubectl apply -f init-pod.yaml #查看pod状态 init没成功 kubectl get pod #查看log kubectl logs init-pod -c init-mydb
使用上述命令可以看到,上面创建的容器一直连不上db,导致一直不能完成。
然后创建db容器
#init-db.yaml kind: Service apiVersion: v1 metadata: name: init-db spec: ports: - protocol: TCP port: 80 targetPort: 3366
创建
#创建svr kubectl create -f init-db.yaml #查看 kubectl get svc #svc有ip地址,等待init容器运行成功 kubectl get pod
显示原来的容器已经启动成功。
删除容器
#删除 kubectl delete -f init-pod.yaml kubectl delete -f init-db.yaml
三、容器探针
(一)概述
探针是由kubectl对容器执行的定期诊断,要执行诊断,kubelet调用由容器实现的Handler,探测的诊断结果有成功、失败和未知三种情况,探测类型有以下三种:
1、ExecAction:在容器内执行指定命令,如果命令退出时返回码为0则认为诊断成功
2、TCPSocketAction:对指定端口上的容器的IP地址进行TCP检查,如果端口打开,则认为诊断成功
3、HTTPGetAction:对指定的端口和路径上的容器IP执行http get请求,如果响应状态码大于200小于400,则认为诊断成功。
上面提到了存活探测和就绪探测,那么对于两种探测的方式做个解释:
1、livenessProbe(存活探测):存活探测是用来探测容器是否正在运行,如果探测失败,lubelet会杀死容器,并且容器将受到重启策略的影响。如果容器不提供存活探测,则默认为状态一直为Success。
2、readinessProbe(就绪探测):就绪探测是用来探测容器是否准备好接收服务请求,如果探测失败,端点控制器将从与Pod匹配的所有Service的端点中删除该Pod的IP地址。初始延迟之前的就绪状态默认为Failure,如果容器不提供就绪探针,则默认状态一直为Success。
(二)就绪探针
就绪探针配置:
#readinessProbe-httpget apiVersion: v1 kind: Pod metadata: name: readiness-httpget-pod namespace: default spec: containers: - name: readiness-httpget-container image: nginx imagePullPolicy: IfNotPresent readinessProbe: httpGet: port: 80 path: /index1.html initialDelaySeconds: 1 periodSeconds: 3
以上的配置文件表示创建一个名字为readiness-httpget-pod的就绪探针,readinessProbe的配置就是就绪探针的具体配置,首先使用的是httpget的方式访问80端口,访问路径是/index1.html,延迟一秒访问,每三秒探测一次。
执行以上yaml文件,查看结果:
可以看到,虽然这个Pod状态为Running,但是却为就绪,使用命令查看原因:
kubectl describe pod readiness-httpget-pod
可以看到是就绪探测结果为404。
那么就为该pod创建一个/index1.html配置文件
# 进入pod kubectl exec -it readiness-httpget-pod sh # 进入nginx默认访问目录 cd /usr/share/nginx/html # 创建index1.html echo 'hello lcl' >> index1.html # 从pod中退出 exit
重新查看,该pod已经正常运行
说明:实际生产中,访问的路径一般都是健康检查的路径,这里只是为了做个演示。
(三)存活检测--livenessProbe-exec
配置文件:
apiVersion: v1 kind: Pod metadata: name: liveness-exec-pod namespace: default spec: containers: - name: liveness-exec-container image: hub.lcl.cn/library/busybox:v1 imagePullPolicy: IfNotPresent command: ["/bin/sh","-c","touch /tmp/live;sleep 60;rm -rf /tmp/live;sleep 3600"] livenessProbe: exec: command: ["test","-e","/tmp/live"] initialDelaySeconds: 1 periodSeconds: 3
配置文件简单说明:
容器(liveness-exec-container)的command命令,创建一个/tmp/live文件,然后休眠60秒,然后将该文件删除,然后再休眠3600秒
创建了一个存活探针(livenessProbe),检测/tmp/live文件,延迟一秒开始检测,每三秒探测一次。
执行yaml文件,查看pod运行情况
可以看到,Pod在一段时间后,检测不到/tmp/live文件,自动重启。
(四)存活检测--livenessProbe-Httpget
yaml文件
apiVersion: v1 kind: Pod metadata: name: liveness-httpget-pod namespace: default spec: containers: - name: liveness-httpget-container image: nginx imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 livenessProbe: httpGet: port: http path: /index.html initialDelaySeconds: 1 periodSeconds: 3 timeoutSeconds: 10
配置文件说明:
创建了一个存活探针(livenessProbe),使用httpget方式请求/index.html,检测/tmp/live文件,延迟3秒开始检测,每10秒探测一次。
运行yaml文件:因为nginx默认就有index.html文件,因此pod状态正常
进入pod中,将index.html删除
# 进入容器 kubectl exec -it liveness-httpget-pod sh # 切换到nginx默认访问目录 cd /usr/share/nginx/html # 删除文件 rm -f index.html
可以看到pod就开始重启了。
(五)存活检测-livenessProbe-TCP
yaml文件
apiVersion: v1 kind: Pod metadata: name: probe-tcp spec: containers: - name: probetcp image: nginx livenessProbe: initialDelaySeconds: 5 timeoutSeconds: 1 tcpSocket: port: 80 periodSeconds: 3
配置文件说明:使用tcp检测,延迟5秒开始检测,检测超时时间为1秒,检测端口80,每3秒检测一次。
(六)存活探测和就绪探测
在实际生产中,一个Pod中应该同时存在就绪探测和存活探测,yaml样例如下所示
apiVersion: v1 kind: Pod metadata: name: liveness-httpget-pod namespace: default spec: containers: - name: liveness-httpget-container image: hub.lcl.com/library/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 readinessProbe: httpGet: port: 80 path: /index1.html initialDelaySeconds: 1 periodSeconds: 3 livenessProbe: httpGet: port: http path: /index.html initialDelaySeconds: 1 periodSeconds: 3 timeoutSeconds: 10
(七)勾子函数
在前面说Pod的生命周期中有start勾子函数和stop勾子函数,就是在启动前和停止后分别要做哪些事情,这里做个演示:
apiVersion: v1 kind: Pod metadata: name: lifecycle-startstop spec: containers: - name: lifecycle-container image: nginx lifecycle: postStart: exec: command: ["/bin/sh","-c","echo Hello from the postStart handler > /usr/share/message"] preStop: exec: command: ["/bin/sh","-c","echo Hello container stop"]
配置文件也比较简单,就是在启动的时候输出一句话到message文件,在pod停止时,再输出一句话
执行yaml文件,待pod运行后,进入pod,查看message文件
停止的由于已经停止,就看不了~~~
-----------------------------------------------------------
---------------------------------------------
朦胧的夜 留笔~~