优雅地关闭kubernetes中的nginx

SIGINT SIGTERM SIGKILL区别

三者都是结束/终止进程运行。

1.SIGINT SIGTERM区别

前者与字符ctrl+c关联,后者没有任何控制字符关联。
前者只能结束前台进程,后者则不是。

2.SIGTERM SIGKILL的区别

前者可以被阻塞、处理和忽略,但是后者不可以。KILL命令的默认不带参数发送的信号就是SIGTERM.让程序有好的退出。因为它可以被阻塞,所以有的进程不能被结束时,用kill发送后者信号,即可。即:kill-9 进程号。

《优雅地关闭kubernetes中的nginx》

docker stop与docker kill

docker stop

当我们用docker stop命令来停掉容器的时候,docker默认会允许容器中的应用程序有10秒的时间用以终止运行。

在docker stop命令执行的时候,会先向容器中PID为1的进程(main process)发送系统信号SIGTERM,然后等待容器中的应用程序终止执行,如果等待时间达到设定的超时时间,或者默认的10秒,会继续发送SIGKILL的系统信号强行kill掉进程。在容器中的应用程序,可以选择忽略和不处理SIGTERM信号,不过一旦达到超时时间,程序就会被系统强行kill掉,因为SIGKILL信号是直接发往系统内核的,应用程序没有机会去处理它。

docker kill

默认情况下,docker kill命令不会给容器中的应用程序有任何gracefully shutdown的机会。它会直接发出SIGKILL的系统信号,以强行终止容器中程序的运行。

与docker stop命令不一样的地方在于,docker kill没有任何的超时时间设置,它会直接发送SIGKILL信号,以及用户通过signal参数指定的其他信号。

docker stop命令,更类似于Linux系统中的kill命令,二者都是发送系统信号SIGTERM。而docker kill命令,更像是Linux系统中的kill -9或者是kill -SIGKILL命令,用来发送SIGKILL信号,强行终止进程。

关于pid为1的进程

process ID为1的进程,通常是UNIX init进程, 在操作系统中有重要的作用:每当一个进程退出了,如果它还有子进程存在,则该子进程变成了孤儿进程,init进程过来接管。Unix被设计为这样一种方式,父进程必须明确地“等待”子进程终止,以便收集它的退出状态。

子进程在结束后,内核仍然会为其维护一个基本的结构,保存其pid, 退出原因和状态等信息,父进程通过waitpid系统调用,可以获得这些信息。如果父进程没有调用waitpid,这些状态信息会一直保留,变成所谓僵尸进程。如果子进程后于父进程结束,一般来说, init进程会负责这些孤儿进程。

根据一般一个容器只运行一个进程的原则,对于一个web服务,它在容器中运行时的pid是1,假设它调用了bash的cgi脚本,而这个脚本又调用了grep,一段时间后,cgi脚本因为某种原因超时,web服务开始尝试杀死它,但是grep却并未受到影响,因此变成了孤儿进程。这时,pid=1的web服务应该能接管,但是绝大多数的web服务并没有init那样的能力,所以grep就变成了僵尸进程。

kubernetes的pod关闭机制

pod代表着一个集群中节点上运行的进程,让这些进程不再被需要,优雅的退出是很重要的(与粗暴的用一个KILL信号去结束,让应用没有机会进行清理操作)。用户应该能请求删除,并且在室进程终止的情况下能知道,而且也能保证删除最终完成。当一个用户请求删除pod,系统记录想要的优雅退出时间段,在这之前Pod不允许被强制的杀死,TERM信号会发送给容器主要的进程。一旦优雅退出的期限过了,KILL信号会送到这些进程,pod会从API服务器其中被删除。如果在等待进程结束的时候,Kubelet或者容器管理器重启了,结束的过程会带着完整的优雅退出时间段进行重试。

一个示例流程:

  • 1.用户发送一个命令来删除Pod,默认的优雅退出时间是30秒

  • 2.API服务器中的Pod更新时间,超过该时间Pod被认为死亡

  • 3.在客户端命令的的里面,Pod显示为”Terminating(退出中)”的状态

  • 4.(与第3同时)当Kubelet看到Pod标记为退出中的时候,因为第2步中时间已经设置了,它开始pod关闭的流程

    • 4.1如果该Pod定义了一个停止前的钩子,其会在pod内部被调用。如果钩子在优雅退出时间段超时仍然在运行,第二步会意一个很小的优雅时间断被调用

    • 4.2进程被发送TERM的信号

  • 5.(与第三步同时进行)Pod从service的列表中被删除,不在被认为是运行着的pod的一部分。缓慢关闭的pod可以继续对外服务,当负载均衡器将他们轮流移除。

  • 6.当优雅退出时间超时了,任何pod中正在运行的进程会被发送SIGKILL信号被杀死。

  • 7.Kubelet会完成pod的删除,将优雅退出的时间设置为0(表示立即删除)。pod从API中删除,不在对客户端可见。

默认情况下,所有的删除操作的优雅退出时间都在30秒以内。kubectl delete命令支持–grace-period=的选项,以运行用户来修改默认值。0表示删除立即执行,并且立即从API中删除pod这样一个新的pod会在同时被创建。在节点上,被设置了立即结束的的pod,仍然会给一个很短的优雅退出时间段,才会开始被强制杀死。

nginx与SIGTERM

有两种方式可以向正在运行的Nginx进程的发送信号以完全管理操作:使用Nginx进程的 -s 选项或者直接使用系统命令kill发送信号给master进程。使用 -s 选项时,nginx会自动查找运行中的master进程ID(master进程负责接收并处理信号,同时根据不同的信号,对所有工作进程完成不同的管理操作).

SIGINT和SIGTERM作用一样,用于强制 Nginx进程退出;master进程接到强制退出信号时,会向所有工作进程发送强制退出信号,如果工作进程未能及时退出,master使用计时器重复发送强制信号,计时器触发时会发送SIGALRM信号;SIGIO信号被Nginx显式忽略;SIGCHLD信号告诉 master进程有工作进程退出,需要完成资源回收或者重启工作进程的工作。

Nginx进程退出分为两种

  • master进程接到SIGQUIT信号时
    将此信号转发给工作进程。工作进程随后关闭监听端口以便不再接收新的连接请求,并闭空闲连接,等待活跃连接全部正常结速后,调用 ngx_worker_process_exit 退出。而 master 进程在所有工作进程都退出后,调用 ngx_master_process_exit 函数退出;

  • master进程接收到SIGTERM或者SIGINT信号时
    将信号转发给工作进程。工 作进程直接调用 ngx_worker_process_exit 函数退出。master进程在所有工作进程都退出后,调用ngx_master_process_exit 函数退出。另外,如果工作进程未能正常退出,master进程会等待1秒后,发送SIGKILL信号强制终止工作进程。

nginx的优雅退出

 -s signal      Send signal to the master process. The argument signal can be
                one of: stop, quit, reopen, reload.

                The following table shows the corresponding system signals.

                stop    SIGTERM
                quit    SIGQUIT
                reopen  SIGUSR1
                reload  SIGHUP

其中
stop — 快速关闭
quit — 优雅退出,执行完当前的请求后退出
reload — 重新加载配置文件
reopen — 重新打开日志文件

使用kubernetes Lifecycle Hooks优雅退出nginx

kind: Deployment
metadata:
  name: nginx-demo
  namespace: scm
  labels:
    app: nginx-demo
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx-demo
    spec:
      containers:
      - name: nginx-demo
        image: library/nginx-demo
        imagePullPolicy: IfNotPresent
        lifecycle:
          preStop:
            exec:
              # nginx -s quit gracefully terminate while SIGTERM triggers a quick exit
              command: ["/usr/local/openresty/nginx/sbin/nginx","-s","quit"]
        env:
          - name: PROFILE
            value: "test"
        ports:
          - name: http
            containerPort: 8080

题外

如何优雅地关闭java应用

command: ["/bin/bash", "-c", "PID=`pidof java` && kill -SIGTERM $PID && while ps -p $PID > /dev/null; do sleep 1; done;"]

doc

    原文作者:codecraft
    原文地址: https://segmentfault.com/a/1190000008233992
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞