信也科技基础架构部为解决服务调用不统一、异构语言和异构框架服务治理等问题,采用istio落地Service Mesh,进行了云原生的落地。本文将分享我们在大规模落地istio过程中遇到的两个挑战。
落地k8s时,选择了MacVlan架构落地容器网络,来支持静态IP管理,拉平虚拟机网络,进而支持虚拟机平滑的容器化迁移,服务之间使用nginx进行请求转发并未使用k8s的service,详情介绍请查看拍拍贷k8s实践。
istio默认下发到边车的配置包含集群内全量服务信息,并且只要集群中相关配置有变化,都会触发全量配置xDS下发到每个边车envoy中。当服务数量越来越大时,istio控制面推送到边车envoy的xDS配置随之增多,边车envoy内存消耗就会越来越大,这造成了服务器资源极大的浪费。同时由于istio控制面推送xDS配置的大小、次数、时长、失败率都在增高,导致istio控制面出现了不稳定的情况。
Istio是由Google、IBM、Lyft等共同开源的Service Mesh框架,用于连接、监视和保护Kubernetes集群中的容器,关于Istio的详情请阅读 Istio官网
Envoy是一款由Lyft开源的,使用C++编写的L7代理和通信总线,它是Istio服务网格中默认的数据平面。关于Envoy的详情请阅读 Envoy官网
我们在fat测试环境大规模推进istio落地的过程中,发现当应用开启站点1000+,实例3000+时,边车envoy占用内存就会高达300M~500M。Mesh控制面网络发送率达到50M/s,xDS推送次数达到30次/s,推送时间99线高达10s,失败次数也随之增多。于是如何降低边车envoy内存成为急需解决的问题。
边车内存过大根本原因是istio默认将全量服务信息下发到边车envoy,但正常情况下一个微服务运行时,访问其他服务并不会超过30个,所以最合理的方案应该是istio仅为每个边车下发其所需的xDS配置即可,这样每个边车内只会存储自己依赖的服务配置,此时边车内存将大幅减少。istio官方提供了Sidecar配置,可以在Sidecar中配置此服务的调用关系,这时istio将仅会下发每个边车所需的xDS配置。虽然官方提供了这种解决方案,但是真正落地时还是有些问题需要解决,比如:1)如何明确服务间的调用关系?2)服务新增了调用关系,如何实现懒加载配置?
第1步:获取服务间调用情况
如下图所示,新增demoa服务调用demob服务,此时demoa边车envoy中没有demob的服务信息,demoa边车envoy会透传这部分流量到nginx上,然后利用Istio的可观测性,使用prometheus从边车envoy中抓取出统计调用关系的metrics以便动态计算调用关系。
第2步:动态计算调用关系
抓取到metrics后,利用Mesh管理服务进行筛选、计算,得出新增的调用关系:demoa调用demob,然后将其更新到Mesh的SidecarScope配置中。
通过上述方案,边车envoy内存消耗减少近80%,Mesh控制面xDS配置推送次数、推送时间等都大幅降低。具体如下:
通过istio官方提供的Sidecar配置结合动态的计算调用关系,实现了istio按需进行xDS配置推送,极大的减少istio服务网格部署对内存的额外消耗,同时也大幅降低了Mesh控制面下发xDS配置的压力。
fat是默认的测试环境,同时开发与测试同学也可以在“星云多环境”中创建独立的测试环境,用于个人专属的开发与测试。
fat环境与星云多环境之间的路由规则如下:
同环境优先:如上图所示,在env-01环境与fat环境同时有demob应用实例的情况下,env-01环境中demoa应用访问demob应用时,demoa会优先请求env-01环境中的demob实例。
流量fallback默认fat环境:如上图所示,在env-02环境中没有demob应用实例情况下,env-02环境中demoa应用访问demob应用时,demoa会请求到默认fat环境的demob应用实例。
流量fallback星云环境:如上图所示,在env-02环境没有demob应用实例,并且与fat环境同时有democ应用实例的情况下,当服务访问链路为 demoa -> demob -> democ 时,访问democ的流量会优先请求到env-02环境。
星云环境mock应用:如下图所示,当env-01环境中的demob应用配置了mock,此时demoa应用访问demob应用时请求都将访问到mock服务。
在推进istio落地过程中,如何通过istio流量控制实现星云多环境路由功能成为另一个待解决的问题。Istio支持多环境路由核心使用的是“流量打标”、“按标路由”以及流量Fallback的能力。
实现原理:在边车envoy出口流量上打标星云环境特有的HTTP请求头header,并保持此header在整个请求链路上透传,边车envoy可根据不同的环境请求头,将流量路由到各环境的服务,进而实现上述星云多环境路由能力。
HTTP请求头header全链路透传依赖了架构组自研的 谛听项目,感兴趣的同学请查看链接中文章了解学习。
istio实现星云多环境路由主要是这两点:1)流量打标 2)istio多环境路由配置
流量打标也称为流量染色,指对具有一定特征的流量请求打上具体的染色标记。istio是通过透明拦截的方式劫持了应用流量,边车envoy能够对应用流量进行染色标记。
istio要实现流量染色,首先需要边车envoy能够判断出其所在星云环境名称,然后根据环境名称在出口流量上进行打标。
具体实现如下:
发布系统在某环境发布实例时,将环境名称设置在实例边车envoy环境变量ISTIO_SUB_ENV中。
自定义边车envoy环境变量案例:
第1步:在Istioremote集群中,修改istio-sidecar-injector中名为istio-proxy的容器配置,添加环境变量。具体配置如下:
- name: ISTIO_SUB_ENV
valueFrom:
fieldRef:
fieldPath: metadata.labels['env_name']
第2步:发布系统发布pod时,在pod上写入env_name=环境名称的标签。然后边车envoy启动时会根据第1步的配置,读取pod上env_namelabels的值,将其赋值给ISTIO_SUB_ENV环境变量。
istio的Envoy Filter获取到实例边车envoy中ISTIO_SUB_ENV环境变量值(星云环境名称)后,会在边车envoy的HTTP出流量添加ENV-ROUTER-TARGET=星云环境名称的请求头,进行流量打标。
Envoy Filter是Istio中自定义的一种网络资源对象,用来更新配置envoy中的filter,为服务网格控制面提供了更强大的扩展能力,使envoy中filterchain具备自定义配置的能力。详细介绍请查看Istio官网Envoy Filter介绍。
Envoy Filter配置lua代码实现如下:
function envoy_on_request(handle)
local mesh_sub_env = os.getenv("ISTIO_SUB_ENV") # 获取边车中环境变量 ISTIO_SUB_ENV 值(环境名称)
local router_target = handle:headers():get("ENV-ROUTER-TARGET")# 获取流量中名称为 ENV-ROUTER-TARGET 的 HTTP 请求头 header
if((mesh_sub_env ~= nil) and (router_target == nil)) # 环境名称不为 null 并且不存在 ENV-ROUTER-TARGET 请求头时,进行种头操作
then
handle:headers():add("ENV-ROUTER-TARGET", mesh_sub_env)
end
end
流量打标案例:
第1步:星云env-01环境发布demoa应用,发布系统会在demoa应用实例的边车envoy中写入ISTIO_SUB_ENV=env-01环境变量。
第2步:边车envoy通过Envoy Filter判断不存在环境请求头时,根据获取到的ISTIO_SUB_ENV=env-01,对经过边车envoy的所有出流量都添加ENV-ROUTER-TARGET=env-01请求头。
第3步:如下图所示,边车envoy会根据不同环境HTTP请求头,将请求发送到指定环境的服务。
istio多环境路由配置主要是通过目标规则(Destination Rule)和虚拟服务(Virtual Service)实现的。详细介绍请查看 istio官网流量管理介绍。
目标规则(Destination Rule)可以将目标服务根据不同环境分为不同子集。demob服务分布在3个不同环境中,目标规则会将这三个实例分别标记为3个子集。
具体配置如下:
---
apiVersion: "networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
name: "demob.ppdapi.com"
namespace: "istio-config"
spec:
host: "demob.ppdapi.com"
subsets:
- labels:
cluster: "env-01" # 实例分组
name: "env-01" # 星云 env-01 环境
- labels:
cluster: "env-02"
name: "env-02" # 星云 env-02 环境
- labels:
cluster: "default"
name: "default" # FAT 默认 fallback 环境
每个虚拟服务(Virtual Service)包含一组路由规则,istio按顺序进行评估,将不同的请求匹配到虚拟服务指定的目标地址。我们可以根据不同的环境请求头,将流量路由到各环境服务,进而实现按环境路由请求。
具体配置如下:
---
apiVersion: "networking.istio.io/v1alpha3"
kind: "VirtualService"
metadata:
name: "demob.ppdapi.com"
namespace: "istio-config"
spec:
hosts: # 对 demob 应用有效
- "demob.ppdapi.com"
http:
- match: # 条件匹配块
- headers: # 当请求头 ENV-ROUTER-TARGET = env-01 匹配成功
ENV-ROUTER-TARGET:
exact: "env-01"
name: "env-01-routes" # 条件匹配块名称
route:
- destination: # 指定符合此条件的流量目标地址
host: "demob.ppdapi.com"
subset: "env-01"
- match: # 条件匹配块
- headers: # 请求头 ENV-ROUTER-TARGET = env-02 匹配成功
ENV-ROUTER-TARGET:
exact: "env-02"
name: "env-02-routes" # 条件匹配块名称
rewrite: # 重写 header authority:demob.ppdapi.com
authority: "demob.ppdapi.com"
route:
- destination: # 流量重新路由到 fat 的 mock 服务
host: "mockserver.ppdapi.com"
subset: "fat"
- match: # 条件匹配块
- headers: # 请求头 ENV-ROUTER-TARGET = default 匹配成功
ENV-ROUTER-TARGET:
exact: "default"
name: "default-routes"
route:
- destination: # 流量路由到 fat 默认环境
host: "demob.ppdapi.com"
subset: "default"
- name: "origin-default-routes"
route: # 默认路由规则
- destination:
host: "demob.ppdapi.com"
subset: "default"
上述配置简化成流程图如下:
通过istio的“流量打标”、“按标路由”以及流量Fallback,并结合谛听透传HTTP环境请求头能力,很好的实现了fat环境与星云多环境之间的“同环境优先”、“流量fallback默认fat环境”等路由规则。
istio官方文档https://istio.io
envoy官方文档https://www.envoyproxy.io
Mossad-K,基础框架研发专家,主要参与信也科技Service Mesh的研发、落地工作。
nonumber1989,基础架构资深技术专家,主要负责信也科技微服务治理体系,当前专注Service Mesh的研发、落地和推广工作。
Java、大数据、前端、测试等各种技术岗位热招中,欢迎扫码了解~
更多福利请关注官方订阅号“拍码场”
好内容不要独享!快告诉小伙伴们吧!