zoukankan      html  css  js  c++  java
  • 一篇文章为你图解Kubernetes网络通信原理

    本文来自51cto因出现文章被删除提示,为避免丢失将其复制备查

    Kubernetes对集群内部的网络进行了重新抽象,以实现整个集群网络扁平化。我们可以理解网络模型时,可以完全抽离物理节点去理解,我们用图说话,先有基本印象。

    名词解释

    1、网络的命名空间:Linux在网络栈中引入网络命名空间,将独立的网络协议栈隔离到不同的命令空间中,彼此间无法通信;docker利用这一特性,实现不容器间的网络隔离。

    2、Veth设备对:也叫虚拟网络接口对。Veth设备对的引入是为了实现在不同网络命名空间的通信。

    3、Iptables/Netfilter:Netfilter负责在内核中执行各种挂接的规则(过滤、修改、丢弃等),运行在内核 模式中;Iptables模式是在用户模式下运行的进程,负责协助维护内核中Netfilter的各种规则表;通过二者的配合来实现整个Linux网络协议栈中灵活的数据包处理机制。

    4、网桥:网桥是一个二层网络设备,通过网桥可以将linux支持的不同的端口连接起来,并实现类似交换机那样的多对多的通信。

    5、路由:Linux系统包含一个完整的路由功能,当IP层在处理数据发送或转发的时候,会使用路由表来决定发往哪里。

    令人头大的网络模型

    Kubernetes对集群内部的网络进行了重新抽象,以实现整个集群网络扁平化。我们可以理解网络模型时,可以完全抽离物理节点去理解,我们用图说话,先有基本印象。

    其中,重点讲解以下几个关键抽象概念。

    一个Service

    Service是Kubernetes为为屏蔽这些后端实例(Pod)的动态变化和对多实例的负载均衡而引入的资源对象。Service通常与deployment绑定,定义了服务的访问入口地址,应用(Pod)可以通过这个入口地址访问其背后的一组由Pod副本组成的集群实例。Service与其后端Pod副本集群之间则是通过Label Selector来实现映射。

    Service的类型(Type)决定了Service如何对外提供服务,根据类型不同,服务可以只在Kubernetes cluster中可见,也可以暴露到集群外部。Service有三种类型,ClusterIP,NodePort和LoadBalancer。具体的使用场景会在下文中进行阐述。

    在测试环境查看:

    $ kubectl get svc --selector app=nginx 
    NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 
    nginx ClusterIP 172.19.0.166 <none> 80/TCP 1m 
    $ kubectl describe svc nginx 
    Name: nginx 
    Namespace: default 
    Labels: app=nginx 
    Annotations: <none> 
    Selector: app=nginx 
    Type: ClusterIP 
    IP: 172.19.0.166 
    Port: <unset> 80/TCP 
    TargetPort: 80/TCP 
    Endpoints: 172.16.2.125:80,172.16.2.229:80 
    Session Affinity: None 
    Events: <none> 

    上述信息中该svc后端代理了2个Pod实例:172.16.2.125:80,172.16.2.229:80

    二个IP

    Kubernetes为描述其网络模型的IP对象,抽象出Cluster IP和Pod IP的概念。

    PodIP是Kubernetes集群中每个Pod的IP地址。它是Docker Engine 根据docker0网桥的IP地址段进行分配的,是一个虚拟的二层网络。Kubernetes中Pod间能够彼此直接通讯,Pod里的容器访问另外一个Pod里的容器,是通过Pod IP所在进行通信。

    Cluster IP仅作用于Service,其没有实体对象所对应,因此Cluster IP无法被ping通。它的作用是为Service后端的实例提供统一的访问入口。当访问ClusterIP时,请求将被转发到后端的实例上,默认是轮询方式。Cluster IP和Service一样由kube-proxy组件维护,其实现方式主要有两种,iptables和IPVS。在1.8版本后kubeproxy开始支持IPVS方式。在上例中,SVC的信息中包含了Cluster IP。

    这里未列出nodeip概念,由于其本身是物理机的网卡IP。因此可理解为nodeip就是物理机IP。

    三个Port

    在Kubernetes中,涉及容器,Pod,Service,集群各等多个层级的对象间的通信,为在网络模型中区分各层级的通信端口,这里对Port进行了抽象。

    Port

    该Port非一般意义上的TCP/IP中的Port概念,它是特指Kubernetes中Service的port,是Service间的访问端口,例如Mysql的Service默认3306端口。它仅对进群内容器提供访问权限,而无法从集群外部通过该端口访问服务。

    nodePort

    nodePort为外部机器提供了访问集群内服务的方式。比如一个Web应用需要被其他用户访问,那么需要配置type=NodePort,而且配置nodePort=30001,那么其他机器就可以通过浏览器访问scheme://node:30001访问到该服务,例如http://node:30001。

    targetPort

    targetPort是容器的端口(最根本的端口入口),与制作容器时暴露的端口一致(DockerFile中EXPOSE),例如docker.io官方的nginx暴露的是80端口。

    举一个例子来看如何配置Service的port:

    kind: Service 
    apiVersion: v1 
    metadata: 
     name: mallh5-service 
     namespace: abcdocker 
    spec: 
     selector: 
     app: mallh5web 
     type: NodePort 
     ports: 
     - protocol: TCP 
     port: 3017 
     targetPort: 5003 
     nodePort: 31122 

    这里举出了一个service的yaml,其部署在abcdocker的namespace中。这里配置了nodePort,因此其类型Type就是NodePort,注意大小写。若没有配置nodePort,那这里需要填写ClusterIP,即表示只支持集群内部服务访问。

    集群内部通信

    单节点通信

    集群单节点内的通信,主要包括两种情况,同一个pod内的多容器间通信以及同一节点不同pod间的通信。由于不涉及跨节点访问,因此流量不会经过物理网卡进行转发。

    通过查看路由表,也能窥见一二:

    root@node-1:/opt/bin# route -n 
    Kernel IP routing table 
    Destination Gateway Genmask Flags Metric Ref Use Iface 
    0.0.0.0 172.23.100.1 0.0.0.0 UG 0 0 0 eth0 
    10.1.0.0 0.0.0.0 255.255.0.0 U 0 0 0 flannel.1 #flannel 网络内跨节点的通信会交给 flannel.1 处理 
    10.1.1.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0 #flannel 网络内节点内的通信会走 docker0 

    1 Pod内通信

    如下图所示:

    这种情况下,同一个pod内共享网络命名空间,容器之间通过访问127.0.0.1:(端口)即可。图中的veth*即指veth对的一端(另一端未标注,但实际上是成对出现),该veth对是由Docker Daemon挂载在docker0网桥上,另一端添加到容器所属的网络命名空间,图上显示是容器中的eth0。

    图中演示了bridge模式下的容器间通信。docker1向docker2发送请求,docker1,docker2均与docker0建立了veth对进行通讯。

    当请求经过docker0时,由于容器和docker0同属于一个子网,因此请求经过docker2与docker0的veth*对,转发到docker2,该过程并未跨节点,因此不经过eth0。

    2 Pod间通信

    同节点pod间通信

    由于Pod内共享网络命名空间(由pause容器创建),所以本质上也是同节点容器间的通信。同时,同一Node中Pod的默认路由都是docker0的地址,由于它们关联在同一个docker0网桥上,地址网段相同,所有它们之间应当是能直接通信的。来看看实际上这一过程如何实现。如上图,Pod1中容器1和容器2共享网络命名空间,因此对pod外的请求通过pod1和Docker0网桥的veth对(图中挂在eth0和ethx上)实现。

    访问另一个pod内的容器,其请求的地址是PodIP而非容器的ip,实际上也是同一个子网间通信,直接经过veth对转发即可。

    跨节点通信

    CNI:容器网络接口

    CNI 是一种标准,它旨在为容器平台提供网络的标准化。不同的容器平台(比如目前的 kubernetes、mesos 和 rkt)能够通过相同的接口调用不同的网络组件。

    目前kubernetes支持的CNI组件种类很多,例如:bridge calico calico-ipam dhcp flannel host-local ipvlan loopback macvlan portmap ptp sample tuning vlan。在docker中,主流的跨主机通信方案主要有一下几种:

    1)基于隧道的overlay网络:按隧道类型来说,不同的公司或者组织有不同的实现方案。docker原生的overlay网络就是基于vxlan隧道实现的。ovn则需要通过geneve或者stt隧道来实现的。flannel最新版本也开始默认基于vxlan实现overlay网络。

    2)基于包封装的overlay网络:基于UDP封装等数据包包装方式,在docker集群上实现跨主机网络。典型实现方案有weave、flannel的早期版本。

    3)基于三层实现SDN网络:基于三层协议和路由,直接在三层上实现跨主机网络,并且通过iptables实现网络的安全隔离。典型的方案为Project Calico。同时对不支持三层路由的环境,Project Calico还提供了基于IPIP封装的跨主机网络实现

    通信方式

    集群内跨节点通信涉及到不同的子网间通信,仅靠docker0无法实现,这里需要借助CNI网络插件来实现。图中展示了使用flannel实现跨节点通信的方式。

    简单说来,flannel的用户态进程flanneld会为每个node节点创建一个flannel.1的网桥,根据etcd或apiserver的全局统一的集群信息为每个node分配全局唯一的网段,避免地址冲突。同时会为docker0和flannel.1创建veth对,docker0将报文丢给flannel.1,。

    Flanneld维护了一份全局node的网络表,通过flannel.1接收到请求后,根据node表,将请求二次封装为UDP包,扔给eth0,由eth0出口进入物理网路发送给目的node。

    在另一端以相反的流程。Flanneld解包并发往docker0,进而发往目的Pod中的容器。

    外部访问集群

    从集群外访问集群有多种方式,比如loadbalancer,Ingress,nodeport,nodeport和loadbalancer是service的两个基本类型,是将service直接对外暴露的方式,ingress则是提供了七层负载均衡,其基本原理将外部流量转发到内部的service,再转发到后端endpoints,在平时的使用中,我们可以依据具体的业务需求选用不同的方式。这里主要介绍nodeport和ingress方式。

    Nodeport

    通过将Service的类型设置为NodePort,就可以在Cluster中的主机上通过一个指定端口暴露服务。注意通过Cluster中每台主机上的该指定端口都可以访问到该服务,发送到该主机端口的请求会被kubernetes路由到提供服务的Pod上。采用这种服务类型,可以在kubernetes cluster网络外通过主机IP:端口的方式访问到服务。

     这里给出一个influxdb的例子,我们也可以针对这个模板去修改成其他的类型:

    kind: Service 
    apiVersion: v1 
    metadata: 
     name: influxdb 
    spec: 
     type: NodePort 
     ports: 
     - port: 8086 
     nodePort: 31112 
     selector: 
     name: influxdb 

    Ingress

    Ingress是推荐在生产环境使用的方式,它起到了七层负载均衡器和Http方向代理的作用,可以根据不同的url把入口流量分发到不同的后端Service。外部客户端只看到foo.bar.com这个服务器,屏蔽了内部多个Service的实现方式。采用这种方式,简化了客户端的访问,并增加了后端实现和部署的灵活性,可以在不影响客户端的情况下对后端的服务部署进行调整。

    其部署的yaml可以参考如下模板:

    apiVersion: extensions/v1beta1 
    kind: Ingress 
    metadata: 
     name: test 
     annotations: 
     ingress.kubernetes.io/rewrite-target: / 
    spec: 
     rules: 
     - host: test.name.com 
     http: 
     paths: 
     - path: /test 
     backend: 
     serviceName: service-1 
     servicePort: 8118 
     - path: /name 
     backend: 
     serviceName: service-2 
     servicePort: 8228

    这里我们定义了一个ingress模板,定义通过test.name.com来访问服务,在虚拟主机test.name.com下面定义了两个Path,其中/test被分发到后端服务s1,/name被分发到后端服务s2。

    集群中可以定义多个ingress,来完成不同服务的转发,这里需要一个ingress controller来管理集群中的Ingress规则。Ingress Contronler 通过与 Kubernetes API 交互,动态的去感知集群中 Ingress 规则变化,然后读取它,按照自定义的规则,规则就是写明了哪个域名对应哪个service,生成一段 Nginx 配置,再写到 Nginx-ingress-control的 Pod 里,这个 Ingress Contronler 的pod里面运行着一个nginx服务,控制器会把生成的nginx配置写入/etc/nginx.conf文件中,然后 reload使用配置生效。

    Kubernetes提供的Ingress Controller模板如下:

    apiVersion: extensions/v1beta1 
    kind: Ingress 
    metadata: 
     name: test 
     annotations: 
     ingress.kubernetes.io/rewrite-target: / 
    spec: 
     rules: 
     - host: foo.bar.com 
     http: 
     paths: 
     - path: /foo 
     backend: 
     serviceName: s1 
     servicePort: 80 
     - path: /bar 
     backend: 
     serviceName: s2 
     servicePort: 80 

    总结及展望

    本文针对kubernetes的网络模型,从一个service,二个IP,三个port出发进行图解。详解kubernetes集群内及集群外部访问方式

  • 相关阅读:
    【HTML5 绘图与动画】使用canvas
    【H5新增元素和文档结构】新的全局属性 1. contentEditable 可编辑内容 2. contextmenu 快捷菜单 3. data 自定义属性 4. draggable 可拖动 5. dropzone 拖动数据 6. hidden 隐藏 7. spellcheck 语法检查 8. translate 可翻译
    【H5新增元素和文档结构】完善旧元素 1. a 超链接 2. ol 有序列表 3. dl 定义列表 4. cite 引用文本 5. small 小号字体 6. iframe 浮动框架 7. script 脚本
    【H5新增元素和文档结构】新的语义信息 1. address 2. time 3. figure 跟 figcaption 4. details 和 summary 5. mark 6. progress 7. meter 8. dialog 9.bdi 10. wbr 11. ruby、rt、rp 12. command
    【H5新增元素跟文档结构】新的文档结构 1. article 文章块 2. section 区块 3. nav 导航条 4. aside 辅助栏 5. main 主要区域 6. header 标题栏 7. hgroup 标题组 8. footer 页脚栏
    5_PHP数组_3_数组处理函数及其应用_9_数组集合运算函数
    【华为云技术分享】鲲鹏弹性云服务器GCC交叉编译环境搭建指南
    【华为云技术分享】7 分钟全面了解位运算
    【华为云技术分享】Linux内核编程环境 (1)
    【华为云技术分享】华为云MySQL 8.0正式商用,全新增强版开源利器强势来袭
  • 原文地址:https://www.cnblogs.com/rengke2002/p/12128388.html
Copyright © 2011-2022 走看看