Docker k8s 网络架构对比

docker 网络架构

Docker k8s 网络架构对比
设计缺陷:

  • 网桥是主机虚拟出来的,外部无法寻址。如果希望被外部寻址到,就需要通过映射容器端口到宿主机端口;而实际上端口映射是通过在iptables的NAT表中添加相应的规则来实现,也将这种方式称为NAT方式。NAT模型需要对各种端口进行映射,这会限制宿主机的能力,在容器编排上也增加了复杂度。
  • 端口是稀缺资源,这就需要解决端口冲突和动态分配端口问题。这不但使调度度杂化,而且应用程序的配置也变得复杂,具体表现在端口冲突、重用和耗尽。
  • NAT将地址分段的做法引入额外的复杂度。比如容器中应用所见的ip并不是对外暴露的ip,因为网络隔离,容器中的应用实际上只能检测到容器的ip,但是需要对外宣称的则是宿主机的ip,这种信息的不对称将带来诸如破环自注册机制等问题。

k8s网络模型

kubenetes从docker网络模型(NAT方式的网络模型)中独立出来形成一套新的网络模型。
每一个Pod都拥有一个扁平化共享网络命名空间的「PodIP」。
通过PodIP跨越网络与其他物理机和Pod进行通信。
IP-Per-Pod 模型创建了一个干净的、反向兼容的模型。
该模型中,从端口分配、网络、域名解析、服务发现、负载均衡、应用配置和迁移等角度,Pod都能被看作是虚拟机或者物理机,应用可以平滑地从非容环境迁移到同一个Pod内的容器环境。

为实现这个模型,需要解决几个问题:

容器间通信 (Container to Container)

  • Pod是容器的集合
  • Pod包含的容器都运行在同一个宿主机,拥有相同网络空间,容器之间可以互相通信
  • Pod运行的容器中包含业务容器和网络容器 「pause」
  • 「pause」顾名思义,暂停,防止容器退出
  • 网络容器只用来接管Pod的网络,业务容器通过加入网络容器的网络实现网络共享

Pod内容器类似使用以下命令运行:

docker run -p 80:80 -p 8080:8080 --name network-container -d gcr.io/google_container/pause:3.1
 
docker run --net container:network-container -d jonlangemak/docker:web_container_8080

Pod中的容器网络拓扑
Docker k8s 网络架构对比

Pod间通信 (Pod to Pod)

  • k8s网络模型是一个扁平化的网络平面
  • Pod作为一个网络单元通k8s Node网络处于一个层级

最小的k8s网络拓扑

  • Pod间通信:Pod1和Pod2(同主机), Pod1和Pod3(跨主机通信)
  • Node与Pod间通信:Node1与Pod1/Pod2(同主机),Pod3(跨主机能够通信)
  • [ ] Docker k8s 网络架构对比

问题:

  1. 如何保证Pod的PodIP是全局唯一的?·
  2. 同一个k8s Node上的Pod/容器原生能通信,不通Node之间的Pod如何通信?
  • [x] Pod的PodIP是Docker网桥分配的,所以将不同k8s Node的Docker网桥配置成不同的IP网段即可。
  • [x] 对Docker进行增强,在容器集群中创建一个覆盖网络(Overlay Network),联通各节点,如Flannel 。

Flannel 网络模型

  • Flannel 是由CoreOS团队设计的覆盖网络工具
  • Flannel为主机设定一个子网,通过隧道协议封装容器之间的通信报文,实现容器的跨主机通信。
  • Flannel在运行之前需要使用Etcd进行配置
etcdctl set /coreos.com/network/config '{"Network": "10.0.0.0/16"}'
  • 配置完成,Node上运行Flannel
  • 初次启动时会检查Etcd中的配置
  • 为当前节点分配可用的IP网段
  • 在Etcd中创建一个路由表
etcdctl ls /coreos.com/network/subnets
ectdctl get /coreos.com/network/subnets/10.0.62.0-24
ectdctl get /coreos.com/network/subnets/10.0.10.0-24
  • 分配网段之后,会创建一个虚拟网卡
ip addr show flannel.1
  • 还会配置Docker网桥(docker0),通过修改Docker启动参数 --bip实现。
ip addr show docker0
  • 除此之外,Flannel会修改路由表,使得Flannel虚拟网卡可以接管容器跨主机的通信。
route -n

网络拓扑
Docker k8s 网络架构对比

Service到Pod的通信(Service to Pod)

Service在Pod之间起「服务代理」的作用,对外表现为一个单一访问接口,将请求转发给Pod。

$ kubectl decsribe service myservice
Name: myservice
Namespace: default
Lables: <none>
Selector: name=mypod
Type: ClusterIP
IP: 10.254.206.220
Port: http 80/TCP
Endpoints: 10.0.62.87:80,10.0.62.88:80,10.0.62.89:80

Session Affinity: None
No evevts.

解读如下:

  • Service的虚拟IP是 10.254.206.220
  • 端口80/TCP对应3个后端:10.0.62.87:80,10.0.62.88:80,10.0.62.89:80
  • 请求 10.254.206.220:80时会转发到这些后端之一
  • 虚拟IP是K8s创建的,虚拟网段是API server启动参数--service-cluster-ip-range=10.254.0.0/16配置的

kube-proxy组件负责虚拟IP路由和转发,在容器覆盖网络之上实现的虚拟转发网络,功能如下:

  • 转发访问Service的虚拟IP的请求到Endpoints
  • 监控Service和Endpoints的变化,实现刷新转发规则
  • 提供负载均衡能力

kube-proxy实现机制,通过启动参数--proxy-mode指定:

  • Userspace
  • 当前kube-proxy只是3层(TCP/UDP Over IP)的转发,默认策略是轮询。
  • kube-proxy会为Service创建两台iptables规则,包含下面两个iptables自定义链

    • KUBE-PORTALS-CONTAINER 用于匹配容器发出的报文,绑定在NAT表PREROUTING
    • KUBE-PORTALS-HOST 用于匹配宿主机发出的报文,绑定在NAT表OUTPUT链
  • 这两条规则的目的是将目的ip为10.254.206.220,目的端口80的报文重定向到本机35841端口
  • 这个端口是kube-proxy监听的端口

Docker k8s 网络架构对比

  • Iptables

完全通过创建iptables规则,直接重定向访问Service的虚拟IP的请求到endpoints;当Endpoints发生变化的时候,kube-proxy会刷新相关的iptables规则,在此模式下,kube-proxy只负责监控Service和Endpoints,更新iptables规则,报文的转发依赖于Linux内核,默认的负载均衡方式是随机方式。
 

Docker k8s 网络架构对比
查询关于Service myservice的iptables规则

iptables-save | grep myservice

kube-proxy会为Service创建一系列Iptables规则,其中包含Iptables自定义链:

  • KUBE-SERVICES: 绑定在NAT表PREROUTING链和OUTPUT链
  • KUBE-SVC-*: 代表一个Service , 绑定在KUBE-SERVICES
  • KUBE-SEP-: 代表Endpoints的每一个后端,绑定在KUBE-SVC-

查询转发规则

iptables -t nat -L -n