K8S搭建完毕之后,碰到个问题,如何进行远程debug(别在生产环境远程debug哦)?那就需要打通局域网和K8S内部网络了。本文主要介绍Pod通信、K8S网络插件、局域网和K8S网络如何打通。
我们在实际使用K8S过程中,出现了以下需求:
以上问题,如果在办公室网络 和 K8S Pod 网络不通的情况下就很难受。
由于Kube.NETes集群会使用CNI插件创建Pod/Service内部子网,外面一般无法访问内部IP和域名,给开发、测试、 联调带来了很大的麻烦,因此打通开发测试环境Kubernetes集群内部子网和办公室的局域网,实现互联互通是经常遇到的问题。
在打通之前,我们先了解下K8S网络的基本知识。K8S的网络架构比较复杂,Kubernetes本身并不负责网络通信,但提供了容器网络接口CNI(ContAIner Network Interface),具体的网络通信交由CNI插件来实现。
这是一种标准设计,为了让用户在创建或销毁容器时都能够更容易地配置容器网络。用户只需要使用CNI插件就可以轻松的管理K8S网络。目前主流的开源CNI插件非常多,像Flannel、Calico等。
在探索CNI插件之前先了解下几个术语:
在打通局域网与Kubernetes内部网络之前,先简单描述下,K8S的Pod之间是如何通信的。
Pod通过虚拟Ethernet接口对(Veth Pair)与外部通信,Veth Pair像一根网线,一端在Pod内部,一端在Pod外部。同一个节点上的Pod通过网桥(Linux Bridge)通信,如下图所示。
在同一节点上的Pod会通过Veth设备将一端连接到网桥,且它们的IP地址是通过网桥动态获取的,和网桥IP属于同一网段。此外,同一节点上的所有Pod默认路由都指向网桥,网桥会负责将所有非本地地址的流量进行转发。因此,同一节点上的Pod可以直接通信。
Kubernetes要求集群Pod的地址唯一,因此集群中的每个节点都会分配一个子网,以保证Pod的IP地址在整个集群内部不会重复。在不同节点上运行的Pod通过IP地址互相访问,该过程需要通过集群网络插件实现,按照底层依赖大致可分为Overlay模式、路由模式、Underlay模式三类。
本文只介绍Calico,因为Flannel我也没用过,云上的K8S网络插件基本都是云厂商结合自己的VPC网络实现的,更不在介绍范围内了。
k8s网络插件主要分为:underlay和overlay,calico 主要分为3种模式:BGP、IPIP、VXLAN,BGP属于underlay、IPIP和VXLAN属于overlay。
Calico 是一种开源网络和网络安全解决方案,适用于容器,虚拟机和基于主机的本机工作负载。Calico 支持广泛的平台,包括 Kubernetes,Docker,OpenStack 和裸机服务。Calico 后端支持多种网络模式。
本文主要分析calico的ipip模式,旨在理解IPIP网络模式下产生的calixxxx,tunl0等设备以及跨节点网络通信方式。ipip模式主要原理就是在pod ip的基础上再封装一层node ip,这样在通过对应的路由规则,就可以转发到对应的目的地。
我采用的CNI插件Calico,例如,通过ip addr命令可以看到K8S Node节点下有许多cali打头的虚拟网卡,同时,还有tunl0这种IP隧道,可以看到采用的是Calico的IPIP模式。
通过route -n命令也能看到每个pod会对应一个虚拟网卡,访问别的网段会通过tunl0这个隧道发出去。
IPIP 模式:Calico默认使用这种方式。在原有 IP 报文中封装一个新的 IP 报文,新的 IP 报文中将源地址 IP 和目的地址 IP 都修改为对端宿主机 IP。开启时将Node路由之间做一个tunnel,再把两个网络连接起来的模式,会在各Node节点上创建一个名为tunl0的虚拟网络接口。
IP模式下的通信流程,见如下2个图:
在K8S-Node上执行ip addr可以看到以下信息,其中包含了tunl0和calixxxxxx:
# 回环地址
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
# 物理网卡
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:50:56:95:32:45 brd ff:ff:ff:ff:ff:ff
inet 10.20.1.22/24 brd 10.20.1.255 scope global noprefixroute ens192
valid_lft forever preferred_lft forever
# Docker0
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:4f:0d:6a:48 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
# cali打头的Calico网卡
4: cali7533706c752@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 0
7: cali6bf54ec99f4@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 3
8: calif0a8819d7b4@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 4
9: cali2299828844c@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 5
10: cali7c84f4d310b@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 6
# Calico IP隧道使用的Tunl0网卡
15: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1440 qdisc noqueue state UNKNOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
inet 10.21.230.0/32 brd 10.21.230.0 scope global tunl0
valid_lft forever preferred_lft forever
我们也可以看到veth是成对出现的,比如进入到Pod,查看网络情况,在K8S-Master执行命令kubectl exec -it ingress-Nginx-controller-nginx-d864d97df-22ljk -n ingress-nginx -- ip addr,可以看到虚拟网卡的编号if19,如下情况:
# 回环网卡
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
# 虚拟网卡
3: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1440 qdisc noqueue state UP
link/ether 66:e5:5b:b6:77:9a brd ff:ff:ff:ff:ff:ff
inet 10.21.69.212/32 scope global eth0
valid_lft forever preferred_lft forever
# 隧道网卡
4: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
在到这个Pod所在K8S-Node执行命令ip addr,可以看到有一个编号是19的cailixxxxx,如下情况:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:50:56:95:c0:f4 brd ff:ff:ff:ff:ff:ff
inet 10.20.1.24/24 brd 10.20.1.255 scope global noprefixroute ens192
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:2c:ea:87:52 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
5: cali7359ae97a07@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 1
6: cali763ea01ddd0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 2
8: cali0140629a81f@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 4
10: calid3a5006f559@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 6
11: calic2abb800440@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 7
12: cali06eecb511af@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 8
13: cali26321116fa3@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 9
17: calia5d32a88758@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 13
# Pod内的eht0标记的是19号和这里的19号是配对的
19: calib51fc1cd61e@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 15
20: cali5910af186a4@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 16
21: calie8b8d191185@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 17
BGP 模式:将节点做为虚拟路由器通过 BGP 路由协议来实现集群内容器之间的网络访问。不再创建额外的tunnel。它会以daemonset方式安装在所有node主机,每台主机启动一个bird(BGP client),它会将calico网络内的所有node分配的ip段告知集群内的主机,并通过本机的默认网关的网卡(如:eth0)转发数据 BGP网络相比较IPIP网络,最大的不同之处就是没有了隧道设备 tunl0。 前面介绍过IPIP网络pod之间的流量发送到 tunl0,然后tunl0发送对端设备。BGP网络中,pod之间的流量直接从网卡发送目的地,减少了tunl0这个环节。
BGP模式下的通信流程,见下图:
如果K8S集群就部署在局域网内或者部署在自己的数据中心,整个链路上的网关可配的话,用静态路由表是最简单的办法,其原理是作用在网络模型的第三层 网络层,直接告诉网关某些IP要发给谁。
通过上面的K8S网络小知识和执行命令看到的路由转发的截图,我们知道K8S-Node其实也是一个虚拟路由,只要请求被转发到K8S-Node,那么就可以访问到Pod。
举一个最简单的例子,某开发环境的K8S部署在和办公室同一个局域网,有下面两条线路可以打通网络,如下图:
此时只需要公司的运维在网关路由器上添加静态路由规则,把属于K8S的Pod/Service CIDR的IP包全转给其中某个K8S节点,这样访问10.96.0.1这样的IP,网络包会到达某个集群物理节点,而集群内的物理节点或VM,一般K8S网络插件(CNI)都会做与Pod/Service CIDR的互通。
如果K8S部署的机器和公司办公室不在同一个网关下,或者部署在自建数据中心的,整个链路会多几个网关,链路上每个网关都需要配置路由表路由相应的CIDR到相邻的跃点,如果是办公网络到到达云上的K8S集群,也只需要从路由器经过VPN到达云上即可,如下图: