【轉】干貨,Kubernetes中的Source Ip機制。


准備工作

你必須擁有一個正常工作的 Kubernetes 1.5 集群,用來運行本文中的示例。該示例使用一個簡單的 nginx webserver 回送它接收到的請求的 HTTP 頭中的源 IP 地址。你可以像下面這樣創建它:

$ kubectl run ``source``-ip-app --image=k8s.gcr.io``/echoserver``:1.4``deployment ``"source-ip-app"` `created

Type=ClusterIP 類型 Services 的 Source IP

如果你的 kube-proxy 運行在 iptables 模式下,從集群內部發送到 ClusterIP 的包永遠不會進行源地址 NAT,這從 Kubernetes 1.2 開始是默認選項。Kube-proxy 通過一個 proxyMode endpoint 暴露它的模式。

$ kubectl get nodes``NAME                           STATUS     AGE     VERSION``kubernetes-minion-group-6jst   Ready      2h      v1.6.0+fff5156``kubernetes-minion-group-cx31   Ready      2h      v1.6.0+fff5156``kubernetes-minion-group-jj1t   Ready      2h      v1.6.0+fff5156``kubernetes-minion-group-6jst $ curl localhost:10249``/proxyMode``iptables

你可以通過在 source IP 應用上創建一個服務來測試源 IP 保留。

$ kubectl expose deployment ``source``-ip-app --name=clusterip --port=80 --target-port=8080``service ``"clusterip"` `exposed
$ kubectl get svc clusterip``NAME         CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE``clusterip    10.0.170.92   <none>        80``/TCP`    `51s

從相同集群中的一個 pod 訪問這個 ClusterIP:

$ kubectl run busybox -it --image=busybox --restart=Never --``rm``Waiting ``for` `pod default``/busybox` `to be running, status is Pending, pod ready: ``false``If you don't see a ``command` `prompt, try pressing enter.``# ip addr``1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue``    ``link``/loopback` `00:00:00:00:00:00 brd 00:00:00:00:00:00``    ``inet 127.0.0.1``/8` `scope host lo``       ``valid_lft forever preferred_lft forever``    ``inet6 ::1``/128` `scope host``       ``valid_lft forever preferred_lft forever``3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1460 qdisc noqueue``    ``link``/ether` `0a:58:0a:f4:03:08 brd ff:ff:ff:ff:ff:ff``    ``inet 10.244.3.8``/24` `scope global eth0``       ``valid_lft forever preferred_lft forever``    ``inet6 fe80::188a:84ff:feb0:26a5``/64` `scope link``       ``valid_lft forever preferred_lft forever
# wget -qO - 10.0.170.92``CLIENT VALUES:``client_address=10.244.3.8``command``=GET``...

如果客戶端 pod 和 服務端 pod 在相同的節點上,client_address 就是客戶端 pod 的 IP 地址。但是,如果它們在不同的節點上, client_address 將會是客戶端 pod 所在節點的 flannel IP 地址。

Type=NodePort 類型 Services 的 Source IP

​ 對於 Kubernetes 1.5,發送給類型為 Type=NodePort Services 的數據包默認進行源地址 NAT。你可以創建一個 NodePort Service 來進行測試:

$ kubectl expose deployment ``source``-ip-app --name=nodeport --port=80 --target-port=8080 --``type``=NodePort``service ``"nodeport"` `exposed``$ NODEPORT=$(kubectl get -o jsonpath=``"{.spec.ports[0].nodePort}"` `services nodeport)``$ NODES=$(kubectl get nodes -o jsonpath=``'{ $.items[*].status.addresses[?(@.type=="ExternalIP")].address }'``)

如果你的集群運行在一個雲服務上,你可能需要為上面報告的 nodes:nodeport 開啟一條防火牆規則。 現在,你可以通過上面分配的節點端口從外部訪問這個 Service。

$ ``for` `node ``in` `$NODES; ``do` `curl -s $node:$NODEPORT | ``grep` `-i client_address; ``done``client_address=10.180.1.1``client_address=10.240.0.5``client_address=10.240.0.3

請注意,這些並不是正確的客戶端 IP,它們是集群的內部 IP。這是所發生的事情:

1、客戶端發送數據包到 node2:nodePort

2、node2 使用它自己的 IP 地址替換數據包的源 IP 地址(SNAT)

3、node2 使用 pod IP 地址替換數據包的目的 IP 地址

4、數據包被路由到 node 1,然后交給 endpoint

5、Pod 的回復被路由回 node2

6、Pod 的回復被發送回給客戶端

形象的:

image.png

​ 為了防止這種情況發生,Kubernetes 提供了一個特性來保留客戶端的源 IP 地址(點擊此處查看可用特性)。設置 service.spec.externalTrafficPolicy 的值為 Local,請求就只會被代理到本地 endpoints 而不會被轉發到其它節點。這樣就保留了最初的源 IP 地址。如果沒有本地 endpoints,發送到這個節點的數據包將會被丟棄。這樣在應用到數據包的任何包處理規則下,你都能依賴這個正確的 source-ip 使數據包通過並到達 endpoint。

設置 service.spec.externalTrafficPolicy 字段如下:

$ kubectl patch svc nodeport -p ``'{"spec":{"externalTrafficPolicy":"Local"}}'``service ``"nodeport"` `patched

現在,重新運行測試:

$ ``for` `node ``in` `$NODES; ``do` `curl --connect-timeout 1 -s $node:$NODEPORT | ``grep` `-i client_address; ``done``client_address=104.132.1.79

請注意,你只從 endpoint pod 運行的那個節點得到了一個回復,這個回復有正確的客戶端 IP。

這是發生的事情:

1、客戶端發送數據包到 node2:nodePort,它沒有任何 endpoints

2、數據包被丟棄

3、客戶端發送數據包到 node1:nodePort,它endpoints

4、node1 使用正確的源 IP 地址將數據包路由到 endpoint

形象的:

image.png

Type=LoadBalancer 類型 Services 的 Source IP

​ 對於 Kubernetes 1.5,發送給類型為 Type=LoadBalancer Services 的數據包默認進行源地址 NAT,這是由於所有處於 Ready 狀態的 Kubernetes 節點對於負載均衡的流量都是符合條件的。所以如果數據包到達一個沒有 endpoint 的節點,系統將把這個包代理到有 endpoint 的節點,並替換數據包的源 IP 為節點的 IP(如前面章節所述)。

​ 你可以通過在一個 loadbalancer 上暴露這個 source-ip-app 來進行測試。

$ kubectl expose deployment ``source``-ip-app --name=loadbalancer --port=80 --target-port=8080 --``type``=LoadBalancer``service ``"loadbalancer"` `exposed``$ kubectl get svc loadbalancer``NAME           CLUSTER-IP    EXTERNAL-IP       PORT(S)   AGE``loadbalancer   10.0.65.118   104.198.149.140   80``/TCP`    `5m``$ curl 104.198.149.140``CLIENT VALUES:``client_address=10.240.0.5``...

​ 然而,如果你的集群運行在 Google Kubernetes Engine/GCE 上,設置 service.spec.externalTrafficPolicy 字段值為 Local 可以強制使沒有 endpoints 的節點把他們自己從負載均衡流量的可選節點名單中刪除。這是通過故意使它們健康檢查失敗達到的。

形象的:

image.png

你可以設置 annotation 來進行測試:

$ kubectl patch svc loadbalancer -p ``'{"spec":{"externalTrafficPolicy":"Local"}}'

你應該能夠立即看到 Kubernetes 分配的 service.spec.healthCheckNodePort 字段:

$ kubectl get svc loadbalancer -o yaml | ``grep` `-i healthCheckNodePort``  ``healthCheckNodePort: 32122

service.spec.healthCheckNodePort 字段指向每個節點在 /healthz 路徑上提供的用於健康檢查的端口。你可以這樣測試:

$ kubectl get pod -o wide -l run=``source``-ip-app``NAME                            READY     STATUS    RESTARTS   AGE       IP             NODE``source``-ip-app-826191075-qehz4   1``/1`       `Running   0          20h       10.180.1.136   kubernetes-minion-group-6jst``kubernetes-minion-group-6jst $ curl localhost:32122``/healthz``1 Service Endpoints found``kubernetes-minion-group-jj1t $ curl localhost:32122``/healthz``No Service Endpoints Found

​ 主節點運行的 service 控制器負責分配 cloud loadbalancer。在這樣做的同時,它也會分配指向每個節點的 HTTP 健康檢查的 port/path。等待大約 10 秒鍾之后,沒有 endpoints 的兩個節點的健康檢查會失敗,然后 curl 負載均衡器的 ip:

$ curl 104.198.149.140``CLIENT VALUES:``client_address=104.132.1.79``...

跨平台支持

​ 由於 Kubernetes 1.5 在類型為 Type=LoadBalancer 的 Services 中支持源 IP 保存的特性僅在 cloudproviders 的子集中實現(GCP and Azure)。你的集群運行的 cloudprovider 可能以某些不同的方式滿足 loadbalancer 的要求:

1、使用一個代理終止客戶端連接並打開一個到你的 nodes/endpoints 的新連接。在這種情況下,源 IP 地址將永遠是雲負載均衡器的地址而不是客戶端的。

2、使用一個包轉發器,因此從客戶端發送到負載均衡器 VIP 的請求在擁有客戶端源 IP 地址的節點終止,而不被中間代理。

​ 第一類負載均衡器必須使用一種它和后端之間約定的協議來和真實的客戶端 IP 通信,例如 HTTP X-FORWARDED-FOR 頭,或者 proxy 協議。 第二類負載均衡器可以通過簡單的在保存於 Service 的 service.spec.healthCheckNodePort 字段上創建一個 HTTP 健康檢查點來使用上面描述的特性。

原文地址:

http://www.damonyi.cc/kubernetes中的source-ip機制/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM