Kubernetes网络揭秘:从DNS到核心概念,一站式综述

文章目录

一.overlay vs underlay

overlay网络是在传统网络上虚拟出一个虚拟网络,承载的底层网络不再需要做任何适配。在容器的世界里,物理网络只承载主机网络通信,虚拟网络只承载容器网络通信。overlay网络的任何协议都要求在发送方对报文进行包头封装,接收方剥离包头。

L2 overlay传统的二层网络的范围有限,L2 overlay网络是构建在底层物理网络上的L2网络,相较于传统的L2网络,L2 overlay网络是个"大二层"的概念,其中"大"的含义是可以跨越多个数据中心(即容器可以跨L3 underlay进行L2通信),而"二层"指的是通信双方在同一个逻辑的网段内,例如172.17.1.2/16和172.17.2.3/16。VXLAN就是L2 overlay网络的典型实现,其通过在UDP包中封装原始L2报文,实现了容器的跨主机通信。

L2 overlay网络容器可在任意宿主机间迁移而不改变其IP地址的特性,使得构建在大二层overlay网络上的容器在动态迁移时具有很高的灵活性。

L3 overlayL3 overlay组网类似L2 overlay,但会在节点上增加一个网关。每个节点上的容器都在同一个子网内,可以直接进行二层通信。跨节点的容器间通信只能走L3,都会经过网关转发,性能相比于L2 overlay较弱。牺牲的性能获得了更高的灵活性,跨节点通信的容器可以存在于不同的网段中,例如192.168.1.0/24和172.17.16.0/24。flannel的UDP模式采用的就是L3 overlay模型。L3 overlay网络容器在主机间迁移时可能需要改变其IP地址。

underlay网络underlay网络一般理解为底层网络,传统的网络组网就是underlay类型,区别于上文提到的overlay网络。

L2 underlay

L2 underlay网络就是链路层(L2)互通的底层网络。IPvlan L2模式和Macvlan属于L2underlay类型的网络。

L3 underlay

在L3 underlay组网中,可以选择IPvlan的L3模式,该模式下IPvlan有点像路由器的功能,它在各个虚拟网络和主机网络之间进行不同网络报文的路由转发工作。只要父接口相同,即使虚拟机/容器不在同一个网络,也可以互相ping通对方,因为IPvlan会在中间做报文的转发工作。IPvlan的L3模式,flannel的host-gw模式和Calico的BGP组网方式都是L3 underlay类型的网络。

二、calico vs flannel

2.1 calico架构

Calico的设计灵感源自通过将整个互联网的可扩展IP网络原则压缩到数据中心级别。Calico在每一个计算节点,利用Linux Kernel实现高效的vRouter来负责数据转发,而每个vRouter通过BGP把自己节点上的工作负载的路由信息向整个Calico网络传播。小规模部署可以直接互联,大规模下可通过指定的BGP Route Reflector完成。保证最终所有的容器之间的数据流量都是通过IP路由的方式完成互联的。

路由方案的另一个优点是出了问题也很容易排查。路由方案往往需要用户了解底层网络基础结构,因此使用和运维门槛较高。

2.2 flannel架构

flannel的想法很好:每个主机负责一个网段,在这个网段里分配一个IP地址。访问另外一台主机时,通过网桥到达主机上的IP地址,这边会有一个设备,程序会把你发的包读出来,去判断你的目的地址是什么,归哪台机器管。flannel的UDP封包协议会在原始的IP包外面加一个UDP包,发到目的地址。收到之后,会把前面的UDP包扔掉,留下来的就是目标容器地址。这个方法有几个问题,第一个问题是要做封包的操作。第二个问题是每个主机上的容器是固定的,容器的不同主机之间的迁移必然带来IP的变化。使用UDP封包的一个比较大的问题是性能较差,我们会在后面的章节专门说明。

overlay网络最大的优点是适用于几乎所有网络基础架构,它唯一的要求是主机之间的IP连接。但overlay网络的问题是随着节点规模的增长,复杂度也会随之增加,而且用到了封包,因此出了网络问题定位起来比较麻烦。

三、iptables

iptables是用户空间的一个程序,通过netlink和内核的netfilter框架打交道,负责往钩子上配置回调函数。一般情况下用于构建Linux内核防火墙,特殊情况下也做服务负载均衡(这是Kubernetes的特色操作,我们将在后面章节专门分析)。iptables的工作原理如图所示。

iptables的工作原理我们常说的iptables 5X5,即5张表(table)和5条链(chain)。5条链即iptables的5条内置链,对应上文介绍的netfilter的5个钩子。这5条链分别是:

  • INPUT链:一般用于处理输入本地进程的数据包;
  • OUTPUT链:一般用于处理本地进程的输出数据包;
  • FORWARD链:一般用于处理转发到其他机器/network namespace的数据包;
  • PREROUTING链:可以在此处进行DNAT;
  • POSTROUTING链:可以在此处进行SNAT。

除了系统预定义的5条iptables链,用户还可以在表中定义自己的链,我们将通过例子详细说明。5张表如下所示。

那么一个网络包经过iptables的处理路径如图所示

任何概念都不是凭空想象出来的,它都是有实际用途的。其实,iptables的表是来分类管理iptables规则(rule)的,系统所有的iptables规则都被划分到不同的表集合中。上文也提到了,filter表中会有过滤数据包的规则,nat表中会有做地址转换的规则。因此,iptables的规则就是挂在netfilter钩子上的函数,用来修改数据包的内容或过滤数据包,iptables的表就是所有规则的5个逻辑集合!

ilter表上挂了5条链,分别是INPUT、FORWARD和OUTPUT这三条系统内置链,以及KUBE-FIREWALL和KUBE-FORWARD这两条用户自定义链。iptables的内置链都有默认规则,例如在我们的例子中,INPUT、FORWARD和OUTPUT的默认规则是ACCEPT,即全部放行。用户自己定义的链后面都有一个引用计数,在我们的例子中KUBE-FIREWALL和KUBE-FORWARD都有一个引用,它们分别在INPUT和FORWARD中被引用。iptables的每条链下面的规则处理顺序是从上到下逐条遍历的,除非中途碰到DROP,REJECT,RETURN这些内置动作。如果iptables规则前面是自定义链,则意味着这条规则的动作是JUMP,即跳到这条自定义链遍历其下的所有规则,然后跳回来遍历原来那条链后面的规则。

四、Vxlan

VXLAN(Virtual eXtensible LAN,虚拟可扩展的局域网),是一种虚拟化隧道通信技术。它是一种overlay(覆盖网络)技术,通过三层的网络搭建虚拟的二层网络。RFC7348中是这样介绍VXLAN的:A framework for overlaying virtualized layer 2 networks over lay 3 networks.

简单来讲,VXLAN是在底层物理网络(underlay)之上使用隧道技术,依托UDP层构建的overlay的逻辑网络,使逻辑网络与物理网络解耦,实现灵活的组网需求。它不仅能适配虚拟机环境,还能用于容器环境。由此可见,VXLAN这类隧道网络的一个特点是对原有的网络架构影响小,不需要对原网络做任何改动,就可在原网络的基础上架设一层新的网络。不同于其他隧道协议,VXLAN是一个一对多的网络,并不仅是一对一的隧道协议。一个VXLAN设备能通过像网桥一样的学习方式学习到其他对端的IP地址,也可以直接配置静态转发表。

VXLAN协议原理简介

VXLAN隧道网络方案相比改造传统的二层或三层网络,对原有的网络架构影响小。隧道网络不需要原来的网络做任何改动,可直接在原来的网络基础上架设一层新的网络。图1-21所示为VXLAN的工作模型,它创建在原来的IP网络(三层)上,只要是三层可达(能够通过IP互相通信)的网络就能部署VXLAN。在VXLAN网络的每个端点都有一个VTEP设备,负责VXLAN协议报文的封包和解包,也就是在虚拟报文上封装VTEP通信的报文头部。物理网络上可以创建多个VXLAN网络,可以将这些VXLAN网络看作一个隧道,不同节点上的虚拟机/容器能够通过隧道直连。通过VNI标识不同的VXLAN网络,使得不同的VXLAN可以相互隔离。

VXLAN的几个重要概念如下:

  1. VTEP(VXLAN Tunnel Endpoints):VXLAN网络的边缘设备,用来进行VXLAN报文的处理(封包和解包)。VTEP可以是网络设备(例如交换机),也可以是一台机器(例如虚拟化集群中的宿主机);
  2. VNI(VXLAN Network Identifier):VNI是每个VXLAN的标识,是个24位整数,因此最大值是224=16777216。如果一个VNI对应一个租户,那么理论上VXLAN可以支撑千万级别的租户。
  3. tunnel:隧道是一个逻辑上的概念,在VXLAN模型中并没有具体的物理实体相对应。隧道可以看作一种虚拟通道,VXLAN通信双方(图中的虚拟机)都认为自己在直接通信,并不知道底层网络的存在。从整体看,每个VXLAN网络像是为通信的虚拟机搭建了一个单独的通信通道,也就是隧道。

    前文提到,VXLAN其实是在三层网络上构建出来的一个二层网络的隧道。VNI相同的机器逻辑上处于同一个二层网络中。VXLAN封包格式如图1-22所示。

    VXLAN的报文就是MAC in UDP,即在三层网络的基础上构建一个虚拟的二层网络。为什么这么说呢?VXLAN的封包格式显示原来的二层以太网帧(包含MAC头部、IP头部和传输层头部的报文),被放在VXLAN包头里进行封装,再套到标准的UDP头部(UDP头部、IP头部和MAC头部),用来在底层网络上传输报文。
    可以看出,VXLAN报文比原始报文多出了50个字节,包括8个字节的VXLAN协议相关的部分,8个字节的UDP头部,20个字节的IP头部和14个字节的MAC头部。这降低了网络链路传输有效数据的比例,特别是对小包。
    需要注意的是,UDP目的端口是接收方VTEP设备使用的端口,IANA(Internet AssignedNumbers Authority,互联网号码分配局)分配了4789作为VXLAN的目的UDP端口。
    VXLAN的配置管理使用iproute2包,这个工具是和VXLAN一起合入内核的,我们常用的ip命令就是iproute2的客户端工具。VXLAN要求Linux内核版本在3.7以上,最好为3.9以上,所以在一些旧版本的Linux上无法使用基于VXLAN的封包技术。

五、kubernetes网络架构综述

谈到Kubernetes的网络模型,就不能不提它著名的"单Pod单IP"模型,即每个Pod都有一个独立的IP,Pod内所有容器共享network namespace(同一个网络协议栈和IP)。

"单Pod单IP"网络模型为我们勾勒了一个Kubernetes扁平网络的蓝图,在这个网络世界里:容器是一等公民,容器之间直接通信,不需要额外的NAT,因此不存在源地址被伪装的情况;Node与容器网络直连,同样不需要额外的NAT。扁平化网络的优点在于:没有NAT带来的性能损耗,而且可追溯源地址,为后面的网络策略做铺垫,降低网络排错的难度等。

总体而言,集群内访问Pod,会经过Service;集群外访问Pod,经过的是Ingress。Service和Ingress是Kubernetes专门为服务发现而抽象出来的相关概念,后面会做详细讨论。

与CRI之于Kubernetes的runtime类似,Kubernetes使用CNI作为Pod网络配置的标准接口。需要注意的是,CNI并不支持Docker网络,也就是说,docker0网桥会被大部分CNI插件"视而不见"

具体过程如下:

  1. 当用户在Kubernetes的Master里创建了一个Pod后,Kubelet观察到新Pod的创建,于是首先调用CRI(后面的runtime实现,比如dockershim、containerd等)创建Pod内的若干个容器。
  2. 在这些容器里,第一个被创建的pause容器是比较特殊的,这是Kubernetes系统"赠送"的容器,也称pause容器。里面运行着一个功能十分简单的C程序,具体逻辑是一启动就把自己永远阻塞在那里。一个永远阻塞而且没有实际业务逻辑的pause容器到底有什么用呢?用处很大。我们知道容器的隔离功能利用的是Linux内核的namespace机制,而只要是一个进程,不管这个进程是否处于运行状态(挂起亦可),它都能"占"用着一个namespace。因此,每个Pod内的第一个系统容器pause的作用就是占用一个Linux的network namespace。
  3. Pod内其他用户容器通过加入这个network namespace的方式共享同一个networknamespace。用户容器和pause容器之间的关系有点类似于寄居蟹和海螺。因此,Containerruntime创建Pod内的用户容器时,调用的都是同一个命令:docker run--net=none。意思是只创建一个network namespace,不初始化网络协议栈。如果这个时候通过nsenter方式进入容器,会看到里面只有一个本地回环设备lo。
  4. 容器的eth0是怎么创建出来的呢?答案是CNI。CNI主要负责容器的网络设备初始化工作。Kubelet目前支持两个网络驱动,分别是Kubenet和CNI。Kubenet是一个历史产物,即将废弃,因此本节不过多介绍。CNI有多个实现,官方自带的插件就有p2p、bridge等,这些插件负责初始化pause容器的网络设备,也就是给pause容器内的eth0分配IP等,到时候,Pod内其他容器就使用这个IP与其他容器或节点进行通信。Kubernetes主机内容器的默认组网方案是bridge。flannel、Calico这些第三方插件解决容器之间的跨机通信问题,典型的跨机通信解决方案有bridge和overlay等。

六、DNS

Kubernetes DNS的命名方案也遵循可预测的模式,使各种服务的地址更容易被记住。服务不仅可以通过完全限定域名(FQDN)引用,还可以仅通过服务本身的name引用。

目前,Kubernetes DNS加载项支持正向查找(A Record)、端口查找(SRV记录)、反向IP地址查找(PTR记录)及其他功能。对于Service,Kubernetes DNS服务器会生成三类DNS记录,分别是A记录、SRV记录和CNAME记录。

headless"服务与"normal"服务的不同之处在于它们未分配Cluster IP且不执行负载均衡。

"normal"服务分配一个DNS A Record作为表单your-svc.your-namespace.svc.cluster.local的name(根域名可以在kubelet设置中更改)。此name解析为服务的集群IP。"

headless"服务为表单your-svc.your-namespace.svc.cluster.local的name分配一个DNS A Record。与"normal"服务相反,此name解析的是,为服务选择的一组PodIP。DNS不会自动将此设置解析为特定的IP,因此客户端应该负责集合中进行的负载均衡或循环选择。

  1. A记录
    A记录与普通Service和headless Service有区别。普通Service的A记录的映射关系是:

    headless Service的A记录的映射关系是:

    一旦启用了DNS,Pod将被分配一个DNS A记录,格式如下所示:

    如果在Pod Spec指定hostname和subdomain,那么Kubernetes DNS会额外生成Pod的A记录:
  2. SRV记录
    SRV记录是通过描述某些服务协议和地址促进服务发现的。SRV记录通常定义一个符号名称和作为域名一部分的传输协议(如TCP),并定义给定服务的优先级、权重、端口和目标。详细内容请参阅下面的示例:

    在上面的示例中,_sip是服务的符号名称,_tcp是服务的使用传输协议。记录内容代表:两个记录都定义了10的优先级。另外,第一个记录的权重为70,第二个记录的权重为20。优先级和权重通常用于建议指定使用某些服务器。记录中的最后两个值定义了要连接的端口和主机名,以便与服务通信。

Kubernetes DNS的SRV记录是按照一个约定俗成的规定实现了对服务端口的查询:

SRV记录是为"normal"或"headless"服务的部分指定端口创建的。SRV记录采用_my-port-name._my-port-protocol.my-svc.my-namespace.svc.cluster.local的形式。对于常规服务,它被解析的端口号和域名是my-svc.my-namespace.svc.cluster.local。在"headless"服务的情况下,此name解析为多个answer,每个answer都支持服务。每个answer都包含auto-generated-name.my-svc.my-namespace.svc.cluster.local表单的Pod端口号和域名。

  1. CNAME记录

    CNAME记录用于将域或子域指向另一个主机名。为此,CNAME使用现有的A记录作为其值。相反,A记录会解析为指定的IP地址。此外,在Kubernetes中,CNAME记录可用于联合服务的跨集群服务发现。在整个场景中会有一个跨多个Kubernetes集群的公共服务。所有Pod都可以发现这项服务(无论这些Pod在哪个集群上)。这是一种跨集群服务发现方法。

  2. dns使用及kubernetes dns策略

    options ndots:5的含义是当查询的域名字符串内的点字符数量超过ndots(5)值时,则认为是完整域名,直接解析,否则Linux系统会自动尝试用default.pod.cluster.local、default.svc.cluster.local或svc.cluster.local补齐域名后缀。

    例如,查询whoami会自动补齐成whoami.default.pod.cluster.local、whoami.default.svc.cluster.local和whoami.svc.cluster.local,查询过程中,任意一个记录匹配便返回,显然能返回DNS记录的匹配是whoami+default.svc.cluster.local。而查询whoami.default能返回DNS记录的匹配是whoami.default+svc.cluster.local。

最后,运行DNS Pod可能需要特权,即配置Kubelet的参数:--allow-privileged=true。

七、Kubernetes域名解析策略

Kubernetes域名解析策略对应到Pod配置中的dnsPolicy,有4种可选策略,分别是None、ClusterFirstWithHostNet、ClusterFirst和Default,其中:

  • None:从Kubernetes 1.9版本起引入的一个新选项值。它允许Pod忽略Kubernetes环境中的DNS设置。应使用dnsConfigPod规范中的字段提供所有DNS设置;
  • ClusterFirstWithHostNet:对于使用hostNetwork运行的Pod,用户应该明确设置其DNS策略为ClusterFirstWithHostNet;
  • ClusterFirst:任何与配置的群集域后缀(例如cluster.local)不匹配的DNS查询(例如"www.kubernetes.io")将转发到从宿主机上继承的上游域名服务器。集群管理员可以根据需要配置上游DNS服务器;
  • Default:Pod从宿主机上继承名称解析配置。None一个配置了None类型的dnsPolicy的Pod如下:

    以上Pod被创建后,test容器的/etc/resolv.conf内容如下所示:

    ClusterFirstWithHostNet
    使用ClusterFirstWithHostNet策略的一个Pod配置文件如下:

    如上所示,当Pod使用主机网络(hostNetwork:true)时,DNS策略需要设置成ClusterFirstWithHostNet。对于那些使用主机网路的Pod,它们是可以直接访问宿主机的/etc/resolv.conf文件的。因此,如果不加上dnsPolicy:ClusterFirstWithHostNet,则Pod将默认使用宿主机的DNS配置,这样会导致集群内容器无法通过域名访问Kubernetes的服务(除非在宿主机的/etc/resolv.conf文件配置了Kubernetes的DNS服务器)。
    ClusterFirst
    使用ClusterFirst策略的一个Pod配置文件如下所示:

    ClusterFirst策略就是优先使用Kubernetes的DNS服务解析,失败后再使用外部级联的DNS服务解析,工作流程如图3-21所示:
相关推荐
lichenyang4532 天前
Docker 学习笔记(四):Dockerfile,把项目打成自己的镜像
docker·容器
lichenyang4532 天前
Docker 学习笔记(三):Docker 网络、bridge、子网和容器互通
docker·容器
lichenyang4532 天前
Docker 学习笔记(二):docker run 的参数到底在控制什么?
docker·容器
运维开发故事5 天前
基于 Arthas 的多集群在线诊断系统设计与实现
kubernetes
Patrick_Wilson7 天前
从「改个端口」到 502:Next.js on k8s 的容器端口、Service 映射与 env 覆盖
docker·kubernetes·next.js
探索云原生7 天前
K8s 1.36 这个 GA 特性,把 initContainer 拉模型的 hack 干掉了
ai·云原生·kubernetes
云恒要逆袭7 天前
运行你的第一个Docker容器
后端·docker·容器
Java之美8 天前
一次k8s升级引发的DevicePlugin注册失败
云原生·kubernetes
程序员老赵9 天前
10 分钟部署 OpenCode:Docker 一键安装,浏览器打开就能用 AI 写代码(附完整命令与排错)
docker·容器·ai编程