在 Kubernetes Pod 中如何獲取客戶端的真實 IP


Kubernetes 依靠 kube-proxy 組件實現 Service 的通信與負載均衡。在這個過程中,由於使用了 SNAT 對源地址進行了轉換,導致 Pod 中的服務拿不到真實的客戶端 IP 地址信息。本篇主要解答了在 Kubernetes 集群中負載如何獲取客戶端真實 IP 地址這個問題。

創建一個后端服務

服務選擇

這里選擇 containous/whoami 作為后端服務鏡像。在 Dockerhub 的介紹頁面,可以看到訪問其 80 端口時,會返回客戶端的相關信息。在代碼中,我們可以在 Http 頭部中拿到這些信息。

Hostname :  6e0030e67d6a
IP :  127.0.0.1
IP :  ::1
IP :  172.17.0.27
IP :  fe80::42:acff:fe11:1b
GET / HTTP/1.1
Host: 0.0.0.0:32769
User-Agent: curl/7.35.0
Accept: */*

集群環境

簡單介紹一下集群的狀況。集群有三個節點,一個 master ,兩個 worker 節點。如下圖:

創建服務

  • 創建企業空間、項目

如下圖所示,這里將企業空間和項目命名為 realip

  • 創建服務

這里創建無狀態服務,選擇 containous/whoami 鏡像,使用默認端口。

  • 將服務改為 NodePort 模式

編輯服務的外網訪問方式,修改為 NodePort 模式。

查看訪問服務的 NodePort 端口,發現端口為 31509。

  • 訪問服務

瀏覽器打開 Master 節點的 EIP + :31509 時,返回如下內容:

Hostname: myservice-fc55d766-9ttxt
IP: 127.0.0.1
IP: 10.233.70.42
RemoteAddr: 192.168.13.4:21708
GET / HTTP/1.1
Host: dev.chenshaowen.com:31509
User-Agent: Chrome/86.0.4240.198 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: lang=zh;
Dnt: 1
Upgrade-Insecure-Requests: 1

可以看到 RemoteAddr 是 Master 節點的 IP ,並不是訪問客戶端的真實 IP 地址。這里的 Host 指的是訪問入口的地址,為了方便快速訪問,我使用的是域名,並不影響測試結果。

直接通過 NortPort 訪問獲取真實 IP

在上面的訪問中,獲取不到客戶端真實 IP 的原因是 SNAT 使得訪問 SVC 的源 IP 發生了變化。將服務的 externalTrafficPolicy 改為 Local 模式可以解決這個問題。

打開服務的配置編輯頁面

將服務的 externalTrafficPolicy 設置為 Local 模式。

訪問服務,可以得到如下內容:

Hostname: myservice-fc55d766-9ttxt
IP: 127.0.0.1
IP: 10.233.70.42
RemoteAddr: 139.198.254.11:51326
GET / HTTP/1.1
Host: dev.chenshaowen.com:31509
User-Agent: hrome/86.0.4240.198 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cache-Control: max-age=0
Connection: keep-alive
Cookie: lang=zh;
Dnt: 1
Upgrade-Insecure-Requests: 1

Cluster 隱藏了客戶端源 IP,可能導致第二跳到另一個節點,但具有良好的整體負載分布。 Local 保留客戶端源 IP 並避免 LoadBalancer 和 NodePort 類型服務的第二跳,但存在潛在的不均衡流量傳播風險。

下面是對比簡圖:

當請求落到沒有服務 Pod 的節點時,將無法訪問。用 curl 訪問時,會一直停頓在 TCP_NODELAY , 然后提示超時:

*   Trying 139.198.112.248...
* TCP_NODELAY set
* Connection failed
* connect to 139.198.112.248 port 31509 failed: Operation timed out
* Failed to connect to 139.198.112.248 port 31509: Operation timed out
* Closing connection 0

通過 LB -> Service 訪問獲取真實 IP

在生產環境,通常會有多個節點同時接收客戶端的流量,如果僅使用 Local 模式將會導致服務可訪問性變低。引入 LB 的目的是為了利用其探活的特點,僅將流量轉發到存在服務 Pod 的節點上。

這里以青雲的 LB 為例進行演示。在青雲的控制,可以創建 LB ,添加監聽器,監聽 31509 端口,可以參考 LB 的使用文檔(https://docs.qingcloud.com/product/network/loadbalancer/),在此不再贅述。

如下圖可以看到,在服務的 31509 端口僅 master 節點處於活躍狀態,流量也僅會導向 master 節點,符合預期。

接着繼續增加副本數量到 3

遺憾的是,Pod 並沒有均勻分布在三個節點,其中有兩個處於 master 上。因此 LB 的后端節點也沒有完全點亮。如下圖:

這就需要給 deploy 加上反親和性的描述。有兩種選擇。第一種是配置軟策略,但不能保證全部 LB 后端點亮,均勻分配到流量。

spec:
  template:
    metadata:
      labels:
        app: myservice
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchExpressions:
                    - key: app
                      operator: In
                      values:
                        - myservice
                topologyKey: kubernetes.io/hostname

另一種是配置硬策略,強制 Pod 分配在不同的節點上,但會限制副本數量,也就是 Pod 總數不能超過 Node 總數。

spec:
  template:
    metadata:
      labels:
        app: myservice
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: app
                    operator: In
                    values:
                      - myservice
              topologyKey: kubernetes.io/hostname

采用硬策略的配置,最終點亮全部后端,如下圖:

通過 LB -> Ingress -> Service 訪問獲取真實 IP

如果每一個服務都占用一個 LB,成本很高,同時配置不夠靈活,每次新增服務時,都需要去 LB 增加新的端口映射。

還有一種方案是 LB 將 80、443 的流量導給 Ingress Controller,然后將流量轉發到 Service,接着達到 Pod 中的服務。

此時,需要 LB 能做 TCP 層的透傳,或者 HTTP 層的帶真實 IP 轉發,將 Ingress Controller 的 externalTrafficPolicy 設置為 Local 模式,而 Service 可以不必設置為 Local 模式。

如果想要提高可訪問性,同樣可以參考上面配置反親和性,保證在每個后端節點上都有 Ingress Controller 。

流量的轉發路徑:

LB(80/443) -> Ingress Controller(30000) -> myservice(80) -> myservice-fc55d766-xxxx(80)

首先需要勾選 LB 【獲取客戶端IP】的配置

接着開啟項目的外網訪問網關

然后添加服務的路由

最后還需要在【平台管理】-> 【集群管理】,進入集群,在系統項目 kubesphere-controls-system 中找到 realip 項目對應的網關。

編輯服務的配置文件,將 externalTrafficPolicy 改為 Local 模式即可。

訪問服務,可以得到如下內容:

Hostname: myservice-7dcf6b965f-vv6md
IP: 127.0.0.1
IP: 10.233.96.152
RemoteAddr: 10.233.70.68:34334
GET / HTTP/1.1
Host: realip.dev.chenshaowen.com
User-Agent: Chrome/87.0.4280.67 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cache-Control: max-age=0
Cookie: _ga=GA1.2.896113372.1605489938; _gid=GA1.2.863456118.1605830768
Cookie: lang=zh;
Upgrade-Insecure-Requests: 1
X-Forwarded-For: 139.198.113.75
X-Forwarded-Host: realip.dev.chenshaowen.com
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Original-Uri: /
X-Real-Ip: 139.198.113.75
X-Request-Id: 999fa36437a1180eda3160a1b9f495a4
X-Scheme: https

總結

本文介紹了三種獲取真實 IP 的部署方式:

  • 直接通過 NortPort 訪問獲取真實 IP

受制於 Local 模式,可能會導致服務不可訪問。需要保證對外提供入口的節點上,必須具有服務的負載。

  • 通過 LB -> Service 訪問獲取真實 IP

利用 LB 的探活能力,能夠提高服務的可訪問性。適用於服務較少,或者願意每個服務一個 LB 的場景。

  • 通過 LB -> Ingress -> Service 訪問獲取真實 IP

通過 LB 將 80、443 端口的流量轉到 Ingress Controller ,再進行服務分發。但 Ingress Controller 使用 Local 模式,就要求 LB 的每個后端節點都有 Ingress Controller 副本。適用於對外暴露服務數量較多的場景。

當然也可以組合使用,對於並不需要獲取客戶端真實 IP 的服務,可以繼續使用 Cluster 模式。

參考

關於 KubeSphere

KubeSphere (https://kubesphere.io)是在 Kubernetes 之上構建的開源容器混合雲,提供全棧的 IT 自動化運維的能力,簡化企業的 DevOps 工作流。

KubeSphere 已被 Aqara 智能家居、本來生活、新浪、華夏銀行、四川航空、國葯集團、微眾銀行、紫金保險、中通、中國人保壽險、中國太平保險、中移金科、Radore、ZaloPay 等海內外數千家企業采用。KubeSphere 提供了開發者友好的向導式操作界面和豐富的企業級功能,包括多雲與多集群管理、Kubernetes 資源管理、DevOps (CI/CD)、應用生命周期管理、微服務治理 (Service Mesh)、多租戶管理、監控日志、告警通知、審計事件、存儲與網絡管理、GPU support 等功能,幫助企業快速構建一個強大和功能豐富的容器雲平台。

GitHub:https://github.com/kubesphere
官網(中國站):https://kubesphere.com.cn
微信群:請搜索添加群助手微信號 kubesphere

本文由博客一文多發平台 OpenWrite 發布!


免責聲明!

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



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