k8s獲取client ip

通常,爲了訪問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
  1. 客戶端將請求發送到node 2
  2. node 2 根據iptables 中的轉發策略,將報文轉發至pod所在節點 node 1
  3. node 1 根據本地路由將報文發送給 pod
  4. node 1 將pod的響應報文做SNAT後,發給node 2
  5. node 2 回覆給client

(此時pod所見的source ip爲node 2的IP)

  • pod在所請求的node上
toplogy:
   client
    | ^   
    | |SNAT 
    v |
   node 1 
    | ^   
    | |   
    v |
  endpoint
  1. 客戶端請求node1:nodeport
  2. node 1 根據iptables,通過SNAT 將原地址改爲node1 ip, 通過DNAT 將目的地址改爲pod ip
  3. 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
  1. 客戶端請求node1:nodeport
  2. node 1 根據iptables, 通過DNAT將目的地址改爲pod ip
  3. 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 to Local 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 分佈在不同節點上的情況。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章