假標題
寫這篇文章的原因是:
- 一段時間後,老忘記 k8s 的 IP,ClusterIP,NodePort,Port 等等是什麼意思
- 週末沒事做 -.-
環境說明
dnsDomain: cluster.local podSubnet: 192.167.0.0/16 serviceSubnet: 10.96.0.0/12 k8s (v1.15.0) 部署在 3 臺主機上,網路外掛使用的是 flannel centos0 192.168.20.70 [master] centos1 192.168.20.71 centos2 192.168.20.72
k8s 例子使用的是 https://kubernetes.io/docs/tutorials/state...
$ kubectl get service -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR frontend NodePort 10.99.173.89 <none> 80:30193/TCP 13h app=guestbook,tier=frontend kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 20h <none> redis-master ClusterIP 10.106.223.234 <none> 6379/TCP 13h app=redis,role=master,tier=backend redis-slave ClusterIP 10.106.94.73 <none> 6379/TCP 13h app=redis,role=slave,tier=backend $ kubectl describe service frontend Name: frontend ... Type: NodePort IP: 10.99.173.89 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 30193/TCP Endpoints: 192.167.1.2:80,192.167.1.3:80,192.167.1.4:80 + 2 more... Session Affinity: None External Traffic Policy: Cluster
k8s 模組
在開始下面的分析之前,先看看 k8s 的模組大概有哪些?
主要有 3 個模組, Node, Service, Pod
- Node 即我們本機,NodeIP 就是我們本機的 IP(對應我們上面部署的主機 centos0/centos1/centos2),NodePort 即 Service 提供的服務對映到我們本機的埠號
- 總所周知 Pod 是一組容器的集合(從網路的角度來看,Pod 相當於一個 docker,Pod 裡面的數個 docker 的網路名稱空間是共享的)
- Service 存在的意義:因為在 k8s 中,Pod 的 IP 是變化的(比如 增/減 pod 個數),Pod 的 IP 有多個。因此抽象出一個 Service,以一個 Service IP 對應多個 Pod IP(例如輪詢),ServicePort 對應 Pod 的 Port。另外 Service 在一些場景下,也叫 Cluster,ClusterIP 即 ServiceIP。還有個另外,ServiceIP 是虛擬的,這在最後會解釋
Service
Service 型別:
- ClusterIP
不暴露埠,即 ServiceIP 相當於只能在 Service 內部訪問 - NodePort
暴露埠,實際上在任何一個 Node 上都可以根據該埠訪問到
再看看 Service 的資訊
$ kubectl get service -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR frontend NodePort 10.99.173.89 <none> 80:30193/TCP 13h app=guestbook,tier=frontend redis-master ClusterIP 10.106.223.234 <none> 6379/TCP 13h app=redis,role=master,tier=backend
frontend 的型別是 NodePort,ClusterIP(即 ServiceIP)為 10.99.173.89,ServicePort 為 80,NodePort 30193。可以通過兩種方式訪問 Frontend Service
- 直接訪問 Service
$ curl http://10.99.173.89:80
- 通過 Node 訪問,再轉發到 Service
$ curl http://192.168.20.70:30193 $ curl http://192.168.20.71:30193 $ curl http://192.168.20.72:30193
redis-master 的型別是 ClusterIP,訪問方式只能通過 Service 直接訪問
$ redis-cli -h 10.106.223.234
$ kubectl describe service frontend Type: NodePort # 型別是 NodePort IP: 10.99.173.89 # ServiceIP Port: <unset> 80/TCP # ServicePort TargetPort: 80/TCP # 對應的 Pod Port NodePort: <unset> 30193/TCP # NodePort Endpoints: 192.167.1.2:80,192.167.1.3:80,192.167.1.4:80 + 2 more... # 對應 Pod 的 IP:Port
實現
k8s 預設是通過 iptables (也有 使用者空間[不懂] 的 和 ipvs [需要設定才能開啟])來實現轉發的
上面,我們說過,ServiceIP 是虛擬的。虛擬的意思是我們找不到該地址對應的網路裝置
當我們要訪問 ServiceIP 的時候,iptables 匹配到該 ServiceIP 之後,就會轉發到對應的 Pod。原理就這麼簡單,看得懂 iptables 規則的自然也就看懂了,看不懂的說了等於白說 - -
$ iptables-save -t nat # 也可以使用 iptables -t nat -L 檢視 ... 01. -A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES 02. -A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING 03. -A KUBE-SERVICES ! -s 192.167.0.0/16 -d 10.99.173.89/32 -p tcp -m comment --comment "default/frontend: cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ 04. -A KUBE-SERVICES -d 10.99.173.89/32 -p tcp -m comment --comment "default/frontend: cluster IP" -m tcp --dport 80 -j KUBE-SVC-GYQQTB6TY565JPRW 05. -A KUBE-SVC-GYQQTB6TY565JPRW -m statistic --mode random --probability 0.20000000019 -j KUBE-SEP-5M6IMKFOUATBNJIE 06. -A KUBE-SVC-GYQQTB6TY565JPRW -m statistic --mode random --probability 0.25000000000 -j KUBE-SEP-TOJEKTNBMDCO7OBL 07. -A KUBE-SVC-GYQQTB6TY565JPRW -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-2TZ5ELMBBIM6AEEL 08. -A KUBE-SVC-GYQQTB6TY565JPRW -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-BXGUNY74Y2MJVOFH 09. -A KUBE-SVC-GYQQTB6TY565JPRW -j KUBE-SEP-U2DUTXQ5GM4LVCNQ 10. -A KUBE-SEP-TOJEKTNBMDCO7OBL -s 192.167.1.3/32 -j KUBE-MARK-MASQ 11. -A KUBE-SEP-TOJEKTNBMDCO7OBL -p tcp -m tcp -j DNAT --to-destination 192.167.1.3:80 12. -A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000 13. -A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -m mark --mark 0x4000/0x4000 -j MASQUERADE