通常,當 Kubernetes 集群內的客戶端連接到服務的時候,是支持服務的 Pod 可以獲取到客戶端的 IP 地址的,但是,當通過節點端口接收到連接時,由於對數據包執行了源網絡地址轉換(SNAT),因此數據包的源 IP 地址會發生變化,后端的 Pod 無法看到實際的客戶端 IP,對於某些應用來說是個問題,比如,nginx 的請求日志就無法獲取准確的客戶端訪問 IP 了,比如下面我們的應用:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx spec: selector: app: nginx type: NodePort ports: - protocol: TCP port: 80 targetPort: 80
直接創建后可以查看 nginx 服務被自動分配了一個 32761 的 NodePort 端口:
$ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 28d nginx NodePort 10.106.190.194 <none> 80:32761/TCP 48m $ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-54f57cf6bf-nwtjp 1/1 Running 0 3m 10.244.3.15 ydzs-node3 <none> <none> nginx-54f57cf6bf-ptvgs 1/1 Running 0 2m59s 10.244.2.13 ydzs-node2 <none> <none> nginx-54f57cf6bf-xhs8g 1/1 Running 0 2m59s 10.244.1.16 ydzs-node1 <none> <none>
我們可以看到這個3個 Pod 被分配到了 3 個不同的節點,這個時候我們通過 master 節點的 NodePort 端口來訪問下我們的服務,因為我這里只有 master 節點可以訪問外網,這個時候我們查看 nginx 的 Pod 日志可以看到其中獲取到的 clientIP 是 10.151.30.11
,其實是 master 節點的內網 IP,並不是我們期望的真正的瀏覽器端訪問的 IP 地址:
$ kubectl logs -f nginx-54f57cf6bf-xhs8g 10.151.30.11 - - [07/Dec/2019:16:44:38 +0800] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" "-"
這個是因為我們 master 節點上並沒有對應的 Pod,所以通過 master 節點去訪問應用的時候必然需要額外的網絡跳轉才能到達其他節點上 Pod,在跳轉過程中由於對數據包進行了 SNAT,所以看到的是 master 節點的 IP。這個時候我們可以在 Service 設置 externalTrafficPolicy
來減少網絡跳數:
spec:
externalTrafficPolicy: Local
如果 Service 中配置了 externalTrafficPolicy=Local
,並且通過服務的節點端口來打開外部連接,則 Service 會代理到本地運行的 Pod,如果本地沒有本地 Pod 存在,則連接將掛起,比如我們這里設置上該字段更新,這個時候我們去通過 master 節點的 NodePort 訪問應用是訪問不到的,因為 master 節點上並沒有對應的 Pod 運行,所以需要確保負載均衡器將連接轉發給至少具有一個 Pod 的節點。
但是需要注意的是使用這個參數有一個缺點,通常情況下,請求都是均勻分布在所有 Pod 上的,但是使用了這個配置的話,情況就有可能不一樣了。比如我們有兩個節點上運行了 3 個 Pod,假如節點 A 運行一個 Pod,節點 B 運行兩個 Pod,如果負載均衡器在兩個節點間均衡分布連接,則節點 A 上的 Pod 將接收到所有請求的 50%,但節點 B 上的兩個 Pod 每個就只接收 25% 。
由於增加了externalTrafficPolicy: Local
這個配置后,接收請求的節點和目標 Pod 都在一個節點上,所以沒有額外的網絡跳轉(不執行 SNAT),所以就可以拿到正確的客戶端 IP,如下所示我們把 Pod 都固定到 master 節點上:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: tolerations: - operator: "Exists" nodeSelector: kubernetes.io/hostname: ydzs-master containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx spec: externalTrafficPolicy: Local selector: app: nginx type: NodePort ports: - protocol: TCP port: 80 targetPort: 80
更新服務后,然后再通過 NodePort 訪問服務可以看到拿到的就是正確的客戶端 IP 地址了:
182.149.166.11 - - [07/Dec/2019:17:03:43 +0800] "GET / HTTP/1.1" 200 612 "-" "M
source: https://mp.weixin.qq.com/s/Jy5Y-b5rVJwec6V0u_jjvw