新聞中心
使用源 IP
運(yùn)行在 Kubernetes 集群中的應(yīng)用程序通過(guò) Service 抽象發(fā)現(xiàn)彼此并相互通信,它們也用 Service 與外部世界通信。 本文解釋了發(fā)送到不同類型 Service 的數(shù)據(jù)包的源 IP 會(huì)發(fā)生什么情況,以及如何根據(jù)需要切換此行為。

創(chuàng)新互聯(lián)公司長(zhǎng)期為近1000家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開(kāi)放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為潤(rùn)州企業(yè)提供專業(yè)的成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站,潤(rùn)州網(wǎng)站改版等技術(shù)服務(wù)。擁有十年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開(kāi)發(fā)。
術(shù)語(yǔ)表
本文使用了下列術(shù)語(yǔ):
- NAT
- Source NAT
- Destination NAT
- VIP
- Kube-proxy
網(wǎng)絡(luò)地址轉(zhuǎn)換
替換數(shù)據(jù)包上的源 IP;在本頁(yè)面中,這通常意味著替換為節(jié)點(diǎn)的 IP 地址
替換數(shù)據(jù)包上的目標(biāo) IP;在本頁(yè)面中,這通常意味著替換為 Pod 的 IP 地址
一個(gè)虛擬 IP 地址,例如分配給 Kubernetes 中每個(gè) Service 的 IP 地址
一個(gè)網(wǎng)絡(luò)守護(hù)程序,在每個(gè)節(jié)點(diǎn)上協(xié)調(diào) Service VIP 管理
在開(kāi)始之前
你必須擁有一個(gè) Kubernetes 的集群,同時(shí)你的 Kubernetes 集群必須帶有 kubectl 命令行工具。 建議在至少有兩個(gè)節(jié)點(diǎn)的集群上運(yùn)行本教程,且這些節(jié)點(diǎn)不作為控制平面主機(jī)。 如果你還沒(méi)有集群,你可以通過(guò) Minikube 構(gòu)建一個(gè)你自己的集群,或者你可以使用下面任意一個(gè) Kubernetes 工具構(gòu)建:
- Katacoda
- 玩轉(zhuǎn) Kubernetes
示例使用一個(gè)小型 nginx Web 服務(wù)器,服務(wù)器通過(guò) HTTP 標(biāo)頭返回它接收到的請(qǐng)求的源 IP。 你可以按如下方式創(chuàng)建它:
kubectl create deployment source-ip-app --image=K8S.gcr.io/echoserver:1.4
輸出為:
deployment.apps/source-ip-app created
教程目標(biāo)
- 通過(guò)多種類型的 Service 暴露一個(gè)簡(jiǎn)單應(yīng)用
- 了解每種 Service 類型如何處理源 IP NAT
- 了解保留源 IP 所涉及的權(quán)衡
Type=ClusterIP 類型 Service 的源 IP
如果你在 iptables 模式(默認(rèn))下運(yùn)行 kube-proxy,則從集群內(nèi)發(fā)送到 ClusterIP 的數(shù)據(jù)包永遠(yuǎn)不會(huì)進(jìn)行源 NAT。 你可以通過(guò)在運(yùn)行 kube-proxy 的節(jié)點(diǎn)上獲取 ?http://localhost:10249/proxyMode? 來(lái)查詢 kube-proxy 模式。
kubectl get nodes
輸出類似于:
NAME STATUS ROLES AGE VERSION
kubernetes-node-6jst Ready 2h v1.13.0
kubernetes-node-cx31 Ready 2h v1.13.0
kubernetes-node-jj1t Ready 2h v1.13.0 在其中一個(gè)節(jié)點(diǎn)上獲取代理模式(kube-proxy 監(jiān)聽(tīng) 10249 端口):
# 在要查詢的節(jié)點(diǎn)上的 shell 中運(yùn)行
curl http://localhost:10249/proxyMode輸出為:
iptables
你可以通過(guò)在源 IP 應(yīng)用程序上創(chuàng)建 Service 來(lái)測(cè)試源 IP 保留:
kubectl expose deployment source-ip-app --name=clusterip --port=80 --target-port=8080
輸出為:
service/clusterip exposed
kubectl get svc clusterip
輸出類似于:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
clusterip ClusterIP 10.0.170.92 80/TCP 51s 并從同一集群中的 Pod 中訪問(wèn) ?ClusterIP?:
kubectl run busybox -it --image=busybox:1.28 --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.然后,你可以在該 Pod 中運(yùn)行命令:
# 從 “kubectl run” 的終端中運(yùn)行
ip addr1: lo: 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: 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 ?查詢本地 Web 服務(wù)器:
# 將 “10.0.170.92” 替換為 Service 中名為 “clusterip” 的 IPv4 地址
wget -qO - 10.0.170.92CLIENT VALUES:
client_address=10.244.3.8
command=GET
...?client_address? 始終是客戶端 Pod 的 IP 地址,不管客戶端 Pod 和服務(wù)器 Pod 位于同一節(jié)點(diǎn)還是不同節(jié)點(diǎn)。
Type=NodePort 類型 Service 的源 IP
默認(rèn)情況下,發(fā)送到 ?Type=NodePort? 的 Service 的數(shù)據(jù)包會(huì)經(jīng)過(guò)源 NAT 處理。你可以通過(guò)創(chuàng)建一個(gè) ?NodePort ?的 Service 來(lái)測(cè)試這點(diǎn):
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=="InternalIP")].address }')如果你在云供應(yīng)商上運(yùn)行,你可能需要為上面報(bào)告的 ?nodes:nodeport ?打開(kāi)防火墻規(guī)則。 現(xiàn)在你可以嘗試通過(guò)上面分配的節(jié)點(diǎn)端口從集群外部訪問(wèn) 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請(qǐng)注意,這些并不是正確的客戶端 IP,它們是集群的內(nèi)部 IP。這是所發(fā)生的事情:
- 客戶端發(fā)送數(shù)據(jù)包到 ?
node2:nodePort? - ?
node2?使用它自己的 IP 地址替換數(shù)據(jù)包的源 IP 地址(SNAT) - ?
node2?將數(shù)據(jù)包上的目標(biāo) IP 替換為 Pod IP - 數(shù)據(jù)包被路由到 node1,然后到端點(diǎn)
- Pod 的回復(fù)被路由回 node2
- Pod 的回復(fù)被發(fā)送回給客戶端
用圖表示:
如圖。使用 SNAT 的源 IP(Type=NodePort)
為避免這種情況,Kubernetes 有一個(gè)特性可以保留客戶端源 IP。 如果將 ?service.spec.externalTrafficPolicy? 設(shè)置為 ?Local?, kube-proxy 只會(huì)將代理請(qǐng)求代理到本地端點(diǎn),而不會(huì)將流量轉(zhuǎn)發(fā)到其他節(jié)點(diǎn)。 這種方法保留了原始源 IP 地址。如果沒(méi)有本地端點(diǎn),則發(fā)送到該節(jié)點(diǎn)的數(shù)據(jù)包將被丟棄, 因此你可以在任何數(shù)據(jù)包處理規(guī)則中依賴正確的源 IP,你可能會(huì)應(yīng)用一個(gè)數(shù)據(jù)包使其通過(guò)該端點(diǎn)。
設(shè)置 ?service.spec.externalTrafficPolicy? 字段如下:
kubectl patch svc nodeport -p '{"spec":{"externalTrafficPolicy":"Local"}}'
輸出為:
service/nodeport patched
現(xiàn)在,重新運(yùn)行測(cè)試:
for node in $NODES; do curl --connect-timeout 1 -s $node:$NODEPORT | grep -i client_address; done
輸出類似于:
client_address=198.51.100.79
請(qǐng)注意,你只從運(yùn)行端點(diǎn) Pod 的節(jié)點(diǎn)得到了回復(fù),這個(gè)回復(fù)有正確的客戶端 IP。
這是發(fā)生的事情:
- 客戶端將數(shù)據(jù)包發(fā)送到?jīng)]有任何端點(diǎn)的?
node2:nodePort? - 數(shù)據(jù)包被丟棄
- 客戶端發(fā)送數(shù)據(jù)包到 ?
node1:nodePort?,它確實(shí)有端點(diǎn) - node1 使用正確的源 IP 地址將數(shù)據(jù)包路由到端點(diǎn)
用圖表示:
如圖。源 IP(Type=NodePort)保存客戶端源 IP 地址
Type=LoadBalancer 類型 Service 的 Source IP
默認(rèn)情況下,發(fā)送到 ?Type=LoadBalancer? 的 Service 的數(shù)據(jù)包經(jīng)過(guò)源 NAT處理,因?yàn)樗刑幱?nbsp;?Ready ?狀態(tài)的可調(diào)度 Kubernetes 節(jié)點(diǎn)對(duì)于負(fù)載均衡的流量都是符合條件的。 因此,如果數(shù)據(jù)包到達(dá)一個(gè)沒(méi)有端點(diǎn)的節(jié)點(diǎn),系統(tǒng)會(huì)將其代理到一個(gè)帶有端點(diǎn)的節(jié)點(diǎn),用該節(jié)點(diǎn)的 IP 替換數(shù)據(jù)包上的源 IP(如上一節(jié)所述)。
你可以通過(guò)負(fù)載均衡器上暴露 source-ip-app 進(jìn)行測(cè)試:
kubectl expose deployment source-ip-app --name=loadbalancer --port=80 --target-port=8080 --type=LoadBalancer
輸出為:
service/loadbalancer exposed
打印 Service 的 IP 地址:
kubectl get svc loadbalancer
輸出類似于:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
loadbalancer LoadBalancer 10.0.65.118 203.0.113.140 80/TCP 5m接下來(lái),發(fā)送請(qǐng)求到 Service 的 的外部IP(External-IP):
curl 203.0.113.140
輸出類似于:
CLIENT VALUES:
client_address=10.240.0.5
...然而,如果你在 Google Kubernetes Engine/GCE 上運(yùn)行, 將相同的 ?service.spec.externalTrafficPolicy? 字段設(shè)置為 ?Local?, 故意導(dǎo)致健康檢查失敗,從而強(qiáng)制沒(méi)有端點(diǎn)的節(jié)點(diǎn)把自己從負(fù)載均衡流量的可選節(jié)點(diǎn)列表中刪除。
用圖表示:
你可以通過(guò)設(shè)置注解進(jìn)行測(cè)試:
kubectl patch svc loadbalancer -p '{"spec":{"externalTrafficPolicy":"Local"}}'
你應(yīng)該能夠立即看到 Kubernetes 分配的 ?service.spec.healthCheckNodePort? 字段:
kubectl get svc loadbalancer -o yaml | grep -i healthCheckNodePort
輸出類似于:
healthCheckNodePort: 32122
?service.spec.healthCheckNodePort? 字段指向每個(gè)在 ?/healthz? 路徑上提供健康檢查的節(jié)點(diǎn)的端口。你可以這樣測(cè)試:
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-node-6jst使用 ?curl ?獲取各個(gè)節(jié)點(diǎn)上的 ?/healthz? 端點(diǎn):
# 在你選擇的節(jié)點(diǎn)上本地運(yùn)行
curl localhost:32122/healthz1 Service Endpoints found
在不同的節(jié)點(diǎn)上,你可能會(huì)得到不同的結(jié)果:
# 在你選擇的節(jié)點(diǎn)上本地運(yùn)行
curl localhost:32122/healthzNo Service Endpoints Found
在控制平面上運(yùn)行的控制器負(fù)責(zé)分配云負(fù)載均衡器。 同一個(gè)控制器還在每個(gè)節(jié)點(diǎn)上分配指向此端口/路徑的 HTTP 健康檢查。 等待大約 10 秒,讓 2 個(gè)沒(méi)有端點(diǎn)的節(jié)點(diǎn)健康檢查失敗,然后使用 ?curl ?查詢負(fù)載均衡器的 IPv4 地址:
curl 203.0.113.140
輸出類似于:
CLIENT VALUES:
client_address=198.51.100.79
...跨平臺(tái)支持
只有部分云提供商為 ?Type=LoadBalancer? 的 Service 提供保存源 IP 的支持。 你正在運(yùn)行的云提供商可能會(huì)以幾種不同的方式滿足對(duì)負(fù)載均衡器的請(qǐng)求:
- 使用終止客戶端連接并打開(kāi)到你的節(jié)點(diǎn)/端點(diǎn)的新連接的代理。 在這種情況下,源 IP 將始終是云 LB 的源 IP,而不是客戶端的源 IP。
- 使用數(shù)據(jù)包轉(zhuǎn)發(fā)器,這樣客戶端發(fā)送到負(fù)載均衡器 VIP 的請(qǐng)求最終會(huì)到達(dá)具有客戶端源 IP 的節(jié)點(diǎn),而不是中間代理。
第一類負(fù)載均衡器必須使用負(fù)載均衡器和后端之間商定的協(xié)議來(lái)傳達(dá)真實(shí)的客戶端 IP, 例如 HTTP 轉(zhuǎn)發(fā)或 X-FORWARDED-FOR 表頭,或代理協(xié)議。 第二類負(fù)載均衡器可以通過(guò)創(chuàng)建指向存儲(chǔ)在 Service 上的 ?service.spec.healthCheckNodePort? 字段中的端口的 HTTP 健康檢查來(lái)利用上述功能。
清理
刪除 Service:
$ kubectl delete svc -l app=source-ip-app
刪除 Deployment、ReplicaSet 和 Pod:
$ kubectl delete deployment source-ip-app
標(biāo)題名稱:創(chuàng)新互聯(lián)kubernetes教程:Kubernetes使用源IP
網(wǎng)頁(yè)路徑:http://www.dlmjj.cn/article/cdsieoe.html


咨詢
建站咨詢
