1. 程式人生 > >k8s技術預研12--kubernetes的常見開源網路元件

k8s技術預研12--kubernetes的常見開源網路元件

k8s的網路模型假定了所有Pod都在一個可以直接連通的扁平的網路空間中。這是因為k8s出自Google,而在GCE裡面是提供了網路模型作為基礎設施的,所以k8s就假定這個網路已經存在。而在大傢俬有的平臺設施裡搭建k8s叢集,就不能假定這種網路已經存在了。我們需要自己實現這個網路,將不同節點上的Docker容器之間的互相訪問先打通,然後執行k8s。

目前已經有多個開源元件支援容器網路模型。本節介紹幾個常見的網路元件及其安裝配置方法,包括Flannel、Open vSwitch、直接路由和Calico。

1. Flannel

1.1 Flannel通訊原理

Flannel之所以可以搭建k8s依賴的底層網路,是因為它能實現以下兩點。(1)它能協助k8s,給每一個Node上的Docker容器分配互相不衝突的IP地址。(2)它能在這些IP地址之間建立一個覆蓋網路(Overlay Network),通過這個覆蓋網路,將資料包原封不動地傳遞到目標容器內。我們通過下圖來看看Flannel是如何實現這兩點的。

可以看到,Flannel首先建立了一個名為flannel0的網橋,這個網橋的一端連線docker0網橋,另一端連線一個叫作flanneld的服務程序。flanneld程序很重要:
  • flanneld首先要連線etcd,利用etcd來管理可分配的IP地址段資源,同時監控etcd中每個Pod的實際地址,並在記憶體中建立了一個Pod節點路由表;
  • 然後flanneld程序下連docker0和物理網路,使用記憶體中的Pod節點路由表,將docker0發給它的資料包包裝起來,利用物理網路的連線將資料包投遞到目標flanneld上,從而完成Pod到Pod之間的直接地址通訊。
Flannel之間的底層通訊協議的可選餘地很多,有UDP, VxLAN, AWS VPC等多種方式,只要能通到對端的Flannel就可以了。源flanneld加包,目標flanneld解包,最終docker0看到的就是原始的資料,非常透明,根本感覺不到中間Flannel的存在。常用的是UDP。
Flannel是如何做到為不同Node上的Pod分配IP且不產生衝突的?因為Flannel使用集中的etcd服務管理這些地址資源資訊,它每次分配的地址段都在同一個公共區域獲取,這樣自然能隨時協調,避免衝突了。在Flannel分配好地址段後,接下來的工作就轉交給Docker完成了。Flannel通過修改Docker的啟動引數將分配給它的地址段傳遞進去。--bip=172.17.18.1/24通過這些操作,Flannel就控制了每個Node節點上的docker0地址段的地址,也能保障所有Pod的IP地址在同一水平的網路中且不產生衝突了。Flannel完美地解決了對k8s網路的支援,但是它引入了多個網路元件,在網路通訊時需要轉到flannel0網路介面,再轉到使用者態的flanneld程式,到對端後還需要走這個過程的反過程,所以會引入一些網路的延時消耗。另外,Flannel模型預設使用了UDP作為底層傳輸協議,UDP協議本身的非可靠性,在大流量、高併發應用場景下還需要反覆測試,確保沒有問題。

1.2 Flannel的安裝和配置方法

1)安裝etcd由於Flannel使用etcd作為資料庫,所以需要預先安裝好,這裡不做描述。2)安裝Flannel需要在每臺Node上都安裝Flannel。Flannel軟體的下載地址為:https://github.com/coreos/flannel/releases 。將下載好的flannel-<version>-linux-amd64.tar.gz解壓,把二進位制檔案flanneld和mk-docker-opts.sh複製到/usr/bin中,即可完成對Flannel的安裝。3)配置Flannel此處以使用systemd系統為例對flanneld服務進行配置。編輯服務配置檔案/usr/lib/systemd/system/flanneld.service:[[email protected] sysconfig]# more /usr/lib/systemd/system/flanneld.service[Unit]Description=flanneld overlay address etcd agentAfter=network.targetBefore=docker.service[Service]Type=notifyEnvironmentFile=/etc/sysconfig/flannelExecStart=/usr/bin/flanneld -etcd-endpoints=http://10.0.2.15:2379 $FLANNEL_OPTIONS[Install]RequiredBy=docker.serviceWantedBy=multi-user.target編輯配置檔案/etc/sysconfig/flannel,設定etcd的URL地址:[[email protected] sysconfig]# more flannel# flanneld configuration options# etcd url location. Point this to the server where etcd runsFLANNEL_ETCD="http://10.0.2.15:2379"# etcd config key. This is the configuration key that flannel queries# For address range assignment在啟動flanneld服務之前,需要在etcd中新增一條網路配置記錄,這個配置將用於flanneld分配給每個Docker的虛擬IP地址段。[[email protected] ~]# etcdctl set /coreos.com/network/config '{ "Network": "172.16.0.0/16" }'{ "Network": "172.16.0.0/16" }由於Flannel將覆蓋docker0網橋,所以如果Docker服務已啟動,則需要停止Docker服務。4)啟動Flannel服務systemctl daemon-reloadsystemctl restart flanneld5)重新啟動Docker服務systemctl daemon-reloadsystemctl restart docker6)設定docker0網橋的IP地址mk-docker-opts.sh -isource /run/flannel/subnet.envifconfig docker0 ${FLANNEL_SUBNET}完成後確認網路介面docker0的IP屬於flannel0的子網:[[email protected] system]# ip a1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00    inet 127.0.0.1/8 scope host lo       valid_lft forever preferred_lft forever    inet6 ::1/128 scope host       valid_lft forever preferred_lft forever2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000    link/ether 08:00:27:9f:89:14 brd ff:ff:ff:ff:ff:ff    inet 10.0.2.4/24 brd 10.0.2.255 scope global noprefixroute dynamic enp0s3       valid_lft 993sec preferred_lft 993sec    inet6 fe80::a00:27ff:fe9f:8914/64 scope link       valid_lft forever preferred_lft forever3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default    link/ether 02:42:c9:52:3d:15 brd ff:ff:ff:ff:ff:ff    inet 172.16.70.1/24 brd 172.16.70.255 scope global docker0       valid_lft forever preferred_lft forever    inet6 fe80::42:c9ff:fe52:3d15/64 scope link       valid_lft forever preferred_lft forever6: flannel0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1472 qdisc pfifo_fast state UNKNOWN group default qlen 500    link/none    inet 172.16.70.0/16 scope global flannel0       valid_lft forever preferred_lft forever    inet6 fe80::4b31:c92f:8cc9:3a22/64 scope link flags 800       valid_lft forever preferred_lft forever[[email protected] system]#至此,就完成了Flannel覆蓋網路的設定。使用ping命令驗證各Node上docker0之間的相互訪問。例如在Node1(docker0 IP=172.16.70.1)機器上ping Node2的docker0(docker0 IP=172.16.13.1),通過Flannel能夠成功連線到其他物理機的Docker網路:[[email protected] system]# ifconfig flannel0flannel0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1472        inet 172.16.70.0  netmask 255.255.0.0  destination 172.16.70.0        inet6 fe80::524a:4b9c:3391:7514  prefixlen 64  scopeid 0x20<link>        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 500  (UNSPEC)        RX packets 5  bytes 420 (420.0 B)        RX errors 0  dropped 0  overruns 0  frame 0        TX packets 8  bytes 564 (564.0 B)        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0[[email protected] system]# ifconfig docker0docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500        inet 172.16.70.1  netmask 255.255.255.0  broadcast 172.16.70.255        inet6 fe80::42:c9ff:fe52:3d15  prefixlen 64  scopeid 0x20<link>        ether 02:42:c9:52:3d:15  txqueuelen 0  (Ethernet)        RX packets 0  bytes 0 (0.0 B)        RX errors 0  dropped 0  overruns 0  frame 0        TX packets 8  bytes 648 (648.0 B)        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0[[email protected] system]# ping 172.16.13.1PING 172.16.13.1 (172.16.13.1) 56(84) bytes of data.64 bytes from 172.16.13.1: icmp_seq=1 ttl=62 time=1.63 ms64 bytes from 172.16.13.1: icmp_seq=2 ttl=62 time=1.55 ms^C--- 172.16.13.1 ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 1002msrtt min/avg/max/mdev = 1.554/1.595/1.637/0.057 ms我們可以在etcd中檢視到Flannel設定的flannel0地址與物理機IP地址的對應規則:

2 Open vSwitch

2.1 基本原理

Open vSwitch是一個開源的虛擬交換軟體,有點兒像Linux中的bridge,但是功能要複雜得多。Open vSwitch的網橋可以直接建立多種通訊通道(隧道),例如Open vSwitch with GRE/VxLAN。這些通道的建立可以很容易地通過OVS的配置命令實現。在k8s、Docker場景下,我們主要是建立L3到L3的隧道,例如下面樣子的網路架構。
首先,為了避免Docker建立的docker0地址產生衝突,我們需要手動配置和指定下各個Node節點上docker0網橋的地址段分佈。其次,建立Open vSwitch的網橋ovs,然後使用ovs-vsctl命令給ovs網橋增加gre埠,新增gre埠時要將目標連線的NodeIP地址設定為對端的IP地址。對每一個對端IP地址都需要這麼操作(對於大型網路,需要做自動化指令碼來完成)。最後,將ovs的網橋作為網路介面,加入Docker的網橋上。重啟ovs網橋和Docker的網橋,並新增一個Docker的地址段到Docker網橋的路由規則項,就可以將兩個容器的網路連線起來了。

2.2 網路通訊過程

當容器內的應用訪問另一個容器的地址時,資料包會通過容器內的預設路由傳送給docker0網橋。ovs的網橋是作為docker0網橋的埠存在的,安會將資料傳送給ovs網橋。ovs網路已經通過配置建立了和其他ovs網橋的GRE/VxLAN隧道,自然能將資料送達對端的Node,並送往docker0及Pod。通過新增的路由項,使用得Node節點本身的應用的資料也路由到docker0網橋上,和剛才的通訊過程一樣,自然也可以訪問其他Node上的Pod。

2.3 OVS with GRE/VxLAN組網方式的特點

OVS的優勢是,作為開源虛擬交換機軟體,它相對成熟和穩定,支援各類網路隧道協議,經過了OpenStack等專案的考驗。另一方面,相對於Flannel不但可以建立OverlayNetwork,實現Pod到Pod的通訊,還和k8s、Docker架構體系緊密結合,感知k8s的Service,動態維護自己的路由表,還通過etcd來協助Docker對整個k8s叢集中的docker0的子網地址進行分配。使用OVS時,很多事情就需要手工完成了。此外,無外是OVS,還是Flannel,通過建立Overlay Network,實現Pod到Pod的通訊,都會引入一些額外的通訊開銷。如果是對網路依賴特別重的應用,則需要評估對業務的影響。

2.4 Open vSwitch的安裝與配置

以兩個Node為例,目標網路拓撲如下圖所示。
1)在兩個Node上安裝ovs需要確認下關閉了Node節點上的selinux。同時在兩個Node節點上:yum -y install openvswitch檢視Open vSwitch服務狀態,需要有ovsdb-server與ovs-vswitchd兩個程序。[[email protected] system]# systemctl start openvswitch[[email protected] system]# systemctl status openvswitch● openvswitch.service - Open vSwitch   Loaded: loaded (/usr/lib/systemd/system/openvswitch.service; disabled; vendor preset: disabled)   Active: active (exited) since Sun 2018-06-10 17:06:40 CST; 6s ago  Process: 8368 ExecStart=/bin/true (code=exited, status=0/SUCCESS)Main PID: 8368 (code=exited, status=0/SUCCESS)Jun 10 17:06:40 k8s-node2.test.com systemd[1]: Starting Open vSwitch...Jun 10 17:06:40 k8s-node2.test.com systemd[1]: Started Open vSwitch.[[email protected] system]# ps -ef|grep ovsroot      8352     1  0 17:06 ?        00:00:00 ovsdb-server: monitoring pid 8353 (healthy)root      8353  8352  0 17:06 ?        00:00:00 ovsdb-server /etc/openvswitch/conf.db -vconsole:emer -vsyslog:err -vfile:info --remote=punix:/var/run/openvswitch/db.sock --private-key=db:Open_vSwitch,SSL,private_key --certificate=db:Open_vSwitch,SSL,certificate --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert --no-chdir --log-file=/var/log/openvswitch/ovsdb-server.log --pidfile=/var/run/openvswitch/ovsdb-server.pid --detach --monitorroot      8364     1  0 17:06 ?        00:00:00 ovs-vswitchd: monitoring pid 8365 (healthy)root      8365  8364  0 17:06 ?        00:00:00 ovs-vswitchd unix:/var/run/openvswitch/db.sock -vconsole:emer -vsyslog:err -vfile:info --mlockall --no-chdir --log-file=/var/log/openvswitch/ovs-vswitchd.log --pidfile=/var/run/openvswitch/ovs-vswitchd.pid --detach --monitor2)建立網橋和GRE隧道接下來需要在每個Node上建立ovs的網橋br0,然後在網橋上建立一個GRE隧道連線對端的網橋,最後把ovs的網橋br0作為一個埠連線到docker0這個Linux網橋上。這樣一來,兩個節點機器上的docker0網段就能互通了。以Node1節點為例,具體操作步驟如下:(1)建立ovs網橋[[email protected] system]# ovs-vsctl add-br br0(2)建立GRE隧道連線對端,remote_ip為對端的eth0網絡卡地址[[email protected] system]# ovs-vsctl add-port br0 gre1 -- set interface gre1 type=gre option:remote_ip=10.0.2.5(3)新增br0到本地docker0,使得容器流量通過OVS流經tunnel[[email protected] system]# brctl addif docker0 br0(4)啟動br0與docker0網橋[[email protected] system]# ip link set dev br0 up[[email protected] system]# ip link set dev docker0 up(5)新增路由規則由於10.0.2.5與10.0.2.4的docker0網段分別為172.16.20.0/24與172.16.10.0/24,這兩個網段的路由都需要經過本機的docker0網橋路由,其中一個24網段是通過OVS的GRE隧道到達對端的。因此需要在每個Node上新增通過docker0網橋轉發的172.16.0.0/16的路由規則:[[email protected] system]# ip route add 172.16.0.0/16 dev docker0(6)清空Docker自帶的iptables規則及Linux的規則,後者存在拒絕icmp報文通過防火牆的規則[[email protected] system]# iptables -t nat -F[[email protected] system]# iptables -F在Node1節點上完成以上操作後,在Node2節點上進行相同的配置。配置完成後,Node1節點的IP地址、docker0的IP地址及路由等重要資訊顯示如下:[[email protected] system]# ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00    inet 127.0.0.1/8 scope host lo       valid_lft forever preferred_lft forever    inet6 ::1/128 scope host       valid_lft forever preferred_lft forever2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000    link/ether 08:00:27:9f:89:14 brd ff:ff:ff:ff:ff:ff    inet 10.0.2.4/24 brd 10.0.2.255 scope global noprefixroute dynamic enp0s3       valid_lft 842sec preferred_lft 842sec    inet6 fe80::a00:27ff:fe9f:8914/64 scope link       valid_lft forever preferred_lft forever3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default    link/ether 02:42:c9:52:3d:15 brd ff:ff:ff:ff:ff:ff    inet 172.16.10.1/24 brd 172.16.10.255 scope global docker0       valid_lft forever preferred_lft forever    inet6 fe80::42:c9ff:fe52:3d15/64 scope link       valid_lft forever preferred_lft forever10: ovs-system: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000    link/ether 5e:a9:02:75:aa:98 brd ff:ff:ff:ff:ff:ff11: br0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UNKNOWN group default qlen 1000    link/ether 82:e3:9a:29:3c:46 brd ff:ff:ff:ff:ff:ff    inet6 fe80::a8de:24ff:fef4:f8ec/64 scope link       valid_lft forever preferred_lft forever12: [email protected]: <NOARP> mtu 1476 qdisc noop state DOWN group default qlen 1000    link/gre 0.0.0.0 brd 0.0.0.013: [email protected]: <BROADCAST,MULTICAST> mtu 1462 qdisc noop state DOWN group default qlen 1000    link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff14: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65490 qdisc pfifo_fast master ovs-system state UNKNOWN group default qlen 1000    link/ether 76:53:6f:11:e0:f8 brd ff:ff:ff:ff:ff:ff    inet6 fe80::7453:6fff:fe11:e0f8/64 scope link       valid_lft forever preferred_lft forever[[email protected] system]#[[email protected] system]# ip routedefault via 10.0.2.1 dev enp0s3 proto dhcp metric 10010.0.2.0/24 dev enp0s3 proto kernel scope link src 10.0.2.4 metric 100172.16.0.0/16 dev docker0 scope link172.16.10.0/24 dev docker0 proto kernel scope link src 172.16.10.13)兩個Node上容器之間的互通測試[[email protected] system]# ping 172.16.20.1PING 172.16.20.1 (172.16.20.1) 56(84) bytes of data.64 bytes from 172.16.20.1: icmp_seq=1 ttl=64 time=2.39 ms64 bytes from 172.16.20.1: icmp_seq=2 ttl=64 time=3.36 ms^C--- 172.16.20.1 ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 1004msrtt min/avg/max/mdev = 2.398/2.882/3.366/0.484 ms[[email protected] system]#在Node2上抓包觀察:[[email protected] system]# tcpdump -i docker0 -nnntcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes23:43:59.020039 IP 172.16.10.1 > 172.16.20.1: ICMP echo request, id 20831, seq 26, length 6423:43:59.020096 IP 172.16.20.1 > 172.16.10.1: ICMP echo reply, id 20831, seq 26, length 6423:44:00.020899 IP 172.16.10.1 > 172.16.20.1: ICMP echo request, id 20831, seq 27, length 6423:44:00.020939 IP 172.16.20.1 > 172.16.10.1: ICMP echo reply, id 20831, seq 27, length 6423:44:01.021706 IP 172.16.10.1 > 172.16.20.1: ICMP echo request, id 20831, seq 28, length 6423:44:01.021750 IP 172.16.20.1 > 172.16.10.1: ICMP echo reply, id 20831, seq 28, length 64接下來我們從前面曾做過的實驗中找出來一份建立2例項的RC資原始檔來,實際建立兩個容器來測試下兩個Pods間的網路通訊:[[email protected] ~]# more frontend-rc.yamlapiVersion: v1kind: ReplicationControllermetadata:  name: frontend  labels:    name: frontendspec:   replicas: 2   selector:     name: frontend   template:     metadata:       labels:         name: frontend     spec:       containers:       - name: php-redis         image: kubeguide/guestbook-php-frontend         ports:         - containerPort: 80           hostPort: 80         env:         - name: GET_HOSTS_FROM           value: env[[email protected] ~]#建立並觀察下結果:[[email protected] ~]# kubectl get rcNAME       DESIRED   CURRENT   READY     AGEfrontend   2         2         2         33m[[email protected] ~]# kubectl get pods -o wideNAME             READY     STATUS    RESTARTS   AGE       IP            NODEfrontend-b6krg   1/1       Running   1          33m       172.16.20.2   10.0.2.5frontend-qk6zc   1/1       Running   0          33m       172.16.10.2   10.0.2.4我們繼續登入進入Node1節點上的容器內部:[[email protected] ~]# kubectl exec -it frontend-qk6zc -c php-redis /bin/bash[email protected]:/var/www/html# ip a1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00    inet 127.0.0.1/8 scope host lo       valid_lft forever preferred_lft forever2: [email protected]: <NOARP> mtu 1476 qdisc noop state DOWN group default qlen 1000    link/gre 0.0.0.0 brd 0.0.0.03: [email protected]: <BROADCAST,MULTICAST> mtu 1462 qdisc noop state DOWN group default qlen 1000    link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff22: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default    link/ether 02:42:ac:10:0a:02 brd ff:ff:ff:ff:ff:ff    inet 172.16.10.2/24 brd 172.16.10.255 scope global eth0       valid_lft forever preferred_lft forever[email protected]:/var/www/html#從Node1上執行的Pod中ping一個Nod2上執行的Pod的地址:[email protected]:/var/www/html# ping 172.16.20.2PING 172.16.20.2 (172.16.20.2): 56 data bytes64 bytes from 172.16.20.2: icmp_seq=0 ttl=63 time=2017.587 ms64 bytes from 172.16.20.2: icmp_seq=1 ttl=63 time=1014.193 ms64 bytes from 172.16.20.2: icmp_seq=2 ttl=63 time=13.232 ms64 bytes from 172.16.20.2: icmp_seq=3 ttl=63 time=1.122 ms64 bytes from 172.16.20.2: icmp_seq=4 ttl=63 time=1.379 ms64 bytes from 172.16.20.2: icmp_seq=5 ttl=63 time=1.474 ms64 bytes from 172.16.20.2: icmp_seq=6 ttl=63 time=1.371 ms64 bytes from 172.16.20.2: icmp_seq=7 ttl=63 time=1.583 ms^C--- 172.16.20.2 ping statistics ---8 packets transmitted, 8 packets received, 0% packet lossround-trip min/avg/max/stddev = 1.122/381.493/2017.587/701.350 ms[email protected]:/var/www/html#在Node2節點上抓包看到資料包互動:[[email protected] system]# tcpdump -i docker0 -nnntcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes00:13:18.601908 IP 172.16.10.2 > 172.16.20.2: ICMP echo request, id 38, seq 4, length 6400:13:18.601947 IP 172.16.20.2 > 172.16.10.2: ICMP echo reply, id 38, seq 4, length 6400:13:18.601956 IP 172.16.20.2 > 172.16.10.2: ICMP echo reply, id 38, seq 4, length 6400:13:28.609109 IP 172.16.10.2 > 172.16.20.2: ICMP echo request, id 38, seq 5, length 6400:13:28.609165 IP 172.16.20.2 > 172.16.10.2: ICMP echo reply, id 38, seq 5, length 6400:13:28.609179 IP 172.16.20.2 > 172.16.10.2: ICMP echo reply, id 38, seq 5, length 6400:13:29.612564 IP 172.16.10.2 > 172.16.20.2: ICMP echo request, id 38, seq 6, length 64注:如果以上網路通訊測試沒有完全成功,不妨檢查下Node節點上的firewalld防火牆配置。至此,基於OVS的網路搭建成功,由於GRE是點對點的隧道通訊方式,所以如果有多個Node,則需要建立N*(N-1)條GRE隧道,即所有Node組成一個網狀的網路,以實現全網互通。

3 直接路由

我們在前幾節的實驗中已經測試過通過直接手動寫路由的方式,實現Node之間的網路通訊功能了,配置方法不再討論。該直接路由配置方法的問題是,在叢集節點發生變化時,需要手動去維護每個Node上的路由表資訊,效率很低。為了有效管理這些動態變化的網路路由資訊,動態地讓其他Node都感知到,就需要使用動態路由發現協議來同步這些變化。在實現這些動態路由發現協議的開源軟體中,常用的有Quagga、Zebra等。下面簡單介紹下配置步驟和注意事項。(1)仍然需要手動分配每個Node節點上的Docker bridge的地址段無論是修改預設的docker0使用的地址段,還是另建一個bridge並使用--bridge=XX來指定使用的網橋,都需要確保每個Node上Docker網橋使用的地址段不能重疊。(2)然後在每個Node上執行Quagga既可以選擇在每臺伺服器上安裝Quagga軟體並啟動,也可以使用Quagga容器來執行。在每臺Node上下載Docker映象:# docker pull georce/router在每臺Node上啟動Quagga容器,需要說明的是,Quagga需要以--privileged特權模式執行,並且指定--net=host,表示直接使用物理機的網路。# docker run -itd --name=router --privileged --net=host georce/router啟動成功後,各Node上的Quagga會相互學習來完成到其他機器的docker0路由規則的新增。至此,所有Node上的docker0都可以互聯互通了。注:如果叢集規模在數千臺Node以上,則需要測試和評估路由表的效率問題。

4 Calico容器網路和網路策略

4.1 Calico簡介

Calico 是容器網路的又一種解決方案,和其他虛擬網路最大的不同是,它沒有采用 overlay 網路做報文的轉發,提供了純 3 層的網路模型。三層通訊模型表示每個容器都通過 IP 直接通訊,中間通過路由轉發找到對方。在這個過程中,容器所在的節點類似於傳統的路由器,提供了路由查詢的功能。要想路由工作能夠正常,每個虛擬路由器(容器所在的主機節點)必須有某種方法知道整個叢集的路由資訊,calico 採用的是 BGP 路由協議,全稱是 Border Gateway Protocol。除了能用於 容器叢集平臺 kubernetes、共有云平臺 AWS、GCE 等, 也能很容易地整合到 openstack 等 Iaas 平臺。Calico在每個計算節點利用Linux Kernel實現了一個高效的vRouter來負責資料轉發。每個vRouter通過BGP協議把在本節點上執行的容器的路由資訊向整個Calico網路廣播,並自動設定到達其他節點的路由轉發規則。Calico保證所有容器之間的資料流量都是通過IP路由的方式完成互聯互通的。Calico節點組網可以直接利用資料中心的網路結構(L2或者L3),不需要額外的NAT、隧道或者Overlay Network,沒有額外的封包解包,能夠節約CPU運算,提高網路通訊效率。Calico的資料包結構示意圖如下。
Calico在小規模叢集中可以直接互聯,在大規模叢集中可以通過額外的BGP route reflector來完成。
此外,Calico基於iptables還提供了豐富的網路策略,實現了k8s的Network Policy策略,提供容器間網路可達性限制的功能。Calico的主要元件如下:
  • Felix:Calico Agent,執行在每臺Node上,負責為容器設定網路源(IP地址、路由規則、iptables規則等),保證主機容器網路互通。
  • etcd:Calico使用的儲存後端。
  • BGP Client(BIRD):負責把Felix在各Node上設定的路由資訊通過BGP協議廣播到Calico網路。
  • BGP Route Reflector(BIRD):通過一個或者多個BGP Route Reflector來完成大規模叢集的分級路由分發。
  • calicoctl:Calico命令列管理工具。

4.2 部署Calico服務

在k8s中部署Calico的主要步驟包括兩部分。4.2.1 修改kubernetes服務的啟動引數,並重啟服務
  • 設定Master上kube-apiserver服務的啟動引數:--allow-privileged=true(因為Calico-node需要以特權模式執行在各Node上)。
  • 設定各Node上kubelet服務的啟動引數:--network-plugin=cni(使用CNI網路外掛), --allow-privileged=true
本例中的K8s叢集包括兩臺Node:Node1(10.0.2.4)和Node2(10.0.2.5)4.2.2 建立Calico服務,主要包括Calico-node和Calico policy controller需要創建出以下的資源物件:
  • 建立ConfigMap calico-config,包含Calico所需的配置引數。
  • 建立Secret calico-etcd-secrets,用於使用TLS方式連線etcd。
  • 在每個Node上執行calico/node容器,部署為DaemonSet。
  • 在每個Node上安裝Calico CNI二進位制檔案和網路配置引數(由install-cni容器完成)。
  • 部署一個名為calico/kube-policy-controller的Deployment,以對接k8s叢集中為Pod設定的Network Policy。
4.2.3 Calico服務安裝與配置的詳細說明該配置檔案中包括了啟動Calico所需的全部資源物件的定義。下面對其逐個進行說明。(1)Calico所需的配置以ConfigMap物件進行建立,如下所示# Calico Version v2.1.5# This manifest includes the following component versions:#   calico/node:v1.1.3#   calico/cni:v1.8.0#   calico/kube-policy-controller:v0.5.4# This ConfigMap is used to configure a self-hosted Calico installation.kind: ConfigMapapiVersion: v1metadata:  name: calico-config  namespace: kube-systemdata:  # Configure this with the location of your etcd cluster.  etcd_endpoints: "http://10.0.2.15:2379"  # Configure the Calico backend to use.  calico_backend: "bird"  # The CNI network configuration to install on each node.  cni_network_config: |-    {        "name": "k8s-pod-network",        "type": "calico",        "etcd_endpoints": "__ETCD_ENDPOINTS__",        "etcd_key_file": "__ETCD_KEY_FILE__",        "etcd_cert_file": "__ETCD_CERT_FILE__",        "etcd_ca_cert_file": "__ETCD_CA_CERT_FILE__",        "log_level": "info",        "ipam": {            "type": "calico-ipam"        },        "policy": {            "type": "k8s",            "k8s_api_root": "https://__KUBERNETES_SERVICE_HOST__:__KUBERNETES_SERVICE_PORT__",            "k8s_auth_token": "__SERVICEACCOUNT_TOKEN__"        },        "kubernetes": {            "kubeconfig": "__KUBECONFIG_FILEPATH__"        }    }  # If you're using TLS enabled etcd uncomment the following.  # You must also populate the Secret below with these files.  etcd_ca: ""   # "/calico-secrets/etcd-ca"  etcd_cert: "" # "/calico-secrets/etcd-cert"  etcd_key: ""  # "/calico-secrets/etcd-key"主要引數如下:
  • etcd_endpoints:Calico使用etcd來儲存網路拓撲和狀態,該引數指定etcd的地址,可以使用k8s Master所用的etcd,也可以另外搭建。
  • calico_backend:Calico的後端,預設為bird。
  • cni_network_config:符合CNI規範的網路配置。其中type=calico表示kubelet將從/opt/cni/bin目錄下搜尋名為“Calico”的可執行檔案,並呼叫它完成容器網路的設定。ipam中type=calico-ipam表示kubelet將在/opt/cni/bin目錄下搜尋名為"calico-ipam"的可執行檔案,用於完成容器IP地址的分配。
etcd如果配置了TLS安全認證,則還需要指定相應的ca、cert、key等檔案。(2)訪問etcd所需的secret,對於無TLS的etcd服務,將data設定為空即可# The following contains k8s Secrets for use with a TLS enabled etcd cluster.apiVersion: v1kind: Secrettype: Opaquemetadata:  name: calico-etcd-secrets  namespace: kube-systemdata:  # Populate the following files with etcd TLS configuration if desired, but leave blank if  # not using TLS for etcd.  # This self-hosted install expects three files with the following names.  The values  # should be base64 encoded strings of the entire contents of each file.  # etcd-key: null  # etcd-cert: null  # etcd-ca: null(3)calico-node,以Daemonset形式在每臺Node上執行一個calico-node服務和一個install-cni服務# This manifest installs the calico/node container, as well# as the Calico CNI plugins and network config on# each master and worker node in a Kubernetes cluster.kind: DaemonSetapiVersion: extensions/v1beta1metadata:  name: calico-node  namespace: kube-system  labels:    k8s-app: calico-nodespec:  selector:    matchLabels:      k8s-app: calico-node  template:    metadata:      labels:        k8s-app: calico-node      annotations:        scheduler.alpha.kubernetes.io/critical-pod: ''        scheduler.alpha.kubernetes.io/tolerations: |          [{"key": "dedicated", "value": "master", "effect": "NoSchedule" },           {"key":"CriticalAddonsOnly", "operator":"Exists"}]    spec:      hostNetwork: true      containers:        # Runs calico/node container on each Kubernetes node.  This        # container programs network policy and routes on each        # host.        - name: calico-node          image: quay.io/calico/node:v1.1.3          env:            # The location of the Calico etcd cluster.            - name: ETCD_ENDPOINTS              valueFrom:                configMapKeyRef:                  name: calico-config                  key: etcd_endpoints            # Choose the backend to use.            - name: CALICO_NETWORKING_BACKEND              valueFrom:                configMapKeyRef:                  name: calico-config                  key: calico_backend            # Disable file logging so `kubectl logs` works.            - name: CALICO_DISABLE_FILE_LOGGING              value: "true"            # Set Felix endpoint to host default action to ACCEPT.            - name: FELIX_DEFAULTENDPOINTTOHOSTACTION              value: "ACCEPT"            # Configure the IP Pool from which Pod IPs will be chosen.            - name: CALICO_IPV4POOL_CIDR              value: "192.168.0.0/16"            - name: CALICO_IPV4POOL_IPIP              value: "always"            # Disable IPv6 on Kubernetes.            - name: FELIX_IPV6SUPPORT              value: "false"            # Set Felix logging to "info"            - name: FELIX_LOGSEVERITYSCREEN              value: "info"            # Location of the CA certificate for etcd.            - name: ETCD_CA_CERT_FILE              valueFrom:                configMapKeyRef:                  name: calico-config                  key: etcd_ca            # Location of the client key for etcd.            - name: ETCD_KEY_FILE              valueFrom:                configMapKeyRef:                  name: calico-config                  key: etcd_key            # Location of the client certificate for etcd.            - name: ETCD_CERT_FILE              valueFrom:                configMapKeyRef:                  name: calico-config                  key: etcd_cert            # Auto-detect the BGP IP address.            - name: IP              value: ""          securityContext:            privileged: true          resources:            requests:              cpu: 250m          volumeMounts:            - mountPath: /lib/modules              name: lib-modules              readOnly: true            - mountPath: /var/run/calico              name: var-run-calico              readOnly: false            - mountPath: /calico-secrets              name: etcd-certs        # This container installs the Calico CNI binaries        # and CNI network config file on each node.        - name: install-cni          image: quay.io/calico/cni:v1.8.0          command: ["/install-cni.sh"]          env:            # The location of the Calico etcd cluster.            - name: ETCD_ENDPOINTS              valueFrom:                configMapKeyRef:                  name: calico-config                  key: etcd_endpoints            # The CNI network config to install on each node.            - name: CNI_NETWORK_CONFIG              valueFrom:                configMapKeyRef:                  name: calico-config                  key: cni_network_config          volumeMounts:            - mountPath: /host/opt/cni/bin              name: cni-bin-dir            - mountPath: /host/etc/cni/net.d              name: cni-net-dir            - mountPath: /calico-secrets              name: etcd-certs      volumes:        # Used by calico/node.        - name: lib-modules          hostPath:            path: /lib/modules        - name: var-run-calico          hostPath:            path: /var/run/calico        # Used to install CNI.        - name: cni-bin-dir          hostPath:            path: /opt/cni/bin        - name: cni-net-dir          hostPath:            path: /etc/cni/net.d        # Mount in the etcd TLS secrets.        - name: etcd-certs          secret:            secretName: calico-etcd-secrets該Pod中包括如下兩個容器:
  • calico-node:Calico服務程式,用於設定Pod的網路資源,保證Pod的網路與各Node互聯互通,它還需要以hostNetwork模式執行,直接使用宿主機網路。
  • install-cni:在各Node上安裝CNI二進位制檔案到/opt/cni/bin目錄下,並安裝相應的網路配置檔案到/etc/cni/net.d目錄下。
calico-node服務的主要引數如下:
  • CALICO_IPV4POOL_CIDR:Calico IPAM的IP地址池,Pod的IP地址將從該池中進行分配。
  • CALICO_IPV4POOL_IPIP:是否啟用IPIP模式。啟用IPIP模式時,Calico將在Node上建立一個名為"tunl0"的虛擬隧道。
  • FELIX_IPV6SUPPORT:是否啟用IPV6。
  • FELIX_LOGSEVERITYSCREEN:日誌級別。
IP Pool可以使用兩種模式:BGP或IPIP模式。使用IPIP模式時,設定CALICO_IPV4POOL_IPIP=“always”,不使用IPIP模式時,設定CALICO_IPV4POOL_IPIP="off",此時將使用BGP模式。IPIP是一種將各Node的路由之間做一個tunnel,再把兩個網路連線起來的模式。啟用IPIP模式時,Calico將在各Node上建立一個名為"tunl0"的虛擬網路介面。如下圖所示。
BGP模式則直接使用物理機作為虛擬路由路(vRouter),不再建立額外的tunnel。(4)calico-policy-controller容器用於對接k8s叢集中為Pod設定的Network Policy。# This manifest deploys the Calico policy controller on Kubernetes.apiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: calico-policy-controller  namespace: kube-system  labels:    k8s-app: calico-policy  annotations:    scheduler.alpha.kubernetes.io/critical-pod: ''    scheduler.alpha.kubernetes.io/tolerations: |      [{"key": "dedicated", "value": "master", "effect": "NoSchedule" },       {"key":"CriticalAddonsOnly", "operator":"Exists"}]spec:  # The policy controller can only have a single active instance.  replicas: 1  strategy:    type: Recreate  template:    metadata:      name: calico-policy-controller      namespace: kube-system      labels:        k8s-app: calico-policy    spec:      # The policy controller must run in the host network namespace so that      # it isn't governed by policy that would prevent it from working.      hostNetwork: true      containers:        - name: calico-policy-controller          image: quay.io/calico/kube-policy-controller:v0.5.4          env:            # The location of the Calico etcd cluster.            - name: ETCD_ENDPOINTS              valueFrom:                configMapKeyRef:                  name: calico-config                  key: etcd_endpoints            # Location of the CA certificate for etcd.            - name: ETCD_CA_CERT_FILE              valueFrom:                configMapKeyRef:                  name: calico-config                  key: etcd_ca            # Location of the client key for etcd.            - name: ETCD_KEY_FILE              valueFrom:                configMapKeyRef:                  name: calico-config                  key: etcd_key            # Location of the client certificate for etcd.            - name: ETCD_CERT_FILE              valueFrom:                configMapKeyRef:                  name: calico-config                  key: etcd_cert            # The location of the Kubernetes API.  Use the default Kubernetes            # service for API access.            - name: K8S_API            # Since we're running in the host namespace and might not have KubeDNS            # access, configure the container's /etc/hosts to resolve            # kubernetes.default to the correct service clusterIP.            - name: CONFIGURE_ETC_HOSTS              value: "true"          volumeMounts:            # Mount in the etcd TLS secrets.            - mountPath: /calico-secrets              name: etcd-certs      volumes:        # Mount in the etcd TLS secrets.        - name: etcd-certs          secret:            secretName: calico-etcd-secrets使用者在k8s叢集中設定了Pod的Network Policy之後,calico-policy-controller就會自動通知各個Node上的calico-node服務,在宿主機上設定相應的iptables規則,完成Pod間網路訪問策略的設定。做好以上配置檔案的準備工作後,就可以開始建立Calico的各資源物件了。[[email protected] ~]# kubectl create -f calico.yamlconfigmap "calico-config" createdsecret "calico-etcd-secrets" createddaemonset "calico-node" createddeployment "calico-policy-controller" created[[email protected] ~]#確保各服務正確執行:
[[email protected] ~]# kubectl get pods --namespace=kube-system -o wideNAME                                        READY     STATUS    RESTARTS   AGE       IP         NODEcalico-node-59n9j                           2/2       Running   1          9h        10.0.2.5   10.0.2.5calico-node-cksq5                           2/2       Running   1          9h        10.0.2.4   10.0.2.4calico-policy-controller-54dbfcd7c7-ctxzz   1/1       Running   0          9h        10.0.2.5   10.0.2.5[[email protected] ~]#[[email protected] ~]# kubect