一 Pod生命周期(接上)
整个拓扑结构
(1)Docker容器中应用的运行要求
Kubernetes对'长时间运行'容器的要求: 主程序需要在'前台'一直执行 -->'pid=1的前台常驻进程'
前台是方便docker'进行管理'
*************** '错误的方式' ***************
nohup start.sh &
原因: kubelet在创建'包含这个容器的Pod之后'运行完'该命令',即认为'Pod执行结束',将立即'销毁'该Pod
场景: Pod是以'Deployment方式'创建的,而'command启动'不是常驻进程,而pod的重启策略是'Alaways',就会陷入pod'无限循环'过程
*************** '正确的方式' ***************
nginx前台: nginx -g 'daemon off'
http前台: httpd -D FOREGROUND
(2)Pod Hook
重点小结
1)Pod Hook是由'kubelet发起的'
2)'重点掌握preStop',postStart用的不多,知道'提供这样一个能力'即可
postSTart场景:nginx'优雅退出'、'core dump'日志保存
3)Hook是'容器级别'的,一般用在'主容器'中
容器两种类型的钩子函数
钩子函数的常见两种实现方式
exec --> shell命令格式,容器'存在该命令'
HTTP --> 一般容器是'WebServer服务'
① postStart
模拟: 配置'文件初始化','简化卷的挂载'
需求: 定义了一个' Nginx Pod',其中设置了 PostStart 钩子函数,即在'容器创建成功后','写入一句话'到 /usr/share/message 文件中
容器'启动之前'确实'做了'这么一件事
command --> 等价于 Dockerfile中的'ENTRYPOINT'
args --> 等价于 CMD-->'如果没有ENTRYPOINT,默认'
② preStop
k8s默认删除容器和优雅退出的区别
场景: pod由于某些原因'无法删除',就'强制删除'
思考: 这种方式pod信息在etcd中'有残留',如何回收? --> '这个是重点'
当node中'pod数量'达到一定限额度,会把这些'垃圾pod信息回收掉'
1)nginx还有'很多连接正在处理',应该让nginx'优雅退出',让'nginx执行完'再退出
2)容器退出前'保存容器的状态'
上面定义的两个 Pod,一个是利用 preStop 来进行'优雅删除',另外一个是利用 preStop 来做一些'信息记录'的事情
apiVersion: v1
kind: Pod
metadata:
name: hook-demo2
spec:
containers:
- name: hook-demo2
image: nginx
lifecycle:
preStop:
exec:
command: ["/usr/sbin/nginx","-s","quit"] 'nginx优雅退出'
--- '容器退出前做状态保存持久化'
apiVersion: v1
kind: Pod
metadata:
name: hook-demo3
spec:
volumes:
- name: message
hostPath:
path: /tmp
containers:
- name: hook-demo2
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: message
mountPath: /usr/share/
lifecycle:
preStop:
exec:
command: ['/bin/sh', '-c', 'echo Hello from the preStop Handler > /usr/share/message']
备注: Hook 调用的日志'没有暴露'给 Pod,所以只能'通过 describe 命令'来获取,如果'有错误'将可以看到 'FailedPostStartHook' 或 'FailedPreStopHook' 这样的 event事件
后续补充这个:删除pod'是异步的',service下面的endpoint'还没有完全摘除',还会'接受流量',但是pod已经退出,所以'有一部分请求'会异常,会给preStop设计'sleep 10'再退出
二 Pod的健康检查
(1)为什么需要存活探针
如果没有探针,k8s无法知道'应用是否还活着',只要进程还在运行'例如:僵尸进程',k8s则认为'容器是健康的'
理解: '存活探针'更多的是从'业务应用侧'来考虑的,算是'服务可用性'的一个环节
readiness probe'可读性探针' '重要性 > ' liveness probe '存活探针'
排查问题: 可以去掉'liveness probe'进行排查
说明: libeness probe对于我们'排查问题不是特别友好',因为一旦'检测异常',就会'重启','不保留现场信息' -->'附加preSTop-->重启保留现场'
(2)存活探针的工作机理
1)用'指定三种方式的一种'进入容器检测'容器中的应用'是否正常运行
2)如果'检测失败',则认为容器不健康,那么 kubelet 将根据 Pod 中设置的 'restartPolicy 重启策略'来判断,Pod '是否'要进行重启操作
3)如果容器配置中'没有配置 livenessProbe 存活探针',kubelet 将认为'存活探针'探测一直为'成功状态'
① 支持的配置方式
关注下面'参数'的'默认数值'
这种最常见,以运行中的容器'服务-->指的是应用'是否可用作为'容器存活的标准' --> '业界常用'
② exec
用 exec 执行命令的方式来'检测容器的存活' --> 主要是观察'容器是否重启'
httpGet:任何'200<= x <400'的状态码都会认定是'成功的'返回码,其他返回码都会被认为是'失败的返回码'
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec
spec:
containers:
- name: liveness
image: busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
descrble查看'Events'信息
kubectl 'describe' pods liveness-exec --> '核心信息的解读'
以下存活探针表示:容器启动后'delay=5s'再进行探测,如果1s'timeout=1s'内容器'没有给出回应'则记作探测失败,如果连续回应1次'success=1'则认为健康,后续每次'间隔5s(period=5s)'进行一次探测,在探测'连续失败3次(failure=3)'后重启容器
连续三次失败: 防止'误判'
思考:超时设置多少是'合理的'
③ initialDelaySeconds问题和startupProbe引入意义
1)initialDelaySeconds'没有设置的现象'
'initialDelaySeconds'如果'没有设置',探针将在'启动时立即探测',通常会'导致探测失败',因为程序'还没准备好'开始接收请求
这种情况'很常见',看到容器在重启,使用'describe'会看到'容器退出码为137或者143'
备注: 'liverness 探针' 从'业务的角度'就是在容器运行一端时间类似'OOM'导致容器'重启'应用场景
2)initialDelaySeconds'设置不合理'
+++++++++++++++ '分割线'
引出 startupProbe探针,注意是'v1.16 版本引入alpha'的
startupProbe探针用法和'其它探针'用法一致
kubectl explain pods.spec.containers.startupProbe --> '了解程序到底需要多长时间'
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30 尽量'设置大点'-->检测30次失败'才认为失败'
periodSeconds: 10
----------------------
解释:表示我们的'慢速容器最多'可以有'5分钟'(30个检查 * 10秒= 300s)来完成启动,才会执行'liverness和readiness',避免'频繁的重启'
后续:k8s1.18内置Sidecar控制pod中容器的启动顺序
(3)为什么需要就续探针
'场景引入'
问题1:在 Kubernetes 中启动 Pod,'显示明明 Pod 已经启动成功',且能访问里面的端口,但是却'返回错误信息',典型的应用'jenkins和gitlab'
问题2:还有就是在'执行滚动更新'时候,总会出现一段时间,Pod虽然'对外提供网络访问',但是'访问却发生 404',用用户体验'极差'
原因:都是因为 'Pod 已经成功启动',但是 Pod 的的容器中'应用程序还在启动中'导致
(4)就续探针的工作机理
休息片刻:kube-node-lease 'kubernetes集群节点租约状态' 'v1.13加入'
查看命名空间的' 所有资源 '
kubectl get all --namespace=wzj
'删除对应的ns',就会'删除所有的pod'
三 资源配置
(1)概念
1)注意'换算单位'
2)spec.containers[].resources.requests.cpu'影响pod的调度'
3)目前先从'容器声明周期'角度粗略的了解 -->'limit导致OOM,restartPolicy进行' -->后续'细讲'
4)资源配额是'针对容器',pod在物理上是不存在,是'逻辑'的概念,所以我们'真正限制'的是'容器的资源'
'limit = request' --> '优先级最高'
(2)应用
Kubernetes v'1.14 或更高'版本,则可以指定大页'Huge Page'资源,大页是 'Linux 特有'的功能,节点内核在其中'分配的内存块'比默认页大小大得多
注意: CPU 资源是'可压缩资源',也就是容器'达到了这个设定的上限后',容器'性能会下降',但是'不会终止或退出'
apiVersion: v1
kind: Pod
metadata:
name: resource
spec:
containers:
- name: resource
image: nginx
ports:
- containerPort: 80
resources:
requests:
memory: 50Mi
cpu: 50m
limits:
memory: 100Mi
cpu: 100m
操作系统查看容器'内存和cpu限制'
备注: 'memory'查看同'cpu'
(3)memory的换算
补充: pod创建的过程
1)用户使用'create <--> apply' yaml创建pod,请求给apiseerver,apiserver将yaml中的属性信息(metadata)'写入etcd'
2)apiserver'触发watch机制'准备创建pod,信息转发给'调度器',调度器使用'调度算法'选择node,调度器将node信息给apiserver',apiserver将'绑定的node信息'写入etcd
3) apiserver又通过watch机制,Node'调用kubelet'指定pod信息,触发'docker run命令'创建容器创建完成之后'反馈给'kubelet,kubelet又将pod的'状态信息'给apiserver,apiserver又将'pod的状态'信息'写入etcd
图中有'三次write'
(1) pod'元信息'
(2) pod分配到'哪个node'
(3) 记录'pod状态'