《深入剖析kubernetes》学习笔记(5)——容器网络
32. 单机容器网络
-
一个完整网络栈包含的内容
- 网卡、回环设备、路由表、iptables规则
-
容器与外界通信,有两种可选的网络栈:
- 启动容器时,用参数
-net=host
声明使用宿主机的网络栈【虽然简单,但容易端口冲突】 - 使用自己Network Namespace里的网络栈,即有自己的IP地址和端口【推荐方式】
- 启动容器时,用参数
-
Docker容器通信的基本方式
- Docker默认会在每一个宿主机上创建一个叫docker0的虚拟网桥
- 网桥(Bridge)设备工作在数据链路层,根据MAC地址将数据包转发到网桥不同的端口上
- 各容器通过一个叫做Veth Pair的设备连接到docker0上
- Veth Pair表现为两张成对出现的虚拟网卡(Veth Peer),其中一张网卡发出的数据包,会直接出现在两一张上,即使它们不在同一个Network Namespace下
- 在容器中用ifconfig命令可以看到一个叫eth0的网卡,就是Veth Pair的一端,另一端在docker0上
- 可以简单地将Veth Pair理解为连接不同 Network Namespace 的“网线”
- 数据包流向
- 容器1---->docker0------>容器2
- Docker默认会在每一个宿主机上创建一个叫docker0的虚拟网桥
-
被限制在 Network Namespace 里的容器进程,实际上是通过 Veth Pair 设备 + 宿主机网桥的方式,实现了跟同其他容器的数据交换。
-
Docker的默认配置下,一台宿主机上的docker0无法和另一台宿主机上的docker0互通,即若无额外配置,无法进行跨主机容器通信
33. 容器跨主机通信
- Flannel项目的三种实现模式:UDP、VXLAN、host-gw
Flannel的UDP模式实现思路
-
Flannel在宿主机上创建一个第三层(网络层)虚拟网络设备,即一种Tunnel设备,也叫TUN设备,默认名称为flannel0,用来在操作系统内核和用户进程之间传递IP包
-
Flannel 管理的容器网络里,一台宿主机上的所有容器,都属于一个“子网”,例如Node 1 的子网是 100.96.1.0/24,Node 2 的子网是 100.96.2.0/24,子网和Node的对应关系保存在Etcd中,由此可以通过容器的IP地址获知其运行在哪个容器上
-
消息流:
容器A--->docker0---->flannel0--->flanneld进程加UDP封装--->源宿主机eth0网卡---->目的宿主机eth0网卡---->目的宿主机flanneld进程解封--->...
-
Flannel UDP可以看作是三层(网络层)的Overlay网络,因为跨主机的两个容器间可以用IP直接通信,无需关注容器和宿主机的分布。
-
性能缺点:发送IP包过程,经历3次用户态与内核态之间的数据拷贝,开销太大。
1> 容器A--->docker0
2> flannel0--->flanneld进程 //问题所在,即UDP封装/解封操作在用户态的flanneld进程(port: 8285)中完成
3> flanneld进程--->eth0
Flannel的VXLAN模式实现思路
-
VXLAN,Virtual Extensible LAN(虚拟可扩展局域网),本身就是Linux内核的一部分
-
Flannel在宿主机上创建VXLAN Tunnel End Point(虚拟隧道端点),也叫VTEP设备,名字叫flannel.1设备,用于封装/解封二层数据帧,工作在内核中完成
-
VXLAN设计思想:
- 在现有三层网络上,覆盖一个虚拟的二层网络,使该二层网络上的虚拟机/容器,可以像在同一个LAN下自由通信。
-
当新节点接入Flannel网络时,自动的在其他所有节点上添加:
(1)本节点上的flannel.1的IP路由规则(包含了flannel.1的IP,及其负责接收报文的网段)
(2)本节点上的flannel.1的mac地址
-
消息流
容器A的IP包--->docker0--->flannel.1(封装数据帧)--->eth0---->目的主机eth0--->....
-
flannel.1上完成的数据包封装操作(均在内核态完成):
- 1> 内部帧 = 【目的VTEP的MAC地址】【目的容器的IP地址】+源IP包
- 2> 外部帧 = 【目的主机MAC地址】【目的主机IP地址】【UDP头】【VNI=1】+ 内部帧
-
数据包封装时所需信息来源:
- 目的主机MAC地址:由宿主机网络ARP协议维护,无需flannel参与
- 目的主机IP地址的获取 : flanneld进程维护flannel.1作为“网桥”设备的转发数据库(Forwarding Database, FDB),包含了目的VTEP设备所在宿主机的IP地址
- UDP头:flannel.1封装
- VNI=1:是VTEP设备识别某数据帧是否归自己处理的重要标识
-
VXLAN相比UDP模式的优点:
- VXLAN本身就是LINUX内核的一部分,数据帧的封装/解封操作可以在内核中完成,故只有源容器------>docker0这一次用户态到内核态的切换。
34.kubernetes网络模型和CNI插件
- k8s通过CNI的接口,在每个node上维护了一个单独的网桥替代docker0,叫做CNI网桥,默认名称为cni0。K8S中的网络管理方式和上述的方式几乎没有区别,只是用cni0取代了docker0。
- 用cni0取代docker0的原因:
- 1>k8s没有也不愿意绑定docker的网络管理模型(CNM)
- 2>k8s自身的pod有特殊的infra容器设计,pod创建时先启动infra容器,而后k8s调用cni插件为infra容器的network namespace设置符合预期的网络栈
- Hairpin Mode(发夹模式)
- 在默认情况下,网桥设备是不允许一个数据包从一个端口进来后,再从这个端口发出去的。但是,它允许你为这个端口开启 Hairpin Mode,从而取消这个限制。
- 例如:从容器中访问宿主机8080端口,则IP包经过veth端口进入docker0, 进入宿主机,有要通过docker0的veth端口返回容器,此时要开启docker0的veth端口的发夹模式。
- 部署 Kubernetes 的时候,有一个步骤是安装 kubernetes-cni 包,它的目的就是在宿主机上安装CNI 插件所需的基础可执行文件,位于宿主机的/opt/cni/bin/目录下
35. kubernetes三层网络方案
flannel的host-gw模式
-
基本原理:
- 将每个目的flannel子网的“下一跳”地址设置为该子网对应的宿主机的IP地址
-
核心在于IP包封装成帧发送出去时,根据“下一跳”地址设置目的MAC地址,因此,host-gw模式必须要求宿主机之间二层联通。(由于一般部署环境下,宿主机都在同一个子网下,影响不大)
-
host-gw模式下宿主机自身充当了网关(gateway)的作用,这是"host-gw"名称的含义。
-
Flannel子网和主机信息,保存在etcd中,flanneld进程监听其变化,实时更新路由表即可。
Calico——另一个三层网络方案
-
Calico项目与flannel的host-gw模式基本原理完全一样,不同之处在于:
- 1>flannel由flanneld进程通过etcd维护路由信息,但Calico采用边界网关协议(Border Gateway Protocal,BGP)在集群中分发、共享路由消息。
- 2>flannel会在宿主机上使用cni网桥,而Calico没有cni网桥,而是为每个容器创建veth pair连接到宿主机上,并配备相应路由规则。
-
若在Calico项目下,宿主机之间二层不连通,有两类解决方法:
- 1>打开IPIP模式,宿主机上安装tunl0(IP隧道设备),从容器发出的IP包被封装进宿主机网络的IP包中,经路由器发出
- 2>将宿主机的网关加入BGP Mesh,避免使用IPIP,两种方案
- 1° 所有宿主机和其网关建立BGP Peer关系
- 2° 使用一个或多个独立组件负责搜集整个集群里的所有路由信息,然后通过 BGP 协议同步给网关【推荐方案】
-
三层方案和二层VXLAN方案的比较:
- 相同点:
- 目的都是为了容器跨主机的三层互通
- 不同点:
- 实现方式——三层网络通过配置路由规则中IP包的“下一跳”地址实现,二层方案在IP包外额外封装一层MAC包实现
- 性能——三层网络没有封包拆包的过程,性能更高;二层性能差一些
- 实现工作量——三层网络实现更复杂,需要想办法维护路由规则;二层网络实现更简单,主要工作在linux内核完成
- 相同点:
36. kubernetes的网络隔离能力NetworkPolicy
-
Kubernetes 里的 Pod 默认都是“允许所有”(Accept All)的,即:Pod 可以接收来自任何发送方的请求;或者,向任何接收方发送请求。
-
一旦 Pod 被 NetworkPolicy 选中,那么这个 Pod 就会进入“拒绝所有”(Deny All)的状态,即:这个 Pod 既不允许被外界访问,也不允许对外界发起访问。而 NetworkPolicy 定义的规则,其实就是“白名单”。
-
policyTypes 字段,定义了这个 NetworkPolicy 的类型
- ingress 表示会影响流入(ingress)请求
- egress表示会影响流出(egress)请求。
-
NetworkPolicy 实际上只是宿主机上的一系列 iptables 规则。
-
注:job,cronjob这类计算型pod不需要也不应该对外提供服务,可以拒绝所有流入流量,提高系统安全。
-
K8S中仅有“弱多租户”(soft multi-tenancy)的能力
-
一段NetworkPolicy实例
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: test-network-policy namespace: default spec: podSelector: matchLabels: role: db policyTypes: - Ingress - Egress ingress: - from: - ipBlock: cidr: 172.17.0.0/16 except: - 172.17.1.0/24 - namespaceSelector: matchLabels: project: myproject - podSelector: matchLabels: role: frontend ports: - protocol: TCP port: 6379 egress: - to: - ipBlock: cidr: 10.0.0.0/24 ports: - protocol: TCP port: 5978