Istio、Tempo和Loki如何加快微服务的调试速度

844 阅读7分钟

"我应该如何调试这个?"

试想一下。周五晚些时候,你正准备关闭你的笔记本电脑,然后......出现了一个问题。警告、提醒、红色。所有这些都是我们这些开发人员最讨厌的。

架构师决定基于微服务来开发这个系统。数以百计的微服务!你,作为一个开发者,会想为什么?为什么建筑师这么讨厌我?然后,就是当下最主要的问题。我应该如何调试这个?

当然,我们都明白微服务架构的好处。但我们也讨厌它的缺点。其中之一就是在数百个服务中进行调试或运行事后分析的过程。这是很乏味和令人沮丧的。

这里有一个例子:

*-我从哪里开始呢?你想,然后你选择了一个明显的候选者来开始分析:*app1*。然后你阅读了按时间段缩减的日志:*:

kubectl logs -l app=app1 --since=3h […]

-什么都没有,一切看起来都很正常。你想,也许问题来自这个其他相关的服务。 然后再想。

kubectl logs -l app=app2 --since=3h […]

-啊哈,我看到了一些奇怪的东西。这个应用程序2向这个其他的应用程序,即应用程序3*,运行了一个请求。让我们来看看。再来一次:

kubectl logs -l app=app3 --since=3h […]

调试需要很长时间。有很多挫折感,直到最后,人们设法弄清楚事情。

正如你所看到的,这个过程很慢。相当低效。前段时间,我们还没有记录和追踪功能。

今天,情况可能会有所不同。有了Grafana Loki、Grafana Tempo和其他工具,我们几乎可以立即调试事情。

可追踪性:你需要的功能

为了能够快速调试,你需要用一个独特的ID来标记请求。这个标记被称为Trace ID。而所有参与请求的元素,添加另一个独特的ID,称为span。

在最后,你能够过滤出产生问题的请求中所涉及的确切痕迹集:

而且不仅如此。你还可以把所有这些画在一个可视化的图表中,以方便了解组成你的系统的各个部分:

如何使用Grafana堆栈做到这一点

你要做的任务是用服务网模拟一个微服务系统。Istio。

你会说。Istio通过Kiali提供可观察性。

对于可观察性,Istio依靠的是Kiali。然而,在微服务的世界里,我们应该始终表明,有一些替代品可以满足要求,至少和默认的一样好。

Grafana TempoGrafana Loki是做得很好的日志和追踪后端的例子。无论性能如何比较,以下几点是值得考虑的:

  • Tempo和Loki都集成了S3桶来存储数据。这使你不必维护和索引存储,根据你的要求,可能不需要。
  • Tempo和Loki是Grafana的一部分。因此,它可以与Grafana仪表盘无缝集成。 (让我们在这里说实话:我们都喜欢并使用Grafana仪表盘。)

现在,让我们看看你如何利用Istio和Grafana Stack加快调试过程。

这将是你的架构:

先决条件

准备Istio

你需要让Istio启动并运行。

让我们安装Istio操作器:

istioctl operator init

现在,让我们实例化服务网。Istio代理在x-b3-traceid 。注意,你将设置访问日志来注入该跟踪ID作为日志消息的一部分:

kubectl apply -f - << 'EOF'
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: istio-operator
namespace: istio-system
spec:
 profile: default
 meshConfig:
   accessLogFile: /dev/stdout
   accessLogFormat: |
     [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %RESPONSE_CODE_DETAILS% %CONNECTION_TERMINATION_DETAILS% "%UPSTREAM_TRANSPORT_FAILURE_REASON%" %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%" %UPSTREAM_CLUSTER% %UPSTREAM_LOCAL_ADDRESS% %DOWNSTREAM_LOCAL_ADDRESS% %DOWNSTREAM_REMOTE_ADDRESS% %REQUESTED_SERVER_NAME% %ROUTE_NAME% traceID=%REQ(x-b3-traceid)%
   enableTracing: true
   defaultConfig:
     tracing:
       sampling: 100
       max_path_tag_length: 99999
       zipkin:
         address: otel-collector.tracing.svc:9411
EOF

让我们创建命名空间,并给它贴上标签以自动注入Istio代理:

kubectl create ns bookinfo
 
kubectl label namespace bookinfo istio-injection=enabled --overwrite

现在是演示应用程序bookinfo:

kubectl apply -n bookinfo -f https://raw.githubusercontent.com/istio/istio/release-1.10/samples/bookinfo/platform/kube/bookinfo.yaml

为了通过Istio访问该应用程序,你需要对其进行配置。它需要一个网关和一个虚拟服务:

kubectl apply  -f - << 'EOF'
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
 name: bookinfo-gateway
 namespace: bookinfo
spec:
 selector:
   istio: ingressgateway # use istio default controller
 servers:
 - port:
     number: 80
     name: http
     protocol: HTTP
   hosts:
   - "*" # Mind the hosts. This matches all
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: bookinfo
 namespace: bookinfo
spec:
 hosts:
 - "*"
 gateways:
 - tracing/bookinfo-gateway
 - bookinfo-gateway
 http:
 - match:
   - uri:
       prefix: "/"
   route:
   - destination:
       host: productpage.bookinfo.svc.cluster.local
       port:
         number: 9080
EOF

为了访问该应用程序,让我们打开一个通往Istio ingress网关(网状结构的入口)的隧道:

kubectl port-forward svc/istio-ingressgateway -n istio-system  8080:80

安装Grafana栈

现在,让我们来创建Grafana组件。让我们从Tempo开始,这是我们之前提到的追踪后端:

kubectl create ns tracing
 
helm repo add grafana https://grafana.github.io/helm-charts
 
helm repo update
 
helm install tempo grafana/tempo --version 0.7.4 -n tracing -f - << 'EOF'
tempo:
 extraArgs:
   "distributor.log-received-traces": true
 receivers:
   zipkin:
   otlp:
     protocols:
       http:
       grpc:
EOF

下一个组件?让我们创建一个Loki的简单部署:

helm install loki grafana/loki-stack --version 2.4.1 -n tracing -f - << 'EOF'
fluent-bit:
 enabled: false
promtail:
 enabled: false
prometheus:
 enabled: true
 alertmanager:
   persistentVolume:
     enabled: false
 server:
   persistentVolume:
     enabled: false
EOF

现在,让我们部署Opentelemetry Collector。你用这个组件在你的基础设施上分布追踪:

kubectl apply -n tracing -f https://raw.githubusercontent.com/antonioberben/examples/master/opentelemetry-collector/otel.yaml
 
kubectl apply -n tracing -f - << 'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
 name: otel-collector-conf
 labels:
   app: opentelemetry
   component: otel-collector-conf
data:
 otel-collector-config: |
   receivers:   
     zipkin:
       endpoint: 0.0.0.0:9411
   exporters:
     otlp:
       endpoint: tempo.tracing.svc.cluster.local:55680
       insecure: true
   service:
     pipelines:
       traces:
         receivers: [zipkin]
         exporters: [otlp]
EOF

下面的组件是fluent-bit。你将使用这个组件从你的集群中刮取日志痕迹:

(注意:在配置中,你指定只取符合以下模式的容器/var/log/containers/*istio-proxy*.log)

helm repo add fluent https://fluent.github.io/helm-charts
 
helm repo update
 
helm install fluent-bit fluent/fluent-bit --version 0.16.1 -n tracing -f - << 'EOF'
logLevel: trace
config:
 service: |
   [SERVICE]
       Flush 1
       Daemon Off
       Log_Level trace
       Parsers_File custom_parsers.conf
       HTTP_Server On
       HTTP_Listen 0.0.0.0
       HTTP_Port {{ .Values.service.port }}
 inputs: |
   [INPUT]
       Name tail
       Path /var/log/containers/*istio-proxy*.log
       Parser cri
       Tag kube.*
       Mem_Buf_Limit 5MB
 outputs: |
   [OUTPUT]
       name loki
       match *
       host loki.tracing.svc
       port 3100
       tenant_id ""
       labels job=fluentbit
       label_keys $trace_id
       auto_kubernetes_labels on
 customParsers: |
   [PARSER]
       Name cri
       Format regex
       Regex ^(?[^ ]+) (?stdout|stderr) (?[^ ]*) (?.*)$
       Time_Key    time
       Time_Format %Y-%m-%dT%H:%M:%S.%L%z
EOF

现在,Grafana查询。这个组件已经被配置为连接到Loki和Tempo:

helm install grafana grafana/grafana -n tracing --version 6.13.5  -f - << 'EOF'
datasources:
 datasources.yaml:
   apiVersion: 1
   datasources:
     - name: Tempo
       type: tempo
       access: browser
       orgId: 1
       uid: tempo
       url: http://tempo.tracing.svc:3100
       isDefault: true
       editable: true
     - name: Loki
       type: loki
       access: browser
       orgId: 1
       uid: loki
       url: http://loki.tracing.svc:3100
       isDefault: false
       editable: true
       jsonData:
         derivedFields:
           - datasourceName: Tempo
             matcherRegex: "traceID=(\\w+)"
             name: TraceID
             url: "$${__value.raw}"
             datasourceUid: tempo
 
env:
 JAEGER_AGENT_PORT: 6831
 
adminUser: admin
adminPassword: password
 
service:
 type: LoadBalancer
 
EOF

测试它

安装完成后,让我们为Grafana查询打开一个转发端口的隧道:

kubectl port-forward svc/grafana -n tracing 8081:80

使用你在安装时配置的凭证访问它:

  • 用户:admin
  • 密码:password

你会被提示到探索标签。在那里,你可以选择Loki显示在一侧,然后点击分割,选择Tempo显示在另一侧:

你会看到像这样的东西:

最后,让我们用我们已经创建的隧道创建一些流量到bookinfo应用程序。

刷新(硬刷新以避免缓存)该页面数次,直到你能看到进入Loki的痕迹。记得添加过滤器到ProductPage ,以看到其访问日志的痕迹:

点击日志,会出现一个Tempo按钮:

随即,TraceID将被传递到Tempo仪表盘上,显示出追踪和图示:

最后的想法

通过微服务显示请求中涉及的所有元素的图表,可以提高发现bug的速度,或者在运行事后分析时了解系统中发生了什么。

通过减少这个时间,你提高了效率,这样你的开发者就可以继续工作并产生更多的业务需求。

在我个人看来,这就是关键点:提高业务生产力。

可追溯性,以及在这种情况下,Grafana Stack帮助你实现这一目标。

现在,让我们让它为生产做好准备。