目录
3.1.6 Route Reflector 模式(RR)(路由反射)
一.故事背景
继续学习k8s的两个模块
二. 常见网络组件
1. Flannel
Flannel 是K8s的一个简单而常用的网络插件,主要用于提供覆盖网络。它通过在每个主机上创建一个虚拟网络,将各个Pod连接起来。
实现原理: Flannel使用VXLAN(虚拟扩展局域网)或其他后端(如host-gw)来创建一个虚拟网络。每个Pod都有一个独特的IP地址,Flannel负责在不同主机间转发流量。
YAML示例:
apiVersion: v1
kind: Pod
metadata:
name: flannel
namespace: kube-system
spec:
containers:
- name: flannel
image: quay.io/coreos/flannel:v0.14.0
args: [ "--ip-masq", "--kube-subnet-mgr=kube-subnet" ]
使用场景: Flannel适合用于小型集群和开发环境。
例子: 假设一个小型开发团队正在开发一个Web应用,使用Flannel可以快速配置一个K8s集群,使得开发人员能够专注于编码,而无需担心网络设置。
2. Calico
Calico 是一种更复杂的网络插件,它不仅提供覆盖网络功能,还实现了网络策略和安全功能。
实现原理: Calico使用BGP(边界网关协议)来动态路由流量。它允许每个Pod有一个唯一的IP地址,并可以根据用户定义的网络策略控制流量。
YAML示例:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-access
spec:
podSelector:
matchLabels:
role: frontend
ingress:
- from:
- podSelector:
matchLabels:
role: backend
使用场景: 适合需要严格网络安全控制的生产环境。
例子: 假设一家医院的应用程序,需要确保患者数据的安全。运维团队可以使用Calico设置网络策略,限制只有特定的服务能够访问敏感数据,确保数据的隐私和安全。配置示例:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-policy
namespace: my-namespace
spec:
podSelector:
matchLabels:
app: frontend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: backend
在这个例子中,只有app=backend标签的Pod可以访问app=frontend标签的Pod。
3. Weave Net
Weave Net 是另一个流行的K8s网络插件,提供简单的安装和良好的可视化功能。
实现原理: Weave Net使用Overlay Network技术,通过在每个节点上创建一个虚拟网络,并通过加密和可视化工具来增强网络安全性和监控。
YAML示例:
apiVersion: v1
kind: Pod
metadata:
name: weave
namespace: kube-system
spec:
containers:
- name: weave
image: weaveworks/weave-kube:latest
使用场景: 适合需要高可用性和可扩展性的场景。
例子: 一家在线商店,随着用户的增加,流量不断增长。使用Weave Net,开发者可以轻松地扩展服务并保持网络连接稳定。可以通过Weave提供的命令行工具监控网络状态:
weave status
Ingress的使用
Ingress 是K8s中用于管理外部访问集群服务的一种API对象。它提供了HTTP和HTTPS路由到服务的能力。
Ingress的实现原理
Ingress通过一个或多个Ingress Controller(控制器)来工作,这些控制器负责接收外部请求并将其路由到相应的服务。Ingress Controller通常运行在集群内部,并使用负载均衡、SSL终止等技术来处理流量。
配套使用:Ingress通常与LoadBalancer服务类型和DNS服务一起使用。LoadBalancer服务为Ingress Controller提供一个外部IP地址,而DNS可以将域名解析到这个IP地址。
Ingress的YAML示例
以下是一个简单的Ingress配置示例:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
spec:
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80
使用场景
Ingress适合需要通过域名访问多个服务的情况。
例子:假设你有多个微服务,如用户服务和订单服务,你可以使用Ingress将所有流量路由到这些服务,而不需要在每个服务上暴露一个外部IP。用户可以通过myapp.example.com来访问这些服务,而Ingress Controller会根据配置将请求转发到相应的服务。
查看所用网络组件
要查看K8s集群中使用的网络组件,可以使用以下命令:
kubectl get pods -n kube-system
这将列出在kube-system命名空间中运行的所有Pod,你可以查找与网络相关的Pod(如Flannel、Calico等)以确认所用的网络插件。
不单独配置网络组件的影响
如果不单独配置网络组件,K8s集群仍然可以正常运行,但可能会面临一些影响和限制:
默认网络模式: 使用K8s默认的网络配置可能不支持高级功能,如网络策略、流量监控等。这可能会导致安全性降低,尤其是在生产环境中。
性能问题: 默认网络模式可能无法满足高负载或高可用性需求,影响应用的性能。
可扩展性: 在缺少合适网络插件的情况下,集群的可扩展性和灵活性可能受到限制。
配置网络组件的好处
安全性: 网络插件(如Calico)允许实现细粒度的网络策略,从而提高集群的安全性。
性能优化: 专业的网络插件提供了更高的性能和更低的延迟,适合处理高负载的应用。
可视化和监控: 一些网络组件提供流量监控和可视化工具,便于运维团队了解流量状况和网络健康。
功能丰富: 使用专用网络插件可以访问更丰富的功能,如负载均衡、故障恢复和流量控制。
类似组件
除了Ingress之外,还有一些其他组件也可以用于处理K8s中的外部访问:
1. Service
K8s的Service是一个基本的资源,用于定义如何访问一组Pod。Service可以创建不同类型的访问方式,包括ClusterIP(集群内部访问)、NodePort(节点IP访问)和LoadBalancer(负载均衡器)。
使用场景: Service通常用于需要直接访问某个Pod或服务的情况。
2. API Gateway
API Gateway是一个更高级的解决方案,通常用于微服务架构中。它可以提供更多的功能,如请求路由、负载均衡、安全认证和API版本管理。
使用场景: 适合大型微服务架构,尤其是需要多种功能集成的场景。
选择合适的网络插件和Ingress配置
在选择K8s网络插件和Ingress配置时,需要考虑几个因素:
集群规模: 小型集群可以选择Flannel,而大型或复杂的集群可能需要Calico或Weave Net。
安全需求: 如果你的应用对网络安全有较高要求,Calico是一个不错的选择。
外部访问方式: 根据应用的外部访问需求选择Ingress或API Gateway。
网络性能: 不同的网络插件在性能上有所不同,建议在正式环境前进行测试。
总结
理解 Kubernetes 的网络架构及其插件是成功使用K8s的关键。
Flannel 适合小型集群和开发环境;
Calico 适合需要严格网络安全控制的生产环境;
Weave Net 适合需要高可用性和可扩展性的场景。
Ingress 适合需要通过域名访问多个服务的情况。
三. 网络组件 Calico
1. calico 概述
1.1、calico 介绍
Calico 是一套开源的网络和网络安全方案,用于容器、虚拟机、宿主机之前的网络连接,可以用在kubernetes、OpenShift(红帽的容器编排工具)、DockerEE、OpenStack等PaaS或IaaS平台上。
1.2、calico 的优点
endpoints组成的网络是单纯的三层网络,报文的流向完全通过路由规则控制,没有overlay等额外开销;
calico的endpoint可以漂移,并且实现了acl。
1.3、calico的缺点
路由的数目与容器数目相同,非常容易超过路由器、三层交换、甚至node的处理能力,从而限制了整个网络的扩张;
calico的每个node上会设置大量(海量)的iptables规则、路由;运维、排障难度大;
calico的原理决定了它不可能支持VPC,容器只能从calico设置的网段中获取ip;
calico目前的实现没有流量控制的功能,会出现少数容器抢占node多数带宽的情况;
calico的网络规模受到BGP网络规模的限制。
1.4、calico 优势
更优的资源利用: 二层网络通讯需要依赖广播消息机制,广播消息的开销与host的数量成指数级增长,calico使用的三层路由方法,则完全抑制了二层广播,减少了资源开销。另外二层网络使用VLAN隔离技术,天生有4096个规格的限制,即便可以使用vxlan解决,单vxlan又带来了隧道开销的新问题,而calico不使用VLAN或者vxlan,资源利用率更高;
可扩展性:Calico使用与Internet类似的方案,Internet的网络比任何数据中心都大,Calico同样天然具有可扩展性;
更简单的容器调试:没有隧道,workloads之间的路径更短更简单,配置更少,在host上使用容器进行debug调试;
更少依赖:Calico仅依赖三层路由可达;
可适配性:较少的依赖使它能适配所有的VM、Container、白盒(程序源代码)或者混合环境场景;
2. Calico结构组成
Calico不使用重叠网络比如flannel和libnetwork重叠网络驱动,它是一个纯三层的方法,使用虚拟路由代替虚拟交换,每一台虚拟路由通过BGP协议传播可达信息(路由)到其他数据中心;Calico在每一个计算节点利用Linux Kernel实现了一个高效的vRouter来负责数据转发,而每个vRouter通过BGP协议负责把自己上运行的workload的路由信息向整个Calico网络内传播——小规模部署可以直接互联,大规模下可通过指定的BGP route reflector来完成。
2.1 架构
Felix:calico的核心组件,运行在每个节点上。主要的功能有接口管理、路由规则、ACL规则和状态报告
接口管理:Felix为内核编写一些接口信息,以便让内核能正确的处理主机endpoint的流量。特别是主机之间的ARP请求和处理ip转发。
路由规则:Felix负责主机之间路由信息写到linux内核的FIB(Forwarding Information Base)转发信息库,保证数据包可以在主机之间相互转发。
ACL规则:Felix负责将ACL策略写入到linux内核中,保证主机endpoint的为有效流量不能绕过calico的安全措施。
状态报告:Felix负责提供关于网络健康状况的数据。特别是,它报告配置主机时出现的错误和问题。这些数据被写入etcd,使其对网络的其他组件和操作人员可见。
Etcd:保证数据一致性的数据库,存储集群中节点的所有路由信息。为保证数据的可靠和容错建议至少三个以上etcd节点。
Orchestrator plugin:协调器插件负责允许kubernetes(容器)或OpenStack(虚拟机)等原生云平台方便管理Calico,可以通过各自的API来配置Calico网络实现无缝集成。如kubernetes的cni网络插件。
Bird:BGP客户端,Calico在每个节点上的都会部署一个BGP客户端,它的作用是将Felix的路由信息读入内核,并通过BGP协议在集群中分发。当Felix将路由插入到Linux内核FIB中时,BGP客户端将获取这些路由并将它们分发到部署中的其他节点。这可以确保在部署时有效地路由流量。
BGP Router Reflector:大型网络仅仅使用 BGP client 形成 mesh 全网互联的方案就会导致规模限制,所有节点需要 N^2 个连接,为了解决这个规模问题,可以采用 BGP 的 Router Reflector 的方法,使所有 BGP Client 仅与特定 RR 节点互联并做路由同步,从而大大减少连接数。
Calicoctl: calico 命令行管理工具。
2.2 名词解释
名词 | 作用 |
---|---|
endpoint | 接入到calico网络中的网卡称为endpoint |
AS | 网络自治系统,通过BGP协议与其它AS网络交换路由信息 |
ibgp | AS内部的BGP Speaker,与同一个AS内部的ibgp、ebgp交换路由信息。 |
ebgp | AS边界的BGP Speaker,与同一个AS内部的ibgp、其它AS的ebgp交换路由信息。 |
workloadEndpoint | 虚拟机、容器使用的endpoint |
hostEndpoints | 物理机(node)的地址 |
实际上,Calico 项目提供的 BGP 网络解决方案,与 Flannel 的 host-gw 模式几乎一样。也就是说,Calico也是基于路由表实现容器数据包转发,但不同于Flannel使用flanneld进程来维护路由信息的做法,而Calico项目使用BGP协议来自动维护整个集群的路由信息。
2.3 组网原理
calico组网的核心原理就是IP路由,每个容器或者虚拟机会分配一个workload-endpoint(wl)。
从nodeA上的容器A内访问nodeB上的容器B时:
+--------------------+ +--------------------+
| +------------+ | | +------------+ |
| | | | | | | |
| | ConA | | | | ConB | |
| | | | | | | |
| +-----+------+ | | +-----+------+ |
| |veth | | |veth |
| wl-A | | wl-B |
| | | | | |
+-------node-A-------+ +-------node-B-------+
| | | |
| | type1. in the same lan | |
| +-------------------------------+ |
| |
| type2. in different network |
| +-------------+ |
| | | |
+-------------+ Routers |-------------+
| |
+-------------+
#从ConA中发送给ConB的报文被nodeA的wl-A接收,根据nodeA上的路由规则,经过各种iptables规则后,转发到nodeB。
#如果nodeA和nodeB在同一个二层网段,下一条地址直接就是node-B,经过二层交换机即可到达。
#如果nodeA和nodeB在不同的网段,报文被路由到下一跳,经过三层交换或路由器,一步步跳转到node-B。
核心问题是,nodeA怎样得知下一跳的地址?
答案是node之间通过BGP协议交换路由信息。
每个node上运行一个软路由软件bird,并且被设置成BGP Speaker,与其它node通过BGP协议交换路由信息。 可以简单理解为,每一个node都会向其它node通知这样的信息:
我是X.X.X.X,某个IP或者网段在我这里,它们的下一跳地址是我。
通过这种方式每个node知晓了每个workload-endpoint的下一跳地址。
2.4、Calico 工作原理
Calico把每个操作系统的协议栈认为是一个路由器,然后把所有的容器认为是连在这个路由器上的网络终端,在路由器之间跑标准的路由协议——BGP的协议,然后让它们自己去学习这个网络拓扑该如何转发。所以Calico方案其实是一个纯三层的方案,也就是说让每台机器的协议栈的三层去确保两个容器,跨主机容器之间的三层连通性。
对于控制平面,它每个节点上会运行两个主要的程序,一个是Felix,它会监听ECTD中心的存储,从它获取事件,比如说用户在这台机器上加了一个IP,或者是分配了一个容器等。接着会在这台机器上创建出一个容器,并将其网卡、IP、MAC都设置好,然后在内核的路由表里面写一条,注明这个IP应该到这张网卡。绿色部分是一个标准的路由程序,它会从内核里面获取哪一些IP的路由发生了变化,然后通过标准BGP的路由协议扩散到整个其他的宿主机上,让外界都知道这个IP在这里,你们路由的时候得到这里来。
由于Calico是一种纯三层的实现,因此可以避免与二层方案相关的数据包封装的操作,中间没有任何的NAT,没有任何的overlay,所以它的转发效率可能是所有方案中最高的,因为它的包直接走原生TCP/IP的协议栈,它的隔离也因为这个栈而变得好做。因为TCP/IP的协议栈提供了一整套的防火墙的规则,所以它可以通过IPTABLES的规则达到比较复杂的隔离逻辑。
3. Calico 网络模式
BGP 边界网关协议(Border Gateway Protocol, BGP):是互联网上一个核心的去中心化自治路由协议。BGP不使用传统的内部网关协议(IGP)的指标。
Route Reflector 模式(RR)(路由反射):Calico 维护的网络在默认是(Node-to-Node Mesh)
全互联模式,Calico集群中的节点之间都会相互建立连接,用于路由交换。
但是随着集群规模的扩大,mesh模式将形成一个巨大服务网格,连接数成倍增加。
这时就需要使用 Route Reflector(路由器反射)模式解决这个问题。
IPIP模式:把 IP 层封装到 IP 层的一个 tunnel。作用其实基本上就相当于一个基于IP层的网桥!一般来说,普通的网桥是基于mac层的,根本不需 IP,而这个 ipip 则是通过两端的路由做一个 tunnel,把两个本来不通的网络通过点对点连接起来。
3.1、BGP 概述
BGP(border gateway protocol)是外部路由协议(边界网关路由协议),用来在AS之间传递路由信息是一种增强的距离矢量路由协议(应用场景),基本功能是在自治系统间自动交换无环路的路由信息,通过交换带有自治系统号序列属性的路径可达信息,来构造自治系统的拓扑图,从而消除路由环路并实施用户配置的路由策略。
(边界网关协议(BGP),提供自治系统之间无环路的路由信息交换(无环路保证主要通过其AS-PATH实现),BGP是基于策略的路由协议,其策略通过丰富的路径属性(attributes)进行控制。BGP工作在应用层,在传输层采用可靠的TCP作为传输协议(BGP传输路由的邻居关系建立在可靠的TCP会话的基础之上)。在路径传输方式上,BGP类似于距离矢量路由协议。而BGP路由的好坏不是基于距离(多数路由协议选路都是基于带宽的),它的选路基于丰富的路径属性,而这些属性在路由传输时携带,所以我们可以把BGP称为路径矢量路由协议。如果把自治系统浓缩成一个路由器来看待,BGP作为路径矢量路由协议这一特征便不难理解了。除此以外,BGP又具备很多链路状态(LS)路由协议的特征,比如触发式的增量更新机制,宣告路由时携带掩码等。)
实际上,Calico 项目提供的 BGP 网络解决方案,与 Flannel 的 host-gw 模式几乎一样。
也就是说,Calico也是基于路由表实现容器数据包转发,
但不同于Flannel使用flanneld进程来维护路由信息的做法,
而Calico项目使用BGP协议来自动维护整个集群的路由信息。
3.1.1 BGP两种模式
全互联模式(node-to-node mesh)
全互联模式 每一个BGP Speaker都需要和其他BGP Speaker建立BGP连接,这样BGP连接总数就是2^N - 2,如果数量过大会消耗大量连接。如果集群数量超过100台官方不建议使用此种模式。
路由反射模式Router Reflection(RR)
RR模式 中会指定一个或多个BGP Speaker为RouterReflection,它与网络中其他Speaker建立连接,每个Speaker只要与Router Reflection建立BGP就可以获得全网的路由信息。在calico中可以通过Global Peer实现RR模式。
3.1.2 Calico BGP 概述
3.1.3 BGP 是怎么工作的?
这个也是跨节点之间的通信,与flannel类似,其实这张图相比于flannel,通过一个路由器来路由,flannel.1 就相比于vxlan模式去掉,所以会发现这里是没有网桥存在,完全就是通过路由来实现,这个数据包也是先从veth设备对另一口发出,到达宿主机上的cali开头的虚拟网卡上,到达这一头也就到达了宿主机上的网络协议栈,另外就是当创建一个pod时帮你先起一个infra containers的容器,调用calico的二进制帮你去配置容器的网络,然后会根据路由表决定这个数据包到底发送到哪里去,可以从ip route看到路由表信息,这里显示是目的cni分配的子网络和目的宿主机的网络,当进行跨主机通信的时候之间转发到下一跳地址走宿主机的eth0网卡出去,也就是一个直接的静态路由,这个下一跳就跟host-gw的形式一样,和host-gw最大的区别是calico使用BGP路由交换,而host-gw是使用自己的路由交换,BGP这个方案比较成熟,在大型网络中用的也比较多,所以要比flannel的方式好,而这些路由信息都是由BGP client传输。
3.1.4 为什么叫边界网关协议呢?
和 flannel host-gw 工作模式基本上一样,BGP是一个边界路由器,主要是在每个自治系统的最边界与其他自治系统的传输规则,而这些节点之间组成的BGP网络是一个全网通的网络,这个网络就称为一个 BGP Peer。
启动文件放在 /opt/cni/bin 目录下,/etc/cni/net.d 目录下记录子网的相关配置信息。
$ cat /etc/cni/net.d/10-calico.conflist
{
"name": "k8s-pod-network",
"cniVersion": "0.3.0",
"plugins": [
{
"type": "calico",
"log_level": "info",
"etcd_endpoints": "https://10.10.0.174:2379",
"etcd_key_file": "/etc/cni/net.d/calico-tls/etcd-key",
"etcd_cert_file": "/etc/cni/net.d/calico-tls/etcd-cert",
"etcd_ca_cert_file": "/etc/cni/net.d/calico-tls/etcd-ca",
"mtu": 1440,
"ipam": {
"type": "calico-ipam"
},
"policy": {
"type": "k8s"
},
"kubernetes": {
"kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
}
},
{
"type": "portmap",
"snat": true,
"capabilities": {"portMappings": true}
}
]
}
3.1.5 Pod 1 访问 Pod 2 流程如下
1、数据包从 Pod1 出到达Veth Pair另一端(宿主机上,以cali前缀开头)
2、宿主机根据路由规则,将数据包转发给下一跳(网关)
3、到达 Node2,根据路由规则将数据包转发给 cali 设备,从而到达 Pod2。
其中,这里最核心的 下一跳 路由规则,就是由 Calico 的 Felix 进程负责维护的。这些路由规则信息,则是通过 BGP Client 中 BIRD 组件,使用 BGP 协议来传输。
不难发现,Calico 项目实际上将集群里的所有节点,都当作是边界路由器来处理,它们一起组成了一个全连通的网络,互相之间通过 BGP 协议交换路由规则。这些节点,我们称为 BGP Peer。
而 Flannel host-gw 和 Calico 的唯一不一样的地方就是当数据包下一跳到达node2节点容器时发生变化,并且出数据包也发生变化,知道它是从veth的设备流出,容器里面的数据包到达宿主机上,这个数据包到达node2之后,它又根据一个特殊的路由规则,这个会记录目的通信地址的cni网络,然后通过cali设备进去容器,这个就跟网线一样,数据包通过这个网线发到容器中,这也是一个二层的网络互通才能实现。
3.1.6 Route Reflector 模式(RR)(路由反射)
设置方法请参考官方链接 https://docs.projectcalico.org/master/networking/bgp
Calico 维护的网络在默认是 (Node-to-Node Mesh)全互联模式,Calico集群中的节点之间都会相互建立连接,用于路由交换。但是随着集群规模的扩大,mesh模式将形成一个巨大服务网格,连接数成倍增加。这时就需要使用 Route Reflector(路由器反射)模式解决这个问题。确定一个或多个Calico节点充当路由反射器,让其他节点从这个RR节点获取路由信息。
在BGP中可以通过calicoctl node status看到启动是 node-to-node mesh 网格的形式,这种形式是一个全互联的模式,默认的BGP在k8s的每个节点担任了一个BGP的一个喇叭,一直吆喝着扩散到其他节点,随着集群节点的数量的增加,那么上百台节点就要构建上百台链接,就是全互联的方式,都要来回建立连接来保证网络的互通性,那么增加一个节点就要成倍的增加这种链接保证网络的互通性,这样的话就会使用大量的网络消耗,所以这时就需要使用Route reflector,也就是找几个大的节点,让他们去这个大的节点建立连接,也叫RR,也就是公司的员工没有微信群的时候,找每个人沟通都很麻烦,那么建个群,里面的人都能收到,所以要找节点或着多个节点充当路由反射器,建议至少是2到3个,一个做备用,一个在维护的时候不影响其他的使用。
3.2、IPIP 模式概述(默认模式)
IPIP 是linux内核的驱动程序,可以对数据包进行隧道,上图可以看到两个不同的网络 vlan1 和 vlan2。基于现有的以太网将原始包中的原始IP进行一次封装,通过tunl0解包,这个tunl0类似于ipip模块,和Flannel vxlan的veth很类似。
3.2.1 Pod1 访问 Pod2 流程如下:
1、数据包从 Pod1 出到达Veth Pair另一端(宿主机上,以cali前缀开头)。
2、进入IP隧道设备(tunl0),由Linux内核IPIP驱动封装,把源容器ip换成源宿主机ip,目的容器ip换成目的主机ip,这样就封装成 Node1 到 Node2 的数据包。
此时包的类型:
原始IP包:
源IP:10.244.1.10
目的IP:10.244.2.10
TCP:
源IP: 192.168.31.62
目的iP:192.168.32.63
3、数据包经过路由器三层转发到 Node2。
4、Node2 收到数据包后,网络协议栈会使用IPIP驱动进行解包,从中拿到原始IP包。
5、然后根据路由规则,将数据包转发给cali设备,从而到达 Pod2。
3.3 Calico 优势 与 劣势
优势
没有封包和解包过程,完全基于两端宿主机的路由表进行转发
可以配合使用 Network Policy 做 pod 和 pod 之前的访问控制
劣势
要求宿主机处于同一个2层网络下,也就是连在一台交换机上
路由的数目与容器数目相同,非常容易超过路由器、三层交换、甚至node的处理能力,
从而限制了整个网络的扩张。(可以使用大规模方式解决)
每个node上会设置大量(海量)的iptables规则、路由,运维、排障难度大。
原理决定了它不可能支持VPC,容器只能从calico设置的网段中获取ip。
3.4 两种网络的对比
IPIP网络:
流量:tunl0 设备封装数据,形成隧道,承载流量。
适用网络类型:适用于互相访问的pod不在同一个网段中,跨网段访问的场景。
外层封装的ip能够解决跨网段的路由问题。
效率:流量需要tunl0设备封装,效率略低
BGP网络:
流量:使用路由信息导向流量
适用网络类型:适用于互相访问的pod在同一个网段,适用于大型网络。
效率:原生hostGW,效率高
总结:
项目 | IPIP | BGP |
---|---|---|
流量 | tunl0封装数据,形成隧道,承载流量 | 路由信息导向流量 |
适用场景 | Pod跨网段互访 | Pod同网段互访,适合大型网络 |
效率 | 需要tunl0设备封装,效率略低 | 原生hostGW, 效率高 |
类型 | overlay | underlay |
pod抓包
创建pod
kind: Pod
apiVersion: v1
metadata:
name: nginx1
spec:
nodeName: k8s-node1
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
kind: Pod
apiVersion: v1
metadata:
name: nginx2
spec:
nodeName: k8s-node2
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
查看pod网络接口编号
kubectl exec pods/nginx1 -- bash -c 'cat /sys/class/net/eth0/iflink'
查看对应pod的运行node
去对应节点查看网卡信息
抓取数据包并重定向到文件nginx.cap中
tcpdump -i caliefb4110901d -w nginx.cap
使用wireshark进行数据分析
Calico管理工具
curl -L https://github.com/projectcalico/calico/releases/download/v3.24.6/calicoctl-linux-amd64 -o /usr/sbin/kubectl-calico
chmod +x /usr/sbin/kubectl-calico# 验证插件是否有效
kubectl-calico node status
四. 网络组件 Flannel
1. 简介
flannel是coreos团队针对k8s设计的一个网络规划服务,简单来说,他的功能是让集群中的不同节点主机创建的docker容器具有全集群唯一的虚拟IP地址。
在默认的docker配置中,每个节点上的docker服务会分别负责所在节点容器的IP分配。这样导致的一个问题是,不同节点上容器可能获得相同的内外IP地址。并使这些容器之间能够通过IP地址相互找到,也就是互相ping通。
flannel的设计目的就是为集群中的所有节点重新规划IP地址的使用规则,从而使得不同节点上的容器能够获得“同属一个内网”且“不重复”的IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。
flannel实际上是一种“覆盖网络(overlaynetwork)”,也就是将TCP数据包装在另一种网络包里面进行路由转发和通信,目前已经支持udp、vxlan、host-gw、aws-vpc、gce和alloc路由等数据转发方式,默认的节点间数据通信是UDP转发。
flannel的特点:
(1)是集群中的不同node主机创建的docker容器都具有全集群唯一的虚拟IP地址
(2)建立一个覆盖网络(overlay network),通过这个覆盖网络,将数据包原封不动的传递到目标容器。覆盖容器是建立在另一个网络之上并由其基础设施支持的虚拟网络。覆盖网络通过将一个分组封装在另一个分组内来将网络服务与底层基础实施分离。在将封装的数据包转发到端点后,将其解封。
(3)创建一个新的虚拟网卡flannel0接受docker网桥的数据,通过维护路由表,通过维护路由表,对接受到的数据进行封包和转发(vxlan)
(4)etcd保证了所有node上flannel所看到的配置是一致的。同时每个node上的flanned监控etcd上的数据变化,实时感知集群中node的变化。
2. flannel对网络要求提出的解决方法
互相不冲突的IP
(1)flannel利用k8s api通过etcd存储整个集群的网络配置,根据配置记录集群使用的网段。
(2)flannel在每个主机上建立flanneld作为agent,它会为所在主机从集群的网络地址空间中获取一个小网段subnet,本主机内所有容器IP将从中分配。
在flannel network中,每个pod都会分配一个唯一的IP地址,且每个k8s node的subent不重叠
pod之间互相访问
(1)flanneld将本机获取的subnet以及主机之间通信的public ip通过etcd存储起来,需要时发给相应模块
(2)flanneld通过各种 backend mechanism,如vxlan、udp等跨主机转发容器之间的网络流量,完成容器之间的跨主机通信。
3. flannel架构原理
(一)flannel架构图和各组件
cni0:网桥设备,每创建一个pod都会创建一对veth pair。其中一端是pod中的eth0,另一端是cni0网桥的端口(网卡)。pod中从网卡eth0发出的流量都会发送到cni网桥设备的端口(网卡上)
cni0设备获得的IP地址是该节点分配到的网段的第一个地址。
flannel.1:overlay网络的设备,用来进行vxlan报文的处理(封包和解包)。不同node之间的pod数据流量都从overlya设备以隧道的形式发送到对端。
不同node上的Pod的通信流程
1)pod产生数据,根据pod的路由信息,将数据发送到cni0
2)cni0根据节点的路由表,将数据发送到隧道设备flannel.1
3)flannel.1查看数据包的目的IP,从flanneld获得对端隧道设备的必要信息,封装数据包。
4)flannel.1将数据包发送到对端设备。对端节点的网卡接收到数据包,发现数据包为overlay数据包,解开外层封装,并发送内层封装到flannel.1设备。
5)flannel.1设备查看数据包,根据路由表匹配,将数据发送给cni0设备。
6)cni0匹配路由表,发送数据给网桥上对应的端口。
五. CoreDNS组件
1. 概述
在 Kubernetes 中,服务发现有几种方式:
基于环境变量的方式
基于内部域名的方式
基本上,使用环境变量的方式很少,主要还是使用内部域名这种服务发现的方式。
其中,基于内部域名的方式,涉及到 Kubernetes 内部域名的解析,而 kubedns,是 Kubernetes 官方的 DNS 解析组件。从 1.11 版本开始,kubeadm 已经使用第三方的 CoreDNS 替换官方的 kubedns 作为 Kubernetes 集群的内部域名解析组件。
Kubernetes 中的域名是如何解析的?
在 Kubernetes 中,比如服务 a 访问服务 b,对于同一个 Namespace下,可以直接在 pod 中,通过 curl b 来访问。对于跨 Namespace 的情况,服务名后边对应 Namespace即可。比如 curl b.default。那么,使用者这里边会有几个问题:
服务名是什么?
为什么同一个 Namespace 下,直接访问服务名即可?不同 Namespace 下,需要带上 Namespace 才行?
为什么内部的域名可以做解析,原理是什么?
DNS 如何解析,依赖容器内 resolv 文件的配置
cat /etc/resolv.conf
nameserver 10.10.0.3
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
这个文件中,配置的 DNS Server,一般就是 K8S 中,kubedns 的 Service 的 ClusterIP,这个IP是虚拟IP,无法ping,但可以访问。
service 是否可以ping取决于 svc使用iptables 以及ipvs的区别。
如果pod使用dns策略为clusterfirst的时候 ,要经过 kubedns 的虚拟IP 10.10.0.3 进行解析,不论是 Kubernetes 内部域名还是外部的域名。
2. pod DNS策略
Kubernetes 中 Pod 的 DNS 策略有四种类型。
Default:Pod 继承所在主机上的 DNS 配置;
ClusterFirst:K8s 的默认设置;先在 K8s 集群配置的 coreDNS 中查询,查不到的再去继承自主机的上游 nameserver 中查询;
dnsPolicy: ClusterFirst
ClusterFirstWithHostNet:对于网络配置为 hostNetwork 的 Pod 而言,其 DNS 配置规则与 ClusterFirst 一致;
None:忽略 K8s 环境的 DNS 配置,只认 Pod 的 dnsConfig 设置。
dnsPolicy: "None"
dnsConfig:
nameservers:
- 114.114.114.114
searches:
- default.svc.cluster.local
options:
- name: ndots
value: "5" ###默认为5
3. CoreDns解析规则
在部署 pod 的时候, kubelet 在起容器的时候,会将其 DNS 解析配置初始化成集群内的配置。因此在每个pod里面都会有/etc/resolv.conf
文件,通过修改其中的配置可以更改DNS的查询规则
如下启动一个pod,查看/etc/resolv.conf配置
#在集群中 pod 之间互相用 svc name 访问的时候,会根据 resolv.conf 文件的 DNS 配置来解析域名
nameserver
:集群中的DNS服务器IP,一般来说就是CoreDNS
的ClusterIP
search
:需要搜索的域,默认情况下会从该pod所属的namespace开始逐级补充
#解析域名的时候,将要访问的域名依次带入 search 域,进行 DNS 查询。
#例如在pod 中访问一个域名为 nginx 的服务,其进行的 DNS 域名查询的顺序是:
nginx.default.svc.cluster.local -> nginx.svc.cluster.local -> nginx.cluster.local
#按照上述顺序直到查到为止
options ndots:触发上面的search的域名点数’.',在K8S中默认为5,上限15;例如test.com这个域名的ndots是1,test.com.这个域名的ndots才是2(需要注意所有域名其实都有一个根域.,因此test.com的全称应该是test.com.)。如果dnots 指定查询的域名包含的点 “.” 小于 5,则先走 search 域,再用绝对域名;如果查询的域名包含点数大于或等于 5,则先用绝对域名,再走 search 域。
#例如当ndots大于等于5时访问的是 a.b.c.e.f.g ,那么域名查找的顺序如下:
a.b.c.e.f.g. -> a.b.c.e.f.g.default.svc.test.com.
-> a.b.c.e.f.g.svc.test.com. -> a.b.c.e.f.g.test.com.
#例如当ndots小于5时访问的是 a.b.c.e. ,那么域名查找的顺序如下:
a.b.c.e.default.svc.test.com. -> a.b.c.e.svc.test.com.
-> a.b.c.e.test.com. -> a.b.c.e.
4. pod之间通信
通过svc的方式通信
在 K8s 中,Pod 之间通过 svc 访问的时候,会经过 DNS 域名解析,再拿到 ip 通信。而 K8s 的域名全称为 "<service-name>.<namespace>.svc.test.com"
,通常只需将 svc name 当成域名就能访问到 pod。
1:使用deploy启动一个nginx的pod,svc名称为nginx-svc,如下:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: docker.io/library/nginx:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
selector:
app: nginx
ports:
- port: 80
protocol: TCP
targetPort: 80
type: ClusterIP
查看pod中/etc/resolv.conf配置如下:
root@nginx-5977dc5756-lcmwq:/# cat etc/resolv.conf
search default.svc.test.com svc.test.com test.com
nameserver 169.254.25.10
options ndots:5
root@nginx-5977dc5756-lcmwq:/#
2:使用另外一个pod访问nginx-svc这个域名,如下:
[root@master yaml]# kubectl exec -it mypod bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
[root@mypod /]# ping nginx-svc
PING nginx-svc.default.svc.test.com (10.10.23.221) 56(84) bytes of data.
64 bytes from nginx-svc.default.svc.test.com (10.10.23.221): icmp_seq=1 ttl=64 time=0.152 ms
64 bytes from nginx-svc.default.svc.test.com (10.10.23.221): icmp_seq=2 ttl=64 time=0.143 ms
64 bytes from nginx-svc.default.svc.test.com (10.10.23.221): icmp_seq=3 ttl=64 time=0.114 ms
^C
--- nginx-svc.default.svc.test.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 0.114/0.136/0.152/0.018 ms
[root@mypod /]#
#############################
返回的nginx-svc的地址为10.10.23.221,此地址为nginx-svc的地址,如下:
[root@master yaml]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-svc ClusterIP 10.10.23.221 <none> 80/TCP 2d17h
[root@master yaml]#
#############################
如果要访问其他ns的svc,需要带上ns name即可,如下:
[root@master yaml]# kubectl exec -it mypod bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
[root@mypod /]# ping harbor-core
ping: harbor-core: Name or service not known ###不添加ns的话,无法解析habor-core
[root@mypod /]# ping harbor-core.harbor
PING harbor-core.harbor.svc.test.com (10.10.30.184) 56(84) bytes of data.
64 bytes from harbor-core.harbor.svc.test.com (10.10.30.184): icmp_seq=1 ttl=64 time=0.095 ms
64 bytes from harbor-core.harbor.svc.test.com (10.10.30.184): icmp_seq=2 ttl=64 time=0.130 ms
64 bytes from harbor-core.harbor.svc.test.com (10.10.30.184): icmp_seq=3 ttl=64 time=0.165 ms
^C
--- harbor-core.harbor.svc.test.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 0.095/0.130/0.165/0.028 ms
[root@mypod /]#
通过hostname和subdomain通信
在 K8s 中,如果不指定 pod 的 hostname,其默认为 pod.metadata.name,通过 spec.hostname 字段可以自定义;另外还可以给 pod 设置 subdomain,通过 spec.subdomain 字段。如下:
---
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: web
spec:
hostname: nginx ####设置hostnam为nginx
subdomain: subdomain-test ####
containers:
- name: nginx
image: docker.io/library/nginx:latest
imagePullPolicy: IfNotPresent
dnsPolicy: "None"
dnsConfig:
nameservers:
- 114.114.114.114
searches:
- default.svc.test.com
options:
- name: ndots
value: "5" ###默认为5
查看dns发现已经改成114.114.114.114了
继续加入内容
---
apiVersion: v1
kind: Service
metadata:
name: subdomain-test
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80
protocol: TCP
5.CoreDns Corefile 文件
CoreDNS 实现了应用的插件化,用户可以选择所需的插件编译到可执行文件中;CoreDNS 的配置文件是 Corefile 形式的,以下是CoreDns的configmap的配置:
apiVersion: v1
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes test.com in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
kind: ConfigMap
metadata:
creationTimestamp: "2023-12-08T17:18:27Z"
name: coredns
namespace: kube-system
resourceVersion: "224"
uid: ccf7598d-8b5c-48db-9230-7a539d6c7e98
配置文件分析
#第一部分
errors
health {
lameduck 5s
}
coredns内部插件,错误日志以及健康监测等,其他插件可以参考https://coredns.io/plugins/kubernetes/
#第二部分
kubernetes test.com in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
#指明 test.com 后缀的域名,都是 kubernetes 内部域名,coredns 会监听 service 的
变化来维护域名关系,test.com 相关域名都在这里解析。
#ttl 30:设置标准的DNS域名TTL,默认值为 5 秒。允许的最小 TTL 为 0 秒,
最大值为 3600 秒。将 TTL 设置为 0 将防止记录被缓存。
#pods insecure:总是从请求中返回带有 IP 的 A 记录(不检查 k8s),即查询域名1-2-3-
4.ns.pod.cluster.local.的时候,不论是否存在一个IP地址为1.2.3.4的pod,
都返回这个结果给客户端。如果与通配符 SSL 证书一起被恶意使用,此选项很容易被滥用。
提供此选项是为了向后兼容 kube-dns。
#fallthrough in-addr.arpa ip6.arpa:正常情况下一个客户端对CoreDNS发起了一个DNS查询,
如果该记录不存在,那么就会直接返回一个NXDOMAIN的响应。
#第三部分
forward . /etc/resolv.conf {
max_concurrent 1000
}
#指 coredns 中没有找到记录,则去 /etc/resolv.conf 中的 nameserver 请求解析,
而 coredns 容器中的 /etc/resolv.conf 是继承自宿主机的。实际是如果不是 k8s 内部域名,
就会去默认的 dns 服务器请求解析,并返回给 coredns 的请求者。
#第四部分
prometheus :9153
cache 30
loop
reload
loadbalance
#prometheus:CoreDNS 的监控地址为: http://localhost:9153/metrics ,
满足 Prometheus 的格式。
#cache:允许缓存
#loop:如果找到循环,则检测简单的转发循环并停止 CoreDNS 进程。
#reload:允许 Corefile 的配置自动更新。在更改 ConfigMap 后两分钟,修改生效
#loadbalance:这是一个循环 DNS 负载均衡器,可以在答案中随机化 A,AAAA 和 MX 记录的顺序。
#第五部分
#当某个域名服务不在集群内部时,为了让pod可以访问,可以在corefile中添加host选项如下:
hosts {
192.168.10.10 edu.com
fallthrough
}
案例:添加外部解析
在 Kubernetes 1.28 版本中,如果您想要通过 CoreDNS 的 ConfigMap 添加外部指定主机名的解析,可以使用 CoreDNS 的 hosts
插件。以下是如何配置 CoreDNS 的 ConfigMap 以添加 hosts
字段的示例:
获取当前 CoreDNS ConfigMap: 您可以通过以下命令获取当前的 CoreDNS ConfigMap 配置:
kubectl get cm coredns -n kube-system -o yaml > coredns-configmap.yaml
编辑 CoreDNS ConfigMap: 使用编辑器打开
coredns-configmap.yaml
文件,您将看到 CoreDNS 的配置文件Corefile
。在这个文件中,您可以添加hosts
插件的配置。添加 hosts 插件配置: 在
Corefile
中添加hosts
部分,例如,如果您想要将域名git.k8s.local
解析到 IP 地址10.151.30.11
,可以添加如下配置:
kubectl -n kube-system edit cm coredns
在这个配置中,hosts
插件被用来指定特定的 IP 地址和主机名映射。fallthrough
指令表示如果请求的域名不在 hosts
部分定义,则请求将被转发到下一个插件处理。
应用更改: 保存更改后的 coredns-configmap.yaml
文件,并使用以下命令应用更改:
kubectl apply -f coredns-configmap.yaml
重启 CoreDNS Pod: 更改 ConfigMap 后,您需要重启 CoreDNS Pod 以使更改生效。可以通过以下命令删除现有的 CoreDNS Pod:
kubectl delete pod -n kube-system -l k8s-app=kube-dns
这将触发 CoreDNS Pod 的重启,新的 Pod 将使用更新后的 ConfigMap 配置。
通过以上步骤,您可以在 Kubernetes 1.28 版本中通过 CoreDNS 的 ConfigMap 添加外部指定主机名的解析。这种方法允许您自定义 DNS 解析规则,以满足集群内外的域名解析需求。
验证
dig @10.96.0.10 git.k8s.local
六.总结
本文深入探讨了Kubernetes网络组件与DNS解析机制。重点分析了Flannel、Calico和WeaveNet三大网络插件的实现原理、适用场景及优缺点:Flannel简单易用适合小型集群,Calico提供高级网络策略适用于生产环境,WeaveNet则擅长高可用场景。详细阐述了Calico的BGP和IPIP两种工作模式,以及CoreDNS的解析规则和配置方法。文章还介绍了Pod间通信的DNS策略和解析流程,包括通过Service名称和Hostname/Subdomain两种方式的内部域名解析机制。最后提供了CoreDNS配置文件的核心参数解析和外部域名解析配置案例,为Kubernetes网络配置提供了全面指导。