Kubernetes Nginx:如何实现零停机部署?

Kubernetes Nginx: How to have zero-downtime deployments?

我正在尝试进行零停机的 kubernetes nginx 部署。该过程的一部分是启动 rollingUpdate,它确保至少有一个 pod 始终是 运行 nginx。这非常有效。

当旧的 nginx pod 终止时,我 运行 遇到了错误。 根据 termination 上的 kubernetes 文档,kubernetes 将:

  1. 从服务的端点列表中删除 pod,因此它是 终止开始时未收到任何新流量
  2. 如果定义了预停止挂钩,则调用它,并等待它完成
  3. 向所有剩余进程发送 SIGTERM
  4. 在宽限期到期后向任何剩余进程发送 SIGKILL。

我知道命令 nginx -s quit 应该通过等待所有工作人员在主服务器终止之前完成请求来优雅地终止 nginx。它优雅地响应 SIGQUIT 命令,而 SIGTERM 导致暴力终止。其他论坛说这就像将以下 preStop 挂钩添加到您的部署一样简单:

lifecycle:
  preStop:
    exec:
      command: ["/usr/sbin/nginx", "-s", "quit"]

但是,通过测试这个命令,我发现 nginx -s quit return 会立即执行,而不是等待工作人员完成。它也没有 return 主进程的 PID,这正是我所希望的 D:

发生的事情是,kubernetes 调用 nginx -s quit,它会向 worker children 发送一个适当的 SIGQUIT,但不会等待它们完成。相反,它会直接跳到第 3 步,并用 SIGTERM 命令代替这些进程,从而导致暴力终止,从而失去连接。

问题:有没有人想出一个在滚动部署期间优雅地关闭他们的 nginx 控制器并实现零停机的好方法? sleep 解决方法不够好,我正在寻找更强大的方法。

下面是完整的部署 yaml:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-ingress-controller
spec:
  replicas: 1
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
 template:
    metadata:
      labels:
        app: nginx-ingress-lb
    spec:
      terminationGracePeriodSeconds: 60
      serviceAccount: nginx
      containers:
        - name: nginx-ingress-controller
          image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.8
          imagePullPolicy: Always
          readinessProbe:
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
          livenessProbe:
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            timeoutSeconds: 5
          args:
            - /nginx-ingress-controller
            - --default-backend-service=$(POD_NAMESPACE)/default-backend
            - --v=2
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          ports:
            - containerPort: 80
          lifecycle:
            preStop:
              exec:
                command: ["/usr/sbin/nginx", "-s", "quit"]

我讨厌回答我自己的问题,但经过一番摸索之后,这就是我目前所拥有的。

我创建了一个半阻塞的 bash 脚本,名为 killer:

#!/bin/bash

sleep 3
PID=$(cat /run/nginx.pid)
nginx -s quit

while [ -d /proc/$PID ]; do
  sleep 0.1
done

我发现在 nginx pod 里面有一个文件 /run/nginx.pid 里面有 master 进程的 PID。如果您调用 nginx -s quit 并开始等待直到进程消失,那么您实际上已经执行了退出命令 "blocking".

请注意,在任何事情发生之前都有一个 sleep 3。这是由于 Kubernetes 将 pod 标记为终止的竞争条件,但需要一点时间(< 1 秒)将此 pod 从指向它的流量的服务中删除。

我已将此脚本安装到我的 pod 中,并通过 preStop 指令调用它。它主要工作,但在测试过程中仍然偶尔会出现一些问题,我会收到连接的卷曲错误 "reset by peer." 但这是朝着正确方向迈出的一步。