K8S核心網絡插件Flannel
目錄
- 系列文章說明
-
- 1 flannel功能概述
-
-
- 1.1 flannel運轉流程
- 1.2 flannel的網絡模型
-
-
-
-
- 1.2.1 flannel支援3種網絡模型
- 1.2.2 實際工作中的模型選擇
-
-
-
- 2. 部署flannel插件
-
-
- 2.1 在etcd中寫入網絡資訊
- 2.2 部署準備
-
-
-
-
- 2.2.1 下載下傳軟體
- 2.2.2 拷貝證書
- 2.2.3 配置子網資訊
-
-
-
-
- 2.3 啟動flannel服務
-
-
-
-
- 2.3.1 建立flannel啟動腳本
- 2.3.2 建立supervisor啟動腳本
- 2.3.3 啟動flannel服務并驗證
-
-
-
- 3 優化iptables規則
-
-
- 3.1 前因後果
-
-
-
-
- 3.1.1 優化原因說明
- 3.1.2 問題複現
-
-
-
-
- 3.2 具體優化過程
-
-
-
-
- 3.2.1 先檢視iptables規則
- 3.2.2 安裝iptables并修改規則
- 3.2.3 注意docker重新開機後操作
- 3.2.4 結果驗證
-
-
k8s雖然設計了網絡模型,然後将實作方式交給了CNI網絡插件,而CNI網絡插件的主要目的,就是實作POD資源能夠跨主控端進行通信
常見的網絡插件有
flannel
,
calico
canal
,但是最簡單的
flannel
已經完全滿足我們的要求,故不在考慮其他網絡插件
網絡插件Flannel介紹:
https://www.kubernetes.org.cn/3682.html-
首先
flannel利用Kubernetes API或者etcd用于存儲整個叢集的網絡配置,其中最主要的内容為設定叢集的網絡位址空間。
例如,設定整個叢集内所有容器的IP都取自網段“10.1.0.0/16”。
-
接着
flannel在每個主機中運作flanneld作為agent,它會為所在主機從叢集的網絡位址空間中,擷取一個小的網段subnet,本主機内所有容器的IP位址都将從中配置設定。
例如,設定本主機内所有容器的IP位址網段“10.1.2.0/24”。
-
然後
flanneld再将本主機擷取的subnet以及用于主機間通信的Public IP,同樣通過kubernetes API或者etcd存儲起來。
-
最後
flannel利用各種backend mechanism,例如udp,vxlan等等,跨主機轉發容器間的網絡流量,完成容器間的跨主機通信。
-
網關模型host-gw
{"Network": "xxx", "Backend": {"Type": "host-gw"}}
主要用于主控端不在同網段的情況下POD間通信,即跨網段通信.
此時flannel會在主控端上建立一個flannel.1的虛拟網卡,用于和其他主控端間建立VXLAN隧道
跨主控端通信時,需要經由flannel.1裝置封包、解包,是以效率不高
2混合模型
{"Network": "xxx", "Backend": {"Type": "vxlan","Directrouting": true}}
-
在既有同網段主控端,又有跨網段主控端的情況下,選擇混合模式
flannel會根據通信雙方的網段情況,自動選擇是走網關路由通信還是通過VXLAN隧道通信
很多人不推薦部署K8S的使用的flannel做網絡插件,不推薦的原因是是flannel性能不高,然而
- flannel性能不高是指它的VXLAN隧道模型,而不是gw模型
- 規劃K8S叢集的時候,應規劃多個K8S叢集來管理不同的業務
- 同一個K8S叢集的主控端,就應該規劃到同一個網段
- 既然是同一個網段的主控端通信,使用的就應該是gw模型
- gw模型隻是建立了網關路由,通信效率極高
- 是以,建議工作中使用flannel,且用
模型gw
以下操作在任意etcd節點中執行都可以
/opt/etcd/etcdctl set /coreos.com/network/config '{"Network": "172.7.0.0/16", "Backend": {"Type": "host-gw"}}'
# 檢視結果
[root@hdss7-12 ~]# /opt/etcd/etcdctl get /coreos.com/network/config
{"Network": "172.7.0.0/16", "Backend": {"Type": "host-gw"}}
wget https://github.com/coreos/flannel/releases/download/v0.11.0/flannel-v0.11.0-linux-amd64.tar.gz
mkdir /opt/flannel-v0.11.0
tar xf flannel-v0.11.0-linux-amd64.tar.gz -C /opt/flannel-v0.11.0/
ln -s /opt/flannel-v0.11.0/ /opt/flannel
因為要和apiserver通信,是以要配置client證書,當然ca公鑰自不必說
cd /opt/flannel
mkdir cert
scp hdss7-200:/opt/certs/ca.pem cert/
scp hdss7-200:/opt/certs/client.pem cert/
scp hdss7-200:/opt/certs/client-key.pem cert/
cat >/opt/flannel/subnet.env <<EOF
FLANNEL_NETWORK=172.7.0.0/16
FLANNEL_SUBNET=172.7.21.1/24
FLANNEL_MTU=1500
FLANNEL_IPMASQ=false
EOF
注意:subnet子網網段資訊,每個主控端都要修改
cat >/opt/flannel/flanneld.sh <<'EOF'
#!/bin/sh
./flanneld \
--public-ip=10.4.7.21 \
--etcd-endpoints=https://10.4.7.12:2379,https://10.4.7.21:2379,https://10.4.7.22:2379 \
--etcd-keyfile=./cert/client-key.pem \
--etcd-certfile=./cert/client.pem \
--etcd-cafile=./cert/ca.pem \
--iface=eth0 \
--subnet-file=./subnet.env \
--healthz-port=2401
EOF
# 授權
chmod u+x flanneld.sh
注意:
public-ip為節點IP,注意按需修改
iface為網卡,若本機網卡不是eth0,注意修改
cat >/etc/supervisord.d/flannel.ini <<EOF
[program:flanneld]
command=sh /opt/flannel/flanneld.sh
numprocs=1
directory=/opt/flannel
autostart=true
autorestart=true
startsecs=30
startretries=3
exitcodes=0,2
stopsignal=QUIT
stopwaitsecs=10
user=root
redirect_stderr=true
stdout_logfile=/data/logs/flanneld/flanneld.stdout.log
stdout_logfile_maxbytes=64MB
stdout_logfile_backups=4
stdout_capture_maxbytes=1MB
;子程序還有子程序,需要添加這個參數,避免産生孤兒程序
killasgroup=true
stopasgroup=true
EOF
supervisor的各項配置不再備注,有需要的看K8S二進制安裝中的備注
啟動服務
mkdir -p /data/logs/flanneld
supervisorctl update
supervisorctl status
驗證路由
[root@hdss7-22 ~]# route -n|egrep -i '172.7|des'
Destination Gateway Genmask Flags Metric Ref Use Iface
172.7.21.0 10.4.7.21 255.255.255.0 UG 0 0 0 eth0
172.7.22.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0
[root@hdss7-21 ~]# route -n|egrep -i '172.7|des'
Destination Gateway Genmask Flags Metric Ref Use Iface
172.7.21.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0
172.7.22.0 10.4.7.22 255.255.255.0 UG 0 0 0 eth0
驗證通信結果
[root@hdss7-21 ~]# ping 172.7.22.2
PING 172.7.22.2 (172.7.22.2) 56(84) bytes of data.
64 bytes from 172.7.22.2: icmp_seq=1 ttl=63 time=0.538 ms
64 bytes from 172.7.22.2: icmp_seq=2 ttl=63 time=0.896 ms
[root@hdss7-22 ~]# ping 172.7.21.2
PING 172.7.21.2 (172.7.21.2) 56(84) bytes of data.
64 bytes from 172.7.21.2: icmp_seq=1 ttl=63 time=0.805 ms
64 bytes from 172.7.21.2: icmp_seq=2 ttl=63 time=1.14 ms
優化iptables規則
我們使用的是
gw
網絡模型,而這個網絡模型隻是建立了一條到其他主控端下POD網絡的路由資訊.
因而我們可以猜想:
- 從外網通路到B主控端中的POD,源IP應該是外網IP
- 從A主控端通路B主控端中的POD,源IP應該是A主控端的IP
-
從A的POD-A01中,通路B中的POD,源IP應該是POD-A01的容器IP
此情形可以想象是一個路由器下的2個不同網段的交換機下的裝置通過路由器(gw)通信
然後遺憾的是:
- 前兩條毫無疑問成立
- 第3條理應成立,但實際不成立
不成立的原因是:
- Docker容器的跨網絡隔離與通信,借助了iptables的機制
- 是以雖然K8S我們使用了ipvs排程,但是主控端上還是有iptalbes規則
-
而docker預設生成的iptables規則為:
若資料出網前,先判斷出網裝置是不是本機
docker0
裝置(容器網絡)
如果不是的話,則進行SNAT轉換後再出網,具體規則如下
[root@hdss7-21 ~]# iptables-save |grep -i postrouting|grep docker0
-A POSTROUTING -s 172.7.21.0/24 ! -o docker0 -j MASQUERADE
- 由于
模式産生的資料,是從gw
流出,因而不在此規則過濾範圍内eth0
- 就導緻此跨主控端之間的POD通信,使用了該條SNAT規則
解決辦法是:
- 修改此IPTABLES規則,增加過濾目标:過濾目的地是主控端網段的流量
- 在
主控端中,通路7-21
172.7.22.2
curl 172.7.22.2
-
主控端啟動busybox容器,進入并通路7-21
172.7.22.2
docker pull busybox
docker run --rm -it busybox bash
/ # wget 172.7.22.2
- 檢視
主控端上啟動的nginx容器日志7-22
[root@hdss7-22 ~]# kubectl logs nginx-ds-j777c --tail=2
10.4.7.21 - - [xxx] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.4.7.21 - - [xxx] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
-
第一條日志為對端主控端通路日志
第二條日志為對端容器通路日志
可以看出源IP都是主控端的IP
[root@hdss7-21 ~]# iptables-save |grep -i postrouting|grep docker0
-A POSTROUTING -s 172.7.21.0/24 ! -o docker0 -j MASQUERADE
yum install iptables-services -y
iptables -t nat -D POSTROUTING -s 172.7.21.0/24 ! -o docker0 -j MASQUERADE
iptables -t nat -I POSTROUTING -s 172.7.21.0/24 ! -d 172.7.0.0/16 ! -o docker0 -j MASQUERADE
# 驗證規則并儲存配置
[root@hdss7-21 ~]# iptables-save |grep -i postrouting|grep docker0
-A POSTROUTING -s 172.7.21.0/24 ! -d 172.7.0.0/16 ! -o docker0 -j MASQUERADE
[root@hdss7-21 ~]# iptables-save > /etc/sysconfig/iptables
docker服務重新開機後,會再次增加該規則,要注意在每次重新開機docker服務後,删除該規則
驗證:
修改後會影響到docker原本的iptables鍊的規則,是以需要重新開機docker服務
[root@hdss7-21 ~]# systemctl restart docker
[root@hdss7-21 ~]# iptables-save |grep -i postrouting|grep docker0
-A POSTROUTING -s 172.7.21.0/24 ! -o docker0 -j MASQUERADE
-A POSTROUTING -s 172.7.21.0/24 ! -d 172.7.0.0/16 ! -o docker0 -j MASQUERADE
# 可以用iptables-restore重新應用iptables規則,也可以直接再删
[root@hdss7-21 ~]# iptables-restore /etc/sysconfig/iptables
[root@hdss7-21 ~]# iptables-save |grep -i postrouting|grep docker0
-A POSTROUTING -s 172.7.21.0/24 ! -d 172.7.0.0/16 ! -o docker0 -j MASQUERADE
# 對端啟動容器并通路
[root@hdss7-21 ~]# docker run --rm -it busybox sh
/ # wget 172.7.22.2
# 本端驗證日志
[root@hdss7-22 ~]# kubectl logs nginx-ds-j777c --tail=1
172.7.21.3 - - [xxxx] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"