Kubernetes指南:网络原理

发布于 2018-01-28 · 本文总共 7462 字 · 阅读大约需要 22 分钟

Kubernetes网络模型

Kubernetes网络模型设计的一个基础原则是:每个Pod拥有一个独立的IP地址,假定所有Pod都在一个可以直接联通的、扁平的网络空间中; 不管他们是否运行在同一个Node中,都要求他们可以直接通过对方的IP访问;

用户不需要额外考虑如何建立Pod之间的连接,也无需考虑将容器端口映射到主机端口等问题;

IP-per-Pod模型:

IP以Pod为单位进行分配,一个Pod内的所有容器共享一个网络堆栈(网络命名空间,包括IP地址、网络设备、配置等都是共享); 按照这个网络原则抽象出来的一个Pod一个IP的设计模型即IP-per-Pod模型;

一个Pod一个IP

好处: 1.兼容过去的应用,很好的利用现有各种域名解析和发现机制;
可以很容易的将已有的应用程序从VM或者虚拟机迁移到容器; 2.同一个Pod内的容器可以通过localhost来连接对方端口;

总的来说,从该模型的端口分配、域名解析、服务发现、负载均衡、应用配置和迁移等角度来看,Pod都能被看作一台独立的“虚拟机”或者“物理机”;

对网络的前提和要求

1.所有容器都可以在不用NAT的方式下同别的容器通信;

2.所有节点和所有容器在不用NAT的方式下同所有容器通信;

Docker网络基础

网络命名空间

在Linux的网络命名空间中可以有自己独立的路由表及独立的iptables设置来提供包转发、NAT及IP包过滤等功能;

Veth设备对

引入Veth设备对是为了在不同的网络命名空间之间通信,利用它可以直接将两个网络命名空间连接起来;

在Veth设备的一端发送数据时,它会将数据直接发送到另一端,并触发另一端的接收操作;

在Docker内部,Veth设备对也是连通容器与宿主机的主要网络设备;

网桥

网桥是一个二层的虚拟网络设备,把若干个网络接口“连接”起来,以使得网络接口之间的报文能够互相转发;

网桥能够解析收发的报文,读取目标MAC地址的信息,和自己记录的MAC表结合,来决策报文的转发目标网络接口。 为了实现这些功能,网桥会学习源MAC地址(二层网桥转发的依据就是MAC地址)。 在转发报文时,网桥只需要向特定的网口进行转发,来避免不必要的网络交互。 如果它遇到一个自己从未学习到的地址,就无法知道这个报文应该向哪个网络接口转发,就将报文广播给所有的网络接口(报文来源的网络接口除外);

Docker自动完成了对网桥的创建和维护;

iptables和Netfilter

在Linux网络协议栈中有一组回调函数挂接点,通过这些挂接点挂接的钩子函数可以在Linux网络栈处理数据包的过程中对数据包进行一些操作, 例如过滤、修改、丢弃等; 整个挂接点技术叫作Netfilter和iptables;

Netfilter负责在内核中执行各种挂接的规则,运行在内核模式中; 而iptables是在用户模式下运行的进程,负责协助和维护内核中Netfilter的各种规则表。 二者互相配合来实现整个Linux网络协议栈中灵活的数据包处理机制;

iptables命令用于协助用户维护各种规则。 在使用Kubernetes、 Docker的过程中,通常都会去查看相关的Netfilter配置;

如何查看规则表:

  • iptables-save: 按照命令的方式打印iptables的内容;

  • iptables-vnL: 以另一种格式显示Netfilter表的内容;

路由

路由功能由IP层维护的一张路由表来实现。 当主机收到数据报文时,它用此表来决策接下来应该做什么操作。 当从网络侧接收到数据报文时,IP层首先会检查报文的IP地址是否与主机自身的地址相同。 如果数据报文中的IP地址是主机自身的地址,那么报文将被发送到传输层相应的协议中。 如果报文中的IP地址不是主机自身的地址,并且主机配置了路由功能,那么报文将被转发,否则,报文将被丢弃;

查看当前的路由表:

ip route list
netstat -rn

在显示的信息中,如果标志是U,则说明是可达路由; 如果标志是G,则说明这个网络接口连接的是网关,否则说明这个接口直连主机;

Docker的网络实现

标准的Docker支持4类网络模式:

  • host模式: 使用–net=host指定;

  • container模式: 使用–net=container:NAME_or_ID指定;

  • none模式: 使用–net=none指定;

  • bridge模式: 使用–net=bridge指定,为默认设置;

在bridge模式下,Docker Daemon第1次启动时会创建一个虚拟的网桥,默认的名称是docker0, 然后按照RPC1918的模型在私有网络空间中给这个网桥分配一个子网;

针对由Docker创建的每一个容器,都会创建一个虚拟的以太网设备(Veth设备对), 其中一端关联到网桥上,另一端使用Linux的网络命名空间技术,映射到容器内的eth0设备, 然后从网桥的地址段内给eth0接口分配一个IP地址;

Docker的网络局限: 从Docker对Linux网络协议栈的操作可以看到,Docker一开始没有考虑到多主机互联的网络解决方案;

Kubernetes网络实现

Kubernetes网络设计主要致力于解决以下场景:

1.容器到容器之间的通信;

2.Pod到Pod之间的通信;

3.Pod到Service之间的通信;

4.集群外部与内部组件之间的通信;

容器到容器的通信

同一个Pod内的容器共享一个网络命名空间,共享同一个Linux协议栈; 对于网络的各类操作,就和他们在同一台机器上一样,可以用localhost地址访问彼此的端口;

pod之间的通信

每个pod有自己唯一的IP地址,可以通过一个扁平的、非NAT网络和其他pod通信

网络由系统管理员或者CNI(Container Network Interface)插件建立

同节点pod通信

关联在同一个docker0网桥上,地址段相同,所以能够直接通信;

Kubernetes会记录所有正在运行Pod的IP分配信息,并将这信息保存在etcd中(作为Service的EndPoint)

不同节点上pod通信

不同Node之间的通信只能通过宿主机的物理网卡进行

  • 支持不同Node之间通信的条件:

1.在整个Kubernetes集群中对Pod的IP分配进行规划,不能有冲突;

2.将Pod的IP和其所在的NodeIP关联起来,通过这个关联让Pod可以互相访问;

连接不同节点上网桥的方式: 1.overlay网络 2.underlay网络 3.三层路由

当报文从一个节点上容器发送到其他节点容器, 报文先通过veth pair, 通过网桥到节点物理适配器(如eth0) 然后通过网线传到其他物理适配器, 再通过网桥,最终经过veth pair到达目标容器

引入容器网络接口

1.Calico

2.Flannel

3.Romana

4.Weave Net

5.其他

Pod和Service网络

Service是如何实现的

Service是Kubernetes最核心的概念,通过创建Service,可以为一组具有相同功能的容器应用提供统一的入口地址; 并且将请求进行负载分发到后端的各个容器应用上;

定义详解

apiVersion: v1    // Required
kind: Service     // Required
metadata:         // Required
  annotations:
    meta.helm.sh/release-name: xxxxx
    meta.helm.sh/release-namespace: testing
  creationTimestamp: "2017-05-30T07:33:14Z"
  labels:
    app.kubernetes.io/instance: xxxx
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: xxxxx
    app.kubernetes.io/version: v1.0.0
  name: my-app-service                  // Required,需符合RFC1035规范
  namespace: testing                   // Required
  resourceVersion: "123456"
  selfLink: /api/v1/namespaces/testing/services/my-app-service
  uid: 6110f1d9-f0b0-11e9-915f-xxxxxx
spec:                                 // Required
  clusterIP: 10.xx.xx.xx
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: http
  selector:           // Required,将选择具有指定label标签的Pod作为管理范围
    app.kubernetes.io/instance: my-app-service
  sessionAffinity: None
  type: ClusterIP       // Required,指定Service的访问方式,
                        // 默认为ClusterIP:虚拟的服务IP地址,该地址用于Kubernetes集群内部的Pod访问,
                        //                在Node上kube-proxy通过设置的iptables规则进行转发
                        // NodePort:利用宿主机的端口,使用Node的IP和端口就可以访问服务
                        // LoadBalancer:使用外接负载均衡器,公有云环境
status:
  loadBalancer: {}

基本用法

一般来说,对外提供服务的应用程序需要通过某种机制实现,对于容器应用,最简单的方式是通过TCP/IP机制及监听IP和端口号来实现;

集群外部访问Pod或者Service

为了能让外部客户端可以访问Pod或者Service,可以将Pod或者Service的端口号映射到宿主机, 这样客户端就可以通过物理机访问容器应用了;

1.将容器应用的端口号映射到物理机

  • 通过设置容器级别的hostPort,将容器应用的端口号映射到物理机
containers:
- name:webapp
  image:xxxx/xxxx
  ports:
  - containerPort: 8080
    hostPort: 8001
  • 设置Pod级别的hostNetwork=true
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2020-05-27T10:48:39Z"
  generateName: xxxxx
  labels:
    app.kubernetes.io/instance: xxxx
    pod-template-hash: xxxxxx
  name: pod-name
  namespace: testing
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: pod-name
    uid: d329c7df-9f2b-11ea-8300-xxxxx
  resourceVersion: "123456"
  selfLink: /api/v1/namespaces/testing/pods/pod-name
  uid: 9f07927c-a007-11ea-8300-xxxx
spec:
  hostNetwork: true
  containers:

2.将Service的端口号映射到物理机

  • 通过设置nodePort映射到物理机,同时设置Service的类型为NodePort
apiVersion: v1
kind: Pod
metadata:
  name: pod-name
spec:
  type: NodePort
  ports:
  - port: 8080
   targetPort: 8080
   ndoePort: 8001
  • 通过设置LoadBalancer映射到云服务提供商的LoadBalancer地址;

仅用于在公有云服务提供商的云平台上设置Service的场景;

DNS服务搭建

为了能够通过服务的名字在集群内部进行服务的互相访问,需要创建一个虚拟的DNS服务来完成服务名到ClusterIP的解析;

CNI网络模型

主流的容器网络模型主要有Docker公司提出的Container Network Model(CNM)模型和 CoreOS公司提出的Container Network Interface(CNI)模型;

CNM模型

CNM模型是由Docker公司提出的容器网络模型;

主要通过Network Sandbox、Endpoint和Network这3个组件进行实现;

CNI模型

CNI是由CoreOS公司提出的另一种容器网络规范,现在已经被Kubernetes、rkt、Apache Mesos、Cloud Foundry和Kurma等项目采纳。 另外,Contiv Networking, Project Calico、Weave、SR-IOV、Cilium、 Infoblox、Multus、Romana、 Plumgrid和Midokura等项目也为CNI提供网络插件的具体实现;

CNI定义的是容器运行环境与网络插件之间的简单接口规范, 通过一个JSON Schema定义CNI插件提供的输入和输出参数。 一个容器可以通过绑定多个网络插件加入多个网络中;

在Kubernetes中使用网络插件

Kubernetes目前支持两种网络插件的实现。

  • CNI插件: 根据CNI规范实现其接口,以与插件提供者进行对接;

  • kubenet插件: 使用bridge和host-local CNI插件实现一个基本的cbr0;

为了在Kubernetes集群中使用网络插件,需要在kubelet服务的启动参数上设置下面两个参数:

  • –network-plugin-dir:kubelet启动时扫描网络插件的目录;

  • –network-plugin:网络插件名称,对于CNI插件,设置为cni即可, 无须关注–network-plugin-dir的路径。 对于kubenet插件,设置为 kubenet,目前仅实现了一个简单的cbr0 Linux网桥;

在设置–network-plugin=”cni”时,kubelet还需设置下面两个参数:

  • –cni-conf-dir:CNI插件的配置文件目录,默认为/etc/cni/net.d。 该目录下配置文件的内容需要符合CNI规范;

  • –cni-bin-dir:CNI插件的可执行文件目录,默认为/opt/cni/bin;

Kubernetes网络策略–Network Policy

为了实现细粒度的容器间网络访问隔离策略,Kubernetes从1.3版本开始, 由SIG-Network小组主导研发了Network Policy机制;

Network Policy的主要功能是对Pod间的网络通信进行限制和准入控制, 设置方式为将Pod的Label作为查询条件,设置允许访问或禁止访问的客户端Pod列表;

网络策略配置说明

网络策略的设置主要用于对目标Pod的网络访问进行限制, 在默认情况下对所有Pod都是允许访问的, 在设置了指向Pod的NetworkPolicy网络策略之后,到Pod的访问才会被限制;

在Namespace级别设置默认的网络策略

在Namespace级别还可以设置一些默认的全局网络策略, 以方便管理员对整个Namespace进行统一的网络策略设置;

NetworkPolicy的发展

Kubernetes从1.12版本开始,引入了对SCTP协议的支持, 可以通过打开–feature-gates=SCTPSupport=true特性开关启用;

开启之后,可以在NetworkPolicy资源对象中设置protocol字段的值为SCTP, 启用对SCTP协议的网络隔离设置。 这要求CNI插件提供对SCTP协议的支持;

开源网络组件

Kubernetes的网络模型假定了所有Pod都在一个可以直接连通的扁平网络空间中;

Flannel

1.协助Kubernetes,给每一个Node上的Docker容器都分配互相不冲突的IP地址;

2.在这些IP地址之间建立一个覆盖网络(Overlay Network), 通过这个覆盖网络,将数据包原封不动地传递到目标容器内;

Flannel首先创建了一个名为flannel0的网桥,而且这个网桥的一端连接docker0网桥, 另一端连接一个叫作flanneld的服务进程;

flanneld进程上连etcd,利用etcd来管理可分配的IP地址段资源,同时监控etcd中每个Pod的实际地址, 并在内存中建立了一个Pod节点路由表; 下连docker0和物理网络,使用内存中的Pod节点路由表,将docker0发给它的数据包包装起来, 利用物理网络的连接将数据包投递到目标flanneld上,从而完成Pod到Pod之间的直接地址通信;

Open vSwitch

Open vSwitch是一个开源的虚拟交换机软件;

Open vSwitch的网桥可以直接建立多种通信通道(隧道), 例如Open vSwitch with GRE/VxLAN。 这些通道的建立可以很容易地通过OVS的配置命令实现;

GRE是点对点的隧道通信方式,所以如果有多个Node,则需要建立N×(N-1)条GRE隧道, 即所有Node组成一个网状网络,实现全网互通;

直接路由

在每个Node的路由表中增加对方所有docker0的路由项;

将docker0和Node的匹配关系配置在Linux操作系统的路由项中, 这样通信发起的Node就能够根据这些路由信息直接找到目标Pod所在的Node,将数据传输过去;

Calico

Calico是一个基于BGP的纯三层的网络方案,与OpenStack、 Kubernetes、AWS、GCE等云平台都能够良好地集成。 Calico在每个计算节点都利用Linux Kernel实现了一个高效的vRouter来负责数据转发。 每个vRouter都通过BGP1协议把在本节点上运行的容器的路由信息向整个Calico网络广播,并自动设置到达其他节点的路由转发规则。 Calico 保证所有容器之间的数据流量都是通过IP路由的方式完成互联互通的。 Calico节点组网时可以直接利用数据中心的网络结构(L2或者L3),不需要额外的NAT、隧道或者Overlay Network, 没有额外的封包解包,能够节约CPU运算,提高网络效率;

Calico在小规模集群中可以直接互联,在大规模集群中可以通过额外的BGP route reflector来完成;

refs

《Kubernetes:The Definitive Guide》




本博客所有文章采用的授权方式为 自由转载-非商用-非衍生-保持署名 ,转载请务必注明出处,谢谢。
声明:
本博客欢迎转发,但请保留原作者信息!
博客地址:邱文奇(qiuwenqi)的博客;
内容系本人学习、研究和总结,如有雷同,实属荣幸!
阅读次数:

文章评论

comments powered by Disqus


章节列表