一、利用 PreStop 钩子实现 Pod 优雅关闭

        在发布版本是,有没有遇到500错误问题?原因是部分流量被路由到了Terminating状态的pod上,但Pod里的应用已经不接受新的请求。

        这不是代码问题,而是pod下线的时序问题。

        删除pod时,同时会做两件事:

  • 把pod ip从Endpoint里摘掉。
  • kubec-proxy更新iptables规则和pod收到SIGTERM信号开始关闭进程

如果这两件事不同步,流量就可能会打到正在关闭的Pod上。

解决方法很简单:

加一个PreStop钩子,在下线前sleep一会儿,给网络规则更新流出时间窗口。

lifecycle:
  preStop:
    exec:
      # 先优雅停nginx,在sleep30s等待网络规则同步
      command: ["/bin/sh", "-c", "sleep 25 && nginx -s quit"]

二、使用临时容器快速调试异常Pod

        很多业务镜像为了安全精简,连bash都没有。Pod起不来或者跑着跑着不对劲了。想进去排查,然后发现各种命令无法使用,如:ping,curl等等。

        有一种做法事尝试安装调试工具或者重新制作调试镜像,进行部署。最后发现现场早就没有了。

        在k8s 1.23之后新增了临时容器(Ephemeral Containers)。不用重启Pod,直接在运行的Pod里原地注入一个带调试工具的容器,共享一个进程命名空间和网络命名空间。

# kubectl debug mypod -it --image=busybox -n namespace

这个调试容器会跑在Pod里,你可以用busybox里的各种工具去排查问题。用完退出即可。调试容器会自动清理,不污染业务容器。

三、kubectl debug原生调试容器,镜像无需再预装工具

        临时容器适合原地诊断,不重启Pod,直接往里塞个调试工具,用完就走。

        有限场景它搞不定。比如怀疑环境变量配错或者启动命令不对导致pod无法启动柜,这种就要在Pod启动阶段敢于了。使用临时容器肯定不行:pod没有启动,出现无法注入问题。

        这时候换kubectl debug的pod复制模式:

# 复制一个pod,把业务容器镜像换成调试镜像
# kubectl debug mypod -it --copy-to=my-debugger  --set-image=*=busybox

        这个命令会基于原pod的配置复制出一个新Pod,但把镜像换成你指定的调试镜像。你可以改变环境变量、调启动参数、换镜像版本,在新pod上随便折腾,不影响原Pod。

四、port-forward本地端口转发,直连集群内部服务

在某些场景,只想本地连下集群里的MySQL或者Redis看看数据,不想把他暴漏成NodePort或者LoadBalancer给全网访问。

kubectl pod-forward就是干这个的。

# 把本地3306端口转发到集群里mysql pod的3306端口
# kubectl port-forward pod/mysql-pod 3306:3306

# 转发到service
# kubectl  port-forward service/mysql-svc 3306:3306

然后再本地使用mysql -h 127.0.0.1 -P 3306 -u root -p就能连上集群里的数据库。

五、--previous看CrashLoopBackOff前最后一眼

        Podu已知在CrashLoopBackOff重启,kubectl  logs看到的永远是当前容器的日志,刚启动就挂了,还没有输出什么内容。真正的错误日志还在上一个容器里。

这个时候使用--previous或者-p参数:
# kubectl logs mypod --previous

这个命令会显示上一个容器的标准输出和标准错误,里面往往会有应用启动失败的真实原因。

六、StartupProbe启动探针,避免慢启动应用被误杀重启

有些应用启动特别慢,如:java。如果用传统的livenessProbe和readinessProbe,启动过程中探针失败几次就会被k8s判定为不健康,然后重启pod;越重启越起不来的死循环。

startProbe就是救这个的。

startupProbe在Pod启动阶段生效,只有他通过了,livenessProbe和readinessProbe才开始工作。

startupProbe:
  httpGet:
    path: /healthz
    port: 8080
  failureThreshold: 30    #允许失败30次
  periodSeconds: 5        #每5s探测一次

上面这个配置给了应用最多150s(30x5)的启动时间,完全够一个java应用慢慢起来。

七、CRD自定义资源扩展k8s集群能力

k8s的核心设计是声明式API,你告诉它“想要的最终状态”,控制器负责把实际状态往这个方向赶。

CRD(CustomResourceDefinition)就是让你自己定义“想要的最终状态”长什么样。

比如想要管理一台数据库集群,可以定义一个MySQLCluster资源;

apiVersuib: db.example.io/v1
kind: MySQLCluster
metadata:
  name: my-cluster
spec:
  replicas: 3
  version: 8.0
  storage: 100Gi

然后写一个控制器(用Operator SDK或Kubebuilder),监听这个CRD的增删改,自己去创建StatefulSet、service、PV等底层资源。

想扩展k8s能力,CRD是基本功。

更多推荐