K8S(03)核心插件-Flannel網絡插件


系列文章說明

本系列文章,可以基本算是 老男孩2019年王碩的K8S周末班課程 筆記,根據視頻來看本筆記最好,否則有些地方會看不明白
需要視頻可以聯系我

K8S核心網絡插件Flannel

k8s雖然設計了網絡模型,然后將實現方式交給了CNI網絡插件,而CNI網絡插件的主要目的,就是實現POD資源能夠跨宿主機進行通信

常見的網絡插件有flannel,calico,canal,但是最簡單的flannel已經完全滿足我們的要求,故不在考慮其他網絡插件

網絡插件Flannel介紹:https://www.kubernetes.org.cn/3682.html

1 flannel功能概述

1.1 flannel運轉流程

  1. 首先
    flannel利用Kubernetes API或者etcd用於存儲整個集群的網絡配置,其中最主要的內容為設置集群的網絡地址空間。
    例如,設定整個集群內所有容器的IP都取自網段“10.1.0.0/16”。
  2. 接着
    flannel在每個主機中運行flanneld作為agent,它會為所在主機從集群的網絡地址空間中,獲取一個小的網段subnet,本主機內所有容器的IP地址都將從中分配。
    例如,設定本主機內所有容器的IP地址網段“10.1.2.0/24”。
  3. 然后
    flanneld再將本主機獲取的subnet以及用於主機間通信的Public IP,同樣通過kubernetes API或者etcd存儲起來。
  4. 最后
    flannel利用各種backend mechanism,例如udp,vxlan等等,跨主機轉發容器間的網絡流量,完成容器間的跨主機通信。

1.2 flannel的網絡模型

1.2.1 flannel支持3種網絡模型

  1. host-gw網關模型

    {"Network": "xxx", "Backend": {"Type": "host-gw"}}
    

    主要用於宿主機在同網段的情況下POD間的通信,即不跨網段通信.
    此時flannel的功能很簡單,就是在每個宿主機上創建了一條通網其他宿主機的網關路由
    完全沒有性能損耗,效率極高

  2. vxlan隧道模型

    {"Network": "xxx", "Backend": {"Type": "vxlan"}}
    

    主要用於宿主機不在同網段的情況下POD間通信,即跨網段通信.
    此時flannel會在宿主機上創建一個flannel.1的虛擬網卡,用於和其他宿主機間建立VXLAN隧道
    跨宿主機通信時,需要經由flannel.1設備封包、解包,因此效率不高

  3. 混合模型

    {"Network": "xxx", "Backend": {"Type": "vxlan","Directrouting": true}}
    

    在既有同網段宿主機,又有跨網段宿主機的情況下,選擇混合模式
    flannel會根據通信雙方的網段情況,自動選擇是走網關路由通信還是通過VXLAN隧道通信

1.2.2 實際工作中的模型選擇

很多人不推薦部署K8S的使用的flannel做網絡插件,不推薦的原因是是flannel性能不高,然而

  1. flannel性能不高是指它的VXLAN隧道模型,而不是gw模型
  2. 規划K8S集群的時候,應規划多個K8S集群來管理不同的業務
  3. 同一個K8S集群的宿主機,就應該規划到同一個網段
  4. 既然是同一個網段的宿主機通信,使用的就應該是gw模型
  5. gw模型只是創建了網關路由,通信效率極高
  6. 因此,建議工作中使用flannel,且用gw模型

2. 部署flannel插件

2.1 在etcd中寫入網絡信息

以下操作在任意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"}}

2.2 部署准備

2.2.1 下載軟件

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

2.2.2 拷貝證書

因為要和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/ 

2.2.3 配置子網信息

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子網網段信息,每個宿主機都要修改

2.3 啟動flannel服務

2.3.1 創建flannel啟動腳本

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,注意修改

2.3.2 創建supervisor啟動腳本

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二進制安裝中的備注

2.3.3 啟動flannel服務並驗證

啟動服務

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

3 優化iptables規則

3.1 前因后果

3.1.1 優化原因說明

我們使用的是gw網絡模型,而這個網絡模型只是創建了一條到其他宿主機下POD網絡的路由信息.
因而我們可以猜想:

  1. 從外網訪問到B宿主機中的POD,源IP應該是外網IP
  2. 從A宿主機訪問B宿主機中的POD,源IP應該是A宿主機的IP
  3. 從A的POD-A01中,訪問B中的POD,源IP應該是POD-A01的容器IP
    此情形可以想象是一個路由器下的2個不同網段的交換機下的設備通過路由器(gw)通信

然后遺憾的是:

  • 前兩條毫無疑問成立
  • 第3條理應成立,但實際不成立

不成立的原因是:

  1. Docker容器的跨網絡隔離與通信,借助了iptables的機制

  2. 因此雖然K8S我們使用了ipvs調度,但是宿主機上還是有iptalbes規則

  3. 而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
    
  4. 由於gw模式產生的數據,是從eth0流出,因而不在此規則過濾范圍內

  5. 就導致此跨宿主機之間的POD通信,使用了該條SNAT規則

解決辦法是:

  • 修改此IPTABLES規則,增加過濾目標:過濾目的地是宿主機網段的流量

3.1.2 問題復現

  1. 7-21宿主機中,訪問172.7.22.2

    curl 172.7.22.2
    
  2. 7-21宿主機啟動busybox容器,進入並訪問172.7.22.2

    docker pull busybox
    docker run --rm -it busybox bash
    / # wget 172.7.22.2
    
  3. 查看7-22宿主機上啟動的nginx容器日志

    [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

3.2 具體優化過程

3.2.1 先查看iptables規則

[root@hdss7-21 ~]# iptables-save |grep -i postrouting|grep docker0
-A POSTROUTING -s 172.7.21.0/24 ! -o docker0 -j MASQUERADE

3.2.2 安裝iptables並修改規則

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

3.2.3 注意docker重啟后操作

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

3.2.4 結果驗證

# 對端啟動容器並訪問
[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" "-"


免責聲明!

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



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