网络接入方案
分布式系统中,网络是核心,理解了网络,就理解了微服务
假设发布了一个服务,用户要在外部访问到,需要解决的问题:
- 域名解析
- 虚拟IP地址路由: 把虚拟IP地址路由至对应的负载均衡服务器
- 网段转换: 将集群外部网段的数据包转换成集群内部网段的数据包
- SSL卸载和七层负载均衡
数据中心基础架构
- 接入层: 工作在OSI链路层,接入层交换机
- 汇聚层: 汇聚层交换机负载连接接入层设备,接入层和核心层桥梁的职责,主要是用于汇聚来自接入层额上行链路的数据,减少对核心层接口的需求。同时承担策略配置、安全保证、隔离等职责。(当接入层设备过多是可以切分成多个相互隔离的虚拟网络VLAN, 以避免广播风暴等问题)
- 核心层: 工作在OSI的网络层,为保证可靠性,一般采用双机冗余备份
三层网络,传统的单体应用是南北流量为主,与三层网络契合较好,但是微服务架构将南北流量转为了东西流量,数据中心的网络架构演进为基于路由交换技术的叶脊网络,跨机架的网络传输都只需要恒定的一跳
叶脊网络为了保证多条活路和网络延迟的一致性,所有叶交换机和所有脊交换机是直接互联的,形成网格结构,也就是同样的网络规模下叶脊模式需要的硬件设备更多,成本更高
域名服务,dig返回的结果,dns记录中A是指ipv4的,AAAA指ipv6的
众多云平台都把自己定位为数据中心操作系统,也就是依托于云平台
netfilter是一个基于用户自定义Hook实现多种网络操作的Linux内核框架,支持包过滤、网络地址转换、端口转换等,以此实现包转发或禁止包转发至敏感网络
常见的插件包括iptables、ipvs,不同点在于hook点不同,以及优先级不同(iptables会比ipvs优先处理)
也就是安装的网络插件越多,越有可能排查不了出现的网络问题😹
- NF_IP_PRE_ROUTING: 接收的数据包进入协议栈后触发,进行路由判断发送到哪里
- NF_IP_LOCAL_IN: 经过路由判断后,如果发现目标在本机上,触发
- NF_IP_FORWARD: 经过路由判断后,如果发现目标在其他机器上,触发
- NF_IP_LOCAL_OUT: 本机产生的准备发送的数据包,在进入协议栈后立即触发此回调函数
- NF_IP_POST_ROUTING: 本机产生的准备发送的数据包或者经由本机转发的数据包,经过路由判断之后,触发
iptables
iptables中与这些hook点对应的规则集(iptables Chain): PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTING

不同的hook点可以有多个表,处理的优先级不同,raw最大,nat最小
根据不同的Hook点,根据对数据包的处理目的不同,规则规则又被分为不同的表,包括filter(是否允许包继续向前,用作防火墙)、nat(网络地址转换规则,实现负载均衡等数据包转发功能)、managle(修改数据包的包头,修改ttl、标记、跳数等,仅处理在内核中的数据结构,不会修改数据包本身)、raw(允许根据连接跟踪信息配置规则,连接跟踪信息就是每个创建的连接的源IP地址、目标IP地址、端口等连接状态)、security等,用来存储不同目的的处理规则
ipset
ipset能够简化iptables的规则,将要处理的IP地址及端口放进一个集合后,对这个集合设置一条iptables规则即可取代原来成败上千的命令
ipvs
为了解决iptables的性能问题,社区引入了kube-proxy的IPVS模式
基于传输层的负载均衡和数据转发模块。提供用户态工具(ipvsadm)及内核接口(netlink)管理规则
仅实现了LOCAL_IN、LOCAL_OUT、FORWARD这三个Hook点

负载均衡
负载均衡实现方案有nat转换和TCP/UDP Termination
(新建tcp连接),新建tcp连接的负载均衡需要额外的工具处理,比如nginx、HAProxy、Envoy
另外为了减少响应包的数据占比,可以使用直接路由(DSR)绕过负载均衡使用默认的网关直接返回给客户端
实现DSR的主要方式有基于链路层协议的负载均衡(配置复杂,真实服务器与上游服务器要配置再同一个链路层网络中违背了云原生的无状态原则,只有请求包为流量监控和故障排查带来了挑战)和基于隧道技术(IP over IP)的负载均衡
部署方式包含集中式和进程内负载均衡(客户端负载方案,基于注册中自注册和自发现)
负载均衡的策略: 随机、轮询、最小连接、一致性hash、权重
基于负载均衡的故障检查一般都是连通性检查
负载均衡设备设备的资源大部分都是留给数据转发平面的,控制平面只占很少的CPU,频繁的变更会导致负载均衡设备响应慢,变更生效慢。
Kubernetes的服务发布
提供了基于Service和Ingress的负载均衡技术,使得Kubernetes用户可以通过创建相应的spec来完成服务发布
假设我们使用Deployment对象定义了三个Pod副本,并且创建了一个统一的标签。然后定义Service通过Pod标签进行过滤筛选出对应的pod。然后这些spec传递个APIServer,保存到etcd后,相应的controller也会开始执行并创建相应的对象
Service默认的网络类型是clusterIP(具体值是在集群内部的虚拟IP地址),并且由于service的selector不为空,创建endpoint对象(即endpoint是一个关联Service和Pod的对象),找到对应的pod并判断是否ready并放到相应的属性下
服务类型
- ClusterIP:默认类型,虚拟ip,通常集群外部无法访问
- nodePort: 每个节点的kube-proxy会尝试在服务分配的nodePort上建立侦听器接收请求,并转发给服务对应的后端Pod实例,一般用于集群外部访问,范围时3000-3200,非标准大端口可能需要额外的防火墙配置
- LoadBalancer

ServiceController主要职责就是监控Service和Endpoint的变化,如果捕获到Service创建的事件,则调用负载均衡API完成配置: 负载均衡器调度,虚拟IP地址分配、负载均衡器配置
kube-proxy关注集群的所有Service以及对应Endpoint的变更,并根据这些变更将信息写入到本机的iptables规则或ipvs规则,用户空间代理的数据转发行为也是kube-proxy处理的,尝试对后端的Endpoint创建新的连接。当连接超时失败时,它会继续尝试下一个有效端点,而重试的超时时间也是由kube-proxy控制的,并且逐渐增加
来自容器的客户端访问另一台节点的服务器时,首先会发送至主机网络Namespace中(通常Veth Pair), 请求在主机网络协议栈处理时,iptables规则生效,该请求被转发至本机的kube-proxy所监听的Proxy-Port, 数据拷贝至用户态,经过kube-proxy的转发再转发至后端Pod所在的节点
DNS
Kubernetes提供内置的域名服务CoreDNS,所有创建的服务都会自动获取一个域名,并且只要服务名不变域名也不会变
CoreDNS实现的是服务和服务ClusterIP的域名配置,提供的域名服务仅对集群内部客户端可见
CoreDNS原理就是控制器监听Service和Endpoint的变化并配置DNS,Pod在根据域名解析的时候就是从CoreDNS中查询。对于不同类型的服务创建的DNS记录也不同
- ClusterIP、nodePort、LoadBalancer类型:
$svcname.$namespace.svc.$clusterdomain
- Headless Service:格式为
$podname.$svcname.$namespace.svc.$clusterdomain
的A记录指向PodIP - ExternalName: 用来引用一个已经存在的域名,为该Service创建一个CName记录指向目标域名
pod中的策略属性DNSPolicy,默认为ClusterFirst,pod启动时会改写/etc/resolv.conf, 所有的地址解析优先发送至CoreDNS
API网关和服务网格
Service支持的四层网关主要承担网络接入的职责,它通过对负载均衡设备的整合,实现了外部客户端到集群内部的路由和网络转换,但因为在第四层只提供了基础的流量转换功能,对高级网络协议的支持不够,例如http2、grpc之类
将API网关gauze到负载均衡后端,二者配合完成网络地址转换、负载均衡和高级路由的功能
API网关将各系统对外暴露的服务聚合在一起,所有要调用这些服务的系统都要通过API网关进行访问,基于这种方式,网关可以对API进行统一管控,例如认证、鉴权、流量控制、协议转换、监控等。但是同样带来的挑战时,当开发人员更新胡总删除微服务是API网关也必须要更新,也就是API网关的更新越轻量级越好
或许可以使用一个注册中心,服务自己注册有哪些API?
用户访问统一的API网关入口,再基于请求Header进行L7匹配,并由API网关选择上游服务,以及状态健康的实例。基于路由和负载均衡这两个基本功能之后API网关就可以提供高级的灰度发布功能,管理人员可以通过设置规则将请求转发至应用不同的版本
业界常用的网关方案包括Kubernetes Ingress、Istio, 自动化的生成反向代理软件的配置文件,完成数据转发。反向代理软件包括Nginx、HAProxy、Envoy等,用户转发网络请求也称为转发平面组件或者数据平面组件
API网关是系统的入口,所以必须保证网关的高可用,多实例部署,并且在网关之上还需要通过负载均衡提供虚拟IP地址

基于API网关的数据转发会导致上层设备的流量压力,额外的网络跳转,增加了网络延迟和超时出错的概率,优化方案就是服务网格,一个可配置、低延迟的网络架构,概括就是为了减少网络延迟及对集中式负载均衡器的依赖需要做的事将集中式负载均衡分散开来,编程分布式负载均衡
服务网格就是把API网关功能下发作为Sidecar,这样服务与服务之间的东西流量不再需要经过API网关,只需将请求转交给SideCar,由SideCar做请求路由和负载均衡,然后直接发起向上游服务器的连接

Envoy
支持HTTP1、HTTP2、grpc协议,TLS客户端认证等功能
支持配置管理服务器,用于管理配置,变更都被被Envoy的xDS发现并自动加载
健康检查(Kubernetes默认的Readiness Probe只能测试主机网络Namespace到运行在本机的Pod这一段,无法检查出整个网络链路的连通性)
支持的发现服务
- Cluster Discovery Service
- Endpoint Discovery Service
- Route Discovery Service
- Listener Discovery Service
- Secret Discovery Service
- Health Discovery Service
- Aggregated Discovery Service
Ingress
Kubernetes中默认的仅定义的规范,具体的实现需要使用插件的形式加载,例如社区中默认的Ingress插件式基于Nginx实现的,包含IngressController组件和Nginx组件
ingressController监听Ingress、Service和Endpoint配置解析和生成Nginx配置文件然后对nginx执行reload,但是这种进程频繁创建销毁开销太大,所以很难用于生产环境
ingress在配置转发规则时,目标Service需要与Ingress在同一个namespace中,导致微服务跨Namespace的部署变得不可能,然后具体实现的Ingress Controller对于这种限制一般是扩展Ingress Annotation完成复杂的路由功能
Contour
VMWare公司主导的开源Kubernetes Ingress Controller组件,采用Envoy作为反向代理和负载均衡组件,并且提供了API网关的模型抽象和配置管理
抛弃了原生Ingress,通过CRD定义更复杂的逻辑,引入了HTTPProxy作为API网关的抽象模型,与Kubernetes Ingress相比,具有更大的灵活性: 多租户集群的支持,允许配置转发规则,允许同一个路由规则指向多个Kubernetes Service、无需Annotation扩展
Istio
谷歌主导,最求的是大一统的关于API网关所有需求的开放式解决方案,终极目标是无论业务特性如何,都能接入开放平台
不仅管理入站出站流量,还管理服务网格的微服务之间的流量
架构是Envoy作为数据平面组件,istiod控制平面组件,istiod的相较于早期的分散部署,现在的集中模式,简化运维,以及提供控制平面的稳定性

istio安装好后,可以通过定义Istio对象将服务发布至API网关,并且根据实际场景决定是否加入服务网格,Istio支持的功能包括
- 流量管理: HTTP、grpc、websocket和TCP流量的自动负载均衡, 通过丰富的路由规则、重试、故障转移和故障注入,对流量行为进行细粒度控制
- 策略控制:可插入的策略层和配置API,支持访问控制、速率限制和配额
- 遥测收集:对出入集群入口和出口的所有流量自动度量指标,日志记录和跟踪
- 安全保证: 通过强大的基于身份的验证和授权,在集群中实现安全的服务间通信
istio的流量劫持机制,可以通过手动(istioctl kube-inject -f example.yaml
)或者自动注入(在namespace上打上istio-injection: enabled标签
, 任何pod创建都会激活该webhook)Sidecar Container到pod中
结果就是往用户pod增加了两个容器,istio-init(一个init-container), istio-proxy(一个sidecar container,镜像就是Envoy)
istio-init就是来改写iptables

istio具体如何使用查看官方文档