# 三层网络实现

# 字典

  1. 下一跳:如果ip包从主机A发送到主机B,需要经过路由设备X的中转,那么X的IP地址就应该配置为主机A的下一跳地址。

  2. Host-gw模式:将每个 Flannel 子网(Flannel Subnet,比如10.244.1.0/24)的下一跳 ,设置成该子网对应宿主机的IP地址。也就是主机(host)充当这条容器通信路径里的网关(gateway),也就是host-gw的含义;无封装,纯路由,只经过协议栈一次,性能较高

  3. 边际网关协议:BGP,Border Gateway Protocol,大规模数据中心维护不同“自治系统”之间路由信息、无中心的路由协议; 它不会在宿主上创建任何网桥设备

    相当于:每个边界网关上运行着一个小程序,它们会将各自的路由表信息,通过TCP传输给其它边界网关。其它边界网关上的这个小程序,则会对收到的这些数据进行分析,然后把需要的信息添加到自己的路由表。

    • CNI:与k8s对接的部分
    • Felix:DeamonSet,wmgm宿主机上插入路由规则,即写入Linux内核的FIB转发信息库,以及维护Calico所需要的网络设备等工作
    • BIRD:BGP客户端,专门负责在集群里分发路由信息
  4. 边际网关:负责把自治系统连接在一起的路由器。它的路由表中有其它自治系统里的主机路由信息;

  5. Calico:集群中所有的节点,都是边界路由器,被称为BGP Peer;默认情况下,是一个Node-to-Node Mesh的模式。随着节点的增多,会以N^2的规模快速增长。一般推荐在少于100个节点的集群中

  6. Calico Route Reflector的模式,大集群,它指定一个或几个专门节点,来负责与所有节点建立BGP连接,从而学习全局路由规则,其它节点,只需要给它交换路由信息。

  7. Calico IPIP模式:添加的路由规则10.233.2.0/24 via 192.168.2.2 tunl0,下一跳地址是Node2 IP地址,但发出去包的设备是tunl0(注意,不是flannel UDP模式的tun0,它们的功能是不同的)它是一个IP隧道设备(IP tunnel),IP包进入IP隧道后,被内核IPIP驱动接管。它会将这个IP包直接封装在一个宿主机网络的Ip包中。

Calico IPIP模式:

0000004.png

Flannel Host-gw模式

$ ip route
...
10.244.1.0/24 via 10.168.0.3 dev eth0
1
2
3

目的地址属于10.244.1.0/24网段的IP包,应该经过本机的eth0设备(dev eth0)设备发出去,并且它的下一跳(next-lop)是10.168.0.3(via 10.168.0.3)

从示意图上看,下一跳地址是目的宿主机Node2,所以IP包从应用层到链路层被封装成帆时,etho会使用下一跳地址对应的MAC地址,做为数据帧的MAC地址。

Node 2的内核网络从二层拿到IP包,就会看到这个目的IP地址:10.244.1.3,根据Node 2的路由表,会匹配到第二条路由规则,从而进入cni0网桥,进入infra-containeere-2当中。

Flannel子网和主机的信息,都是保存在Etcd中的。flanneld只需要wacth这些数据变化,然后实时更新路由表则可。

性能损失在10%左右,其它基于VXLAN隧道机制的网络方案,性能损失在20%~30%左右。

Flannel host-gw限制,要求宿主机必须是二层连通;必须有其它宿主机的路由信息;

# 36 为什么k8s只有soft multi-tenancy

# 字典:

  1. NetworkPolicy:宿主机上的一系统iptables规则,与传统Iaas里面的安全组其实是类似的;注意定义的方式是OR还是AND,Flannel不支持NetworkPolicy

    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: test-network-policy
      namespace: default
    spec:
      podSelector: # 定义限制范围
        matchLabels:
          role: db
      policyTypes:
      - Ingress
      - Egress
      ingress:
      - from: # 允许注入的“白名单”
        - ipBlock: # 定义限制范围
          cidr: 172.17.0.0/16
          except:
          - 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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
  2. iptables:操作Linux内核Netfilter子系统的界面,挡在网卡与用户态进程之间的一道“防火墙”

    0000006
    • 第一种:继续本机处理
    • 第二种 :被转发到其它目的地

    https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg

    # 同一台宿主机上容器之间经过CNI网桥进行通信的注入数据包,-physdev-js-bridged意思是,这个FORWARD链匹配的是,通过本机上的网桥设备,发往目的地址podIP的IP包
    iptables -A FORWARD -d $podIP -m physdev --physdev-js-bridged -j KUBE-POD-SPEC-FW-CHAIN
    # 容器间跨主通信,注入容器的数据包,都是经过路由(FORWARD检查点)来的
    iptables -A FORWARD -d $podIP -j KUBE-POD-SPECIFIC-FW-CHAIN
    
    1
    2
    3
    4
    iptables -A KUBE-POD-SPECIFIC-FW-CHAIN -j KUBE-NWPLCY-CHAIN
    iptables -A KUBE-POD-SPECIFIC-FW-CHAIN -j REJECT --reject-with icmp-port-unreachable
    
    1
    2
  3. k8s定位:基础设施与Paas之间的中间层。容器本质上就是进程的抽象粒度;

  4. Paas: platform as a service,平台即服务,把服务器平台做为一种服务提供的商业模式

  5. saas: Software as a service ,通过网络运行程序提供的服务叫says

  6. Iaas: Infrastructure as a Service,基础设备即服务

# 37 找到容器

# 字典:

  1. Service:由kube-proxy组件,加上iptables共同实现的 原因:

    • Pod的ip不是固定的
    • Pod实例间总有负载均衡的需求
    apiVersion: v1
    kind: Service
    metadata:
      name: hostnames
    spec:
      selector:
        app: hostnames
      ports:
      - name: default
        protocol: TCP
        port: 80
        targetPort: 9376
    
    # 被service选中的pod,就称为service的Endpoints,可使用kubectl get endpoints hostnames 查看
    # 只有readinessProbe检查通过的Pod,才会出现在Endpoints列表中,出问题后会自动摘除掉
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: hostnames
    spec:
      selector:
        matchLabels:
          app: hostnames
      replicas: 3
      template:
        metadata:
          labels:
            app: hostnames
        spec:
          containers:
          - name: hostnames
            image: k8s.gcr.io/serve_hostname
            ports:
            - containerPort: 9376
              protocol: TCP
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36

    访问kubectl get sec hostnames就可以访问到它代理的pod了,vip是k8s自动为Service分 配的 ,这 种 模式是 ClusterIP 模式的Service

    $ kubectl get svc hostnamesNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEhostnames ClusterIP 10.0.1.175 80/TCP 5s
    
    $ curl 10.0.1.175:80
    hostnames-0uton
    
    $ curl 10.0.1.175:80
    hostnames-yp2kp
    
    $ curl 10.0.1.175:80
    hostnames-bvc05
    
    # 添加的iptables规则
    -A KUBE-SERVICES -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostname:  cluster IP" -m tcp --dport 80 -j KUBE-SVC-NWV5X2332I40T4T3
    
    # 规则的集合,随机(--mode random)模式的iptables链
    -A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-WNBA2IHDGP2BOBGZ
    -A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-X3P2623AGDH6CDF3
    -A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -j KUBE-SEP-57KPRZ3JQVENLNBR
    
    # 三条DNAT规则。在规则前对注入的 IP包设置了一个标志(--set-xmark),作用是在路由之ueej,将注入IP包的目的地址和端口,改成-to-destination指定的新的目的地址和端口。
    -A KUBE-SEP-57KPRZ3JQVENLNBR -s 10.244.3.6/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
    -A KUBE-SEP-57KPRZ3JQVENLNBR -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.3.6:9376
    
    -A KUBE-SEP-WNBA2IHDGP2BOBGZ -s 10.244.1.7/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
    -A KUBE-SEP-WNBA2IHDGP2BOBGZ -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.1.7:9376
    
    -A KUBE-SEP-X3P2623AGDH6CDF3 -s 10.244.2.3/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
    -A KUBE-SEP-X3P2623AGDH6CDF3 -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.2.3:9376
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28

    iptable规则相当多,而且还需要在控制循环里不断刷新这些规则,保证它们是正确的;基于iptables的Service实现是制约k8s承载更多量级的Pod的主要障碍

  2. ClusterIP 模式:它的A记录的格式是..svc.cluster.local当访问这个A记录时,解析到的是这个Service的VIP地址

  3. Headless Service:clusterIP=None,A记录也是,但是,访问时返回的是Pod的IP 地址的集合。如果本身声明了hostname和subdomain字段,A记录变 为<pod的hostname>...svc.clustere.local比如:

    apiVersion: v1
    kind: Service
    metadata:
      name: defalt-subdomain
    spec:
       selector:
         name:  busybox
       clusterIP: None
       ports:
       - name: foo
         port: 1234
         targetPort: 1234
    ---
    apiVersion: v1
    kind: Pod
    metadata:
      name: busybox1
      labels:
         name: busybox
    spec:
      hostname; busybox-1
      subdomain: default-subdomain
      containers:
      - image: busybox
        command:
         - sleep
         - 3600
        name: busybox
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28

    创建后可用:buusybox-1.default-subdomain.default.svc.clusteer.local解析到pod的析ip地址

  4. Kube-proxy IPVS模式:创建Service后,会在宿主机上创建一个虚拟网卡,kube-ipvs0,并为它分配Service VIP做为IP地址。

    # ip addr
    ...
    73:kube-ipvs0: mtu 1500 qdisc noop state DOWN qlen 1000 link/ether 1a:ce:f5:5f:c1:4d brd ff:ff:ff:ff:ff:ff inet 10.0.1.175/32 scope global kube-ipvs0 valid_lft forever preferred_lft forever
    
    1
    2
    3

    通过linux的ipvs模块,为IP地址设置三个IPVS虚拟主机,并设置 它们之间通过轮询模式作为负载均衡策略

    # ipvsadm -ln
     IP Virtual Server version 1.2.1 (size=4096)
      Prot LocalAddress:Port Scheduler Flags
        ->  RemoteAddress:Port           Forward  Weight ActiveConn InActConn     
      TCP  10.102.128.4:80 rr
        ->  10.244.3.6:9376    Masq    1       0          0         
        ->  10.244.1.7:9376    Masq    1       0          0
        ->  10.244.2.3:9376    Masq    1       0          0
    
    1
    2
    3
    4
    5
    6
    7
    8

    实现:基于Netfilter的Nat模式,转发上没有特别的性能提升。不需要在宿主机上设置iptables规则,而把它们的处理放到了内核态,降低了维护规则的代价。“将重要操作放入内核态”

# 连通Service与调试

# 字典

  1. 访问入口

    • 每台宿主机上kube-proxy生成的iptables规则
    • kube-dns生成的DNS记录
  2. NodePort

    apiVersion: v1
    kind: Service
    metadata:
      name: my-nginx
      labels:
        run: my-nginx
    spec:
      type: nodePort
      ports:
      # Service的8080代理Pod的80端口,任何一台宿主机IP+8080,就可以访问到这个服务
      - nodePort: 8080
        targetPort: 80
        protocol: TCP
        name: http
      - nodePort: 443
        targetPort: 443
        protocol: TCP
        name: https
      selector;
        run: my-nginx
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
  3. LoadBalancer:在CloudProvider里创建一个与该Service对应的负载均衡服务

  4. ExternalName

# Service与Ingress

# 字典:

  1. Ingress:全局的、为了代理不同后端Sercie而设置的负载均衡服务

    apiVersion: extensions/v1beat1
    kind: Ingress
    metadata:
      name: cafe-ingress
    spec:
      tls:
      - hosts:
        # 标准域名格式,不能是IP地址
        - cafe.example.com
        secretName: cafe-secret
      rules:
      - hosts: cafe.example.com
        http:
        - path: /tea
          backend:
            serviceName: tea-svc
            servicePort: 80
        - path: /caffe
          backend:
            serviceName: caffee-svc
            sercvicePort: 80
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    站点:

    • https://cafe.example.com
    • https://cafe.example.com/tea -> tea - > deploment
    • https://cafe.example.com/caffee caffee -> deploment
  2. Ingress对象:k8s项目对反向代理的一种抽象

  3. Ingress Controller:目前支持Nginx、Haproxy、Envoy、Traefik等;nginx-ingress-controller只是被代理的Serive对象被更新,是不需要重新加载的,因为它的Lua方案实现了Nginx Upstream的动态配置。

  4. nginx-igress-service:暴露服务出去,如果是公有云,需要创建LoadBalancer类型的Service了。

  5. default-backend-service:默认规则

Ingress只能工作在7层,而Service只能工作在四层

查看nginx配置

kubectl get pods -n ingress-nginx
kubectl exec nginx-ingress-controller-xxxx -it --namespace="ingress-nginx" --bash
cat /etc/nginx/nginx.conf
1
2
3

# 从集群外访问到服务

  1. Service工作原理:通过kube-proxy设置宿主机iptables规则来实现,kube-proxy通过观察service的创建,然后通过修改本机的iptables规则,将访问service vip的请求转发到真实的Pod上
  2. 基于iptables规则的service的实现,宿主机上有大量Pod时,规则的不断刷新占用大量CPU资源,新的模式:ipvs,通过把规则放到内核态,降低了维护规则的代价
  3. Service的DNS记录:<myservice>.<mynamespace>.svc.cluster.local,访问这条记录时,返回的是Service的VIP或代理Pod的IP地址集合
  4. Pod的DNS记录:<pod_hostname>.<subdomain>.<mynamespace>.svc.cluster.local,注意pod的hostname和subdomain都是在Pod中定义的
  5. 外部宿主机访问
    • NodePort:外部client访问任意一台宿主机的8080端口,就是访问Servicer所代理的Pod的80端口,由接收外部请示请求的宿主机做转发,即client -> nodeIP:nodePort -> serviceVIP:port -> podIp:targetIp
    • LoadBalance:公有云提供的k8s服务自带的loadbalancer做负载均衡和外部流量 的入口
    • ExternalName:通过ExternalName或ExternalIp给Service挂在一个公有IP或者域名,当访问这个公有IP地址时,就会转发到Service所代理的Pod服务上。类似于软链或快捷方式
    • ClusterIP:虚拟IP地址,外部网络无法访问,只有k8s内部访问使用。更像是一个伪造的IP网络
      • 仅仅用于Service这个对象,并由k8s管理和分配IP地址
      • 无法被Ping通,没有一个“实体网络对象”来响应
      • 只能结合Service Port组成一个具体的通信端口,单独的ClusterIP不具备通信的基础,并且它们属于k8s集群这样一个封闭的空间
      • 不同Service下的Pod节点在集群间相互访问可以通过ClusterIP

# 32 容器网络

img

# 字典

  1. 覆盖网络(Overlay Network)
上次更新: : 7 months ago