这个演示是一个Ruby应用程序,可以帮助您管理书架。它由多个微服务组成,并通过 HTTP 使用 JSON 与其他服务通信。有三种服务:
- webapp: 前端
- authors: 管理系统中作者的 API
- books: 管理系统中书籍的API
出于演示目的,该应用程序带有一个简单的流量生成器。整体拓扑如下所示:
先决条件
要使用本指南,您需要在集群上安装 Linkerd 及其 Viz 扩展。如果您还没有这样做,请按照安装 Linkerd 指南进行操作。
安装应用
首先,让我们将 books app 安装到您的集群上。在本地终端中,运行:
kubectl create ns booksapp && \ curl -sL https://run.linkerd.io/booksapp.yml \ | kubectl -n booksapp apply -f -
此命令为 demo 创建一个命名空间,下载其 Kubernetes 资源清单 并使用 kubectl
将其应用到您的集群。该应用程序包含在 booksapp
命名空间中 运行的 Kubernetes deployments 和 services。
第一次下载一堆容器需要一点时间。Kubernetes 可以告诉您所有服务何 时都在运行并准备好进行通信。通过运行以下命令等待它发生:
kubectl -n booksapp rollout status deploy webapp
您还可以通过运行以下命令快速查看添加到集群中的所有组件:
kubectl -n booksapp get all
部署成功完成后,您可以通过本地端口转发 webapp
访问应用程序本身:
kubectl -n booksapp port-forward svc/webapp 7000 &
在浏览器中打开 http://localhost:7000/ 以查看前端。
不幸的是,应用程序中有一个错误:如果您单击 Add Book,它有 50% 的时间会失败。这是一个典型的不明显、间歇性故障的案例——这种故障让服务所有者抓狂,因为它很难调试。Kubernetes 本身无法检测或显示此错误。从 Kubernetes 的角度来看, 看起来一切都很好,但您知道应用程序正在返回错误。
将 Linkerd 添加到服务中
现在我们需要将 Linkerd 数据平面代理添加到服务中。最简单的选择是做这样的事情:
kubectl get -n booksapp deploy -o yaml \ | linkerd inject - \ | kubectl apply -f -
此命令检索 booksapp
命名空间中所有部署的清单, 通过 linkerd inject
运行它们,然后使用 kubectl apply
重新应用。 linkerd inject
命令对每个资源进行注释, 以指定它们应该添加 Linkerd 数据平面代理, 当清单重新应用于集群时,Kubernetes 会执行此操作。最重要的是,由于 Kubernetes 进行滚动部署,因此应用程序始终保持运行。
调试
让我们使用 Linkerd 来发现此应用程序失败的根本原因。要查看 Linkerd 仪表板,请运行:
linkerd viz dashboard &
从命名空间下拉列表中选择 booksapp
并单击 Deployments 工作负载。您应该会看到 booksapp
命名空间中的所有部署都显示出来了。会有成功率、每秒请求数和延迟百分位数。
这很酷,但你会注意到 webapp
的成功率不是 100%。这是因为流量生成器正在提交新书。你可以自己做同样的事情,把成功率推得更低。单击 Linkerd 仪表板中的 webapp
以进行实时调试会话。
您现在应该查看 webapp
服务的详细信息视图。您会看到 webapp
正在从 traffic
(负载生成器)中获取流量, 并且它有两个传出依赖项:authors
和 book
。一个是作者信息拉取服务,一个是图书信息拉取服务。
依赖服务中的故障可能正是导致 webapp
返回错误的原因(以及您作为用户在单击时可以看到的错误)。我们可以看到 books
服务也失败了。让我们进一步向下滚动页面, 我们将看到 webapp
正在接收的所有流量端点的实时列表。这是有趣的:
啊哈!我们可以看到,从 webapp
服务到 books
服务的入站流量在很大一部分时间都失败了。这可以解释为什么 webapp
会引发间歇性故障。让我们点击 tap (🔬) 图标, 然后点击开始按钮来查看实际的请求和响应流。
事实上,许多这些请求都返回 500。
诊断仅影响单一路线的间歇性问题非常容易。您现在拥有了打开详细错误报告所需的一切,该报告准确地解释了根本原因是什么。如果 books
服务是您自己的,您就知道在代码中的确切位置。
Service Profiles
为了了解根本原因,我们使用了实时流量。对于某些问题,这很好,但是如果问题是间歇性的并且发生在半夜会怎样?Service profiles 为 Linkerd 提供了有关您的服务的一些附加信息。这些定义了您正在服务的路由,除其他外,还允许在每个路由的基础上收集指标。通过 Prometheus 存储这些指标,您将能够睡个好觉并在早上查找间歇性问题。
获取 service profiles 设置的最简单方法之一是使用现有的 OpenAPI (Swagger) 规范。此 demo 已发布其每项服务的规范。您可以通过运行以下命令为 webapp
创建 service profile:
curl -sL https://run.linkerd.io/booksapp/webapp.swagger \ | linkerd -n booksapp profile --open-api - webapp \ | kubectl -n booksapp apply -f -
这个命令会做三件事:
- 获取
webapp
的 swagger 规范。 - 获取规范并使用
profile
命令将其转换为服务配置文件。 - 将此配置应用于集群。
除了 install
和 inject
,profile
也是一个纯文本操作。查看生成的配置文件:
apiVersion: linkerd.io/v1alpha2 kind: ServiceProfile metadata: creationTimestamp: null name: webapp.booksapp.svc.cluster.local namespace: booksapp spec: routes: - condition: method: GET pathRegex: / name: GET / - condition: method: POST pathRegex: /authors name: POST /authors - condition: method: GET pathRegex: /authors/[^/]* name: GET /authors/{id} - condition: method: POST pathRegex: /authors/[^/]*/delete name: POST /authors/{id}/delete - condition: method: POST pathRegex: /authors/[^/]*/edit name: POST /authors/{id}/edit - condition: method: POST pathRegex: /books name: POST /books - condition: method: GET pathRegex: /books/[^/]* name: GET /books/{id} - condition: method: POST pathRegex: /books/[^/]*/delete name: POST /books/{id}/delete - condition: method: POST pathRegex: /books/[^/]*/edit name: POST /books/{id}/edit
name
指的是你的 Kubernetes 服务的 FQDN, 在这个实例中 webapp.booksapp.svc.cluster.local
。Linkerd 使用请求的 Host
头将服务配置文件与请求相关联。当代理看到 webapp.booksapp.svc.cluster.local
的 Host
头时, 它将使用它来查找服务配置文件的配置。
路由是包含方法(例如 GET
)和匹配路径的正则表达式的简单条件。这允许您将 REST 风格的资源组合在一起,而不是看到一个巨大的列表。路由的名称可以是您喜欢的任何名称。对于此 demo,该方法附加到路由正则表达式。
要获取 authors
和 books
的配置文件,您可以运行:
curl -sL https://run.linkerd.io/booksapp/authors.swagger \ | linkerd -n booksapp profile --open-api - authors \ | kubectl -n booksapp apply -f - curl -sL https://run.linkerd.io/booksapp/books.swagger \ | linkerd -n booksapp profile --open-api - books \ | kubectl -n booksapp apply -f -
当您使用 linkerd viz tap
时,很容易验证这一切是否有效。每个实时请求都会显示正在看到的 :authority
或 Host
标头以及正在使用的 :path
和 rt_route
。运行:
linkerd viz tap -n booksapp deploy/webapp -o wide | grep req
这将观察流经 webapp
的所有实时请求,看起来像:
req id=0:1 proxy=in src=10.1.3.76:57152 dst=10.1.3.74:7000 tls=true :method=POST :authority=webapp.default:7000 :path=/books/2878/edit src_res=deploy/traffic src_ns=booksapp dst_res=deploy/webapp dst_ns=booksapp rt_route=POST /books/{id}/edit
As you can see:
:authority
是正确的 host:path
正确匹配rt_route
包含 route 名称
这些指标是 linkerd viz routes
命令的一部分, 而不是 linkerd viz stat
。要查看到目前为止累积的指标,请运行:
linkerd viz -n booksapp routes svc/webapp
这将输出所有观察到的路由及其黄金指标的表。 [DEFAULT]
路由是所有与服务配置文件不匹配的所有内容。
配置文件可用于观察传出(outgoing)请求以及传入(incoming)请求。为此,请运行:
linkerd viz -n booksapp routes deploy/webapp --to svc/books
这将显示源自 webapp
deployment 并发往 books
服务的所有请求和路由。与在调试部分使用 tap
和 top
视图类似,此 demo 中错误的根本原因很明显:
ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99 DELETE /books/{id}.json books 100.00% 0.5rps 18ms 29ms 30ms GET /books.json books 100.00% 1.1rps 7ms 12ms 18ms GET /books/{id}.json books 100.00% 2.5rps 6ms 10ms 10ms POST /books.json books 52.24% 2.2rps 23ms 34ms 39ms PUT /books/{id}.json books 41.98% 1.4rps 73ms 97ms 99ms [DEFAULT] books - - - - -
重试
由于更新代码和推出新版本可能需要一段时间,让我们告诉 Linkerd 它可以重试对失败端点的请求。这会 增加请求延迟,因为请求将被多次重试,并不需要推出新版本。
在这个应用中,从 books
deployment 到 authors
service 的请求成功率很低。要查看这些指标,请运行:
linkerd viz -n booksapp routes deploy/books --to svc/authors
输出应如下所示:
ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99 DELETE /authors/{id}.json authors - - - - - GET /authors.json authors - - - - - GET /authors/{id}.json authors - - - - - HEAD /authors/{id}.json authors 50.85% 3.9rps 5ms 10ms 17ms POST /authors.json authors - - - - - [DEFAULT] authors - - - - -
有一点很清楚,从书籍到作者的所有请求都发送到 HEAD /authors/{id}.json
路由, 并且这些请求在大约 50% 的时间内失败。
为了更正这个问题,让我们编辑 authors 服务配置文件并使那些请求可重试,运行以下命令:
kubectl -n booksapp edit sp/authors.booksapp.svc.cluster.local
您需要将 isRetryable
添加到特定路由。它应该看起来像:
spec: routes: - condition: method: HEAD pathRegex: /authors/[^/]*\.json name: HEAD /authors/{id}.json isRetryable: true ### ADD THIS LINE ###
编辑服务配置文件后,Linkerd 将开始自动重试对该路由的请求。我们可以看到通过运行以下命令:
linkerd viz -n booksapp routes deploy/books --to svc/authors -o wide
这应该是这样的:
ROUTE SERVICE EFFECTIVE_SUCCESS EFFECTIVE_RPS ACTUAL_SUCCESS ACTUAL_RPS LATENCY_P50 LATENCY_P95 LATENCY_P99 DELETE /authors/{id}.json authors - - - - - 0ms GET /authors.json authors - - - - - 0ms GET /authors/{id}.json authors - - - - - 0ms HEAD /authors/{id}.json authors 100.00% 2.8rps 58.45% 4.7rps 7ms 25ms 37ms POST /authors.json authors - - - - - 0ms [DEFAULT] authors - - - - - 0ms
你会注意到 -o wide
标志在 routes
视图中添加了一些列。这些显示了 EFFECTIVE_SUCCESS
和 ACTUAL_SUCCESS
之间的区别。这两者之间的差异表明重试的效果如何。 EFFECTIVE_RPS
和 ACTUAL_RPS
显示有多少请求被发送到目标服务, 以及有多少请求被客户端的 Linkerd 代理接收。
现在自动重试,成功率看起来不错,但 p95 和 p99 延迟增加了。这是可以预料的,因为重试需要时间。
超时
Linkerd 可以限制在传出请求到另一个服务失败之前等待的时间。这些超时通过向服务配置文件的路由配置添加另一个 key 来工作。
首先,让我们看一下从 webapp
到 books
服务的请求的当前延迟:
linkerd viz -n booksapp routes deploy/webapp --to svc/books
这应该类似于:
ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99 DELETE /books/{id}.json books 100.00% 0.7rps 10ms 27ms 29ms GET /books.json books 100.00% 1.3rps 9ms 34ms 39ms GET /books/{id}.json books 100.00% 2.0rps 9ms 52ms 91ms POST /books.json books 100.00% 1.3rps 45ms 140ms 188ms PUT /books/{id}.json books 100.00% 0.7rps 80ms 170ms 194ms [DEFAULT] books - - - - -
对 books
服务的 PUT /books/{id}.json
路由的请求包括当该服务调用 authors
服务 作为服务这些请求的一部分时的重试,如上一节所述。这以额外的延迟为代价提高了成功率。出于本 demo 的目的,让我们为对该路由的调用设置 25 毫秒超时。您的延迟数字将根据集群的特征而有所不同。要编辑 books
服务配置文件,请运行:
kubectl -n booksapp edit sp/books.booksapp.svc.cluster.local
更新 PUT /books/{id}.json
路由以设置超时:
spec: routes: - condition: method: PUT pathRegex: /books/[^/]*\.json name: PUT /books/{id}.json timeout: 25ms ### ADD THIS LINE ###
Linkerd 现在将在达到超时时将错误返回给 webapp
REST 客户端。此超时包括重试请求,并且是 REST 客户端等待响应的最长时间。
运行 routes
以查看发生了什么变化:
linkerd viz -n booksapp routes deploy/webapp --to svc/books -o wide
现在发生超时,指标将发生变化:
ROUTE SERVICE EFFECTIVE_SUCCESS EFFECTIVE_RPS ACTUAL_SUCCESS ACTUAL_RPS LATENCY_P50 LATENCY_P95 LATENCY_P99 DELETE /books/{id}.json books 100.00% 0.7rps 100.00% 0.7rps 8ms 46ms 49ms GET /books.json books 100.00% 1.3rps 100.00% 1.3rps 9ms 33ms 39ms GET /books/{id}.json books 100.00% 2.2rps 100.00% 2.2rps 8ms 19ms 28ms POST /books.json books 100.00% 1.3rps 100.00% 1.3rps 27ms 81ms 96ms PUT /books/{id}.json books 86.96% 0.8rps 100.00% 0.7rps 75ms 98ms 100ms [DEFAULT] books - - - - -
延迟数字包括在 webapp
应用程序本身中花费的时间, 因此预计它们会超过我们为从 webapp
到 books
的请求设置的 25 毫秒超时。我们可以通过观察路由的有效成功率下降到 100% 以下来看到超时正在起作用。
清理
要从集群中删除 books 应用程序和 booksapp 命名空间,请运行:
curl -sL https://run.linkerd.io/booksapp.yml \ | kubectl -n booksapp delete -f - \ && kubectl delete ns booksapp