一、service和iptables的關系
service 的代理是 kube-proxy
kube-proxy 運行在所有節點上,它監聽 apiserver 中 service 和 endpoint 的變化情況,創建路由規則以提供服務 IP 和負載均衡功能。簡單理解此進程是Service的透明代理兼負載均衡器,其核心功能是將到某個Service的訪問請求轉發到后端的多個Pod實例上,而kube-proxy底層又是通過iptables和ipvs實現的。
iptables原理
Kubernetes從1.2版本開始,將iptables作為kube-proxy的默認模式。iptables模式下的kube-proxy不再起到Proxy的作用,其核心功能:通過API Server的Watch接口實時跟蹤Service與Endpoint的變更信息,并更新對應的iptables規則,Client的請求流量則通過iptables的NAT機制“直接路由”到目標Pod。
ipvs原理
IPVS在Kubernetes1.11中升級為GA穩定版。IPVS則專門用于高性能負載均衡,并使用更高效的數據結構(Hash表),允許幾乎無限的規模擴張,因此被kube-proxy采納為最新模式。
在IPVS模式下,使用iptables的擴展ipset,而不是直接調用iptables來生成規則鏈。iptables規則鏈是一個線性的數據結構,ipset則引入了帶索引的數據結構,因此當規則很多時,也可以很高效地查找和匹配。
可以將ipset簡單理解為一個IP(段)的集合,這個集合的內容可以是IP地址、IP網段、端口等,iptables可以直接添加規則對這個“可變的集合”進行操作,這樣做的好處在于可以大大減少iptables規則的數量,從而減少性能損耗。
kube-proxy ipvs和iptables的異同
iptables與IPVS都是基于Netfilter實現的,但因為定位不同,二者有著本質的差別:iptables是為防火墻而設計的;IPVS則專門用于高性能負載均衡,并使用更高效的數據結構(Hash表),允許幾乎無限的規模擴張。
與iptables相比,IPVS擁有以下明顯優勢:
-
為大型集群提供了更好的可擴展性和性能;
-
支持比iptables更復雜的復制均衡算法(最小負載、最少連接、加權等);
-
支持服務器健康檢查和連接重試等功能;
-
可以動態修改ipset的集合,即使iptables的規則正在使用這個集合。
二、k8s集群中分析service和kube-proxy
訪問Service的請求,不論是Cluster IP+TargetPort的方式;還是用Node節點IP+NodePort的方式,都被Node節點的Iptables規則重定向到Kube-proxy監聽Service服務代理端口。kube-proxy接收到Service的訪問請求后,根據負載策略,轉發到后端的Pod。
# kubectl logs kube-proxy-5clwf -n kube-system (查看某一個節點的kube-proxy日志)
1
2
3
4
5
|
W1014 13:25:02.120810 1 server_others.go:559] Unknown proxy mode "" , assuming iptables proxy I1014 13:25:02.137813 1 node.go:136] Successfully retrieved node IP: 192.168.40.133 I1014 13:25:02.137952 1 server_others.go:186] Using iptables Proxier. I1014 13:25:02.138750 1 server.go:583] Version: v1.18.0 I1014 13:25:02.139344 1 conntrack.go:100] Set sysctl ‘net/netfilter/nf_conntrack_max‘ to 131072 |
# iptables -vnL 查看所有的規則
1
2
3
4
5
6
|
Chain INPUT (policy ACCEPT 19577 packets, 3755K bytes) pkts bytes target prot opt in out source destination 622K 121M cali-INPUT all -- * * 0.0.0.0/0 0.0.0.0/0 /* cali:Cz_u1IQiXIMmKD4c */ 3017 162K KUBE-SERVICES all -- * * 0.0.0.0/0 0.0.0.0/0 ctstate NEW /* kubernetes service portals */ 3017 162K KUBE-EXTERNAL-SERVICES all -- * * 0.0.0.0/0 0.0.0.0/0 ctstate NEW /* kubernetes externally-visible service portals */ 652K 140M KUBE-FIREWALL all -- * * 0.0.0.0/0 0.0.0.0/0 |
# iptables-save > iptables 保存規則,并輸出到屏幕
1
2
3
4
5
6
|
:cali-OUTPUT - [0:0] :cali-PREROUTING - [0:0] :cali-from-host-endpoint - [0:0] :cali-to-host-endpoint - [0:0] -A PREROUTING -m comment --comment "cali:6gwbT8clXdHdC1b1" -j cali-PREROUTING -A OUTPUT -m comment --comment "cali:tVnHkvAo15HuiPy0" -j cali-OUTPUT |
第一步:接收流量
NodePort訪問入口規則:
1
2
3
4
5
6
7
8
9
10
11
|
# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 78d mydb ClusterIP 10.108.118.149 <none> 80/TCP 50d nginx-dns NodePort 10.103.206.148 <none> 80:30296/TCP 49d web-service NodePort 10.111.113.171 <none> 80:32681/TCP 49d web1 NodePort 10.110.180.143 <none> 80:30746/TCP 12h # iptables -S -t nat | grep 30746 -A KUBE-NODEPORTS -p tcp -m comment --comment "default/web1:" -m tcp --dport 30746 -j KUBE-MARK-MASQ -A KUBE-NODEPORTS -p tcp -m comment --comment "default/web1:" -m tcp --dport 30746 -j KUBE-SVC-7YBM6NGNJ6RVLVXL |
CLUSTER-IP 訪問入口規則:
1
2
3
|
# iptables -S -t nat | grep 10.110.180.143 (cluster IP) -A KUBE-SERVICES ! -s 10.244.0.0/16 -d 10.110.180.143/32 -p tcp -m comment --comment "default/web1: cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ -A KUBE-SERVICES -d 10.110.180.143/32 -p tcp -m comment --comment "default/web1: cluster IP" -m tcp --dport 80 -j KUBE-SVC-7YBM6NGNJ6RVLVXL |
第二步:按概率(probability)分配
(web1有3個pod副本,負載規則如下)
1
2
3
4
5
6
7
|
# iptables -S -t nat | grep KUBE-SVC-7YBM6NGNJ6RVLVXL -N KUBE-SVC-7YBM6NGNJ6RVLVXL -A KUBE-NODEPORTS -p tcp -m comment --comment "default/web1:" -m tcp --dport 30746 -j KUBE-SVC-7YBM6NGNJ6RVLVXL -A KUBE-SERVICES -d 10.110.180.143/32 -p tcp -m comment --comment "default/web1: cluster IP" -m tcp --dport 80 -j KUBE-SVC-7YBM6NGNJ6RVLVXL -A KUBE-SVC-7YBM6NGNJ6RVLVXL -m comment --comment "default/web1:" -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-IXSCGLPBJ6ROUJNA -A KUBE-SVC-7YBM6NGNJ6RVLVXL -m comment --comment "default/web1:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-5MMDFR2YQS26JCDP (概論大,因為有兩個pod在同一節點) -A KUBE-SVC-7YBM6NGNJ6RVLVXL -m comment --comment "default/web1:" -j KUBE-SEP-DHYUVI6YLCFCEZBW |
第三步:根據分配轉發到實際pod中
1
2
3
4
5
6
7
8
9
10
11
12
|
# kubectl get ep NAME ENDPOINTS AGE kubernetes 192.168.40.132:6443 78d nginx-dns 10.244.166.145:80 49d web-service 10.244.166.139:80 49d web1 10.244.104.9:80,10.244.166.140:80,10.244.166.146:80 12h # iptables -S -t nat | grep KUBE-SEP-5MMDFR2YQS26JCDP -N KUBE-SEP-5MMDFR2YQS26JCDP -A KUBE-SEP-5MMDFR2YQS26JCDP -s 10.244.166.140/32 -m comment --comment "default/web1:" -j KUBE-MARK-MASQ -A KUBE-SEP-5MMDFR2YQS26JCDP -p tcp -m comment --comment "default/web1:" -m tcp -j DNAT --to-destination 10.244.166.140:80(ep,到達某一節點的具體pod中) -A KUBE-SVC-7YBM6NGNJ6RVLVXL -m comment --comment "default/web1:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-5MMDFR2YQS26JCDP |
三、kube-proxy 工作原理
kube-proxy當前實現了三種代理模式:userspace, iptables, ipvs
(1)userspace mode: userspace是在用戶空間,通過kube-proxy來實現service的代理服務
(2)iptables mode, 該模式完全利用內核iptables來實現service的代理和LB, 這是K8s在v1.2及之后版本默認模式
API Server 對內(集群中的其他組件)和對外(用戶)提供統一的 REST API,其他組件均通過 API Server 進行通信
Controller Manager、Scheduler、Kube-proxy 和 Kubelet 等均通過 API Server watch API 監測資源變化情況,并對資源作相應的操作
(3)ipvs mode. 在kubernetes 1.8以上的版本中,對于kube-proxy組件增加了除iptables模式和用戶模式之外還支持ipvs模式。
kube-proxy ipvs 是基于 NAT 實現的,通過ipvs的NAT模式,對訪問k8s service的請求進行虛IP到POD IP的轉發。當創建一個 service 后,kubernetes 會在每個節點上創建一個網卡,同時幫你將 Service IP(VIP) 綁定上,此時相當于每個 Node 都是一個 ds,而其他任何 Node 上的 Pod,甚至是宿主機服務(比如 kube-apiserver 的 6443)都可能成為 rs;
與iptables、userspace 模式一樣,kube-proxy 依然監聽Service以及Endpoints對象的變化, 不過它并不創建反向代理, 也不創建大量的 iptables 規則, 而是通過netlink 創建ipvs規則,并使用k8s Service與Endpoints信息,對所在節點的ipvs規則進行定期同步; netlink 與 iptables 底層都是基于 netfilter 鉤子,但是 netlink 由于采用了 hash table 而且直接工作在內核態,在性能上比 iptables 更優。其工作流程大體如下:
k8s的service和endpoine是如何關聯和相互影響的?
1、 api-server創建service對象,與service綁定的pod地址:稱之為endpoints(kubectl get ep可以查看)
2、服務發現方面:kube-proxy監控service后端endpoint的動態變化,并且維護service和endpoint的映射關系
kubernetes服務注冊(dns),服務發現是service
http://www.dockone.io/article/9936 有詳細講解
Kubernetes提供了兩種方式進行服務發現, 即環境變量和DNS
(1)環境變量: 當你創建一個Pod的時候,kubelet會在該Pod中注入集群內所有Service的相關環境變量。需要注意: 要想一個Pod中注入某個Service的環境變量,則必須Service要先比該Pod創建。這一點,幾乎使得這種方式進行服務發現不可用。比如,一個ServiceName為redis-master的Service,對應的ClusterIP:Port為172.16.50.11:6379,則其對應的環境變量為:
1
2
3
4
5
6
7
|
REDIS_MASTER_SERVICE_HOST=172.16.50.11 REDIS_MASTER_SERVICE_PORT=6379 REDIS_MASTER_PORT=tcp: //172.16.50.11:6379 REDIS_MASTER_PORT_6379_TCP=tcp: //172.16.50.11:6379 REDIS_MASTER_PORT_6379_TCP_PROTO=tcp REDIS_MASTER_PORT_6379_TCP_PORT=6379 REDIS_MASTER_PORT_6379_TCP_ADDR=172.16.50.11 |
(2)DNS:這是k8s官方強烈推薦的方式!!! 可以通過cluster add-on方式輕松的創建KubeDNS來對集群內的Service進行服務發現。
四、修改kube-proxy模式為ipvs(默認為iptables)
1、加載內核模塊
# lsmod | grep ip_vs
1
2
3
4
5
6
|
ip_vs_sh 12688 0 ip_vs_wrr 12697 0 ip_vs_rr 12600 0 ip_vs 145497 6 ip_vs_rr,ip_vs_sh,ip_vs_wrr nf_conntrack 139264 7 ip_vs,nf_nat,nf_nat_ipv4,xt_conntrack,nf_nat_masquerade_ipv4,nf_conntrack_netlink,nf_conntrack_ipv4 libcrc32c 12644 4 xfs,ip_vs,nf_nat,nf_conntrack |
2、升級內核模塊,ipvs對內核版本有要求
1
|
yum update -y |
3、查看kube-proxy組件
1
2
3
4
5
6
7
|
# kubectl get pod -n kube-system NAME READY STATUS RESTARTS AGE kube-proxy-5clwf 1/1 Running 29 78d kube-proxy-ht25x 1/1 Running 30 78d kube-proxy-x9ml8 1/1 Running 30 78d kube-scheduler-master 1/1 Running 49 78d metrics-server-584b5f4754-7vlhm 1/1 Running 56 78d |
4、產看kube-proxy的配置文件configmaps
1
2
3
4
5
6
7
8
|
# kubectl get configmaps -n kube-system NAME DATA AGE calico-config 4 78d coredns 1 78d extension-apiserver-authentication 6 78d kube-proxy 2 78d kubeadm-config 2 78d kubelet-config-1.18 1 78d |
5、編輯kube-proxy的configmaps
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# kubectl edit configmaps kube-proxy -n kube-system mode "ipvs" 刪除舊的kube-proxy,kubelet自動重新拉起,應用ipvs # kubectl delete pod kube-proxy-x9ml8 -n kube-system pod "kube-proxy-x9ml8" deleted [root@master ~]# kubectl delete pod kube-proxy-ht25x -n kube- system pod "kube-proxy-ht25x" deleted # kubectl logs kube-proxy-4cnbn -n kube-system I1015 14:03:08.402147 1 node.go:136] Successfully retrieved node IP: 192.168.40.132 I1015 14:03:08.402259 1 server_others.go:259] Using ipvs Proxier. W1015 14:03:08.402618 1 proxier.go:429] IPVS scheduler not specified, use rr by default |
6、安裝ipvsadm
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# yum install -y ipvsadm.x86_64 # ipvsadm -L -n IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 127.0.0.1:30564 rr (輪詢規則) -> 10.244.104.36:8080 Masq 1 0 0 -> 10.244.166.147:8080 Masq 1 0 0 TCP 127.0.0.1:32618 rr -> 10.244.104.20:80 Masq 1 0 0 TCP 172.17.0.1:30564 rr -> 10.244.104.36:8080 Masq 1 0 0 |
7、系統生成虛擬網卡kube-ipvs0
1
2
3
4
5
6
|
# ip a 6: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default link/ether a2:09:a4:9d:fb:f8 brd ff:ff:ff:ff:ff:ff inet 10.110.180.143/32 brd 10.110.180.143 scope global kube-ipvs0 valid_lft forever preferred_lft forever inet 10.110.180.143/32專門用于接收svc web1的請求 |
8、svc的流量被ipvs分發
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE web1 NodePort 10.110.180.143 <none> 80:30746/TCP 23h # curl 10.110.180.143 ... <title>Welcome to nginx!</title> ... # kubectl get ep NAME ENDPOINTS AGE kubernetes 192.168.40.132:6443 78d web1 10.244.104.15:80,10.244.166.143:80,10.244.166.148:80 23h ipvs捕捉到svc的流量 # ipvsadm -L -n TCP 10.110.180.143:80 rr -> 10.244.104.15:80 Masq 1 0 0 -> 10.244.166.143:80 Masq 1 0 0 -> 10.244.166.148:80 Masq 1 0 1 |