通常,爲了訪問kubernetes集羣中的pod,我們會使用service
的形式。以下,就不同的 service
類型來探討對source ip
的影響。
kubernetes的官網對此也有說明:
https://kubernetes.io/docs/tutorials/services/source-ip/
在此簡單總結一下(網絡插件使用calico, 其他插件可能有不同,在此不做討論):
ClusterIP
在創建service
的時候,如果不聲明type
字段,則默認爲 ClusterIP
. 其原理就是利用iptables 對發送到clusterip
的報文進行轉發。
[root@walker-1 hehe]# kubectl describe svc nginx
Name: nginx
Namespace: default
Labels: app=nginx
Selector: app=nginx
Type: ClusterIP
IP: 10.96.9.42
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 192.168.187.212:80
Session Affinity: None
如果是ClusterIP
, 一般用於集羣內pod之間的訪問。
1. 如果client和server在同一個node上,那麼從server觀察到的source ip
爲client的真實IP。
2. 如果client和server分別處於node A, node B上,那麼從server段觀察到source ip
的就是 node B的IP(pod外出的流量會被做SNAT)。
NodePort
NodePort
可以認爲是構建在 ClusterIP
之上的。如果指定service 類型爲NodePort
,那麼在每個 node 上都會監聽同一個端口,並通過iptables對其進行轉發。
請求有兩種情況:
- pod不在所請求的node上
toplogy:
client
\ ^
\ \
v \
node 1 <--- node 2
| ^ SNAT
| | --->
v |
endpoint
- 客戶端將請求發送到node 2
- node 2 根據iptables 中的轉發策略,將報文轉發至pod所在節點 node 1
- node 1 根據本地路由將報文發送給 pod
- node 1 將pod的響應報文做SNAT後,發給node 2
- node 2 回覆給client
(此時pod所見的source ip
爲node 2的IP)
- pod在所請求的node上
toplogy:
client
| ^
| |SNAT
v |
node 1
| ^
| |
v |
endpoint
- 客戶端請求
node1:nodeport
- node 1 根據iptables,通過SNAT 將原地址改爲node1 ip, 通過DNAT 將目的地址改爲pod ip
- node 1 將pod響應報文通過SNAT 返回給客戶端
(此時pod所見的source ip
爲node 1的IP)
這就很煩了,怎麼都獲取不到正確的用戶ip。好在NodePort模式還提供了{"spec":{"externalTrafficPolicy":"Local"}}
參數
加上以後效果是這樣的:
toplogy:
client
^ / \
/ / \
/ v X
node 1 node 2
^ |
| |
| v
endpoint
- 客戶端請求
node1:nodeport
- node 1 根據iptables, 通過DNAT將目的地址改爲pod ip
- node 1 將pod響應報文通過SNAT返回給客戶端
(此時pod所見的source ip
爲client ip)
注:發往node2的報文會被丟棄,因爲報文不會再做SNAT,來將client ip替換爲node2 ip了。
LoadBalancer
默認也會做SNAT,替換客戶端IP。
However, if you’re running on Google Kubernetes Engine/GCE, setting the same
service.spec.externalTrafficPolicy
field toLocal
forces nodes without Service endpoints to remove themselves from the list of nodes eligible for loadbalanced traffic by deliberately failing health checks.
Visually:
client
|
lb VIP
/ ^
v /
health check ---> node 1 node 2 <--- health check
200 <--- ^ | ---> 500
| V
endpoint
總結
要讓 pod 能正常獲取客戶端ip大致有如下幾種方式:
1. 可以使用 NodePort
+ {"spec":{"externalTrafficPolicy":"Local"}}
的配置來實現。
2. 還有個解決思路就是利用 INGERSS。INGRESS 本質上是監聽物理機端口,然後直接將客戶端請求轉發至service
。可以在INGRESS 請求轉發階段將客戶端IP 帶到請求頭中。
3. pod直接使用 HOST 網絡模式。
第三種方式最便捷,但容易造成端口衝突。安全問題也有待考量,因此不推薦。
第二種方式最靈活,即使pod分佈在不同node上也可以通過統一入口訪問,官方INGRESS是由nginx實現的,這樣一來花樣就多了(甚至可以做流控,認證功能)。推薦使用。
第一種方式折中吧,因爲還沒試過當service
的多個endpoint
分佈在不同節點上的情況。