深入理解 kubernetes iptables proxy 模式

2021-02-16 雲原生領域
點上方藍色「雲原生領域」關注我,設個星標,不會讓你失望概述

最近在面試的時候問了不少 network request 如何到 k8s service backend 的問題,覺得可以整合一下網絡上的資料,這篇主要討論 iptables proxy mode。大部分的情況沒有在使用 userspace proxy modes, ipvs proxy mode 可能要等到下一次討論。

事先準備

要先了解 iptable 工作機制,建議可以看這一篇:https://phoenixnap.com/kb/iptables-tutorial-linux-firewall,當然 wikipedia 也是寫的不錯,我下面的文字也大多數引用:https://zh.wikipedia.org/wiki/Iptables

快速帶過 iptable

說到 iptable 要先了解 Tables, Chains 和 Rueles。

Table 指不同類型的封包處理流程,總共有五種,不同的 Tables 處理不同的行為raw:處理異常,追蹤狀態 -> /proc/net/nf_conntrackmangle:處理封包,修改 headler 之類的Chains 來對應進行不同的行為。像是 「filter」 Tables 進行封包過濾的流程,而 「nat」 針對連接進行位址轉換操作。Chains 裡面包含許多規則,主要有五種類型的 ChainsPREROUTING:處理路由規則前通過此 Chains,通常用於目的位址轉換(DNAT)FORWARD:本機轉發的封包通過此 Chains。POSTROUTING:完成路由規則後通過此 Chains,通常用於源位址轉換(SNAT)Rules 規則會被逐一進行匹配,如果匹配,可以執行相應的動作大致的工作流向情況分兩種:
NIC → PREROUTING → INPUT → Local process 
Local process → OUTPUT → POSTROUTING → NIC

NIC→PREROUTING → FORWARD → POSTROUTING→NIC

下面是比較詳細的流程,有包含 EBTABLES,但這個看久頭會昏,我這次會主要討論 Network Layer 這一部分,然後用上面這張比較精簡的圖

Netfilter pic of wikipedia

Kube-proxy 修改了 filter,nat 兩個表,自定義了KUBE-SERVICES,KUBE-NODEPORTS,KUBE-POSTROUTING,KUBE-FORWARD,KUBE-MARK-MASQ 和 KUBE-MARK-DROP,所以我這次會 focus on filter ,nat 兩個 Table

1. filter table 有三個 Chain 「INPUT」 「OUTPUT」 「FORWARD」

https://sites.google.com/site/mrxpalmeiras/linux/iptables-routing

kube-proxy 在 filter table 的 「INPUT」 「OUTPUT」 chain 增加了 KUBE-FIREWALL 在 「INPUT」 「OUTPUT」 「FORWARD」 chain 增加了 KUBE-SERVICES

KUBE_FIREWALL 會丟棄所有被 KUBE-MARK-DROP 標記 0x8000 的封包,而標記的動作可以在其他的 table 中(像是第二部分提到的 NAT table 中)

而 filter table 的 KUBE-SERVICES 可以過濾封包,假如一個 service 沒有對應的 endpoint,就會被 reject,這裡我先要建立一個 service 和沒有正確設定 endpoint。

kind: Service 
apiVersion: v1 
metadata: 
  name: test-error-endpoint 
  namespace: default 
spec: 
  ports: 
    - protocol: TCP 
      port: 7777 
      targetPort: 7777 
--- 
kind: Endpoints 
apiVersion: v1 
metadata: 
  name: test-error-endpoint 
  namespace: default

service cluster ip 為 10.95.58.92

kind: Service 
apiVersion: v1 
metadata: 
  name: test-error-endpoint 
  namespace: default 
  selfLink: /api/v1/namespaces/default/services/test-error-endpoint 
  uid: 5d415d63-6fc3-444e-8b5a-29015b436a83 
  resourceVersion: ' 73026369' 
  creationTimestamp: '2020-11-17T05:48:52Z' 
spec: 
  ports: 
    - protocol: TCP 
      port: 7777 
      targetPort: 7777 
  clusterIP: 10.95.58.92 
  type: ClusterIP 
  sessionAffinity: None 
status: 
  loadBalancer: {}

再次檢查 iptable,就可以看到 default/test-error-endpoint: has no endpoints -> tcp dpt:7777 reject-with icmp-port-unreachable

2. nat table 有三個 Chain 「PREROUTING」 「OUTPUT」 「POSTROUTING」

在前兩個封包處理流程是比較相似和複雜的,大體來說是藉由客制化的規則,來處理符合條件封包,幫它們找到正確的 k8s endpoint (後面會細講),在 POSTROUTING 主要是針對 k8s 處理的封包(標記 0x4000 的封包),在離開 node 的時候做 SNAT

(inbound) 在 「PREROUTING」 將所有封包轉發到 KUBE-SERVICES(outbound) 在 「OUTPUT」 將所有封包轉發到 KUBE-SERVICES(outbound) 在 「POSTROUTING」 將所有封包轉發到 KUBE-POSTROUTING

https://sites.google.com/site/mrxpalmeiras/linux/iptables-routing

當封包進入 「PREROUTING」 和 「OUTPUT」,會整個被 KUBE-SERVICES Chain 整個綁架走,開始逐一匹配 KUBE-SERVICES 中的 rule 和打上標籤。

nat tables

kube-proxy 的用法是一種 O(n) 算法,其中的 n 隨 k8s cluster 的規模同步增加,更簡單的說就是 service 和 endpoint 的數量。

KUBE-SERVICES

我這裡會準備三個最常見的 service type 的 kube-proxy 路由流程

」clusterIP 流程

這裡我使用 default/jeff-api(clusterIP: 10.95.57.19) 舉例,我下面圖過濾掉不必要的資訊

最後會到實際 pod 的位置,podIP: 10.95.35.31,hostIP: 10.20.0.128 是該 pod 所在 node 的 ip

kind: Pod 
apiVersion: v1 
metadata: 
  name: jeff-api-746f4c9985-5qmw6 
  generateName: jeff-api-746f4c9985- 
  namespace: default 
spec: 
  containers: 
    - name: promotion-api 
      image: 'gcr.io/jeff-project/jeff /jeff-api:202011161901' 
      ports: 
        - name: 80tcp02 
          containerPort: 80 
          protocol: TCP 
  nodeName: gke-sit-jeff-k8s-tw-01-default-pool-7983af35-ug91 
status: 
  phase: Running 
  hostIP: 10.20.0.128 
  podIP: 10.95.35.31

nodePort 流程

這裡有一個關鍵就是 KUBE-NODEPORTS 一定是在 KUBE-SERVICES 最後一項,iptables 在處理 packet 會先處理 ip 為 cluster ip 的 service,當全部的 KUBE-SVC-XXXXXX 都對應不到的時候就會使用 nodePort 去匹配。

我們看實際 pod 的資訊,podIP: 10.95.32.17,hostIP: 10.20.0.124 是其中一臺 node 的 ip

kind: Service 
apiVersion: v1 
metadata: 
  name: jeff-frontend 
  namespace: jeff-frontend 
spec: 
  ports: 
    - protocol: TCP 
      port: 80 
      targetPort: 80 
      nodePort: 31929 
  selector: 
    app: jeff-frontend 
  clusterIP: 10.95.58.51 
  type: NodePort 
  externalTrafficPolicy: Cluster 
--- 
kind: Pod 
apiVersion: v1 
metadata: 
  name: jeff-frontend-c94bf68d9-bbmp8 
  generateName: jeff-frontend-c94bf68d9- 
  namespace: jeff-frontend 
spec: 
  containers: 
    - name: jeff-frontend
      image: 'gcr.io/jeff-project/jeff/jeff-image:jeff-1.0.6.5' 
      ports: 
        - name: http 
          containerPort: 80 
          protocol: TCP 
  nodeName: gke-sit-jeff-k8s-tw-01-default -pool-b5692f8d-enk7 
status: 
  phase: Running 
  hostIP: 10.20.0.124 
  podIP: 10.95.32.17

load balancer流程

假如目的地 IP 是 load balancer 就會使用 KUBE-FW-XXXXXX,我建立一個 internal load balancer service 和 endpoint 指到 google postgresql DB(10.28.193.9)

apiVersion: v1 
kind: Service 
metadata: 
  annotations: 
    cloud.google.com/load-balancer-type: Internal 
    networking.gke.io/internal-load-balancer-allow-global-access: 'true' 
  name: external-postgresql 
spec : 
  ports: 
    - protocol: TCP 
      port: 5432 
      targetPort: 5432 
  type: LoadBalancer 
--- 
apiVersion: v1 
kind: Endpoints 
metadata: 
  name: external-postgresql 
subsets: 
- addresses: 
  - ip: 10.28.193.9 
  ports: 
  - port: 5432 
    protocol : TCP

在 NAT table 看到 KUBE-MARK-MASQ 和 KUBE-MARK-DROP 這兩個規則主要是經過的封包打上標籤,打上標籤的封包會做相應的處理。KUBE-MARK-DROP 和 KUBE-MARK-MASQ 本質上就是使用 iptables 的 MARK 指令

-A KUBE-MARK-DROP -j MARK --set-xmark 0x8000/0x8000
-A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000

如果打上了 0x8000 到後面 filter table (上面提到 KUBE_FIREWALL )就會丟棄。

如果打上了 0x4000 k8s 將會在 PREROUTING table 的 KUBE-POSTROUTING chain 對它進行 SNAT 轉換。

POSTROUTING table

KUBE-POSTROUTING Chain

參考:

https://en.wikipedia.org/wiki/Netfilterhttps://zh.wikipedia.org/wiki/Iptableshttps://phoenixnap.com/kb/iptables-tutorial-linux-firewallhttps://www .cnblogs.com/charlieroro/p/9588019.htmlhttps://github.com/kubernetes/kubernetes/blob/master/pkg/proxy/iptables/proxier.gohttps://www.lijiaocn.com/%E9% A1%B9%E7%9B%AE/2017/03/27/Kubernetes-kube-proxy.htmlhttps://juejin.im/post/6844904098605563912https://tizeen.github.io/2019/03/19/ kubernetes-service-iptables%E5%88%86%E6%9E%90/https://www.hwchiu.com/kubernetes-service-ii.htmlhttps://www.hwchiu.com/kubernetes-service-iii .htmlhttps://www.itread01.com/content/1542712570.html「

來源:https://jeff-yen.medium.com/iptables-proxy-mode-in-kube-proxy-6862bb4b329

相關焦點

  • 深入解析Kubernetes service 概念
    在Kubernetes中,每個節點都安裝了kube-proxy,kube-proxy通過kubernetes中固有的watch請求方法持續監聽apiserver。userpace在這種模式下,kube-proxy監視Kubernetes主伺服器以添加和刪除Service和Endpoint對象。
  • Kubernetes 疑難雜症排查分享: 詭異的 No route to host
    比如在 server 滾動更新過程中,舊的 Pod 中的進程很快就停止了(網卡還未完全銷毀),但 client 所在節點的 iptables/ipvs 規則還沒更新,包就可能會被轉發到了這個停止的 Pod (由於 k8s 的 controller 模式,從 Pod 刪除到 service 的 endpoint 更新,再到 kube-proxy watch 到更新並更新 節點上的 iptables/ipvs
  • Kubernetes 1.14 二進位集群安裝
    curl sysstat libseccomp wget關閉防火牆 Linux 以及swap分區systemctl stop firewalldsystemctl disable firewalldiptables -F && iptables -X && iptables -F -t nat && iptables -X -t natiptables
  • 利用 eBPF 支撐大規模 Kubernetes Service
    想更深入地理解基於 iptables 的實現,可參考網上其他一些文章,例如下面這張圖所出自的博客《Kubernetes Networking Demystified: A Brief Guide》。這種類型的 Service 也能被宿主機和 Pod 訪問,但與 ClusterIP 不同的是,它還能被集群外的服務訪問。
  • 不好,WireGuard 與 Kubernetes CNI 摩擦生火了..
    關於全互聯模式的更多詳細內容請參考 👉Wireguard 全互聯模式(full mesh)配置指南。可以通過 Kilo 的啟動參數 --mesh-granularity=full 來指定全互聯模式。app.kubernetes.io/name: kilo  template:    metadata:      labels:        app.kubernetes.io/name: kilo    spec:      serviceAccountName: kilo      hostNetwork: true
  • [譯]將 Kubernetes 擴展至7500個節點
    工作負載在我們深入本文之前,先簡單介紹下我們的工作負載是非常有必要的。我們使用 Kubernetes 運行的應用程式和硬體與你在大部分公司可能遇到的情況有很大不同。一個大型機器學習作業跨越多個節點,當它能夠訪問每個節點上的所有硬體資源時,它的運行效率最高。
  • Proxy-Go v4.4 發布,內網穿透性能大幅度提升
    本身不提供代理功能,只是接受代理請求"轉換並轉發"給已經存在的http(s)代理或者socks5代理;sps可以把已經存在的http(s)代理或者socks5代理轉換為一個埠同時支持http(s)和socks5代理,而且http(s)代理支持正向代理和反向代理(SNI),轉換後的SOCKS5代理不支持UDP功能;另外對於已經存在的http(s)代理或者socks5代理,支持tls、tcp、kcp三種模式
  • Kubernetes 1.8.0 版本發布
    SIG Network 還專注於改進 kube-proxy,除了當前的 iptables 和 userspace 模式,kube-proxy 還引入了一個 alpha 版本的 IPVS 模式。SIG Storage存儲興趣組主要包含存儲和各種存儲卷插件。
  • Proxy-Go v5.0 發布:DNS 汙染?不存在的!
    跨平臺性,無論你是widows,linux,還是mac,甚至是樹莓派,都可以很好的運行proxy.多協議支持,支持HTTP(S),TCP,UDP,Websocket,SOCKS5代理.TCP/UDP埠轉發.支持內網穿透,協議支持TCP和UDP.
  • 教你一次性成功安裝K8S集群(基於一主兩從模式)
    別問我為哈這樣學,只是我覺得對我個人來說,這樣學能讓我更好的理解整套雲原生體系。這不,這次,我總結了如何一次性成功安裝K8S集群的方法。我們今天先來說說如何基於一主兩從模式搭建K8S集群。後面,我們再上如何完全無坑搭建K8S高可用集群的方案。
  • Proxy-Go v8.2 發布,內網穿透P2P!
    增加了tools工具功能,目前有:nat類型判斷,方便查看網絡是否支持p2p,比如:proxy tools -a nattype另外經過兩輪內測,一輪公測的「代理控制面板」即將發布,Windows,Linux,macOS,arm一鍵安裝為系統服務:
  • 深入理解Kubernetes資源限制:CPU
    原文地址:https://medium.com/@betz.mark/understanding-resource-limits-in-kubernetes-cpu-time-9eff74d3161b 作者:Mark Betz 譯者:楊傳勝在關於 Kubernetes
  • Kubernetes CKA線上直播培訓班
    -nodeSelector    -nodeName    -Taints    -Tolerations    -Affinity and AntiAffinity12:日常維護    -事件查看    -日誌信息查看    -監控信息查看    -維護模式
  • Kubernetes學習環境難搭建?Mac筆記本上安裝一個!
    >$ kubectl cluster-infoKubernetes control plane is running at https://192.168.64.2:8443KubeDNS is running at https://192.168.64.2:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
  • Kubernetes 入門命令整理及解析
    方便記憶的規律kubernetes命令有一些相通的規律,可以幫助我們快速掌握。journalctl -l -u kubelet # 查看kubelet日誌kubectl get pods -o yaml # 配置yaml格式詳情kubectl get endpoints # 查看終端服務節點kubectl top node node1 --v=8 #開啟debug模式輸出
  • Kubernetes ELK 日誌收集
    直接在宿主機上安裝,和在kubernetes效果一樣的。>https://i4t.com/3552.htmlKubernetes StatefulSet允許我們為Pod分配一個穩定的標識和持久化存儲,Elasticsearch需要穩定的存儲來保證Pod在重新調度或者重啟後的數據依舊不變,所以要試用StatefulSet來管理Pod我這裡使用NFS進行模式後端存儲
  • Proxy使用詳解
    / receiver為proxylet obj = Object.create(proxy);obj.msg // receiver為objproxy對象是obj對象的原型,obj對象本身並沒有msg屬性,所以根據原型鏈,會在proxy對象上讀取該屬性,導致被攔截。
  • 深入理解 Mybatis 插件開發
    關於Mybatis插件,大部分人都知道,也都使用過,但很多時候,我們僅僅是停留在表面上,知道Mybatis插件可以在DAO層進行攔截,如列印執行的SQL語句日誌,做一些權限控制,分頁等功能;但對其內部實現機制,涉及的軟體設計模式
  • Kubernetes API 安全機制詳解
    平臺通過apiserver組件對外提供HTTPS協議服務接口,其它scheduler、controller-manager、kubelet、kube-proxy組件不提供交互接口。運維中對平臺的管理操作都需要通過apiserver提供的功能接口完成,因此Kubernetes總體的API安全防護機制是對用戶訪問操作HTTPS服務接口的控制。