服务网格简记
注入容器!接管流量!
Istio是什么?
云原生容器技术和微服务应用的出现,推动了人们对服务网格的需求。那么,什么是服务网格?简而言之,服务网格是服务(包括微服务)之间通信的控制器。随着越来越多的容器应用的开发和部署,一个企业可能会有成百上千或数以万计的容器在运行,怎样管理这些容器或服务之间的通信,包括服务间的负载均衡、流量管理、路由、运行状况监视、安全策略及服务间身份验证,就成为云原生技术的巨大挑战。以Istio为代表的服务网格应运而生。在架构上,Istio属于云原生技术的基础设施层,通过在容器旁提供一系列网络代理,来实现服务间的通信控制。其中的每个网络代理就是一个网关,管理容器或容器集群中每个服务间的请求或交互。每个网络代理还拦截服务请求分发到服务网格上,因此,众多服务构成的无数连接“编织“成网,也就有了“网格“这个概念。服务网格的中央控制器,在Kubernetes容器平台的帮助下,通过服务策略来控制和调整网络代理的功能,包括收集服务的性能指标。
- 一句话总结:Istio是一个与Kubernetes紧密结合的适用于云原生场景的Service Mesh形态的用于服务治理的开放平台。
Istio能做什么?
- Connect
- Secure
- Control
- Observe
为什么需要Istio?
我们需要解决服务治理问题。
服务治理的演化过程经历了三个阶段:
- 在微服务程序中直接包含治理逻辑。这就会出现代码重复,维护困难,服务治理逻辑和业务逻辑紧耦合等等问题。
- 独立治理逻辑,SDK模式。将治理逻辑独立出来,形成类库,例如Spring Cloud Netflix Hystrix和Spring Cloud OpenFeign,我曾经使用过OpenFeign,使用起来也算开箱即用,只需在SpringBoot代码中的调用逻辑处添加@FeignClient和@EnableFeignClients注解即可,这种SDK模式的治理工具集在过去一段时间里得到了非常广泛的应用。虽然SDK模式在代码上解耦了业务和治理逻辑,但业务代码和SDK还是要一起编译的,业务代码和治理逻辑还在一个进程内。这就导致了几个问题:业务代码必须和SDK基于同一种语言,即语言绑定。例如,Spring Cloud等大部分治理框架都基于Java,因此只适用于Java语言开发的服务。并且在治理逻辑升级时,还需要用户的整个服务升级,即使业务逻辑没有改变。
- 治理逻辑独立的进程。SDK模式仍旧侵入了用户的代码,那就再解耦一层,把治理逻辑彻底从用户的业务代码中剥离出来,这就形成了Sidecar模式。
在云原生时代,随着采用各种语言开发的服务剧增,应用间的访问拓扑更加复杂,治理需求也越来越多。原来的那种嵌入在应用中的治理功能无论是从形态、动态性还是可扩展性来说都不能满足需求,迫切需要一种具备云原生动态、弹性特点的应用治理基础设施。
首先,从单个应用来看,Sidecar与应用进程的解耦带来的应用完全无侵入、开发语言无关等特点解除了开发语言的约束,从而极大降低了应用开发者的开发成本。这种方式经常被称为一种应用的基础设施层,类比TCP/IP网络协议栈,应用程序像使用TCP/IP一样使用这个通用代理:TCP/IP负责将字节码可靠地在网络节点间传递,Sidecar则负责将请求可靠地在服务间进行传递。TCP/IP面向的是底层的数据流,Sidecar则可以支持多种高级协议(HTTP、gRPC、HTTPS等),以及对服务运行时进行高级控制,使服务变得可监控、可管理。
然后,从全局来看,在多个服务间有复杂的互相访问时才有服务治理的需求。即我们关注的是这些Sidecar组成的网格,对网格内的服务间访问进行管理,应用还是按照本来的方式互相访问,每个程序的Inbound流量和Outbound流量都要经过Sidecar代理,并在Sidecar上执行治理动作。
最后,Sidecar是网格动作的执行体,全局的管理规则和网格内的元数据维护通过一个统一的控制面实现,只有数局面的Sidecar和控制面有联系,应用感知不到Sidecar更不会和控制面有任何联系,用户的业务和控制面彻底解耦。
Istio的缺点
Istio的Sidecar架构引入了两个问题:
- 增加了两处延迟和可能的故障点
- 多出来的这两跳对与访问性能、整体可靠性及整个系统的复杂度都带来了新的挑战。
在云原生环境下需要考虑这点资源损耗吗🤪,一般是通过保证转发代理的轻量和高性能降低时延影响(Istio选择了轻量级的envoy而不是nginx就是处于这个考虑),尤其是考虑到后端实际使用的应用程序一般比代理更重,叠加代理并不会明显影响应用的访问性能;另外,在云原生场景下,多扩展几个replica就行了。
Istio和Kubernetes
之前在Service简记这篇文章中,我分析了Service实现的网络原理,可以明确的是k8s的Service已经可以提供服务注册、服务发现和负载均衡功能,并且可以通过服务名直接访问到服务实例(DNS)。为什么还是需要Istio呢?本质上来看,是因为k8s的Service只提供了OSI的4层负载均衡能力,无法基于应用层的信息进行负载均衡,不会提供应用层的流量管理,更不会提供服务访问指标和调用链追踪这种应用的服务运行诊断能力。但是要问到为什么不使用k8s的Ingress资源呢?Ingress不是也是可以提供7层的路由和负载均衡吗?个人认为Ingress主要提供了k8s集群南北流量的管理(Ingress Controller的实现主要采用Nginx),而Istio对容器注入的proxy更加细粒度的控制了k8s集群内的东西流量(proxy的实现是envoy)。
Istio小实践
部署
以官方的Bookinfo为示例,略过安装步骤。
首先为想要实现Istio边车自动注入功能的命名空间打上标签kubectl label namespace default istio-injection=enabled
,这样在该名称空间内的pod会被自动注入proxy。
然后部署Bookinfo应用kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
。
之后kubectl get pods -n istio-system
可以看到
ingress、egress和istiod这三个pod,这就是Istio的控制平面,而且是以pod的形式存在的,可见Istio和k8s的结合十分紧密,Istio就是附加在k8s上的。
其他四个pod是之后部署的,都是用来Observe,从创建时间可以看出。
再看默认的名称空间,有六个pod,且每个pod里有两个容器。
kubectl describe pod productpage-v1-65b75f6885-qgnzd
可以看到
这个productpage-v1-65b75f6885-qgnzd
pod中有本身的productpage容器还有istio/proxyv2,这个就是被自动注入的proxy,就是流量控制的实际执行者。
当我们向kube-apiserver提交一个网络策略时,时刻watch着api-server动向的Istio控制平面的istiod得知了这个消息,然后分发到各个被注入的pod的proxy容器(envoy)中,然后envoy加载网络策略并执行,这就是istio的实现机制。
进行完以上步骤之后再对外开放服务,添加一系列Observe组件等等一系列过程不再赘述。
测试
在本地机器上执行脚本模拟请求
while true
do
curl http://$INGRESS_HOST:$INGRESS_PORT/productpage
done
之后就可以在kiali这个观测平台上查看流量
总结
Istio与Kubernetes的天然融合且基于Kubernetes构建,补齐了Kubernetes的治理能力,提供了端到端的服务运行治理平台。这都使得Istio、微服务、容器及Kubernetes形成一个完美的闭环。