最近学习k8s遇到很多问题,建了一个qq群:153144292,交流devops、k8s、docker等
Kubernetes的网络模型和网络策略
Kubernetes网络模型和CNI插件
在Kubernetes中设计了一种网络模型,要求无论容器运行在集群中的哪个节点,所有容器
都能通过一个扁平的网络平面进行通信,即在同一IP网络中。
需要注意的是:在K8S集群中,IP地址分配是以Pod对象为单位,而非容器,同一Pod内的所
有容器共享同一网络名称空间。
Docker网络模型
了解Docker的友友们都应该清楚,Docker容器的原生网络模型主要有4种:Bridge(桥接)、
joined、Host(主机)、none。
Bridge:桥接式网络,借助虚拟网桥设备为容器建立网络连接。
joined: 联盟式网络、共享别的容器网络名称空间
Host(open):设置容器直接共享当前节点主机的网络名称空间。
none:不使用网络名称空间。
===========================
#使用以下命令查看docker原生的三种网络
[root@node01 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
11d82740d302 bridge bridge local
e91c59d0b9c0 host host local
37ce079dc9a9 none null local
#none网络,在该网络下的容器仅有lo网卡,属于封闭式网络,通常用于对安全性要求较高并且不需要联网的应用
[root@node01 ~]# docker run -it --network=none busybox
/ # ifconfig
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
#host网络,共享宿主机的网络名称空间,容器网络配置和host一致,但是存在端口冲突的问题
[root@node01 ~]# docker run -it --network=host busybox
/ # ip addr
1: lo:
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 forever
2: ens2f0:
link/ether a0:36:9f:8d:e3:5e brd ff:ff:ff:ff:ff:ff
3: ens2f1:
link/ether a0:36:9f:8d:e3:5f brd ff:ff:ff:ff:ff:ff
4: eno1:
link/ether a0:36:9f:8d:e3:5f brd ff:ff:ff:ff:ff:ff
5: eno2:
link/ether 00:8c:fa:f9:f4:e5 brd ff:ff:ff:ff:ff:ff
6: bond0:
link/ether a0:36:9f:8d:e3:5f brd ff:ff:ff:ff:ff:ff
inet 10.249.6.101/24 brd 10.249.6.255 scope global bond0
valid_lft forever preferred_lft forever
inet6 fe80::a236:9fff:fe8d:e35f/64 scope link
valid_lft forever preferred_lft forever
7: docker0:
link/ether 02:42:a5:5d:da:05 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:a5ff:fe5d:da05/64 scope link
valid_lft forever preferred_lft forever
10: flannel.1:
link/ether ce:74:25:7c:7b:ec brd ff:ff:ff:ff:ff:ff
inet 10.244.1.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet6 fe80::cc74:25ff:fe7c:7bec/64 scope link
valid_lft forever preferred_lft forever
13: cni0:
link/ether 0a:58:0a:f4:01:01 brd ff:ff:ff:ff:ff:ff
inet 10.244.1.1/24 scope global cni0
valid_lft forever preferred_lft forever
inet6 fe80::4073:9cff:fe59:47b/64 scope link
valid_lft forever preferred_lft forever
25: veth99ad7c1e@ens2f1:
link/ether a2:d9:41:28:3b:29 brd ff:ff:ff:ff:ff:ff
inet6 fe80::a0d9:41ff:fe28:3b29/64 scope link
valid_lft forever preferred_lft forever
26: veth21cd58be@ens2f1:
link/ether fe:54:97:71:bb:63 brd ff:ff:ff:ff:ff:ff
inet6 fe80::fc54:97ff:fe71:bb63/64 scope link
valid_lft forever preferred_lft forever
38: veth766df1a0@ens2f1:
link/ether a2:05:42:46:af:24 brd ff:ff:ff:ff:ff:ff
inet6 fe80::a005:42ff:fe46:af24/64 scope link
valid_lft forever preferred_lft forever
47: vethc8a23d2a@ens2f1:
link/ether 06:94:c4:60:39:ea brd ff:ff:ff:ff:ff:ff
inet6 fe80::494:c4ff:fe60:39ea/64 scope link
valid_lft forever preferred_lft forever
49: veth7005c237@ens2f1:
link/ether c2:88:56:4e:fb:ba brd ff:ff:ff:ff:ff:ff
inet6 fe80::c088:56ff:fe4e:fbba/64 scope link
valid_lft forever preferred_lft forever
50: veth08915585@ens2f1:
link/ether d2:d7:48:6d:7c:66 brd ff:ff:ff:ff:ff:ff
inet6 fe80::d0d7:48ff:fe6d:7c66/64 scope link
valid_lft forever preferred_lft forever
/ #
/ # hostname 这里跟node01节点共享网络名称空间
node01
#bridge网络,Docker安装完成时会创建一个名为docker0的linux bridge,不指定网络时,创建的网络默认为桥接网络,都会桥接到docker0上。
桥接式网络是目前较为流行和默认的解决方案。但是这种方案的弊端是无法跨主机通信
的,仅能在宿主机本地进行,而解决该问题的方法就是NAT。所有接入到该桥接设备上的容器
都会被NAT隐藏,它们发往Docker主机外部的所有流量都会经过源地址转换后发出,并且默认
是无法直接接受节点之外的其他主机发来的请求。当需要接入Docker主机外部流量,就需要进
行目标地址转换甚至端口转换将其暴露在外部网络当中。
任何一个pod出去的时候要做源地址转换,需要做nat。
容器内的属于私有地址,需要在左侧的主机上的eth0上进行源地址转换,而右侧的地址需要被访问,就需要将eth0的地址进行NAT转换。SNAT---->DNAT
这样的通信方式会比较麻烦,从而需要借助第三方的网络插件实现这样的跨主机通信的网络策略。
================================
Kubernetes网络模型
在K8S上的网络通信包含以下几类:
1、容器间的通信:同一个Pod内的多个容器间的通信,它们之间通过lo网卡进行通信。
2、Pod之间的通信:通过Pod IP地址进行通信,直达。
3、Pod和Service之间的通信:Pod IP地址和Service IP进行通信,两者并不属于同一网络,实现方式是通过IPVS或iptables规则转发。
4、Service和集群外部客户端的通信,实现方式:Ingress、NodePort、Loadbalance
[root@master ~]# kubectl get configmap -n kube-system
NAME DATA AGE
coredns 1 32d
extension-apiserver-authentication 6 32d
kube-flannel-cfg 2 32d
kube-proxy 2 32d
kubeadm-config 2 32d
kubelet-config-1.13 1 32d
[root@master ~]# kubectl get configmap kube-proxy -o yaml -n kube-system
apiVersion: v1
data:
config.conf: |-
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
clientConnection:
acceptContentTypes: ""
burst: 10
contentType: application/vnd.kubernetes.protobuf
kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
qps: 5
clusterCIDR: 10.244.0.0/16
configSyncPeriod: 15m0s
conntrack:
max: null
maxPerCore: 32768
min: 131072
tcpCloseWaitTimeout: 1h0m0s
tcpEstablishedTimeout: 24h0m0s
enableProfiling: false
healthzBindAddress: 0.0.0.0:10256
hostnameOverride: ""
iptables:
masqueradeAll: false
masqueradeBit: 14
minSyncPeriod: 0s
syncPeriod: 30s
ipvs:
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
syncPeriod: 30s
kind: KubeProxyConfiguration
metricsBindAddress: 127.0.0.1:10249
mode: "" 这里修改为ipvs
nodePortAddresses: null
oomScoreAdj: -999
portRange: ""
resourceContainer: /kube-proxy
udpIdleTimeout: 250ms
kubeconfig.conf: |-
apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
server: https://10.249.6.100:6443
name: default
contexts:
- context:
cluster: default
namespace: default
user: default
name: default
current-context: default
users:
- name: default
user:
tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
kind: ConfigMap
metadata:
creationTimestamp: "2019-02-28T11:23:46Z"
labels:
app: kube-proxy
name: kube-proxy
namespace: kube-system
resourceVersion: "237"
selfLink: /api/v1/namespaces/kube-system/configmaps/kube-proxy
uid: 4ffcc6cf-3b4b-11e9-a704-a0369f95b76e
============================
CNI
K8S网络的实现不是集群内部自己实现,而是依赖于第三方网络插件----CNI(Container Network Interface)
flannel、calico、canel等是目前比较流行的第三方网络插件。
这三种的网络插件需要实现Pod网络方案的方式通常有以下几种:虚拟网桥、多路复用(MacVLAN)、硬件交换(SR-IOV)
无论是上面的哪种方式在容器当中实现,都需要大量的操作步骤,而K8S支持CNI插件进行编排网络,
以实现Pod和集群网络管理功能的自动化。每次Pod被初始化或删除,kubelet都会调用默认的CNI插件去
创建一个虚拟设备接口附加到相关的底层网络,为Pod去配置IP地址、路由信息并映射到Pod对象的网络
名称空间。
在配置Pod网络时,kubelet会在默认的/etc/cni/net.d/目录中去查找CNI JSON配置文件,然后通过
type属性到/opt/cni/bin中查找相关的插件二进制文件,如下面的"portmap"。然后CNI插件调用IPAM插件
(IP地址管理插件)来配置每个接口的IP地址:
[root@master ~]# cat /etc/cni/net.d/10-flannel.conflist
{
"name": "cbr0",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
kubelet调用第三方插件,进行网络地址的分配。
CNI主要是定义容器网络模型规范,链接容器管理系统和网络插件,两者主要通过上面的JSON格式文件进行
通信,实现容器的网络功能。CNI的主要核心是:在创建容器时,先创建好网络名称空间(netns),然后调用
CNI插件为这个netns配置网络,最后在启动容器内的进程。
常见的CNI网络插件包含以下几种:
1、Flannel:为Kubernetes提供叠加网络的网络插件,基于TUN/TAP隧道技术,使用UDP封装IP报文进行创建叠加网络,借助etcd维护网络的分配情况,缺点:无法支持网络策略访问控制。
2、Calico:基于BGP的三层网络插件,也支持网络策略进而实现网络的访问控制;它在每台主机上都运行一个虚拟路由,利用Linux内核转发网络数据包,并借助iptables实现防火墙功能。实际上Calico最后的实现就是将每台主机都变成了一台路由器,将各个网络进行连接起来,实现跨主机通信的功能。
3、Canal:由Flannel和Calico联合发布的一个统一网络插件,提供CNI网络插件,并支持网络策略实现。
4、其他的还包括Weave Net、Contiv、OpenContrail、Romana、NSX-T、kube-router等等。而Flannel和Calico是目前最流行的选择方案。
=================================
Flannel网络插件
在各节点上的Docker主机在docker0上默认使用同一个子网,不同节点的容器都有可能会获取
到相同的地址,那么在跨节点通信时就会出现地址冲突的问题。并且在多个节点上的docker0使用
不同的子网,也会因为没有准确的路由信息导致无法准确送达报文。
而为了解决这一问题,Flannel的解决办法是,预留一个使用网络,如10.244.0.0/16,然后自
动为每个节点的Docker容器引擎分配一个子网,如10.244.1.0/24和10.244.2.0/24,并将分配信息
保存在etcd持久存储。
第二个问题的解决,Flannel是采用不同类型的后端网络模型进行处理。其后端的类型有以下几
种:
1、VxLAN:使用内核中的VxLAN模块进行封装报文。也是flannel推荐的方式,其报文格式如下:
|--------------------------------------|--------------vm---------------------------|
|outer mac---outer ip--outer UDP--VXLAN|MAC-----IP----------TCP/UDP------DATA------|
2、host-gw:即Host GateWay,通过在节点上创建目标容器地址的路由直接完成报文转发,要求
各节点必须在同一个2层网络,对报文转发性能要求较高的场景使用。
3、UDP:使用普通的UDP报文封装完成隧道转发。
VxLAN后端和direct routing
VxLAN(Virtual extensible Local Area Network)虚拟可扩展局域网,采用MAC in UDP封装方式
,具体的实现方式为:
1、将虚拟网络的数据帧添加到VxLAN首部,封装在物理网络的UDP报文中
2、以传统网络的通信方式传送该UDP报文
3、到达目的主机后,去掉物理网络报文的头部信息以及VxLAN首部,并交付给目的终端
跨节点的Pod之间的通信就是以上的一个过程,整个过程中通信双方对物理网络是没有感知的。
我这里环境已经安装
https://blog.csdn.net/yujin2010good/article/details/88043217
[root@master ~]# kubectl get daemonset -n kube-system
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
kube-flannel-ds-amd64 3 3 3 3 3 beta.kubernetes.io/arch=amd64 33d
kube-flannel-ds-arm 0 0 0 0 0 beta.kubernetes.io/arch=arm 33d
kube-flannel-ds-arm64 0 0 0 0 0 beta.kubernetes.io/arch=arm64 33d
kube-flannel-ds-ppc64le 0 0 0 0 0 beta.kubernetes.io/arch=ppc64le 33d
kube-flannel-ds-s390x 0 0 0 0 0 beta.kubernetes.io/arch=s390x 33d
kube-proxy 3 3 3 3 3
运行正常后,flanneld会在宿主机的/etc/cni/net.d目录下生成自已的配置文件,kubelet将会调用它。
网络插件运行成功后,Node状态才Ready。
[root@master ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready master 33d v1.13.3
node01 Ready
node02 Ready
flannel运行后,在各Node宿主机多了一个网络接口
#master节点的flannel.1网络接口,其网段为:10.244.0.0
[root@master ~]# ip a
12: flannel.1:
link/ether 6a:63:a1:96:b6:66 brd ff:ff:ff:ff:ff:ff
inet 10.244.0.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet6 fe80::6863:a1ff:fe96:b666/64 scope link
valid_lft forever preferred_lft forever
#node1节点的flannel.1网络接口,其网段为:10.244.1.0
10: flannel.1:
link/ether ce:74:25:7c:7b:ec brd ff:ff:ff:ff:ff:ff
inet 10.244.1.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet6 fe80::cc74:25ff:fe7c:7bec/64 scope link
valid_lft forever preferred_lft forever
#node2节点的flannel.1网络接口,其网段为:10.244.2.0
12: flannel.1:
link/ether 52:b8:24:34:e6:8c brd ff:ff:ff:ff:ff:ff
inet 10.244.2.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet6 fe80::50b8:24ff:fe34:e68c/64 scope link
valid_lft forever preferred_lft forever
从上面的结果可以知道 :
1、flannel默认就是VXLAN模式,即Overlay Network。
2、flanneld创建了一个flannel.1接口,它是专门用来封装隧道协议的,默认分给集群的Pod网段为10.244.0.0/16。
3、flannel给master节点配置的Pod网络为10.244.0.0段,给node01节点配置的Pod网络为10.244.1.0段,给node02节点配置的Pod网络为10.244.2.0段,如果有更多的节点,以此类推
举个实际例子
#启动一个nginx容器,副本为3
[root@master ~]# kubectl run wolf --image=nginx:1.14-alpine --port=80 --replicas=3
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
deployment.apps/wolf created
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-0 1/1 Running 0 14d
myapp-1 1/1 Running 1 14d
myapp-2 1/1 Running 0 14d
myapp-3 1/1 Running 1 14d
nginx-7849c4bbcd-dscjr 1/1 Running 0 31d
nginx-7849c4bbcd-vdd45 1/1 Running 1 31d
nginx-7849c4bbcd-wrvks 1/1 Running 0 31d
nginx-deploy-84cbfc56b6-scrnt 1/1 Running 1 15d
pod-sa-demo 1/1 Running 0 13d
wolf-7f649b757d-njtr2 1/1 Running 0 3s
wolf-7f649b757d-ntrp9 1/1 Running 0 3s
wolf-7f649b757d-whsb9 1/1 Running 0 3s
#查看Pod
[root@master ~]# kubectl get pods -o wide |grep wolf
wolf-7f649b757d-njtr2 1/1 Running 0 10s 10.244.2.74 node02
wolf-7f649b757d-ntrp9 1/1 Running 0 10s 10.244.1.42 node01
wolf-7f649b757d-whsb9 1/1 Running 0 10s 10.244.2.73 node02
可以看到,3个Pod都分别运行在各个节点之上,在master节点上查看网络接口可以发现在各个节点上多了一个虚拟接口cni0,其ip地址为10.244.0.1。它是由flanneld创建的一个虚拟网桥叫cni0,在Pod本地通信使用。 这里需要注意的是,cni0虚拟网桥,仅作用于本地通信!!!!
13: cni0:
link/ether 0a:58:0a:f4:00:01 brd ff:ff:ff:ff:ff:ff
inet 10.244.0.1/24 scope global cni0
valid_lft forever preferred_lft forever
inet6 fe80::2845:7dff:fecc:30d8/64 scope link
valid_lft forever preferred_lft forever
flanneld为每个Pod创建一对veth虚拟设备,一端放在容器接口上,一端放在cni0桥上。 使用brctl查看该网桥:
#可以看到有一veth的网络接口桥接在cni0网桥上
yum install bridge-utils
[root@master ~]# brctl show cni0
bridge name bridge id STP enabled interfaces
cni0 8000.0a580af40001 no veth0e85a251
veth1da05c4b
[root@master ~]# ping 10.244.2.73
PING 10.244.2.73 (10.244.2.73) 56(84) bytes of data.
64 bytes from 10.244.2.73: icmp_seq=1 ttl=63 time=0.404 ms
在现有的Flannel VxLAN网络中,两台主机上的Pod间通信,也是正常的,如master节点上的Pod访问node01上的Pod:
kubectl exec -it wolf-7f649b757d-njtr2 -- /bin/bash
[root@master ~]# kubectl exec -it wolf-7f649b757d-njtr2 -- /bin/bash
root@wolf-7f649b757d-njtr2:/# ping 10.244.1.41
PING 10.244.1.146 (10.244.1.146) 56(84) bytes of data.
64 bytes from 10.244.1.42: icmp_seq=1 ttl=62 time=1.44 ms
64 bytes from 10.244.1.42: icmp_seq=2 ttl=62 time=0.713 ms
可以看到容器跨主机是可以正常通信的,那么容器的跨主机通信是如何实现的呢?????
master上查看路由表信息:
[root@master ~]# ip route
。。。。。。。。。。。
10.244.0.0/24 dev cni0 proto kernel scope link src 10.244.0.1
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink
。。。。。。。。。。
发送到10.244.1.0/24和10.244.2.0/24网段的数据报文发给本机的flannel.1接口,即进入二层隧
道,然后对数据报文进行封装(封装VxLAN首部-->UDP首部-->IP首部-->以太网首部),到达目标Node
节点后,由目标Node上的flannel.1进行解封装。使用tcpdump进行 抓一下包,如下:
#在宿主机和容器内都进行ping另外一台主机上的Pod ip并进行抓包
[root@master ~]# ping 10.244.2.73
PING 10.244.2.73 (10.244.2.73) 56(84) bytes of data.
64 bytes from 10.244.2.73: icmp_seq=1 ttl=63 time=0.309 ms
64 bytes from 10.244.2.73: icmp_seq=2 ttl=63 time=0.190 ms
[root@master ~]# tcpdump -i flannel.1 -nn host 10.244.2.73
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on flannel.1, link-type EN10MB (Ethernet), capture size 262144 bytes
11:09:09.157126 IP 10.244.0.0 > 10.244.2.73: ICMP echo request, id 26050, seq 35, length 64
11:09:09.157303 IP 10.244.2.73 > 10.244.0.0: ICMP echo reply, id 26050, seq 35, length 64
11:09:10.157108 IP 10.244.0.0 > 10.244.2.73: ICMP echo request, id 26050, seq 36, length 64
11:09:10.157323 IP 10.244.2.73 > 10.244.0.0: ICMP echo reply, id 26050, seq 36, length 64
11:09:11.157122 IP 10.244.0.0 > 10.244.2.73: ICMP echo request, id 26050, seq 37, length 64
11:09:11.157300 IP 10.244.2.73 > 10.244.0.0: ICMP echo reply, id 26050, seq 37, length 64
11:09:12.157103 IP 10.244.0.0 > 10.244.2.73: ICMP echo request, id 26050, seq 38, length 64
11:09:12.157307 IP 10.244.2.73 > 10.244.0.0: ICMP echo reply, id 26050, seq 38, length 64
可以看到报文都是经过flannel.1网络接口进入2层隧道进而转发.
VXLAN是Linux内核本身支持的一种网络虚拟化技术,是内核的一个模块,在内核态实现封装解封装,构
建出覆盖网络,其实就是一个由各宿主机上的Flannel.1设备组成的虚拟二层网络。
由于VXLAN由于额外的封包解包,导致其性能较差,所以Flannel就有了host-gw模式,即把宿主机当作网
关,除了本地路由之外没有额外开销,性能和calico差不多,由于没有叠加来实现报文转发,这样会导致路由
表庞大。因为一个节点对应一个网络,也就对应一条路由条目。
host-gw虽然VXLAN网络性能要强很多。,但是种方式有个缺陷:要求各物理节点必须在同一个二层网络中
。物理节点必须在同一网段中。这样会使得一个网段中的主机量会非常多,万一发一个广播报文就会产生干扰
。在私有云场景下,宿主机不在同一网段是很常见的状态,所以就不能使用host-gw了。
VXLAN还有另外一种功能,VXLAN也支持类似host-gw的玩法,如果两个节点在同一网段时使用host-gw通信
,如果不在同一网段中,即 当前pod所在节点与目标pod所在节点中间有路由器,就使用VXLAN这种方式,使用
叠加网络。结合了Host-gw和VXLAN,这就是VXLAN的Direct routing模式
Flannel VxLAN的Direct routing模式配置
修改kube-flannel.yml文件,将flannel的configmap对象改为:
[root@master ~]# vim kube-flannel.yml
......
net-conf.json: |
{
"Network": "10.244.0.0/16", #默认网段
"Backend": {
"Type": "vxlan",
"Directrouting": true #增加
}
}
......
[root@master ~]# kubectl apply -f kube-flannel.yml
clusterrole.rbac.authorization.k8s.io/flannel configured
clusterrolebinding.rbac.authorization.k8s.io/flannel configured
serviceaccount/flannel unchanged
configmap/kube-flannel-cfg configured
daemonset.extensions/kube-flannel-ds-amd64 created
daemonset.extensions/kube-flannel-ds-arm64 created
daemonset.extensions/kube-flannel-ds-arm created
daemonset.extensions/kube-flannel-ds-ppc64le created
daemonset.extensions/kube-flannel-ds-s390x created
查看路由信息
[root@master ~]# ip route
......
10.244.1.0/24 via 10.249.2.101 dev bond0
10.244.2.0/24 via 10.249.2.102 dev bond0
......
从上面的结果可以看到,发往10.244.1.0/24和10.244.2.0/24的包都是直接经过bond0网络接口直接发出去的,这
就是Directrouting。如果两个节点是跨网段的,则flannel自动降级为VxLAN模式。
再在此之前创建的Pod和宿主机上进行ping测试,可以看到在flannel.1接口上已经抓不到包了,在bond0上可以用抓到ICMP的包,如下:
[root@master ~]# tcpdump -i flannel.1 -nn host 10.244.2.73
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on flannel.1, link-type EN10MB (Ethernet), capture size 262144 bytes
^C
0 packets captured
0 packets received by filter
0 packets dropped by kernel
[root@master ~]# tcpdump -i bond0 -nn host 10.244.2.73
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on flannel.1, link-type EN10MB (Ethernet), capture size 262144 bytes
11:09:09.157126 IP 10.244.0.0 > 10.244.2.73: ICMP echo request, id 26050, seq 35, length 64
11:09:09.157303 IP 10.244.2.73 > 10.244.0.0: ICMP echo reply, id 26050, seq 35, length 64
11:09:10.157108 IP 10.244.0.0 > 10.244.2.73: ICMP echo request, id 26050, seq 36, length 64
11:09:10.157323 IP 10.244.2.73 > 10.244.0.0: ICMP echo reply, id 26050, seq 36, length 64
11:09:11.157122 IP 10.244.0.0 > 10.244.2.73: ICMP echo request, id 26050, seq 37, length 64
11:09:11.157300 IP 10.244.2.73 > 10.244.0.0: ICMP echo reply, id 26050, seq 37, length 64
11:09:12.157103 IP 10.244.0.0 > 10.244.2.73: ICMP echo request, id 26050, seq 38, length 64
11:09:12.157307 IP 10.244.2.73 > 10.244.0.0: ICMP echo reply, id 26050, seq 38, length 64
==================================
Host-gw后端
Flannel除了上面2种数据传输的方式以外,还有一种是host-gw的方式,host-gw后端是通过添加必要
的路由信息使用节点的二层网络直接发送Pod的通信报文。它的工作方式类似于Directrouting的功能,但
是其并不具备VxLan的隧道转发能力。
编辑kube-flannel的配置清单,将ConfigMap资源kube-flannel-cfg的data字段中网络配置进行修改,
如下:
[root@master ~]# vim kube-flannel.yml
......
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "host-gw"
}
}
......
[root@master ~]# kubectl apply -f kube-flannel.yml
配置完成后,各节点会生成类似directrouting一样的 路由,用于实现二层转发Pod网络的通信报文,省
去了隧道转发模式的额外开销。但是存在的问题点是,对于不在同一个二层网络的报文转发,host-gw是
无法实现的。延续上面的例子,进行抓包查看:
查看路由信息
[root@master ~]# ip route
......
10.244.1.0/24 via 10.249.2.101 dev bond0
10.244.2.0/24 via 10.249.2.102 dev bond0
......
在抓包
该模式下,报文转发的相关流程如下:
1、Pod(10.244.2.74)向Pod(10.244.1.42)发送报文,查看到报文中的目的地址为:10.244.1.42,并非本地网段,会直接发送到网关(10.249.6.101);
2、网关发现该目标地址为10.244.2.73,要到达10.244.1.0/24网段,需要送达到node2的物理网卡,node2接收以后发现该报文的目标地址属于本机上的另一个虚拟网卡,然后转发到相对应的Pod(10.244.2.73)
以上就是Flannel网络模型的三种工作模式,但是flannel自身并不具备为Pod网络实现网络策略和网络通信隔离的功能,为此只能借助于Calico联合统一的项目Calnal项目进行构建网络策略的功能。
###################################
网络策略(Network Policy )是 Kubernetes 的一种资源。Network Policy 通过 Label选择 Pod并指定其他 Pod 或外界如何与这些 Pod 通信。
Pod的网络流量包含流入(Ingress)和流出(Egress)两种方向。默认情况下,所有 Pod是非隔离的,即任何来源的网络流量都能够访问 Pod,没有任何限制。当为 Pod 定义了 Network Policy,只有 Policy 允许的流量才能访问 Pod。
Kubernetes的网络策略功能也是由第三方的网络插件实现的,因此,只有支持网络策略功能的网络插件才能进行配置网络策略,比如Calico、Canal、kube-router等等。
部署Canal提供网络策略功能
Calico可以独立地为Kubernetes提供网络解决方案和网络策略,也可以和flannel相结合,由flannel提供网络解决方案,Calico仅用于提供网络策略,此时将Calico称为Canal。结合flannel工作时,Calico提供的默认配置清单式以flannel默认使用的10.244.0.0/16为Pod网络,因此在集群中kube-controller-manager启动时就需要通过--cluster-cidr选项进行设置使用该网络地址,并且---allocate-node-cidrs的值应设置为true。
[root@master ~]# kubectl apply -f https://docs.projectcalico.org/v3.2/getting-started/kubernetes/installation/hosted/canal/rbac.yaml
clusterrole.rbac.authorization.k8s.io/calico created
clusterrole.rbac.authorization.k8s.io/flannel unchanged
clusterrolebinding.rbac.authorization.k8s.io/canal-flannel created
clusterrolebinding.rbac.authorization.k8s.io/canal-calico created
[root@master ~]# kubectl apply -f https://docs.projectcalico.org/v3.2/getting-started/kubernetes/installation/hosted/canal/canal.yaml
configmap/canal-config created
daemonset.extensions/canal created
serviceaccount/canal created
customresourcedefinition.apiextensions.k8s.io/felixconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/bgpconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ippools.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/hostendpoints.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/clusterinformations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworksets.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networkpolicies.crd.projectcalico.org created
[root@master ~]# kubectl get ds canal -n kube-system
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
canal 3 3 0 3 0 beta.kubernetes.io/os=linux 16s
[root@master ~]# kubectl get pods -n kube-system -o wide |grep canal
canal-9wbdl 0/3 ContainerCreating 0 28s 10.249.6.102 node02
canal-cr7rx 0/3 ContainerCreating 0 28s 10.249.6.100 master
canal-q7mc2 0/3 ContainerCreating 0 28s 10.249.6.101 node01
[root@master ~]# kubectl get pod -n kube-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
canal-9wbdl 0/3 ContainerCreating 0 4m12s 10.249.6.102 node02
canal-cr7rx 0/3 ContainerCreating 0 4m12s 10.249.6.100 master
canal-q7mc2 0/3 ContainerCreating 0 4m12s 10.249.6.101 node01
coredns-86c58d9df4-fltl9 1/1 Running 2 33d 10.244.0.7 master
coredns-86c58d9df4-kdm2h 1/1 Running 2 33d 10.244.0.8 master
etcd-master 1/1 Running 4 33d 10.249.6.100 master
heapster-5b4b969847-x2rqd 1/1 Running 0 8d 10.244.2.68 node02
kube-apiserver-master 1/1 Running 2 33d 10.249.6.100 master
kube-controller-manager-master 1/1 Running 2 33d 10.249.6.100 master
kube-flannel-ds-amd64-bf7s9 1/1 Running 0 9d 10.249.6.101 node01
kube-flannel-ds-amd64-vm9vw 1/1 Running 4 9d 10.249.6.102 node02
kube-flannel-ds-amd64-xqcg9 1/1 Running 1 9d 10.249.6.100 master
kube-proxy-6fp6m 1/1 Running 1 33d 10.249.6.102 node02
kube-proxy-wv6gg 1/1 Running 0 33d 10.249.6.101 node01
kube-proxy-zndjb 1/1 Running 2 33d 10.249.6.100 master
kube-scheduler-master 1/1 Running 2 33d 10.249.6.100 master
kubernetes-dashboard-57df4db6b-cr5qw 1/1 Running 0 8d 10.244.2.61 node02
monitoring-grafana-564f579fd4-22lrq 1/1 Running 0 8d 10.244.2.66 node02
monitoring-influxdb-8b7d57f5c-5bkrh 1/1 Running 0 8d 10.244.2.67 node02
[root@master ~]# kubectl describe pod canal-9wbdl -n kube-system
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 5m42s default-scheduler Successfully assigned kube-system/canal-9wbdl to node02
Normal Pulling 5m41s kubelet, node02 pulling image "quay.io/calico/node:v3.2.7" 这里一直在努力pull
部署canal需要的镜像,建议先拉取镜像,避免耗死资源:
quay.io/calico/node:v3.2.6
quay.io/calico/cni:v3.2.6
quay.io/coreos/flannel:v0.9.1
我这里等了一晚上,建议先下载
[root@master ~]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
canal-9wbdl 3/3 Running 0 21h
canal-cr7rx 3/3 Running 0 21h
canal-q7mc2 3/3 Running 0 21h
[root@master ~]# kubectl get pod -n kube-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
canal-9wbdl 3/3 Running 0 21h 10.249.6.102 node02
canal-cr7rx 3/3 Running 0 21h 10.249.6.100 master
canal-q7mc2 3/3 Running 0 21h 10.249.6.101 node01
Canal作为DaemonSet部署到每个节点,属于kube-system这个名称空间。需要注意的是,Canal只是直
接使用了Calico和flannel项目,代码本身没有修改,Canal只是一种部署的模式,用于安装和配置项目。
========================