在 上一篇博文里,我们介绍了Service Mesh(服务网格)Istio的组成部分,并回答了Istio新手经常会问的问题。本文会研究如何整理这些在网络上收集到的tracing信息。
在听到Service Mesh这个新概念的时候,开发人员和系统管理员首先考虑的事情是tracing。我们会为每个微服务添加特定的代理服务器来处理所有TCP流量。你可能会自然地认为很容易收集所有网络事件的信息。但不幸的是,实际需要考虑很多细节。让我们一起研究一下。
一大误解是可以轻松得到网络流量上的网络交互数据
实际上,相对容易的仅仅是得到由箭头连接的系统节点图,以及服务间的数据速率(实际上,仅仅是单位时间内的比特数)。但是,绝大多数情况下,服务通过应用层协议来通信,比如HTTP,gRPC,Redis等。当然,我们想看到这些协议的tracing信息,想看到应用级别请求的速率,而不是单纯的数据速率。另外,我们想知道协议的请求延时。最终,则想看到从用户输入触发的请求到收到回复这之间的全路径。不过这可不是一件容易的事情。
首先,从Istio的架构角度看怎么发送tracing信息。上文提到,Istio有一个特定的组件收集Telemetry,称为Mixer。但是,在当前的1.1.*的版本里,代理服务器,也就是Envoy代理,直接发送tracing span。Envoy代理支持使用Zipkin协议发送tracing span。其他协议则要求单独的插件。Istio自带预编译并且预先配置好的Envoy代理,仅支持Zipkin协议。比如,如果用户想使用Jaeger协议并通过UDP发送tracing span,那么他需要构建自定义的istio-proxy镜像。Istio-proxy的确支持自定义插件,但是,还仅仅有alpha版本。因此,如果想避免多个自定义的设置,那么接受并存储tracing span的方案里并没有太多的选择。可以使用最受欢迎的协议,Zipkin或者Jaeger,但是如果使用后者,所有东西都需要使用Zipkin兼容的协议(性能会差一些)来上传。Zipkin协议通过HTTP协议将所有tracing信息发送给收集器,消耗更大。
如上文所说,我们需要跟踪应用层协议。这意味着每个服务旁的代理服务器必须理解当时发生的网络事件。Istio默认所有端口使用plain TCP,这意味着不会发送trace。要发送trace,首先需要在main mesh config里启用相关的配置,然后依据服务内协议的实现来命名所有Kubernetes服务实体的所有端口。比如:
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
ports:
- port: 80
targetPort: 80
name: http
selector:
app: nginx
还可以使用组合名称,比如http-magic(Istio能够识别http并且将端口识别为http端口)。
为了避免修补多个配置来定义协议,有如下workaround:在Pilot组件执行协议决策逻辑[1]的时候修改它的配置。然后,当然需要切回标准逻辑并且切换所有端口的命名惯例。
为了理解协议是否定义正确,可以从Envoy代理进入任意Sidecar容器,从location/config_dump发送请求给Envoy接口的admin端口。在最终的配置里,检查相应服务的Operation字段。在Istio里,它就相当于请求目的地的标识。在Istio里自定义这个参数(之后可以在tracing系统里看到),启动Sidecar容器的时候设置serviceCluster标记。比如,它可以从Kubernetes API得到的变量里计算出来:
--serviceCluster ${POD_NAMESPACE}.$(echo ${POD_NAME} | sed -e 's/-[a-z0-9]*-[a-z0-9]*$//g')
这里[2]很好地解释了Envoy的trace是如何工作的。
在Envoy代理的启动标记里必须指定发送tracing span的端口,比如:—-zipkinAddress tracing-collector.tracing:9411。
另一个误解是用户可以轻松获取系统内请求的所有trace
不幸的是事实并非如此。实现的复杂度取决于服务是如何交互的。为什么是这样呢?
问题在于要让Istio代理理解服务的入站和出站请求的匹配关系,仅仅截获所有流量是不够的。你需要某种匹配标识符。HTTP Envoy代理使用特别的header,这样Envoy能够准确理解服务的哪个请求生成对其他服务的特定请求。这些header包括:
x-request-id
x-b3-spanid
x-b3-parentspanid
x-b3-sampled
x-b3-flags
x-ot-span-context
如果你只有一个单点,比如,一个基础的客户端,这里可以加入逻辑,然后需要做的就是等待library在所有客户端里更新完毕。但是如果你面对的是一个复杂的异构系统,没有统一的服务-服务的网络流量,那么很可能就会有问题。不加这样的逻辑,所有的tracing信息都是单层级的。你可以得到所有服务-服务的交互,但是却无法形成网络流量链。
结论
Istio提供了方便的工具收集网络上的所有tracing信息,但是它的实现要求系统的变更,要考虑到Istio的实现的独特性。这是要解决的两大问题:定义应用层的协议(Envoy代理必须支持)以及设置转发信息,匹配入站和出站请求(如果是HTTP协议的话,使用header)。如果这两大问题都解决了,那么你就有了强大的工具,可以从网络上透明地收集信息,即使这是个高度异构的系统,可能由多种语言使用多种架构组成。
下一篇博文里,我们会讨论Istio最大的挑战之一——每个Sidecar代理容器所带来的高RAM使用率的问题——并且讨论如何解决这个问题。
相关链接:
https://github.com/istio/istio/blob/75153eb2ee73c57da167be1441f9fa9f6316605b/pilot/pkg/serviceregistry/kube/conversion.go#L62
https://github.com/envoyproxy/envoy/tree/master/examples/zipkin-tracing
原文链接:https://medium.com/avitotech/istio-and-kubernetes-in-production-part-2-tracing-6304a5af82e9
Kubernetes入门与进阶实战培训
Kubernetes入门与进阶实战培训将于2019年6月14日在北京开课,3天时间带你系统掌握Kubernetes,学习效果不好可以继续学习。本次培训包括:Docker基础、容器技术、Docker镜像、数据共享与持久化、Docker三驾马车、Docker实践、Kubernetes基础、Pod基础与进阶、常用对象操作、服务发现、Helm、Kubernetes核心组件原理分析、Kubernetes服务质量保证、调度详解与应用场景、网络、基于Kubernetes的CI/CD、基于Kubernetes的配置管理等,点击下方图片或者点击阅读原文了解详情。