[TOC]前端
与 Docker 默认的网络模型不一样,Kubernetes 造成了一套本身的网络模型,该网络模型更加适应传统的网络模式,应用可以平滑的从非容器环境迁移到 Kubernetes 环境中。node
自从 Docker 容器出现,容器的网络通讯一直是众人关注的焦点,而容器的网络方案又能够分为两大部分:linux
利用 Net Namespace 能够为 Docker 容器建立隔离的网络环境,容器具备彻底独立的网络栈,与宿主机隔离。也可使 Docker 容器共享主机或者其余容器的网络命名空间。git
咱们在使用docker run
建立 Docker 容器时,可使用--network=
选项指定容器的网络模式,Docker 有如下 4 种网络模式:github
--network=host
指定,不支持多主机;--network=bridge
指定,默认设置,不支持多主机;--network=container:NAME_or_ID
指定,即joiner 容器
,不支持多主机;--network=none
指定,不支持多主机。链接到 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 模式有利也有弊,主要包括如下缺点:网络
Bridge 模式是 Docker 默认的网络模式,也是开发者最经常使用的网络模式。在这种模式下,Docker 为容器建立独立的网络栈,保证容器内的进行使用独立的网络环境,实现容器之间,容器与宿主机之间的网络栈隔离。同时,经过宿主机上的 Docker0 网桥,容器能够与宿主机乃至外界进行网络通讯。
从上图能够看出,容器是能够与宿主机以及外界的其余机器通讯的,同一宿主机上,容器之间都是桥接在 Docker0 这个网桥上,Docker0 做为虚拟交换机使容器间互相通讯。可是,因为宿主机的 IP 地址与容器 veth pair 的 IP 地址均不在同一个网段,故仅仅依靠 veth pair 和 NameSpace 的技术并不足以使宿主机之外的网络主动发现容器的存在。Docker 采用了端口绑定的方式(经过 iptables 的 NAT),将宿主机上的端口流量转发到容器内的端口上,这样一来,外界就能够与容器中的进程进行通讯。
Container 模式是一种特殊的网络模式。该模式下的容器使用其余容器的网络命名空间,网络隔离性会处于 Bridge 模式与 Host 模式之间。也就是说,当容器与其余容器共享网络命名空间时,这两个容器间不存在网络隔离,但他们与宿主机机器其余容器又存在网络隔离。
Container 模式的容器能够经过 localhost 来与同一网络命名空间下的其余容器通讯,传输效率高。这种模式节约了必定数量的网络资源,但并无改变容器与外界的通讯方式。在 Kubernetes 体系架构下引入 Pod 概念,Kubernetes 为 Pod 建立一个基础设施容器,同一 Pod 下的其余容器都以 Container 模式共享这个基础设施容器的网络命名空间,相互之间以 localhost 访问,构成一个统一的总体。
与前几种不一样,None 模式的 Docker 容器拥有本身的 Network Namespace,但并不为 Docker 容器进行网络配置。也就是说,该 Docker 容器没有网卡、IP、路由等信息。须要用户为 Docker容器添加网卡、配置 IP 等。
常见的跨主机通讯方案主要有如下几种:
主机 IP + 端口
的方式访问容器中的服务。显然,这种方式仅能支持网络栈的 4 层及以上的应用,而且容器与宿主机紧耦合,很难灵活地处理问题,可扩展性不佳;容器网络发展到如今,造成了两大阵营:
CNM 和 CNI 是网络规范或者网络体系,并非网络实现,所以不关心容器的网络实现方式,CNM 和 CNI 关心的只是网络管理。
但从网络实现角度,又可分为: **隧道方案:**隧道方案在 IaaS 层的网络中应用也比较多,它的主要缺点是随着节点规模的增加复杂度会提高,并且出了网络问题后跟踪起来比较麻烦,大规模集群状况下这是须要考虑的一个问题。
**路由方案:**通常是基于3层或者2层实现网络隔离和跨主机容器互通的,出了问题也很容易排查。 **Calico:**基于 BGP 协议的路由方案,支持很细致的 ACL 控制(Nerwork Policy),对混合云亲和度比较高。 **Macvlan:**从逻辑和 Kernel 层来看,是隔离性和性能最优的方案。基于二层隔离,因此须要二层路由器支持,大多数云服务商不支持,因此混合云上比较难以实现。
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 都是私有的。
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 数据包装在另外一种网络包里面进行路由转发和通讯。
咱们使用 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 强一些。
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 规则,控制进出容器的数据包,实现业务需求。
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
请查看个人博文 http://blog.51cto.com/wzlinux/2112061 。
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 网络:
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 虚拟机,则不须要额外配置了。