ingress nginx zero downtime deployment

參考來源:https://github.com/kubernetes/kubernetes/issues/85643

Load Balancing運作

pod在沒有VPC native IP的情況下,必須透過NodePort將pod從node的port上expose出來給前面的cloud load balancer使用

雖然AWS的network load balancer,可以保留原始的client IP直接傳到node上,但LB上註冊的InstanceTarget其實是所有node

externalTrafficPolicy為Cluster時,LB上的node health check會故意全部失敗,讓LB把流量發給所有的node,node再利用kube-proxy轉發給實際有目標pod的node

要是LB使用到的node並沒有目標pod,kube-proxy進一步轉發流量會造成TCP source IP被洗掉,就拿不到client IP

externalTrafficPolicy為Local時,LB上的node health check會顯示實際有pod的node,因此LB可以正確的將第一手source IP送到pod手上

當pod需要shutdown時,k8s會將NodePort關掉,並讓pod進入terminating狀態

問題

使用ingress nginx時,如果希望$remote_addr能直接抓到真的client IP,一般會考慮將externalTrafficPolicy調成Local

但在pod關掉時,LB那邊沒辦法及時知道NodePort已經被k8s關掉,會繼續轉發流量到該node上

一直要到LB自己的health check失敗才會停,這過程可能有20~30秒的時間會導致部分流量無法被正確處理

externalTrafficPolicy改成Cluster雖然可以解決流量遺失的問題,但不一定能拿到原始的client IP

解法

幸好ingress nginx有real ip module可以辨識proxy protocol,而AWS的TCP LB也支援向後送出proxy protocol

因此只要用externalTrafficPolicy=Cluster配上proxy protocol,就能保留client IP又同時支援node之間互傳,達到zero downtime deployment

其他情境

本來有想過改用native IP或許可以解決這個問題

實際測試後發現,AWS的LB之所以可以保留client IP是因為在VPC內連線到instance不是用instance IP,而是透過instance ID

但pod不屬於instance,沒有instance ID,LB必須以正規的TCP連線傳到pod的native IP,這會導致source IP變成LB的IP

所以如果用的application不支援proxy protocol,目前應該是沒有好的解法,可能要等k8s做出改進才行

留言

粗體斜體刪除線連結引用圖片程式碼

注意:您的電子信箱將不會被公開,且網站連結不會被搜尋引擎採計