參考來源: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做出改進才行
留言