Kubernetes 优雅中止Pod

原文:https://i4t.com/4424.html
image_1dpi1lkurv0t15bq1vnut5h1k1l13.png-8.7kBhtml


首先咱们先简单的分析一下"优雅的中止Pod"nginx

优雅中止(Graceful shutdown)这个说法来自于操做系统,好比咱们windows关机系统首先会退出软件而后一步步到达关机,而相对的就是硬终止(Hard shutdown),简单的理解就是直接拔电源spring

到了微服务中,网关会把流量分配给每一个Pod节点上,好比咱们上线更新Pod的时候docker

  • 若是咱们直接将Pod杀死,那这部分流量就没法获得正确处理,会影响部分用户,一般来讲网关或者注册中心会将咱们的服务保持一个心跳,过了心跳超时以后会自动摘除咱们的服务,可是有一个问题就是超时时间多是30秒也多是60秒,虽然不会影响咱们的系统,可是会产生用户轻微抖动。
  • 若是咱们在中止前执行一条命令,通知网关或者注册中心这台主机进行下线,那么注册中心就会标记这台主机已经下线,不进行流量转发,用户就不会有任何影响,这就是优雅中止,将滚动更新影响最小化

Pod Hook

Pod Hook是由kubelet发起的,当容器中的进程启动前或者容器中的进程终止以前运行,这是包含在容器的生命周期之中。咱们能够同时为Pod中的全部容器都配置hookjson

在k8s中,理想的状态是pod优雅释放,并产生新的Pod。可是并非每个Pod都会这么顺利windows

  • Pod卡死,处理不了优雅退出的命令或者操做
  • 优雅退出的逻辑有BUG,陷入死循环
  • 代码问题,致使执行的命令没有效果

对于以上问题,k8s的Pod终止流程中还有一个"最多能够容忍的时间",即grace period (在pod的.spec.terminationGracePeriodSeconds字段定义),这个值默认是30秒,当咱们执行kubectl delete的时候也能够经过--grace-period参数显示指定一个优雅退出时间来覆盖Pod中的配置,若是咱们配置的grace period超过期间以后,k8s就只能选择强制kill Podapi


Kubernetes为咱们提供了两种钩子函数:bash

  • PostStart :这个钩子在容器建立后当即执行。可是,并不能保证钩子将在容器ENTRYPOINT以前运行,由于没有参数传递给处理程序。 主要用于资源部署、环境准备等。不过须要注意的是若是钩子花费时间过长以及于不能运行或者挂起,容器将不能达到Running状态。
  • PreStop :钩子在容器终止前当即被调用。它是阻塞的,意味着它是同步的,因此它必须在删除容器的调用出发以前完成。主要用于优雅关闭应用程序、通知其余系统等。若是钩子在执行期间挂起,Pod阶段将停留在Running状态而且不会达到failed状态

若是PostStart或者PreStop钩子失败,它会杀死容器。因此咱们应该让钩子函数尽量的轻量。固然有些状况下,长时间运行命令是合理的,好比在中止容器以前预先保留状态。服务器

这里稍微简单说一下Pod终止的过程app

  • 用户发送命令删除Pod,Pod进入Terminating状态
  • service摘除Pod节点
  • 当kubelet看到Pod已被标记终止,开始执行preStop钩子,假如preStop hook的运行时间超过了grace period,kubelet会发送SIGTERM并等2秒

官方文档介绍

在Pod Hook钩子函数中有Exec和HTTP两种方式

  • Exec - 用于执行一段特定的命令,不过要注意的是该命令小号的资源会被计入容器
  • HTTP - 对容器上的特定端点执行HTTP请求

基于PostStart命令演示

首先咱们先进行演示PostStart的两种方式

第一种Exec
咱们echo一段话追加到 /tmp/message,在Pod启动前进行操做

cat >>exec_test.yaml<<EOF
apiVersion: v1
kind: Pod
metadata:
  name: abcdocker
  labels:
    name: abcdocker
spec:
  containers:
  - name: abcdocker
    image: nginx
    ports:
      - containerPort: 80
    lifecycle:
      postStart:
        exec:
          command:
          - bash
          - -c
          - 'echo "https://i4t.com" > /tmp/message'
EOF

使用kubectl apply -f exec_test.yaml进行建立

能够经过下面查看结果,pod的目录已经有咱们在yaml文件写的测试文件

[root@abcdocker yaml]# kubectl get pod
NAME        READY   STATUS    RESTARTS   AGE
abcdocker   1/1     Running   0          37s

[root@abcdocker yaml]# kubectl exec -it -n default abcdocker /bin/bash
root@abcdocker:/# cat /tmp/message
https://i4t.com
root@abcdocker:/#
root@abcdocker:/# exit

建立容器后,Kubernetes当即发送postStart事件。可是,不能保证在调用Container的入口点以前先调用postStart处理程序。postStart处理程序相对于Container的代码异步运行,可是Kubernetes对容器的管理会阻塞,直到postStart处理程序完成。在postStart处理程序完成以前,容器的状态不会设置为RUNNING。

第二种HTTP方式
使用HttpGet配置Host、Path、Port

apiVersion: v1
kind: Pod
metadata:
  name: abcdocker
  labels:
    name: abcdocker
spec:
  containers:
  - name: abcdocker
    image: nginx
    ports:
      - containerPort: 80
    lifecycle:
      postStart:
        httpGet:
          host: i4t.com
          path: index.html
          port: 80

这里就不进行演示了,由于日志会看不到这个请求


基于PreStop环境演示

原由:
在生产环境中使用spring框架,因为服务更新过程当中,服务容器被直接充值,部分请求仍被分发到终止的容器(没有配置钩子,熟悉默认环境),致使服务出现500错误,这部分错误请求数据占用比较少,由于Pod滚动更新都是一对一。由于部分用户会产生服务器错误的状况,考虑使用优雅的终止方式,将错误请求降到最低,直至滚动更新不影响用户

Eureka是一个基于REST的服务,做为Spring Cloud服务注册中心,用于定位服务来进行中间层服务器的负载均衡和故障转移。各服务启动时,会向Eureka Server注册本身的信息(IP、端口、服务信息等),Eureka Server会存储这些信息,微服务启动后,会周期性(默认30秒)的向Eureka Server发送心跳以续约本身的租期,而且能够从eureka中获取其余微服务的地址信息,执行相关逻辑

image_1dpi0idnqk981okaacv16l4172p9.png-61kB

因为Eureka默认的心跳检测为30秒,当K8S下线Pod时Eureka会有30秒的异常问题,因此咱们须要在Pod 中止前发送一条请求,通知Eureka进行下线操做,这样进行优雅的中止对用户的影响作到最小

具体yaml以下

apiVersion: v1
kind: Pod
metadata:
  name: abcdocker
  labels:
    name: abcdocker
spec:
  containers:
  - name: abcdocker
    image: nginx
    ports:
      - containerPort: 80
    lifecycle:
      preStop:
        exec:
          command:
            - bash
            - -c
            - 'curl -X POST --data DOWN http://127.0.0.1:8080/service-registry/instance-status  -H
              "Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8";sleep 30'

####### 参数解释
127.0.0.1:8080 #表明eureka地址
service-registry    #表明注册中心
DOWN        #执行down请求
sleep       #等待30秒

当咱们删除Pod的时候就会执行上面的命令操做,而且等待30秒

[root@yzsjhl82-135 yaml]# kubectl get pod
NAME        READY   STATUS    RESTARTS   AGE
abcdocker   1/1     Running   0          2m16s
[root@yzsjhl82-135 yaml]# kubectl delete pod abcdocker
pod "abcdocker" deleted

#此刻Pod不会立刻删除,而是执行Exec中的命令,并等待30秒

配置中添加了一个sleep时间,主要是做为服务中止的缓冲时间

总结: Hook调用的日志没有暴露给Pod的Event,因此只能到经过describe命令来获取,若是是正常的操做是不会有event,若是有错误能够看到FailedPostStartHook和FailedPreStopHook这种event。而且若是Hook调用出现错误,则Pod状态不会是Running

相关文章
相关标签/搜索