Kubernetes & Docker 容器网络终极之战(十四)

[TOC]前端

与 Docker 默认的网络模型不一样,Kubernetes 造成了一套本身的网络模型,该网络模型更加适应传统的网络模式,应用可以平滑的从非容器环境迁移到 Kubernetes 环境中。node

自从 Docker 容器出现,容器的网络通讯一直是众人关注的焦点,而容器的网络方案又能够分为两大部分:linux

  • 单主机的容器间通讯;
  • 跨主机的容器间通讯。

1、单主机 Docker 网络通讯

利用 Net Namespace 能够为 Docker 容器建立隔离的网络环境,容器具备彻底独立的网络栈,与宿主机隔离。也可使 Docker 容器共享主机或者其余容器的网络命名空间。git

咱们在使用docker run建立 Docker 容器时,可使用--network=选项指定容器的网络模式,Docker 有如下 4 种网络模式:github

  • host 模式,使用--network=host指定,不支持多主机;
  • bridge 模式,使用--network=bridge指定,默认设置,不支持多主机;
  • container 模式,使用--network=container:NAME_or_ID指定,即joiner 容器,不支持多主机;
  • none 模式,使用--network=none指定,不支持多主机。

1.一、host 模式

链接到 host 网络的容器共享 Docker host 的网络栈,容器的网络配置与 host 彻底同样。docker

咱们先查看一下主机的网络。数据库

[root@datanode03 ~]# ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:44:8d:48:70  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

enp1s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.1.203  netmask 255.255.255.0  broadcast 192.168.1.255
        inet6 fe80::2e0:70ff:fe92:4779  prefixlen 64  scopeid 0x20<link>
        ether 00:e0:70:92:47:79  txqueuelen 1000  (Ethernet)
        RX packets 46093  bytes 66816291 (63.7 MiB)
        RX errors 0  dropped 1  overruns 0  frame 0
        TX packets 24071  bytes 1814769 (1.7 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 0  (Local Loopback)
        RX packets 170  bytes 107720 (105.1 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 170  bytes 107720 (105.1 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

而后建立 host 网络的容器,再查看容器的网络信息。json

[root@datanode03 ~]# docker run -it --network=host busybox
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
90e01955edcd: Pull complete 
Digest: sha256:2a03a6059f21e150ae84b0973863609494aad70f0a80eaeb64bddd8d92465812
Status: Downloaded newer image for busybox:latest
/ # ifconfig
docker0   Link encap:Ethernet  HWaddr 02:42:44:8D:48:70  
          inet addr:172.17.0.1  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST MULTICAST  MTU:1500  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:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

enp1s0    Link encap:Ethernet  HWaddr 00:E0:70:92:47:79  
          inet addr:192.168.1.203  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::2e0:70ff:fe92:4779/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:45850 errors:0 dropped:1 overruns:0 frame:0
          TX packets:23921 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:66794758 (63.7 MiB)  TX bytes:1783655 (1.7 MiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:170 errors:0 dropped:0 overruns:0 frame:0
          TX packets:170 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:107720 (105.1 KiB)  TX bytes:107720 (105.1 KiB)

在容器中能够看到 host 的全部网卡,而且连 hostname 也是 host 的,能够直接使用宿主机 IP 地址与外界通讯,无需额外进行 NAT 转换。因为容器通讯时,再也不须要经过 Linux Bridge 等方式转发或者数据包的封装,性能上有很大的优点。后端

固然,Host 模式有利也有弊,主要包括如下缺点:网络

  • 容器没有隔离、独立的网络栈:容器因与宿主机共用网络栈而争抢网络资源,而且容器崩溃也可能致使主机崩溃,这再生产环境中是不容许发生的。
  • 端口资源:Docker host 上已经使用的端口就不能再用了。

1.2 Bridge 模式

Bridge 模式是 Docker 默认的网络模式,也是开发者最经常使用的网络模式。在这种模式下,Docker 为容器建立独立的网络栈,保证容器内的进行使用独立的网络环境,实现容器之间,容器与宿主机之间的网络栈隔离。同时,经过宿主机上的 Docker0 网桥,容器能够与宿主机乃至外界进行网络通讯。

从上图能够看出,容器是能够与宿主机以及外界的其余机器通讯的,同一宿主机上,容器之间都是桥接在 Docker0 这个网桥上,Docker0 做为虚拟交换机使容器间互相通讯。可是,因为宿主机的 IP 地址与容器 veth pair 的 IP 地址均不在同一个网段,故仅仅依靠 veth pair 和 NameSpace 的技术并不足以使宿主机之外的网络主动发现容器的存在。Docker 采用了端口绑定的方式(经过 iptables 的 NAT),将宿主机上的端口流量转发到容器内的端口上,这样一来,外界就能够与容器中的进程进行通讯。

1.3 Container 模式

Container 模式是一种特殊的网络模式。该模式下的容器使用其余容器的网络命名空间,网络隔离性会处于 Bridge 模式与 Host 模式之间。也就是说,当容器与其余容器共享网络命名空间时,这两个容器间不存在网络隔离,但他们与宿主机机器其余容器又存在网络隔离。

Container 模式的容器能够经过 localhost 来与同一网络命名空间下的其余容器通讯,传输效率高。这种模式节约了必定数量的网络资源,但并无改变容器与外界的通讯方式。在 Kubernetes 体系架构下引入 Pod 概念,Kubernetes 为 Pod 建立一个基础设施容器,同一 Pod 下的其余容器都以 Container 模式共享这个基础设施容器的网络命名空间,相互之间以 localhost 访问,构成一个统一的总体。

1.四、None 模式

与前几种不一样,None 模式的 Docker 容器拥有本身的 Network Namespace,但并不为 Docker 容器进行网络配置。也就是说,该 Docker 容器没有网卡、IP、路由等信息。须要用户为 Docker容器添加网卡、配置 IP 等。

2、跨主机 Docker 网络通讯分类

2.1 通讯方案

常见的跨主机通讯方案主要有如下几种:

  • Host 模式:容器直接使用宿主机的网络,这样天生就能够支持跨主机主机通讯。这种方式虽然能够解决跨主机通讯问题,但应用场景颇有限,容易出现端口冲突,也没法作到隔离网络环境,一个容器崩溃极可能引发整个宿主机的崩溃;
  • 端口模式:经过绑定容器端口到宿主机端口,跨主机通讯时使用主机 IP + 端口的方式访问容器中的服务。显然,这种方式仅能支持网络栈的 4 层及以上的应用,而且容器与宿主机紧耦合,很难灵活地处理问题,可扩展性不佳;
  • 定义容器网络:使用 Open vSwitch 或 Flannel 等第三方 SDN 工具,为容器构建能够跨主机通讯网络环境。这一类方案通常要求各个主机上的 Docker0 网桥的 cidr 不一样,以免出现 IP 冲突的问题,限制容器在宿主机上可获取的 IP 范围。而且在容器须要对集群外提供服务时,须要比较复杂的配置,对部署实施人员的网络技能要求比较高。

2.二、容器网络规范

容器网络发展到如今,造成了两大阵营:

  • Docker 的 CNM;
  • Google、CoreOS、Kubernetes 主导的 CNI。

CNM 和 CNI 是网络规范或者网络体系,并非网络实现,所以不关心容器的网络实现方式,CNM 和 CNI 关心的只是网络管理。

  • CNM(Container Network Model):CNM 的优点在于原生,容器网络和 Docker 容器生命周期结合紧密;缺点是被 Docker “绑架”。支持 CNM 网络规范的容器网络实现包括:Docker Swarm overlay、Macvlan & IP networkdrivers、Calico、Contiv、Weave等。
  • CNI(Container Network Interface):CNI 的优点是兼容其余容器技术(rkt)以及上层的编排系统(Kubernetes&Mesos)、并且社区活跃势头迅猛;缺点是非 Docker 原生。支持 CNI 的网络规范的容器网络实现包括:Kubernetes、Weave、Macvlan、Calico、Flannel、Contiv、Mesos CNI,由于它们都实现了 CNI 规范,用户不管选择哪一种方案,获得的网络模型都同样,即每一个 Pod 都有独立的 IP,能够直接通讯。区别在于不一样方案的底层实现不一样,有的采用基于 VxLAN 的 Overlay 实现,有的则是 Underlay,性能上有区别。再有就是是否支持 Network Policy。

2.三、网络通讯实现方案

但从网络实现角度,又可分为: **隧道方案:**隧道方案在 IaaS 层的网络中应用也比较多,它的主要缺点是随着节点规模的增加复杂度会提高,并且出了网络问题后跟踪起来比较麻烦,大规模集群状况下这是须要考虑的一个问题。

  • Weave:UDP 广播,本机创建新的 BR,经过 PCAP 互通。
  • Open vSwitch(OVS):基于 VxLAN 和 GRE 协议,可是性能方面损失比较严重。
  • Flannel:UDP 广播,VxLAN。
  • Racher:IPSec。

**路由方案:**通常是基于3层或者2层实现网络隔离和跨主机容器互通的,出了问题也很容易排查。 **Calico:**基于 BGP 协议的路由方案,支持很细致的 ACL 控制(Nerwork Policy),对混合云亲和度比较高。 **Macvlan:**从逻辑和 Kernel 层来看,是隔离性和性能最优的方案。基于二层隔离,因此须要二层路由器支持,大多数云服务商不支持,因此混合云上比较难以实现。

2.四、Kubernetes 网络模型

Kubernetes 采用的是基于扁平地址空间的网络模型,集群中的每一个 Pod 都有本身的 IP 地址,Pod 之间不须要配置 NAT 就能直接通讯。另外,同一个 Pod 中的容器共享 Pod 的 IP,可以经过 localhost 通讯。

这种网络模型对应用开发者和管理员至关友好,应用能够很是方便地从传统网络迁移到 Kubernetes。每一个 Pod 可被看做是一个个独立的系统,而 Pod 中的容器则可被看作同一系统中的不一样进程。

Pod 内容器之间的通讯 当 Pod 被调度到某个节点,Pod 中的全部容器都在这个节点上运行,这些容器共享相同的本地文件系统、IPC 和网络命名空间。

不一样 Pod 之间不存在端口冲突的问题,由于每一个 Pod 都有本身的 IP 地址。当某个容器使用 localhost 时,意味着使用的是容器所属 Pod 的地址空间。

好比 Pod A 有两个容器 container-A1 和 container-A2,container-A1 在端口 1234 上监听,当 container-A2 链接到 localhost:1234,实际上就是在访问 container-A1。这不会与同一个节点上的 Pod B 冲突,即便 Pod B 中的容器 container-B1 也在监听 1234 端口。

Pod 之间的通讯 Pod 的 IP 是集群可见的,即集群中的任何其余 Pod 和节点均可以经过 IP 直接与 Pod 通讯,这种通讯不须要借助任何的网络地址转换、隧道或代理技术。Pod 内部和外部使用的是同一个 IP,这也意味着标准的命名服务和发现机制,好比 DNS 能够直接使用。

Pod 与 Service 的通讯 Pod 间能够直接经过 IP 地址通讯,但前提是 Pod 得知道对方的 IP。在 Kubernetes 集群中, Pod 可能会频繁的销毁和建立,也就是说 Pod 的 IP 不是固定的。为了解决这个问题,Service 提供了访问 Pod 的抽象层。不管后端的 Pod 如何变化,Service 都做为稳定的前端对外提供服务。同时,Service 还提供了高可用和负载均衡功能,Service 负责将请求转发给正确的 Pod。

外部访问 不管是 Pod 的 IP 仍是 Service 的 Cluster IP,它们只能在 Kubernetes 集群中可见,对集群以外的世界,这些 IP 都是私有的。

3、跨主机 Docker 网络

3.1 Flannel 网络方案

Kubernetes 安装方式。

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/bc79dd1505b0c8681ece4de4c0d86c5cd2643275/Documentation/kube-flannel.yml

flannel 是 CoreOS 开发的容器网络解决方案。flannel 为每一个 host 分配一个 subnet,容器今后 subnet 中分配 IP,这些 IP 能够在 host 间路由,容器间无需 NAT 和 port mapping 就能够跨主机通讯。

每一个 subnet 都是从一个更大的 IP 池中划分的,flannel 会在每一个主机上运行一个叫 flanneld 的 agent,其职责就是从池子中分配 subnet。为了在各个主机间共享信息,flannel 用 etcd(与 consul 相似的 key-value 分布式数据库)存放网络配置、已分配的 subnet、host 的 IP 等信息。

数据包如何在主机间转发是由 backend 实现的。flannel 提供了多种 backend,有 UDP、vxlan、host-gw、aws-vpc、gce 和 alloc 路由,最经常使用的有 vxlan 和 host-gw。

Flannel 实质上是一种叠加网络(Overlay Network),也就是将 TCP 数据包装在另外一种网络包里面进行路由转发和通讯。

  1. 容器直接使用目标容器的 IP 访问,默认经过容器内部的 eth0 发送出去;
  2. 报文经过 veth pair 被发送到 vethXXX;
  3. vethXXX 是直接链接到 cni0,报文经过虚拟 bridge cni0 发送出去;
  4. 查找路由表,外部容器 IP 的报文都会转发到 flannel.1 的虚拟网卡,这是一个 P2P 的虚拟网卡,而后报文就被转发到监听在另外一端的 flanneld;
  5. flanneld 经过 etcd 维护了各个节点之间的路由表,把原来的报文 UDP 封装一层,经过配置的 iface 发送出去;
  6. 报文经过主机之间的网络栈找到目标主机;
  7. 报文继续往上送,到达传输层,交给监听的 flanneld 程序处理;
  8. 数据被解包,而后发送给 flannel.1 虚拟网卡;
  9. 查找路由表,发现对应容器的报文要交给 cni0;
  10. cni0 链接到本身的容器,把报文发送过去。

咱们使用 kubectl apply 安装的 flannel 默认的 backend 为 vxlan,host-gw 是 flannel 的另外一个 backend,咱们将前面的 vxlan backend 切换成 host-gw。

与 vxlan 不一样,host-gw 不会封装数据包,而是在主机的路由表中建立到其余主机 subnet 的路由条目,从而实现容器跨主机通讯。要使用 host-gw 首先修改 flannel 的配置 flannel-config.json

kubectl edit cm kube-flannel-cfg -o yaml -n kube-system

找到以下字段进行修改。

net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "host-gw"
      }
    }

host-gw 把每一个主机都配置成网关,主机知道其余主机的 subnet 和转发地址,因为 vxlan 须要对数据包进行额外的打包和拆包,性能会比 vxlan 强一些。

3.二、Calico 网络方案

Kubernetes 安装方式。

kubectl apply -f https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/rbac-kdd.yaml
kubectl apply -f https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml

Calico 把每一个操做系统的协议栈当作一个路由器,而后认为全部的容器是连在这个路由器上的网络终端,在路由器之间运行标准的路由协议——BGP,而后让他们本身去学习这个网络拓扑该如何转发,因此Calico 是一个纯三层的虚拟网络方案,Calico 为每一个容器分配一个 IP,每一个 host 都是 router,把不一样 host 的容器链接起来。与 VxLAN 不一样的是,Calico 不对数据包作额外封装,不须要 NAT 和端口映射,扩展性和性能都很好。

与其余容器网络方案相比,Calico 还有一大优点:network policy。用户能够动态定义 ACL 规则,控制进出容器的数据包,实现业务需求。

  1. **Felix:**运行在每一台 Host 的 agent 进程,主要负责网络接口管理和监听、路由、ARP 管理、ACL 管理和同步、状态上报等;
  2. **Orchestrator Plugin:**编排插件,并非独立运行的某些进程,而是设计与 k8s、OpenStack 等平台集成的插件,如 Neutron’s ML2 plugin 用于用户使用 Neutron API 来管理 Calico,本质是要解决模型和 API 间的兼容性问题;
  3. **Etcd:**Calico 模型的存储引擎;
  4. **BGP Client(BIRD):**Calico 为每一台 Host 部署一个 BGP Client,使用 BIRD 实现,BIRD 是一个单独的持续发展的项目,实现了众多动态路由协议好比 BGP、OSPF、RIP 等。在 Calico 的角色是监听 Host 上由 Felix 注入的路由信息,而后经过 BGP 协议广播告诉剩余 Host 节点,从而实现网络互通;
  5. **BGP Route Reflector(BIRD):**在大型网络规模中,若是仅仅使用 BGP client 造成 mesh 全网互联的方案就会致使规模限制,由于全部节点之间俩俩互联,须要 N^2 个链接,为了解决这个规模问题,能够采用 BGP 的 Router Reflector 的方法,使全部 BGP Client 仅与特定 RR 节点互联并作路由同步,从而大大减小链接数。

3.三、Canal 网络方案

Network Policy Network Policy 是 Kubernetes 的一种资源。Network Policy 经过 Label 选择 Pod,并指定其余 Pod 或外界如何与这些 Pod 通讯。

默认状况下,全部 Pod 是非隔离的,即任何来源的网络流量都可以访问 Pod,没有任何限制。当为 Pod 定义了 Network Policy,只有 Policy 容许的流量才能访问 Pod。

不过,不是全部的 Kubernetes 网络方案都支持 Network Policy。好比 Flannel 就不支持,Calico 是支持的。咱们接下来将用 Canal 来演示 Network Policy。Canal 这个开源项目颇有意思,它用 Flannel 实现 Kubernetes 集群网络,同时又用 Calico 实现 Network Policy。

kubectl apply -f https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/canal/rbac.yaml
kubectl apply -f https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/canal/canal.yaml

3.四、Docker overlay 网络方案

请查看个人博文 http://blog.51cto.com/wzlinux/2112061

3.五、Docker macvlan 网络方案

docker network create -d macvlan --subnet=172.16.86.0/24 --gateway=172.16.86.1 -o parent=enp0s9 mac_net1

macvlan 自己是 linxu kernel 模块,其功能是容许在同一个物理网卡上配置多个 MAC 地址,即多个 interface,每一个 interface 能够配置本身的 IP。macvlan 本质上是一种网卡虚拟化技术,Docker 用 macvlan 实现容器网络就不奇怪了。

macvlan 的最大优势是性能极好,相比其余实现,macvlan 不须要建立 Linux bridge,而是直接经过以太 interface 链接到物理网络。

macvlan 会独占主机的网卡,也就是说一个网卡只能建立一个 macvlan 网络:

但主机的网卡数量是有限的,如何支持更多的 macvlan 网络呢?

docker network create -d macvlan -o parent=enp0s9 mac_net2

好在 macvlan 不只能够链接到 interface(如 enp0s9),也能够链接到 sub-interface(如 enp0s9.xxx)。

VLAN 是现代网络经常使用的网络虚拟化技术,它能够将物理的二层网络划分红多达 4094 个逻辑网络,这些逻辑网络在二层上是隔离的,每一个逻辑网络(即 VLAN)由 VLAN ID 区分,VLAN ID 的取值为 1-4094。

Linux 的网卡也能支持 VLAN(apt-get install vlan),同一个 interface 能够收发多个 VLAN 的数据包,不过前提是要建立 VLAN 的 sub-interface。

好比但愿 enp0s9 同时支持 VLAN10 和 VLAN20,则需建立 sub-interface enp0s9.10 和 enp0s9.20。

在交换机上,若是某个 port 只能收发单个 VLAN 的数据,该 port 为 Access 模式,若是支持多 VLAN,则为 Trunk 模式,因此接下来实验的前提是:

enp0s9 要接在交换机的 trunk 口上。不过咱们用的是 VirtualBox 虚拟机,则不须要额外配置了。

相关文章
相关标签/搜索