《深入剖析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
  • 被限制在 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
    
posted @ 2021-02-04 08:41  lwjj  阅读(617)  评论(0编辑  收藏  举报