精华内容
下载资源
问答
  • k8s网络

    2020-12-16 19:43:04
    k8s网络模型 网络的命名空间:Linux在网络栈中引入网络命名空间,将独立的网络协议栈隔离到不同的命令空间中,彼此间无法通信;docker利用这一特性,实现不容器间的网络隔离。 Veth设备对:也叫虚拟网络接口对。...

    k8s网络模型

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

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

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

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

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

    • cni0: cni0是Linux网络桥接设备,所有veth设备都将连接到该桥接器,因此同一节点上的所有Pod都可以相互通信

    Kubernetes网络有一个重要的基本设计原则: 每个Pod拥有唯一的IP

    Pause容器 - 为解决Pod中的网络问题而生

    每个Pod中都运行着一个 Pause 容器,也可称之为根容器,在Pod中第一个启动的容器,其唯一的任务是保留并持有一个网络命名空间(netns),该命名空间被该Pod内的其他容易所共享, 所以说一个 Pod 里面的所有容器,它们看到的网络视图是完全一样的。即:它们看到的网络设备、IP地址、Mac地址等等,跟网络相关的信息,其实全是一份,这一份都来自于 Pod 第一次创建的这个 Infra container。这就是 Pod 解决网络共享的一个解法。

    图片: 在这里插入图片描述

    Pod中容器通信

    因为同一个Pod中的容器共享一个网络命名空间,所以他们之间通过localhost就能互相访问在这里插入图片描述
    图中的veth即指veth对的一端(另一端未标注,但实际上是成对出现),该veth对是由Docker Daemon挂载在docker0网桥上,另一端添加到容器所属的网络命名空间,图上显示是容器中的eth0。
    图中演示了bridge模式下的容器间通信。容器1向容器2发送请求,容器1,容器2均与docker0建立了veth对进行通讯。
    当请求经过docker0时,由于容器和docker0同属于一个子网,因此请求经过容器2与docker0的veth
    对,转发到容器2,该过程并未跨节点,因此不经过Pod的eth0。

    同节点Pod间通信

    第一步是确保同一节点上的Pod可以相互通信,然后可以扩展到跨节点通信、internet上的通信,等等。
    在这里插入图片描述

    1. 对 Pod1 来说,eth0 通过虚拟以太网设备(veth*)连接到 root namespace
    2. 网桥 docker0 中为 veth* 配置了一个网段。一旦数据包到达网桥,网桥使用ARP (opens new window)协议解析出其正确的目标网段 veth*
    3. 网桥 docker0 将数据包发送到 veth*
    4. 数据包到达 veth* 时,被直接转发到 Pod2 的 network namespace 中的 eth0 网络设备

    跨节点Pod间通信

    在这里插入图片描述
    Node1 中的 Pod1 向 Node2 中的 Pod3

    1. 数据包从 Pod1 的网络设备 eth0,该设备通过 veth0 连接到Node1中的 root namespace

    2. 数据包到达Node中的 root namespace 中的网桥 docker0

    3. 网桥上执行 ARP 将会失败,因为与网桥连接的所有设备中,没有与该数据包匹配的 MAC 地址。一旦 ARP 失败,网桥会将数据包发送到默认路由(root namespace 中的 eth0 设备)。此时,数据包离开节点进入网络

    4. 假设网络可以根据各节点的CIDR网段,将数据包路由到正确的节点

    5. 数据包进入目标节点的 root namespace(Node2 上的 eth0)后,通过网桥路由到正确的虚拟网络设备(veth2)

    6. 最终,数据包通过 veth2 发送到对应 Pod3 的 eth0,完成了数据包传递的过程

    参考文章

    展开全文
  • k8s网络一、前言原则:每个Pod都拥有一个独立的IP地址,而且假定所有Pod都在一个可以直接连通的、扁平的网络空间中,不管是否运行在同一Node上都可以通过Pod的IP来访问。网络插件实现:对k8s集群中所有node上的pod做...

    k8s网络

    一、前言

    原则:

    每个Pod都拥有一个独立的IP地址,而且假定所有Pod都在一个可以直接连通的、扁平的网络空间中,不管是否运行在同一Node上都可以通过Pod的IP来访问。

    网络插件实现:

    1. 对k8s集群中所有node上的pod做IP规划,防止IP冲突。因为Pod之间通过Pod IP通信。
    2. 保存规划的Pod IP与node IP映射关系。因为说到底node之间是通过node IP通信。

    常见网络插件:

    flanner 、calico 、canal

    二、flannel网络

    1、Flannel是 CoreOS 团队针对 Kubernetes 设计的一个覆盖网络(Overlay Network)工具,功能是让集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址。

    **在默认的Docker配置中,每个Node的Docker服务会分别负责所在节点容器的IP分配。Node内部得容器之间可以相互访问,但是跨主机(Node)网络相互间是不能通信。Flannel设计目的就是为集群中所有节点重新规划IP地址的使用规则,从而使得不同节点上的容器能够获得"同属一个内网"且"不重复的"IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。

    2、原理 首先,flannel利用Kubernetes API或etcd用于存储整个网络的网络配置,其中最主要的内容为设置转换的网络地址空间。例如,设置整个内部所有容器的IP都取自网段的“ 10.1.0.0” / 16”。 接着,flannel在每个主机中运行flanneld作为代理,它会为所在主机从托管的网络地址空间中,获取一个小的网段子网,本主机内部所有容器的IP地址都重置中分配。 然后,flanneld再将本主机获取的子网以及用作主机间通信的公用IP,同样通过kubernetes API或etcd存储起来。 最后,flannel利用各种后端,例如udp,vxlan,host-gw等,跨主机转发容器间的网络流量,完成容器间的跨主机通信。

    b29b9d47d101e83e5a589accee705f65.png

    3、后端模式

    目前flannel有多种backend管理网络,常用的有三种:hostgw,udp,vxlan,三者差异如下:

    98071b92b79345c1a78c356a522b9fb4.png

    vxlan和UDP的区别是vxlan是内核封包,而UDP是flanneld用户态程序封包,所以UDP的方式性能会稍差; hostgw模式是一种主机网关模式,容器到另外一个主机上容器的网关设置成所在主机的网卡地址,这个和calico非常相似,只不过calico是通过BGP声明,而hostgw是通过中心的etcd分配,所以hostgw是直连模式,不需要通过overlay封包和拆包,性能比较高,但hostgw模式最大的缺点是必须是在一个二层网络中,毕竟下一跳的路由需要在邻居表中,否则无法通行。

    在实际的生产环境总值,最常用的还是vxlan模式,

    4、部署

    ​ 1、master初始化

    ​ kubeadm init --apiserver-advertise-address=192.168.28.128 (有多个网卡,指定自己要用的) --kubernetes-version v1.18.1 --pod-network-cidr=10.244.0.0/16 (flanner默认范围)

    ​ 2、wget https://raw.githubusercowget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml flanner yaml文件下载

    ​ kubectl apply -f kube-flannel.yml 应用flanner

    **node节点在master应用falnner之前需要kubeadm reset ,重新join

    3、 kube-flannel.yml 中后端选择方式 ,默认为vxlan

    a3d0d9ad86b00b91992e35c311ec1778.png

    3、部署成功

    0b57a19ce0a08ee05bd13be9aff3e424.png

    4、验证网络可以使用

    如图,创建两个nginx pod,分别运行在node1和node2上

    b41ad84b4cad1df13358b15ed05859fc.png

    在node1上访问node2上的nginx pod,可以访问

    04bfc7222d4738fc8ccbcc72bfc0df2a.png

    同理,在node2上也访问成功

    591f106591282ecbf59d8afc3b72f5c8.png

    二、NetworkPolicy

    1、起因

    默认情况下,kubernets集群中所有pod之间、pod与节点之间可以互相通信。因此集群虽然提供namesapce用来做多租户隔离,但是如果不配置网络策略,namespace的隔离也仅仅是作用于在kubernetes编排调度时的隔离,实际上不同namespace下的pod还是可以相互串通的。那么我们该如何限制pod的网络通信,防止非法访问呢?

    此时就需要使用Kubernetes提供的networkPolicy,用于隔离不同租户的应用并减少攻击面。networkpolicy通过标签选择器来模拟传统的网络物理隔离,并通过不同的策略完成访问方向的管控。

    2、简介

    Network Policy是kubernetes中的一种资源类型,它从属于某个namespace。其内容从逻辑上看包含两个关键部分,一是pod选择器,基于标签选择相同namespace下的pod,将其中定义的规则作用于选中的pod。另一个就是规则了,就是网络流量进出pod的规则,其采用的是白名单模式,符合规则的通过,不符合规则的拒绝。

    3、NetworkPolicy资源

    主要由三个主要部分构成

    podSelector: 根据命名空间和label来选择需要应用网络策略的pod

    policyTypes: 网络策略类型,数组类型,可选值为Ingress(为入Pod的规则),engress(为出Pod得规则)或者两个都填写.如果未指定则默认值为ingress,engress。

    ingress: 入口白名单规则,可以通过from设置允许进入的流量,子字段ipBlock,namespaceSelector,podSelector等字段提供了对于来源的灵活选择,也可以通过ports可以设置从哪些端口进入的流量。

    engress: 同ingress基本一致,但是from修改为了to,定义的是pod想要访问的外部destination

    4、规则介绍

    4,1 不定义规则不代表没有规则,此时默认规则生效

    默认禁止所有入Pod流量

    apiVersion: networking.k8s.io/v1 
    kind: NetworkPolicy 
    metadata:  
       name: default-deny 
    spec:
      podSelector: {}          
      policyTypes:  
      -  Ingress             ###无规则

    上例中没有定义pod选择器,表示如果namespace下的某个pod没有被任何Network Policy对象选中,则应用此对象,如果被其它Network Policy先中则不应用此对象。

    policyTypes的值为Ingress,表示本例启用Ingress规则。但是本例没有定义具体的Ingress,那就应用默认规则。默认规则禁止所有入pod流量,但例外情况是如果source就是pod运行的节点,则允许通过。

    默认允许所有入pod流量

        apiVersion: networking.k8s.io/v1   
         kind: NetworkPolicy    metadata:
         name: allow-all    spec:
         podSelector: {}
         ingress:  
         - {}     ####有规则,规则为空
     

    同样没有定义pod选择器,意义与上例同。注意ingress的定义,这个是有规则的,只是规则中的条目为空,与默认规则不同,表示全部允许通过。

    默认禁止所有出pod流量

    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-all
    spec:
      podSelector: {}
      egress:
    
      - {}
        policyTypes:
      - Egress

    与默认允许所有入pod流量同,只是流量由入变成出。

    默认禁止所有入出pod流量

    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: default-deny
    spec:
      podSelector: {}
      policyTypes:
    
      - Ingress
      - Egress

    4.2 策略模型

     apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: test-network-policy
      namespace: default
    spec:
      podSelector:
        matchLabels:
          role: db            #default namespace下label包含role=db的pod,都会被隔绝     
      policyTypes:
    
      - Ingress               
      - Egress        
        #######以下为规定入口和出口的流量
        ingress:
      - from:
        - ipBlock:
          cidr: 172.17.0.0/16      ###允许入口的流量
          except:                  ###除此之外的IP
          - 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

    1、default namespace下label包含role=dbpod,都会被隔绝,他们只能建立“满足networkPolicy的ingress和egress描述的连接”。 2、所有属于172.17.0.0/16网段的IP,除了172.17.1.0/24中的ip,其他的都可以与上述pod的6379端口建立tcp连接。 3、所有包含label:project=myprojectnamespace中的pod可以与上述pod的6379端口建立tcp连接; 4、所有default namespace下的label包含role=frontend的pod可以与上述pod的6379端口建立tcp连接; 5、允许上述pod访问网段为10.0.0.0/24的目的IP的5978端口。

    三、Calico网络

    1、Calico作为Kubernets的网络插件,是一个纯三层协议,使用虚拟路由代替虚拟交换,每一台虚拟路由通过BGP协议传播可达消息(路由)到剩余数据中心。

    2、

    dde69793089904bdb9b8686b8081db85.png

    Calico组件:

    Felix: Calico agent,跑在每台需要运行 node节点上,主要负责配置路由及 ACLs 等信息来确保 endpoint 的连通状态;

    etcd: 分布式键值存储,主要负责网络元数据一致性,确保 Calico 网络状态的准确性;

    BGPClient(BIRD): 主要负责把 Felix 写入 kernel 的路由信息分发到当前 Calico 网络,确保 workload 间的通信的有效性;

    BGP Route Reflector(BIRD): 大规模部署时使用,摒弃所有节点互联的 mesh 模式,通过一个或者多个BGP Route Reflector来完成集中式的路由分发;

    3、原理

    Calico把每个操作系统的协议栈认为是一个路由器,然后把所有的容器认为是连在这个路由器上的网络终端,在路由器之间跑标准的路由协议——BGP的协议,然后让它们自己去学习这个网络拓扑该如何转发。所以Calico方案其实是一个纯三层的方案,也就是说让每台机器的协议栈的三层去确保两个容器,跨主机容器之间的三层连通性。

    对于node节点,它在每个节点上会运行两个主要的程序,一个是Felix,它会监听ECTD中心的存储,从它获取事件,比如说用户在这台机器上加了一个IP,或者是分配了一个容器等。接着会在这台机器上创建出一个容器,并将其网卡、IP、MAC都设置好,然后在内核的路由表里面写一条,注明这个IP应该到这张网卡。绿色部分是一个标准的路由程序,它会从内核里面获取哪一些IP的路由发生了变化,然后通过标准BGP的路由协议扩散到整个其他的宿主机上,让外界都知道这个IP在这里,你们路由的时候得到这里来。

    由于Calico是一种纯三层的实现,因此可以避免与二层方案相关的数据包封装的操作,中间没有任何的NAT,没有任何的overlay,所以它的转发效率可能是所有方案中最高的,因为它的包直接走原生TCP/IP的协议栈,它的隔离也因为这个栈而变得好做。因为TCP/IP的协议栈提供了一整套的防火墙的规则,所以它可以通过IPTABLES的规则达到比较复杂的隔离逻辑。

    IP Pool可以使用两种模式:BGP或IPIP。使用IPIP模式时,设置 CALICO_IPV4POOL_IPIP="always",不使用IPIP模式时,设置为"off",此时将使用BGP模式。

    IPIP是一种将各Node的路由之间做一个tunnel,再把两个网络连接起来的模式,启用IPIP模式时,Calico将在各Node上创建一个名为"tunl0"的虚拟网络接口。

    4、网络工作模型

    60ee80731088e6db3613f3a360c3c6cc.png

    从上图可以看出,当容器创建时,calico为容器生成veth pair,一端作为容器网卡加入容器的网络命名空间,并设置IP和掩码,一端直接暴露在宿主机上,并通过设置路由规则,将容器IP暴露到宿主机的通信路由上。于此同时,calico为每个主机分配了一段子网作为容器可分配的IP范围,这样就可以根据子网的CIDR为每个主机生成比较固定的路由规则。

    ****当容器需要跨主机通信时,主要经过下面的简单步骤:

    1、容器流量通过veth pair到达宿主机的网络命名空间上。 2、根据容器要访问的IP所在的子网CIDR和主机上的路由规则,找到下一跳要到达的宿主机IP。 3、流量到达下一跳的宿主机后,根据当前宿主机上的路由规则,直接到达对端容器的veth pair插在宿主机的一端,最终进入容器。

    5、部署

    1) 确认有kunbeadm集群

    **master初始化 :kubeadm init --pod-network-cidr=192.168.0.0/16

    --kubernetes-version=v1.18.1 --apiserver-advertise-address=192.168.28.128(自己IP)

    2)master

    ​ curl https://docs.projectcalico.org/manifests/calico.yaml -O

    ​ kubectl apply -f calico.yaml

    ​ 运行成功后图

    1aac94fb87a270b1d130b03f4d7a4cf6.png

    2)如图,创建两台nginx pod ,分别在node1 node2

    581500795433a3e52dd78077c1cb7d91.png

    在node1 上访问node2 上nginx

    ace1f071e62d6e1c37329a744f2b2159.png

    在node2上访问node1 上nginx

    547571cf5ad3424c88e0cb64642ae6ef.png

    6、calico应用策略

    kubectl get NetworkPolicy --all-namespaces 查看自己创建的策略

    1)、1-1 便于测试,创建了nginx-svc,用来验证nginx pod入口

    ​apiVersion: v1
    kind: Service
    metadata:
      name: nginx-svc
      namespace: policy-demo
    spec:
      selector:
        app: nginx
      type: NodePort
      ports:
    
       - port: 80
         targetPort: 80
         protocol: TCP
         nodePort: 30080

    82acd3978abcdf003e860b401561ba8b.png

    启动隔离前,宿主机可访问到nginx pod的80端口

    1-2 、创建一个其他namespaces 的nginx

    修改用于创建policy-demo命名空间的nginx.yaml ,用来验证policy-demo命名空间的出口流量

    ef169bf1fb9c86a458931c30f8976f89.png

    1-3 、创建一个policy-demo命名空间里的busybox pod,来测试策略访问,验证出口

    kubectl run --namespace=dep access --rm -ti --image busybox /bin/sh

    执行命令,验证可以出口流量

    af31b322153b9dc29fd54e423d851ffc.png

    2)启用隔离

    1-1 该策略将对`policy-demo中的Pod默认拒绝行为,不许进不许出

    vi policy.yaml
    kind: NetworkPolicy
    apiVersion: networking.k8s.io/v1
    metadata:
    name: default-deny
    namespace: policy-demo
    spec:
    podSelector:
     matchLabels: {}

    显示 http://networkpolicy.networking.k8s.io/default-deny created

    测试隔离

    4e6db89ae3beaea2d96561662d7e88ce.png

    *****kubectl delete ns policy-demo 清除环境

    1-2 拒绝所有入口流量,许出不许进

    vim policy.yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: default-deny-ingress
      namespace: policy-demo
    spec:
      podSelector:
        matchLabels: {}
      policyTypes:
      - Ingress

    测试,不能被访问

    bf5f69d121ca32890bd13f0d89eaa80f.png

    在access上测试出口,可以出口

    25e1e97de8b05c71b218e113bc790238.png

    1-3、 许进不许出,允许policy-demo中pod被访问,不能访问别的

    创建符合规则yaml文件

    vi policy.yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-all
      namespace: policy-demo
    spec:
      podSelector: {}
      egress:
      - {}
      policyTypes:
      - Egress

    测试: 可以被访问

    61f04b8c1a133b81f0aa833a7949bf7d.png

    1-4 允许带有定义标签的规则

    vim policy.yaml
    kind: NetworkPolicy
    apiVersion: networking.k8s.io/v1
    metadata:
      name: access-nginx
      namespace: policy-demo     ##此命名空间满足以下条件的可以访问
    spec:
      podSelector:
        matchLabels:
          app: nginx
      ingress:
      - from:
        - podSelector:
            matchLabels:
              access: "true"
    
     kubectl apply -f policy.yaml

    当没有定义访问标签时,测试访问nginx服务,请求会超时,无法访问。

    kubectl run --namespace=policy-demo  access --rm -ti --image busybox /bin/sh

    f3f91d33bcad53bd6fd2f736d843dd59.png
    kubectl run busybox --rm -ti --labels="access=true" --namespace=policy-demo --image=busybox /bin/sh

    83e41264c9b61309227aab6c93dd8536.png
    展开全文
  • K8S网络详解

    千次阅读 2019-01-15 20:51:33
    K8S网络详解 按照我自己的理解,k8s网络分三个层面(由里到外): 容器网络(不同容器之间怎么相互访问) 集群内网络(不同节点之间怎么相互访问) 集群外网络(集群外怎么访问到集群内的服务) 容器网络 熟悉...

    K8S网络详解

    按照我自己的理解,k8s网络分三个层面(由里到外):

    1. 容器网络(不同容器之间怎么相互访问)
    2. 集群内网络(不同节点之间怎么相互访问)
    3. 集群外网络(集群外怎么访问到集群内的服务)

    容器网络


    熟悉docker的同学会知道,docker创建的容器之间会通过一张docker0的网卡相互通信。而k8s社区为了方便用户适应各自的网络情况,暴露出一套api接口------cni,用户只要实现了这套接口就可以自定义容器网络配置策略。

    CNI(Container Network Interface)是由一组用于配置Linux容器的网络接口的规范和库组成,同时还包含了一些插件。CNI仅关心容器创建时的网络分配,和当容器被删除时释放网络资源。通过此链接浏览该项目:https://github.com/containernetworking/cni。

    1. cni设计考量点
      • 容器运行时必须在调用任何插件之前为容器创建一个新的网络命名空间。
      • 然后,运行时必须确定这个容器应属于哪个网络,并为每个网络确定哪些插件必须被执行。
      • 网络配置采用JSON格式,可以很容易地存储在文件中。网络配置包括必填字段,如name和type以及插件(类型)。网络配置允许字段在调用之间改变值。为此,有一个可选的字段args,必须包含不同的信息。
      • 容器运行时必须按顺序为每个网络执行相应的插件,将容器添加到每个网络中。
      • 在完成容器生命周期后,运行时必须以相反的顺序执行插件(相对于执行添加容器的顺序)以将容器与网络断开连接。
      • 容器运行时不能为同一容器调用并行操作,但可以为不同的容器调用并行操作。
      • 容器运行时必须为容器订阅ADD和DEL操作,这样ADD后面总是跟着相应的DEL。 DEL可能跟着额外的DEL,但是,插件应该允许处理多个DEL(即插件DEL应该是幂等的)。
      • 容器必须由ContainerID唯一标识。存储状态的插件应该使用(网络名称,容器ID)的主键来完成。
      • 运行时不能调用同一个网络名称或容器ID执行两次ADD(没有相应的DEL)。换句话说,给定的容器ID必须只能添加到特定的网络一次。
    2. cni插件
      • bridge:创建网桥,并添加主机和容器到该网桥
      • ipvlan:在容器中添加一个ipvlan接口
        ipvlan类似于macvlan,区别在于端点具有相同的mac地址。 ipvlan支持L2(链路层)和L3(网络层)模式。 在ipvlan L2模式下,每个端点获得相同的mac地址但不同的ip地址。 在ipvlan l3模式下,数据包在端点之间路由,提供了更好的可伸缩性。
      • loopback:创建一个回环接口
      • macvlan:创建一个新的MAC地址,将所有的流量转发到容器
        以往,我们只能为一块以太网卡添加多个IP地址,却不能添加多个MAC地址,因为MAC地址正是通过其全球唯一性来标识一块以太网卡的,即便你使用了创建ethx:y这样的方式,你会发现所有这些“网卡”的MAC地址和ethx都是一样的,本质上,它们还是一块网卡,这将限制你做很多二层的操作。有了MACVLAN技术,你可以这么做了。
        MACVLAN技术是一种将一块以太网卡虚拟成多块以太网卡的极简单的方案。
      • ptp:创建veth对
      • vlan:分配一个vlan设备
        VLAN 技术主要就是在二层数据包的包头加上tag 标签,表示当前数据包归属的vlan 号。VLAN的主要优点: (1)广播域被限制在一个VLAN内,节省了带宽,提高了网络处理能力。 (2)增强局域网的安全性:VLAN间不能直接通信,即一个VLAN内的用户不能和其它VLAN内的用户直接通信,而需要通过路由器或三层交换机等三层设备。 (3)灵活构建虚拟工作组:用VLAN可以划分不同的用户到不同的工作组,同一工作组的用户也不必局限于某一固定的物理范围,网络构建和维护更方便灵活。
    3. ip分配
      作为容器网络管理的一部分,CNI插件需要为接口分配(并维护)IP地址,并安装与该接口相关的所有必要路由。这给了CNI插件很大的灵活性,但也给它带来了很大的负担。众多的CNI插件需要编写相同的代码来支持用户需要的多种IP管理方案(例如dhcp、host-local)。
      为了减轻负担,使IP管理策略与CNI插件类型解耦,我们定义了IP地址管理插件(IPAM插件)。CNI插件的职责是在执行时恰当地调用IPAM插件。 IPAM插件必须确定接口IP/subnet,网关和路由,并将此信息返回到“主”插件来应用配置。 IPAM插件可以通过协议(例如dhcp)、存储在本地文件系统上的数据、网络配置文件的“ipam”部分或上述的组合来获得信息。

    集群内网络


    k8s定义了一种资源Service,Kubernetes Service 定义了这样一种抽象:一个 Pod 的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务。 这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector实现的。

    对 Kubernetes 集群中的应用,Kubernetes 提供了简单的 Endpoints API,只要 Service 中的一组 Pod发生变更,应用程序就会被更新。 对非 Kubernetes 集群中的应用,Kubernetes 提供了基于 VIP(虚拟IP) 的网桥的方式访问 Service,再由 Service 重定向到相应的 Pod。

    在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy(服务代理) 进程。kube-proxy 负责为 Service 实现了一种 VIP(虚拟 IP)的形式,而不是 ExternalName 的形式。

    服务代理模式主要分三种:

    1. userspace 代理模式
      kube-proxy 会监视 Kubernetes master 对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会在本地 Node 上打开一个端口(随机选择)。 任何连接到“代理端口”的请求,都会被代理到 Service 的 Pods 中的某个上面。 使用哪个 Pod,是基于 Service 的 SessionAffinity 来确定的。 最后,它安装 iptables 规则,捕获到达该 Service 的 clusterIP(是虚拟 IP)和 Port 的请求,并重定向到代理端口,代理端口再代理请求到 backend Pod。
      在这里插入图片描述
    2. iptables代理模式
      kube-proxy 会监视 Kubernetes master 对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会安装 iptables 规则,从而捕获到达该 Service 的 clusterIP(虚拟 IP)和端口的请求,进而将请求重定向到 Service 的一组 backend 中的某个上面。 对于每个 Endpoints 对象,它也会安装 iptables 规则,这个规则会选择一个 backend Pod。
      在这里插入图片描述
    3. ipvs代理模式
      kube-proxy会监视Kubernetes Service对象和Endpoints,调用netlink接口以相应地创建ipvs规则并定期与Kubernetes Service对象和Endpoints对象同步ipvs规则,以确保ipvs状态与期望一致。访问服务时,流量将被重定向到其中一个后端Pod。
      在这里插入图片描述

    集群外网络


    Ingress 是从Kubernetes集群外部访问集群内部服务的入口。

    通常情况下,service和pod仅可在集群内部网络中通过IP地址访问。所有到达边界路由器的流量或被丢弃或被转发到其他地方。Ingress是授权入站连接到达集群服务的规则集合。

    你可以给Ingress配置提供外部可访问的URL、负载均衡、SSL、基于名称的虚拟主机等。用户通过POST Ingress资源到API server的方式来请求ingress。 Ingress controller负责实现Ingress,通常使用负载平衡器,它还可以配置边界路由和其他前端,这有助于以HA方式处理流量。

    展开全文
  • K8s网络模型

    2020-05-26 10:12:29
    k8s网络模型设计基础原则:每个Pod都拥有一个独立的 IP地址,而且 假定所有 Pod 都在一个可以直接连通的、扁平的网络空间中 。 所以不管它们是否运行在同 一 个 Node (宿主机)中,都要求它们可以直接通...
    •  

    一、前言

    k8s对Pods之间如何进行组网通信提出了要求,k8s对集群的网络有以下要求:

    • 所有的Pods之间可以在不使用NAT网络地址转换的情况下相互通信
    • 所有的Nodes之间可以在不使用NAT网络地址转换的情况下相互通信
    • 每个Pod自己看到的自己的ip和其他Pod看到的一致

    k8s网络模型设计基础原则:每个Pod都拥有一个独立的 IP地址,而且 假定所有 Pod 都在一个可以直接连通的、扁平的网络空间中 。 所以不管它们是否运行在同 一 个 Node (宿主机)中,都要求它们可以直接通过对方的 IP 进行访问。设计这个原则的原因 是,用户不需要额外考虑如何建立 Pod 之间的连接,也不需要考虑将容器端口映射到主机端口等问题。

    由于 Kubemetes 的网络模型假设 Pod 之间访问时使用的是对方 Pod 的实际地址,所以一个
    Pod 内部的应用程序看到的自己的 IP 地址和端口与集群内其他 Pod 看到的一样。它们都是 Pod 实际分配的IP地址 (从dockerO上分配的)。将IP地址和端口在Pod内部和外部都保持一致, 我们可以不使用 NAT 来进行转换,地址空间也自然是平的。

    鉴于上面这些要求,我们需要解决四个不同的网络问题::

    • Docker容器和Docker容器之间的网络
    • Pod与Pod之间的网络
    • Pod与Service之间的网络
    • Internet与Service之间的网络

    下面我们一一进行讨论每种网络问题,以及如何解决。

    二、容器和容器之间的网络

    image.png

    • 在k8s中每个Pod中管理着一组Docker容器,这些Docker容器共享同一个网络命名空间。
    • Pod中的每个Docker容器拥有与Pod相同的IP和port地址空间,并且由于他们在同一个网络命名空间,他们之间可以通过localhost相互访问。
      什么机制让同一个Pod内的多个docker容器相互通信那?其实是使用Docker的一种网络模型:–net=container

    container模式指定新创建的Docker容器和已经存在的一个容器共享一个网络命名空间,而不是和宿主机共享。新创建的Docker容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等

    每个Pod容器有有一个pause容器其有独立的网络命名空间,在Pod内启动Docker容器时候使用 –net=container就可以让当前Docker容器加入到Pod容器拥有的网络命名空间(pause容器)

    image.png

    三、Pod与Pod之间的网络

    image.png

    • k8s中,每个Pod拥有一个ip地址,不同的Pod之间可以直接使用改ip与彼此进行通讯
    • 在同一个Node上,从Pod的视角看,它存在于自己的网络命名空间中,并且需要与该Node上的其他网络命名空间上的Pod进行通信。

    那么是如何做到的?这多亏了使用linux虚拟以太网设备或者说是由两个虚拟接口组成的veth对使不同的网络命名空间链接起来,这些虚拟接口分布在多个网络命名空间上(这里是指多个Pod上)。

    为了让多个Pod的网络命名空间链接起来,我们可以让veth对的一端链接到root网络命名空间(宿主机的),另一端链接到Pod的网络命名空间。

    每对Veth就像一根接插电缆,连接两侧并允许流量在它们之间流动;这种veth对可以推广到同一个Node上任意多的Pod上,如上图这里展示使用veth对链接每个Pod到虚拟机的root网络命名空间。

    下面我们看如何使用网桥设备来让通过veth对链接到root命名空间的多个Pod进行通信。

    linux以太网桥(Linux Ethernet bridge)是一个虚拟的2层网络设备,目的是把多个以太网段链接起来,网桥维护了一个转发表,通过检查转发表通过它传输的数据包的目的地并决定是否将数据包传递到连接到网桥的其他网段,网桥代码通过查看网络中每个以太网设备特有的MAC地址来决定是传输数据还是丢弃数据。

    image.png

    网桥实现了ARP协议用来根据给定的ip地址找到对应机器的数据链路层的mac地址,一开始转发表为空,当一个数据帧被网桥接受后,网桥会广播该帧到所有的链接设备(除了发送方设备),并且把响应这个广播的设备记录到转发表;随后发往相同ip地址的流量会直接从转发表查找正确的mac地址,然后转发包到对应的设备。

    image.png

    如上图显示了两个Pod通过veth对链接到root网络命名空间,并且通过网桥进行通信

    3.1 同一个Node中的Pod之间的一次通信

    鉴于每个Pod有自己独立的网络命名空间,我们使用虚拟以太网设备把多个Pod的命名空间链接到了root命名空间,并且使用网桥让多个Pod之间进行通信,下面我们看如何在两个pod之间进行通信:

    image.png

    • 通过网桥这里把veth0和veth1组成为一个以太网,他们直接是可以直接通信的,另外这里通过veth对让pod1的eth0和veth0、pod2的eth0和veth1关联起来,从而让pod1和pod2相互通信。
    • Pod 1通过自己默认的以太网设备eth0发送一个数据包,eth0把数据传递给veth0,数据包到达网桥后,网桥通过转发表把数据传递给veth1,然后虚拟设备veth1直接把包传递给Pod2网络命名空间中的虚拟设备eth0.

    3.2 不同Node中的Pod之间通信

    k8s网络模型需要每个pod必须通过ip地址可以进行访问,每个pod的ip地址总是对网络中的其他pod可见,并且每个pod看待自己的ip与别的pod看待的是一样的(虽然他没规定如何实现),下面我们看不同Node间Pod如何交互

    k8s中每个集群中的每个Node都会被分配了一个CIDR块(无类别域间路由选择,把网络前缀都相同的连续地址组成的地址组称为CIDR地址块)用来给该Node上的Pod分配IP地址。(保证pod的ip不会冲突)
    另外还需要把pod的ip与所在的nodeip关联起来()

     

    image

    • 如上图Node1(vm1)上的Pod1与Node2(vm2)上Pod4之间进行交互。
    • 首先pod1通过自己的以太网设备eth0把数据包发送到关联到root命名空间的veth0上,然后数据包被Node1上的网桥设备cbr0接受到,网桥查找转发表发现找不到pod4的Mac地址,则会把包转发到默认路由(root命名空间的eth0设备),然后数据包经过eth0就离开了Node1,被发送到网络。
    • 数据包到达Node2后,首先会被root命名空间的eth0设备,然后通过网桥cbr0把数据路由到虚拟设备veth1,最终数据表会被流转到与veth1配对的另外一端(pod4的eth0)

    每个Node都知道如何把数据包转发到其内部运行的Pod,当一个数据包到达Node后,其内部数据流就和Node内Pod之间的流转类似了。

    对于如何来配置网络,k8s在网络这块自身并没有实现网络规划的具体逻辑,而是制定了一套CNI(Container Network Interface)接口规范,开放给社区来实现。

    例如AWS,亚马逊为k8s维护了一个容器网络插件,使用CNI插件来让亚马逊VPC(虚拟私有云)环境中的Node与Node直接进行交互.

    CoreOS的Flannel是k8s中实现CNI规范较为出名的一种实现。

    3.2.1 VXLAN

    • UDP帧格式

       

      image.png

    • vlan

       

      image.png

    image.png

    • vxlan协议格式
      VXLAN 全称是 Virtual eXtensible Local Area Network,虚拟可扩展的局域网。它是一种 overlay 技术,通过三层的网络来搭建虚拟的二层网络,其帧格式:

       

      image.png

    从这个报文中可以看到三个部分:
    1.最外层的 UDP 协议报文用来在底层网络上传输,也就是 vtep 之间互相通信的基础
    2.中间是 VXLAN 头部,vtep 接受到报文之后,去除前面的 UDP 协议部分,根据这部分来处理 vxlan 的逻辑,主要是根据 VNI 发送到最终的虚拟机
    3.最里面是原始的报文,也就是虚拟机看到的报文内容

     

    image.png

    • VTEP(VXLAN Tunnel Endpoints):vxlan 网络的边缘设备,用来进行 vxlan 报文的处理(封包和解包)。vtep 可以是网络设备(比如交换机),也可以是一台机器(比如虚拟化集群中的宿主机)

    • VNI(VXLAN Network Identifier):VNI 是每个 vxlan 的标识,是个 24 位整数,一共有 2^24 = 16,777,216(一千多万),一般每个 VNI 对应一个租户,也就是说使用 vxlan 搭建的公有云可以理论上可以支撑千万级别的租户

    • Tunnel:隧道是一个逻辑上的概念,在 vxlan 模型中并没有具体的物理实体想对应。隧道可以看做是一种虚拟通道,vxlan 通信双方(图中的虚拟机)认为自己是在直接通信,并不知道底层网络的存在。从整体来说,每个 vxlan 网络像是为通信的虚拟机搭建了一个单独的通信通道,也就是隧道

    3.2.1 Flannel

    Flannel是CoreOS团队针对Kubernetes设计的一个网络规划实现,简单来说,它的功能有以下几点:

    • 使集群中的不同Node主机创建的Docker容器都具有全集群唯一的虚拟IP地址。

    • 建立一个覆盖网络(overlay network),通过这个覆盖网络,将数据包原封不动的传递到目标容器。覆盖网络是建立在另一个网络之上并由其基础设施支持的虚拟网络。覆盖网络通过将一个分组封装在另一个分组内来将网络服务与底层基础设施分离。在将封装的数据包转发到端点后,将其解封装。

    • 创建一个新的虚拟网卡flannel0接收docker网桥的数据,通过维护路由表,对接收到的数据进行封包和转发(vxlan)。

    • 路由信息一般存放到etcd:多个node上的Flanneld依赖一个etcd cluster来做集中配置服务,etcd保证了所有node上flanned所看到的配置是一致的。同时每个node上的flanned监听etcd上的数据变化,实时感知集群中node的变化

    • Flannel首先会在Node上创建一个名为flannel0的网桥(vxlan类型的设备),并且在每个Node上运行一个名为flanneld的代理.每个node上的flannel代理会从etcd上为当前node申请一个CIDR地址块用来给该node上的pod分配地址。

    • Flannel致力于给k8s集群中的nodes提供一个3层网络,他并不控制node中的容器是如何进行组网的,仅仅关心流量如何在node之间流转

      image.png

       

    • 如上图ip为10.1.15.2的pod1与另外一个Node上的10.1.20.3的pod2进行通信。

    • 首先pod1通过veth对把数据包发送到docker0虚拟网桥,网桥通过查找转发表发现10.1.20.3不在自己管理的网段,就会把数据包
      转发给默认路由(这里为flannel0网桥)

    • flannel.0网桥是一个vxlan设备,flannel.0收到数据包后,由于自己不是目的地10.1.20.3,也要尝试将数据包重新发送出去。数据包沿着网络协议栈向下流动,在二层时需要封二层以太包,填写目的mac地址,这时一般应该发出arp:”who is 10.1.20.3″。但vxlan设备的特殊性就在于它并没有真正在二层发出这个arp包,而是由linux kernel引发一个”L3 MISS”事件并将arp请求发到用户空间的flanned程序。

    • flanned程序收到”L3 MISS”内核事件以及arp请求(who is 10.1.20.3)后,并不会向外网发送arp request,而是尝试从etcd查找该地址匹配的子网的vtep信息,也就是会找到node2上的flanel.0的mac地址信息,flanned将查询到的信息放入node1 host的arp cache表中,flanneel0完成这项工作后,linux kernel就可以在arp table中找到 10.1.20.3对应的mac地址并封装二层以太包了:

    image.png

    • 由于是Vlanx设备,flannel0还会对上面的包进行二次封装,封装新的以太网mac帧:

       

      image.png

    • node上2的eth0接收到上述vxlan包,kernel将识别出这是一个vxlan包,于是拆包后将packet转给node上2的flannel.0。flannel.0再将这个数据包转到docker0,继而由docker0传输到Pod2的某个容器里。

    如上图,总的来说就是建立VXLAN 隧道,通过UDP把IP封装一层直接送到对应的节点,实现了一个大的 VLAN。

    4.Pod与Service之间的网络

    上面展示了Pod之间如何通过他们自己的ip地址进行通信,但是pod的ip地址是不持久的,当集群中pod的规模缩减或者pod故障或者node故障重启后,新的pod的ip就可能与之前的不一样的。所以k8s中衍生出来Service来解决这个问题。

    k8s中 Service管理了一系列的Pods,每个Service有一个虚拟的ip,要访问service管理的Pod上的服务只需要访问你这个虚拟ip就可以了,这个虚拟ip是固定的,当service下的pod规模改变、故障重启、node重启时候,对使用service的用户来说是无感知的,因为他们使用的service的ip没有变。

    当数据包到达Service虚拟ip后,数据包会被通过k8s给该servcie自动创建的负载均衡器路由到背后的pod容器。下面我们看看具体是如何做到的

    4.1 netfilter

    为了实现负载均衡,k8s依赖linux内建的网络框架-netfilter。Netfilter是Linux提供的内核态框架,允许使用者自定义处理接口实现各种与网络相关的操作。 Netfilter为包过滤,网络地址转换和端口转换提供各种功能和操作,以及提供禁止数据包到达计算机网络内敏感位置的功能。在linux内核协议栈中,有5个跟netfilter有关的钩子函数,数据包经过每个钩子时,都会检查上面是否注册有函数,如果有的话,就会调用相应的函数处理该数据包

     

             |
             | Incoming
             ↓
    +-------------------+
    | NF_IP_PRE_ROUTING |
    +-------------------+
             |
             |
             ↓
    +------------------+
    |                  |         +----------------+
    | routing decision |-------->| NF_IP_LOCAL_IN |
    |                  |         +----------------+
    +------------------+                 |
             |                           |
             |                           ↓
             |                  +-----------------+
             |                  | local processes |
             |                  +-----------------+
             |                           |
             |                           |
             ↓                           ↓
     +---------------+          +-----------------+
     | NF_IP_FORWARD |          | NF_IP_LOCAL_OUT |
     +---------------+          +-----------------+
             |                           |
             |                           |
             ↓                           |
    +------------------+                 |
    |                  |                 |
    | routing decision |<----------------+
    |                  |
    +------------------+
             |
             |
             ↓
    +--------------------+
    | NF_IP_POST_ROUTING |
    +--------------------+
             |
             | Outgoing
             ↓
    
    • NF_IP_PRE_ROUTING: 接收的数据包刚进来,还没有经过路由选择,即还不知道数据包是要发给本机还是其它机器。

    • NF_IP_LOCAL_IN: 已经经过路由选择,并且该数据包的目的IP是本机,进入本地数据包处理流程。

    • NF_IP_FORWARD: 已经经过路由选择,但该数据包的目的IP不是本机,而是其它机器,进入forward流程。

    • NF_IP_LOCAL_OUT: 本地程序要发出去的数据包刚到IP层,还没进行路由选择。

    • NF_IP_POST_ROUTING: 本地程序发出去的数据包,或者转发(forward)的数据包已经经过了路由选择,即将交由下层发送出去。

    netfilter是工作在那一层?

    4.2 iptables

    iptables是运行在用户态的用户程序,其基于表来管理规则,用于定义使用netfilter框架操作和转换数据包的规则。根据rule的作用分成了好几个表,比如用来过滤数据包的rule就会放到filter表中,用于处理地址转换的rule就会放到nat表中,其中rule就是应用在netfilter钩子上的函数,用来修改数据包的内容或过滤数据包。目前iptables支持的表有下面这些:

    • Filter:从名字就可以看出,这个表里面的rule主要用来过滤数据,用来控制让哪些数据可以通过,哪些数据不能通过,它是最常用的表。

    • NAT(*):里面的rule都是用来处理网络地址转换的,控制要不要进行地址转换,以及怎样修改源地址或目的地址,从而影响数据包的路由,达到连通的目的,这是家用路由器必备的功能。

    • Mangle:里面的rule主要用来修改IP数据包头,比如修改TTL值,同时也用于给数据包添加一些标记,从而便于后续其它模块对数据包进行处理(这里的添加标记是指往内核skb结构中添加标记,而不是往真正的IP数据包上加东西)。

    • Raw:在netfilter里面有一个叫做connection tracking的功能,主要用来追踪所有的连接,而raw表里的rule的功能是给数据包打标记,从而控制哪些数据包不被connection tracking所追踪。

    • Security:里面的rule跟SELinux有关,主要是在数据包上设置一些SELinux的标记,便于跟SELinux相关的模块来处理该数据包。

    在k8s中,iptables规则由kube-proxy控制器配置,该控制器监视K8s API服务器的更改。当对Service或Pod的虚拟IP地址进行修改时,iptables规则也会更新以便让service能够正确的把数据包路由到后端Pod。

    iptables规则监视发往Service虚拟IP的流量,并且在匹配时,从可用Pod集合中选择随机Pod IP地址,iptables规则将数据包的目标IP地址从Service的虚拟IP更改为选定的Pod的ip。总的来说iptables已在机器上完成负载平衡,并将指向Servcie的虚拟IP的流量转移到实际的pod的IP。

    在从service到pod的路径上,IP地址来自目标Pod。 在这种情况下,iptables再次重写IP头以将Pod IP替换为Service的IP,以便Pod认为它一直与Service的虚拟IP通信。

    4.3 IPVS

    k8s的最新版本(1.11)包括了用于集群内负载平衡的第二个选项:IPVS。 IPVS(IP Virtual Server)也构建在netfilter之上,并实现传输层负载平衡(属于Linux内核的一部分)。 IPVS包含在LVS(Linux虚拟服务器)中,它在主机上运行,并在真实服务器集群前充当负载均衡器。 IPVS可以将对基于TCP和UDP的服务的请求定向到真实服务器,并使真实服务器的服务在单个IP地址上显示为虚拟服务。这使得IPVS非常适合Kubernetes服务。

    声明Kubernetes服务时,您可以指定是否要使用iptables或IPVS完成群集内负载平衡。 IPVS专门用于负载平衡,并使用更高效的数据结构(哈希表),与iptables相比,允许几乎无限的规模。在创建IPVS负载时,会发生以下事情:在Node上创建虚拟IPVS接口,将Service的IP地址绑定到虚拟IPVS接口,并为每个Service额IP地址创建IPVS服务器。将来,期望IPVS成为集群内负载平衡的默认方法。

    4.4 Pod到Service的一个包的流转

    image

    • 如上图当从一个Pod发送数据包到Service时候,数据包先从Pod1所在的虚拟设备eth0离开pod1,并通过veth对的另外一端veth0传递给网桥cbr0,网桥找不到service对应ip的mac地址,所以把包转发给默认路由,也就是root命名空间的eth0

    • 在root命名空间的设备eth0接受到数据包前,数据包会经过iptables进行过滤,iptables接受数据包后会使用kube-proxy在Node上安装的规则来响应Service或Pod的事件,将数据包的目的地址从Service的IP重写为Service后端特定的Pod IP(本例子中是pod4).

    • 现在数据包的目的ip就不再是service的ip地址了,而是pod4的ip地址。

    • iptables利用Linux内核的conntrack来记住所做的Pod选择,以便将来的流量路由到同一个Pod(禁止任何扩展事件)。 从本质上讲,iptables直接在Node上进行了集群内负载均衡,然后流量使用我们已经检查过的Pod-to-Pod路由流到Pod。

    4.5 Service到Pod的一个包的流转

    image

    • 收到此数据包的Pod将会回发包到源Pod,回包的源IP识别为自己的IP(比如这里为Pod4的ip),将目标IP设置为最初发送数据包的Pod(这里为pod1的ip)。
    • 数据包进入目标Pod(这里为Pod1)所在节点后,数据包流经iptables,它使用conntrack记住它之前做出的选择,并将数据包的源IP重写为Service的IP。 从这里开始,数据包通过网桥流向与Pod1的命名空间配对的虚拟以太网设备,并流向我们之前看到的Pod1的以太网设备。

    5.Internet与Service之间的网络

    到目前为止,我们已经了解了如何在Kubernetes集群中路由流量。下面我们希望将服务暴露给外部使用(互联网)。 这需要强调两个相关的问题:(1)从k8s的service访问Internet,以及(2)从Internet访问k8s的service.

    5.1 k8s流量到Internet

    从Node到公共Internet的路由流量是特定于网络的,实际上取决于网络配置。为了使本节更具体,下面使用AWS VPC讨论具体细节。

    在AWS中,k8s集群在VPC内运行,其中每个Node都分配了一个可从k8s集群内访问的私有IP地址。要使群集外部的流量可访问,需要将Internet网关连接到VPC。 Internet网关有两个目的:在VPC路由表中提供可以路由到Internet的流量的目标,以及为已分配公共IP地址的任何实例执行网络地址转换(NAT)。 NAT转换负责将群集专用的节点内部IP地址更改为公共Internet中可用的外部IP地址。

    通过Internet网关,Node可以将流量路由到Internet。不幸的是,有一个小问题。 Pod具有自己的IP地址,该IP地址与承载Pod的Node的IP地址不同,并且Internet网关上的NAT转换仅适用于Node的 IP地址,因为它不知道Node上正在运行那些Pod - Internet网关不是容器感知的。让我们再次看看k8s如何使用iptables解决这个问题。

    本质都是使用NAT来做

    5.1.1 Node到Internet

    image

    • 如上图中,数据包源自Pod1的网络命名空间,并通过veth对连接到root命名空间。
    • 一旦root命名空间,数据包就会从网桥cbr0流传到到默认设备eth0,因为数据包上的目的IP与连接到网桥的任何网段都不匹配,在到达root命名空间的以太网设备eth0之前,iptables会修改数据包。
    • 在这种情况下,数据包的源IP地址是Pod1的ip地址,如果我们将源保持为Pod1,则Internet网关将拒绝它,因为网关NAT仅了解连接到vm的IP地址。解决方案是让iptables执行源NAT - 更改数据包源 - 以便数据包看起来来自VM而不是Pod。
    • 有了正确的源IP,数据包现在可以离开VM,并到达Internet网关。 Internet网关将执行另一个NAT,将源IP从VM内部IP重写为Internet IP。最后,数据包将到达公共互联网。在回来数据包的路上,数据包遵循相同的路径,任何源IP都会与发送时候做相同的修改操作,以便系统的每一层都接收它理解的IP地址:Node,以及Pod命名空间中中的Pod IP。

    5.2 Internet到k8s

    image.png

    让Internet流量进入k8s集群,这特定于配置的网络,可以在网络堆栈的不同层来实现:

    • (1) NodePort
    • (2)Service LoadBalancer
    • (3)Ingress控制器。

    5.2.1 第四层流量入口:NodePort

    让外网访问k8s内部的服务的第一个方法是创建一个NodePort类型的Service,
    对于NodePort类型的Service,k8s集群中的每个Node都会打开一个端口(所有Node上的端口相同),并将该端口上收到的流量重定向到具体的Service。

    对于NodePort类型的Service,我们可以通过任何Node的ip和端口号来访问NodePort服务。

    创建NodePort类型的服务:

     

    image.png

    如下图,服务暴露在两个节点的端口30123上,到达任何一个端口的链接会被重定向到一个随机选择的Pod。

     

    image.png

    如何做到的?

    NodePort是靠kube-proxy服务通过iptables的nat转换功能实现的,kube-proxy会在运行过程中动态创建与Service相关的iptables规则,这些规则实现了NodePort的请求流量重定向到kube-proxy进程上对应的Service的代理端口上。

    kube-proxy接受到Service的请求访问后,会从service对应的后端Pod中选择一个进行访问(RR)

    但 NodePort 还没有完全解决外部访问 Service 的所有问题,比如负载均衡问题,假如我们 的集群中有 10 个 Node,则此时最好有一个负载均衡器,外部的请求只需访问此负载均衡器的 IP 地址,由负载均衡器负 责转发流量到后面某个 Node 的 NodePort 上

    5.2.2 第四层流量入口:LoadBalancer

    该方式是NodePort方式的扩展,这使得Service可以通过一个专用的负载均衡器来访问,这个是由具体云服务提供商来提供的,负载均衡器将流量重定向到所有节点的端口上,如果云提供商不支持负载均衡,则退化为NodePort类型

    创建k8s service时,可以选择指定LoadBalancer。 LoadBalancer的实现由云控制器提供,该控制器知道如何为您的service创建负载均衡器。 创建service后,它将公布负载均衡器的IP地址。 作为最终用户,可以开始将流量定向到负载均衡器以开始与提供的service进行通信。

    创建一个负载均衡服务:

     

    image.png

    借助AWS,负载均衡器可以识别其目标组中的Node,并将平衡群集中所有节点的流量。 一旦流量到达Node,之前在整个群集中为Service安装的iptables规则将确保流量到达感兴趣的Service的Pod上。

    下面看下LoadBalancer到Service的一个数据包的流转过程:

     

    image

    • 我们来看看LoadBalancer在实践中是如何运作的。部署Service后,正在使用的云提供商将会创建一个新的负载均衡器(1)。

    • 由于负载均衡器不能识别容器,因此一旦流量到达负载均衡器,它就会把数据包发送到在构成群集的某个Node中(2)。每个Node上的iptables规则将来自负载均衡器的传入流量重定向到正确的Pod(3)

    • 这些iptables规则是在service创建时候创建。从Pod到请求方的响应将返回Pod的IP,但请求方需要具有负载均衡器的IP地址。这就需要iptables和conntrack用于在返回路径上正确地重写IP。

    上图显示了承载Pod的三个Node前面的网络负载平衡器。首先流量被传到的Service的负载均衡器(1)。一旦负载均衡器收到数据包(2),它就会随机选择一个VM。这里我们故意选择了没有Pod运行的node:node 2。在这里,node上运行的iptables规则将使用kube-proxy安装在集群中的内部负载平衡规则将数据包定向到正确的Node 中的Pod。 iptables执行正确的NAT并将数据包转发到正确的Pod(4)。

    需要注意的是每个服务需要创建自己独有的负载均衡器,下面要讲解的一种方式所有服务只需要一个公开服务。

    5.2.3 第七层流量入口:Ingress Controller

    这是一个与上面提到的两种方式完全不同的机制,通过一个公开的ip地址来公开多个服务,第7层网络流量入口是在网络堆栈的HTTP / HTTPS协议范围内运行,并建立在service之上。

     

    image.png

     

    如上图,不像负载均衡器每个服务需要一个公开ip,ingress所有服务只需要一个公网ip,当客户端向Ingress发送http请求时候,ingress会根据请求的主机名和路径决定请求转发到那个服务。

    创建Ingress资源:

     

    image.png

     

    如上定义了一个单一规则的Ingress,确保Ingress控制器接受的所有请求主机kubia.example.com的http请求被发送到端口80上的kubia-nodeport服务上。

    工作原理:
    如下图,客户端首先对kubia.example.com执行DNS查找,DNS服务器返回Ingress控制器的IP,客户端拿到IP后,向Ingress控制器发送Http请求,并在Host投中指定kubia.example.com。控制器接受到请求后从Host头部就知道该访问那个服务,通过与该Service关联的endpoint对象查询Pod ip,并将请求转发到某一个Pod。

    这里Ingress并没把请求转发给Service,而是自己选择一个一个Pod来访问。

     

    image.png

    第7层负载均衡器的一个好处是它们具有HTTP感知能力,因此它们了解URL和路径。 这允许您按URL路径细分服务流量。 它们通常还在HTTP请求的X-Forwarded-For标头中提供原始客户端的IP地址。

    本文参考了大量资料,并结合作者的理解,如有不对,欢迎讨论^^

    参考

     

    展开全文
  • k8s网络通信

    2021-02-26 10:28:14
    k8s网络通信 (一)k8s通过CNI接口接入其他插件来实现网络通讯。目前比较流行的插件有flannel,calico等 CNI插件存放位置:# cat /etc/cni/net.d/10-flannel.conflist (二)插件使用的解决方案如下: 虚拟网桥,虚拟...
  • k8s 网络模型

    2019-04-25 11:13:00
    一、前言 k8s对Pods之间如何进行组网通信提出了要求,k8s对集群的网络有以下要求: 所有的Pods之间可以在不使用...k8s网络模型设计基础原则:每个Pod都拥有一个独立的 IP地址,而且 假定所有 Pod 都在一个可以直...
  • k8s网络主题系列: 一、k8s网络之设计与实现 二、k8s网络之Flannel网络 三、k8s网络之Calico网络 1|0简介 Calico是一种容器之间互通的网络方案。在虚拟化平台中,比如OpenStack、Docker等都需要实现...
  • k8s网络主题系列: 一、k8s网络之设计与实现 二、k8s网络之Flannel网络 三、k8s网络之Calico网络 1|0简介 Flannel是CoreOS团队针对Kubernetes设计的一个网络规划服务,简单来说,它的功能是让集群中的不同...
  • k8s网络-容器网络

    2018-02-28 16:14:47
    k8s网络1.容器网络 pod每个POD分配单独的Ip地址,IP-pre-Pod模型(IP以Pod为单位进行分配的,一个Pod一个IP的设计模型)每个容器也可以称为pod,pod中有个pause容器,pause可以接入网络,并且共享网络给pod中其他...
  • 随着云原生的概念越来越火,k8s作为云原生基础设施当然也备受大家关注,所以不管是小公司大公司都在吵着上容器化,微服务改造等,这块内容也是各家公司面试必问的。...k8s的网络模型k8s网络中存在两...
  • 文中给出6种网络策略配置示例,看各种场景下K8s网络策略的实现。(含映射表)
  • k8s网络通讯模式

    2021-02-20 10:15:43
    k8s网络通讯模式 让不同节点的容器都具有唯一的虚拟ip地址(扁平化,并非真正意义上的扁平化,而是从结果上看的话,是扁平化网络),修改docker0 网桥的网段来实现 flanneld 是一个进程,接收发送不同主机的通讯...
  • k8s网络架构图_k8s架构

    2020-12-21 08:48:08
    master节点k8s的集群由master和node组成,节点上运行着若干k8s服务。master节点之上运行着的后台服务有kube-apiserver 、kube-scheduler、kube-controller-manager、etcd和pod网络。如下图所示1. API Server(kube-...
  • 其实操作到这里,有必要深入的了解K8s网络运行机制和基本结构,否则当真的遇到问题的时候会比较郁闷。首先,要理解K8s的用处其实是容器的编排和管理,最小组成其实不是容器,是pod,物理机或者虚拟机叫node,pod是...
  • K8s 网络插件exec

    千次阅读 2017-01-11 16:32:25
    K8s 网络插件支持exec , CNI,...要求插件满足K8s网络插件接口即可。 type NetworkPlugin interface{  Init(host Host, hairpinModecomponentconfig.HairpinMode) error  Event(name string,details map[str
  • K8s网络实战分析之service调用

    千次阅读 2019-11-24 17:53:52
    在上一篇文章K8s网络实战分析之Calico-ipip模式中,我们通过Pod之间进行ping操作,对基于Calico-IPIP模式的K8s网络进行了实战学习与分析。单单进行Pod-Pod的访问只是K8s的基础功能,基于Service的访问才是K8s的网络...
  • Kubernetes-k8s网络模型原理

    千次阅读 2019-05-15 17:06:19
    Kubernetes-k8s网络工作原理同节点Pod之间的通信不同节点上的Pod通信CNI插件CalicoFlannelRomanaWeave Net Kubernetes 采用的是基于扁平地址空间的、非NAT的网络模型,每个Pod有自己唯一的IP地址。 另外需要注意的...
  • 之前围绕阿里云的容器服务,内部做了一次阿里云基于k8s服务治理的总结。这次系统地学习了docker与k8s的网络模型,并从kubelet出发研究了kubelet -&...K8S网络模型 同POD网络 同NODE网络 不同NODE网络 CNI分析 D...
  • Docker K8S网络模型复盘网络模式简介常见网络模型docker网络模式默认模式Bridge详解Kubernetes网络通信原理Flannel NetWork原理: 网络模式简介 Docker和k8s跟着老师学了一遍,容器化技术及其编排如此强大,最近让我...
  • k8s网络基本要求:三个要求+四个目标 k8s对于pod间的网络没有任何限制,只需满足如下三个基本条件: 所有Pod可以与其它Pod直接通信,无需显示使用NAT 所有Node可以与所有Pod直接通信,无需显示使用NAT Pod可见的...
  • K8S网络模型2. K8S网络解决方案3. K8S网络通信方式 图解Python数据结构与算法-实战篇 1. K8S网络模型        Kubernetes 的网络模型假定了所有Pod 都在一个可以直接连通...
  • K8S网络模型

    2019-07-05 15:16:00
    k8s为pod和service资源对象分别使用了各自的专用网络,pod网络k8s网络插件配置实现,而service的网络则由k8s集群予以指定。k8s网络模型需要借助于外部插件实现,它要求任何实现机制都必...
  • 图片出自:《你女儿也能看懂的插画版 Kubernetes 指南》K8S网络设计原则K8S 网络设计原则如下:每个 Pod 都拥有一个独立 IP 地址,Pod 内所有容器共享该 IP 地址。集群内所有 Pod 都在一个直接连通的扁平网络中,可...
  • k8s网络详解

    2020-06-08 22:42:48
    k8s的各种层次网络 链接:文章链接

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,202
精华内容 1,680
关键字:

k8s网络