Nginx Ingress Reload的坑

最近在設計把Jupyter放在k8s上給多個人使用的應用

遇到了一個問題,就是Jupyter的terminal介面會時不時的斷掉

正個在以前standalone server時代沒發生過的

去追蹤Jupyter的log

...
[I 07:37:41.135 NotebookApp] New terminal with automatic name: 5
TermSocket.open: 5
TermSocket.open: Opened 5
Websocket closed

Websocket的被close掉了,嗯?為什麼k8s上會發生這現象

後來回想了一下我Jupter的連接方式

Pod  <-  Service <- Ingress <- client

我多用了一個Nginx Ingress夾在client跟service中間,猜測應該是Nginx Ingress的問題

把Nginx Ingress移除掉之後就一切正常了

可是為什麼Nginx Ingress會讓我的websocket connection斷掉

用下面指令查詢了一下log

$ kubectl logs -f nginx-ingress-controller-676fbd589f-xxxx -n ingress-nginx

看到了nginx backend reload的訊息

I0715 03:15:14.094602       9 controller.go:169] Configuration changes detected, backend reload required.
I0715 03:15:14.274555       9 controller.go:179] Backend successfully reloaded.

後來調查了一下nginx -s reload這樣一個動作,他雖能保證service不中斷

卻不能保證websocket的冗余,nginx是用master跟worker process的方式分工

nginx所謂的reload,開啟新的worker然後shutdown舊的worker,所以像websocket這種必需一直連接的服務就會有中斷現象

但是為什麼會觸發reload呢,我的nginx ingress會分享給很多的service

每當新ingress的被deploy了,就會觸發nginx ingress reload,進而造成websocket的中斷

有沒有什麼解決方法呢,上網找了一下,發現不少人也有遇過類似的問題

進而找到了--enable-dynamic-configuration這個設定

很高興地給他設定下去,結果

I0714 04:45:26.814482       7 controller.go:169] Configuration changes detected, backend reload required.
I0714 04:45:26.933102       7 controller.go:179] Backend successfully reloaded.
I0714 04:45:26.934808       7 controller.go:196] Dynamic reconfiguration succeeded.

沒用啊,還是觸發了backend reload

一路往下追source code

要不觸發backend reload必需是

n.cfg.DynamicConfigurationEnabled && n.IsDynamicConfigurationEnough(pcfg)

--enable-dynamic-configuration只會影響DynamicConfigurationEnabled

IsDynamicConfigurationEnough又是什麼

func (n *NGINXController) IsDynamicConfigurationEnough(pcfg *ingress.Configuration) bool {
    copyOfRunningConfig := *n.runningConfig
    copyOfPcfg := *pcfg

    copyOfRunningConfig.Backends = []*ingress.Backend{}
    copyOfPcfg.Backends = []*ingress.Backend{}

    return copyOfRunningConfig.Equal(&copyOfPcfg)
}

測了一下只有在是backend更新(增加instance, 修改deployment等)的情況下才能不觸發backend reload

而我是deploy新的ingress,所以他必定會觸發reload

這下可麻煩了

所幸找到了可worker-shutdown-timeout這個設定

使用configmap加到nginx ingress裡面

---

kind: ConfigMap

apiVersion: v1

metadata:

  name: nginx-configuration

  namespace: ingress-nginx

  labels:

    app: ingress-nginx

data:

  worker-shutdown-timeout: 3600s

worker-shutdown-timeout,這個設定可以延遲nginx worker被砍掉

nginx ingress預設是10s,所以目前看到issue report都是說約10秒後會被砍掉

但是worker-shutdown-timeout也不能設定太長,舊的process活太長會有OOM的風險

搭配Pain(less) NGINX Ingress

歸納個總結

在目前nginx不支援dynamic reload的情況下

想要在k8s上做nginx ingress給多個服務共享幾個原則

  • 盡量減少新增修改ingress機會
  • 盡量每個namespace都能配上一個ingress controller來減少不同namepsace之間互相干擾
  • 設定worker-shutdown-timeout來降低nginx backend reload的直接影響

以上

comments powered by Disqus