已有27人围观 来源:高可用架构 发布于:2021-05-04 16:06:06

导读

随着业务的增长,一些传统企业对诸如灰度宣布、服务路由、服务熔断、服务限流等服务治理的需求越来越强烈,但他们又不想对业务代码做大批的改革,因而 Service Mesh 成了他们比较好的选择;不幸的是业内比较成熟能落地的 Service Mesh 计划如 Istio 都是基于各种容器平台构建的,而这些传统企业很多没有接入容器平台也不想做容器化改革,这就导致 Service mesh 很难运用于这些传统企业或者一些非容器化的场景。



作者介绍


张培培


腾讯云微服务团队高等工程师

TSF Mesh 研发及负责人

热衷于云原生和开源技巧,在容器、Service Mesh、资讯队列、区块链等范畴拥有丰硕经验,目前致力于Service Mesh 技巧的落地和推广



前言


为了兼容 Service mesh 的非容器化场景,TSF Mesh 基于 Istio 构建了 Service mesh 微服务平台,对原生 Istio 实现进行了恰当的改革,支撑运用同时运行于容器环境和虚拟机环境(同时也买通了 Spring Cloud 框架,实现 Mesh 服务和 Spring Cloud 服务互联互通,服务统一治理)。


TSF Mesh 对容器和虚拟机统一化的改革主要体现在以下几个方面:


  • 运用安排和Sidecar注入
  • 流量劫持
  • 服务注册与发现


针对这几点,下面会先分析比较 Istio service mesh 基于 K8s 的实现计划,再讲述 TSF Mesh 是如何实现的,做了哪些改革。



运用安排和 Sidecar 注入


首先,回想下 Istio service mesh 的运用安排和 Sidecar 注入方法:


运用安排:Istio service mesh 依附 K8s 进行运用的性命周期管理,包括运用的安排和管理(扩缩容、主动恢复、宣布)


Sidecar 注入:分为手动注入和主动注入, 如下图所示:


  • 手工注入通过手工履行 istioctl kube-inject 来重新构造运用的 CRD yaml

  • 主动注入通过 K8s 的 mutable webhook 回调 istio-sidecar-injector 服务来重新构造运用的 CRD yaml



无论是手工注入还是主动注入,Sidecar 注入的实质是将运行 Sidecar 所须要的镜像地址、启动参数、所衔接的 Istio 集群(Pilot、Mixes、Citadel)及配置信息填充到注入模版,并添加到运用的 CRD yaml 中,最终通过 K8s 持久化资源并拉起运用和 Sidecar 的 POD。


那 TSF Mesh 如何做运用安排和 Sidecar 注入的呢?


由于 TSF Mesh 须要同时支撑容器和虚拟机环境,则首先须要解决虚拟机安排的问题,要实现等同 K8s 的安排才能,须要解决以下几个问题:


  1. 资源和配置管理,如 Istio 集群信息、配置信息等
  2. 对应于容器的镜像,虚拟机就是程序包,那就涉及到包管理
  3. 虚拟机运用性命周期的管理
  4. 虚拟机 Sidecar 注入


为懂得决容器和虚拟机统一安排问题,不能再用 K8s 的存储方法,而是须要更高层的管理模式,我们引入了 tsf-resource 资源管控模块来负责容器和虚拟机相干资源的统一管理,像 Istio 集群相干的信息在掌握平台安排时会持久化在 TSF 的 DB 中。


对于容器平台,当用户从 TSF 掌握台安排一个容器运用时,tsf-resource 从 DB 中获取像容器的镜像地址、Istio 集群信息、配置、启动参数等,进行 K8s CRD 的组装,组装完将 CRD 创立要求发送给容器平台完成运用 POD 的拉起,其实这里在组装 CRD 时已经实现了 Sidecar 的主动注入,注入时的动态参数由掌握台传递,静态参数如 Sidecar 镜像地址、启动参数等从 DB 中获取。


对于虚拟机平台,TSF 引入了以下几个模块来解决程序包管理和运用安排的问题:


  1. tsf-repo,程序包仓库管理,存储运用程序包及相干依附
  2. tsf-master,虚拟机节点管理 master,发送安排/下线/启动/停滞等义务给 tsf-agent
  3. tsf-agent,虚拟机节点管理 agent,安排在运用机器上,负责初始化机器环境、履行运用安排/下线/启动/停滞等义务


对于虚拟机运用的变革,如例如运用安排、启动、停滞、下线,TSF 通过义务的方法来跟踪每个变革,在义务下发的具体流程中,所有义务都是异步履行的,tsf-resource 将义务转发给 tsf-master 后就返回给 TSF 掌握台,并由 tsf-master 完成义务的下发和状况跟踪;用户在 TSF 掌握台履行操作后,可以依据返回的义务 ID 查询履行成果。



流量劫持


Service mesh 须要透明的进行服务治理,也就须要透明的接收服务进出流量,将流量劫持到 Sidecar,由 Sidecar 进行流量管理,传统的方法是 iptables 流量劫持(也可采取 BPF、IPVS 等方法),同样下面先回想下 Istio 的 Service mesh 计划具体是如何劫持流量的,然后再看下 TSF mesh 为了统一容器和虚拟机做了哪些改革。


查看经过 Sidecar 注入后的运用 YAML 文件,发现 istio-sidecar-injector 服务在注入 Sidecar 容器本身时,还注入了 istio-init 容器,istio-init 容器属于 init 容器(init 容器在运用程序容器启动之前运行,用来初始化一些运用镜像中不存在的适用工具或安装脚本),下面是官方例子中注入的 init 容器部分:


initContainers:      - args:        - -p        - "15001"        - -u        - "1337"        - -m        - REDIRECT        - -i        - '*'        - -x        - ""        - -b        - 9080,        - -d        - ""        image: istio/istio-release-proxy_init:1.0.1        imagePullPolicy: IfNotPresent        name: istio-init        resources: {}        securityContext:          capabilities:            add:            - NET_ADMIN          privileged: true        ...


可以看出 init 容器 istio-init,被赋予了 NET_ADMIN 的 POD 网络空间权限,具体履行了哪些初始化还看不出来,那再来看下 istio/istio-release-proxy_init:1.0.1 镜像的 Dockerfile。


FROM ubuntu:xenialRUN apt-get update && apt-get install -y     iproute2     iptables  && rm -rf /var/lib/apt/lists/*ADD istio-iptables.sh /usr/local/bin/ENTRYPOINT ["/usr/local/bin/istio-iptables.sh"]


istio-init 容器的 ENTRYPOINT 是 /usr/local/bin/istio-iptables.sh 脚本,顾名思义用于 Istio iptables 流量劫持的脚本,组合上面 istio-init 容器的启动参数,完全命令为:


$ /usr/local/bin/istio-iptables.sh -p 15001 -u 1337 -m REDIRECT -i '*' -x "" -b 9080 -d ""


该命令的主要作用是,将运用容器中拜访9080端口的流量(inbound 流量)和所有出站流量(outbound 流量)重定向到 Sidecar(即 envoy)的15001端口。


总结下来,Istio 是通过 init 容器完成了流量劫持到 Sidecar 的初始化工作。


TSF Mesh 如何实现流量劫持的呢?


TSF Mesh 同样采取 iptables 方法,不过要统筹虚拟机平台,须要解决两个主要问题:


  1. 虚拟机下如何履行 iptables 运用劫持策略
  2. 虚拟机下如何劫持流量,不能劫持虚拟机全部网络空间的流量


问题1的解决比较简略,我们对 pilot-agent 做些一些扩大,在 pilot-agent 中履行 iptables 脚本,pilot-agent 一个主要工作是生成 envoy 的 bootstrap 配置并启动 envoy、管理 envoy 的性命周期,相似容器环境下做 envoy 启动前的 init 预备,在启动 envoy 前履行 iptables 脚本,也比较合理。


问题2的解决就比较麻烦了,但又非常主要,不像 K8s 的 POD,POD 间网路是隔离的,一个 POD 一般只会运行一个运用,劫持全部 POD 网路空间里的流量完全没有问题,而虚拟机中可能还有其它进程的存在,这些进程可能也有 Outbound 的流量,因此我们不能劫持虚拟机所有的流量,一种比较合理的劫持计划应当是:


  • 对于 Inbound 流量,只劫持到安排运用的端口,这个原生 Istio 已经做到,无需改革
  • 对于 Outbound 流量,只劫持注册中心已注册服务的流量


下面来具体讲下 TSF Mesh 如何针对服务来劫持 Outbound 流量的


其实我们的计划和 K8s 的 kube-DNS+kube-proxy 的服务发现机制相似,TSF Mesh 在数据平面引入了一个 mesh-dns 模块,通过衔接 pilot-discovery 同步获取注册中心的服务变革来更新本地的 DNS cache,对于来自注册中心的服务会被解析到一个特定的 IP,然后在 iptables 策略中把目标地址到这个特定 IP 的流量重定向 envoy,当然,还须要劫持 DNS 53 端口的流量,先把 DNS 要求引到 mesh-dns,可以看下 iptables nat 表中完全的规矩内容:



Inbound 流量劫持跟原生 Istio 实现相似就不赘述了,下图显示的是 Outbound 流量 iptables 劫持的详细进程,其中红色部分为新增的 DNS 劫持规矩。



注册服务的域名劫持,除了引入了 mesh-dns 自研模块,还涉及到 pilot-discovery 和 pilot-agent 的改革:


pilot-discovery 改革点


  1. pilot-discovery 扩大一个 ServiceInfos 的 grpc 服务,供给注册服务变革同步接口
  2. pilot-discovery 早期的 consul controller 实现是,定时通过 Consul 的 Rest API 获取服务数据并和上一次的查询成果进行比较,如果数据产生了变更则通知 Pilot discovery 进行更新,这里我们进行了优化,采取 Consul 的 watch 机制来取代轮询(下面服务注册与发现中也有提到),并在 ServiceInfos 服务初始化时向 consul controller 注册了服务变革的 event 通知
  3. ServiceInfos 服务在 mesh-dns 要求第一次到来时同步全量的服务注册表,之后则依据服务的变革情形增量同步


mesh-dns实现


  1. DNS 服务基于 github.com/miekg/dns 实现(一个非常轻量级的 DNS 库)
  2. 和 pilot-discovery 坚持注册服务列表的同步,mesh-dns 启动时进行全量同步,运行时进行增量同步
  3. 处置 DNS 要求时,先检讨 Domain 是否在注册服务列表里,如果在则返回一个特定的 IP(可配置),否则要求本地配置的域名服务进行解析


pilot-agent 改革点


  1. 相似对 envoy 的管理,pilot-agent 扩大了 mesh-dns 的支撑,负责了 mesh-dns 启动配置组装、启动 mesh-dns 及 mesh-dns 性命周期的管理



服务注册与发现


基于流量比例的路由 Istio 中负责流量管理的组件为 Pilot,其中服务发现也是通过 Pilot 完成,Pilot 的高层架构图如下所示:



Istio服务注册与发现的原理


  • Istio 服务注册:Istio 架构中本身不供给服务注册的才能,而是依附于各种底层平台,底层平台是具体服务信息的生产者和保护者,如 Kubernetes 在 POD 安排时会保留 Service 以及对应的 Pod 实例信息。
  • Istio服务发现:Istio 是服务信息的花费者,服务发现是通过 Pilot 组件实现的,Pilot 组件负责保护网格中的尺度服务模型,该尺度服务模型独立于各种底层平台,Pilot 组件通过适配器和各底层平台对接,以应用底层平台中的服务数据填充此尺度模型,再通过尺度 xDS 协定(CDS 集群信息和 EDS 实例信息)同步给 envoy。


TSF Mesh如何实现服务注册与发现的呢?


同样,TSF Mesh 要统筹虚拟机平台,须要解决三个主要问题:


  1. 注册中心如何选择
  2. 服务如何注册
  3. 实例健康状况如何保护


问题1: TSF Mesh 容器和虚拟机统一采取 Consul 作为注册中心,因为虚拟机安排不会依附容器平台,因此服务注册发现也不能依附容器平台;Pilot 原生实现是支撑 Consul 的,但早期的实现比较鸡肋(1.4.0以前还是通过 Rest API 轮询的方法去获取 Consul 注册服务信息的变革),TSF Mesh 针对早期的版本也做了优化,采取 Consul watch 来取代轮询。


问题2: TSF Mesh 统一了容器和虚拟机的服务注册方法,服务注册都在 envoy 中完成 ,


1. 相似通过 K8s 安排服务一样,TSF Mesh 在安排时须要用户在运用程序所在目录中创立一个 spec.yaml 服务描写文件,spec.yaml 格局如下:


apiVersion: v1kind: Applicationspec:  services:  - name: user # 服务名    ports:             - targetPort: 8091 # 服务监听端口       protocol: http # 服务协定类型    healthCheck:      path: /health # 健康检讨 URL


2. Pilot-agent 将 spec.yaml 文件中内容读出并填充到 envoy-rev0.yaml(envoy启动时的静态配置文件)文件的 node 信息中;


3. Pilot-agent 启动 envoy,envoy 本身是支撑 HDS 的,TSF Mesh 改革了 envoy 代码默认把本地安排的服务 Endpoint 作为 HealthCheck 要求的 cluster,在启动时把要求发送给 Pilot-discovery;


4. Pilot-discovery 原生是不支撑 HDS 掌握的,TSF Mesh 扩大 Pilot-discovery 以支撑 HDS 服务端来吸收 envoy 的 HealthCheck 要求,由于 HDS 定义的要求数据构造里包括 node 信息,也就包括了上面的服务描写信息,Pilot-discovery 组装服务描写信息将服务注册到 consul;


5. Pilot-discovery 是注册中心服务信息的花费者,因此原生是不支撑服务注册的,TSF Mesh 再次扩大了 Pilot-discovery,在 Consul Apater 中增长了 RegisterService 接口;


问题3:TSF Mesh 中服务的实例健康状况也是由 envoy 来保护的:


  1. envoy 在收到 pilot-discovery 的 HealthCheckSpecifier 回应后,会依据回应中的参数如 check 间隔、check 的实例(这里就是本地服务实例)、check 的 Path(这里就是 spec.yaml 中的 healthCheck path)等异步履行本地实例的 Health Check,依据 check 的成果更新本地实例的健康状况;
  2. HDS 除了支撑 HealthCheckRequest 要求,还支撑 EndpointHealthResponse 要求,envoy 依据当前实例的健康状况通过 EndpointHealthResponse 周期性同步给 pilot-discovery;
  3. TSF Mesh 完全扩大了 Pilot-discovery 的 HDS 服务,支撑对 EndpointHealthResponse 要求的处置,依据要求中实例的健康状况进行 TTL 上报;
  4. TSF Mesh 再次扩大了 Pilot-discovery,在 Consul Apater 中增长了 ReportHealthCheckInfo 接口以支撑服务治理的 TTL 上报



总结


TSF Mesh 在深刻懂得了 Istio service mesh 计划的基本上对其进行了针对性的改革和优化,使得运用能同时运行于容器环境和非容器环境,通过抽象出更高层的管控平台,TSF Mesh 可以不依附于具体的底层平台而对运用进行统一管理和掌握;而 TSF Mesh 作为 TSF 的一种微服务框架实现,不仅仅解决了平台统一化问题,还供给了运用全性命周期管理、数据化运营、立体化监控和服务治理的整套解决计划,具体介绍和应用可参考 TSF Mesh 官网。


参考链接:


- ServiceMesher 社区:

https://www.servicemesher.com/


- TSF Mesh 微服务平台:

https://cloud.tencent.com/product/tsf-mesh


- Istio 服务注册插件机制代码解析:

https://www.servicemesher.com/blog/istio-pilot-service-registry-code-analysis/


参考浏览



技巧原创及架构实践文章,欢迎通过大众,号菜单「接洽我们」进行投稿。


高可用架构
转变互联网的构建方法