Service Profiles
Linkerd 服务网格解决的最重要问题之一是可观察性:提供服务行为的详细视图,Linkerd 对可观察性的价值主张是,它可以为你的 HTTP 和 gRPC 服务提供黄金指标,这些都是自动执行,无需更改代码或开发人员参与的。
部署测试应用
了解 Linkerd
如何为一项服务工作,安装一个简单的示例应用 Emojivoto
,该应用是一个简单的独立 Kubernetes
应用程序,它混合使用 gRPC
和 HTTP
调用,允许用户对他们最喜欢的表情符号进行投票。
通过运行以下命令可以将 Emojivoto
安装到 emojivoto
命名空间中:
1
| $ curl -fsL https://run.linkerd.io/emojivoto.yml | kubectl apply -f -
|
在我们对它进行网格(mesh
)划分之前,让我们先来看看这个应用程序。可以通过 port-forward
来暴露 web-svc
服务kubectl -n emojivoto port-forward svc/web-svc 8080:80
,也可以使用Ingress
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: emojivoto-web namespace: emojivoto spec: entryPoints: - web routes: - match: Host(`emoji.od.com`) kind: Rule services: - name: web-svc port: 80
|
现在我们可以在浏览器通过 http://emoji.od.com
访问 Emojivoto
应用了。
如果你点击一个甜甜圈表情符号,你会得到一个 404
页面。别担心,这些错误是故意的。(我们可以使用 Linkerd
来识别问题)。
接下来我们可以将上面的示例应用加入到 Service Mesh
中来,向其添加 Linkerd 的数据平面代理,直接运行下面的命令即可将 Emojivoto
应用网格化:
1 2 3
| $ kubectl get -n emojivoto deploy -o yaml \ | linkerd inject - \ | kubectl apply -f -
|
此命令检索在 emojivoto
命名空间中运行的所有部署(Deployments
),通过 linkerd inject
运行清单,然后将其重新应用到集群。linkerd inject
命令向 pod spec
添加注解(annotations
),指示 Linkerd
将代理(proxy
)作为容器添加(注入
)到 pod spec
中。(有关更多信息,请参阅自动代理注入。)
将注释添加到现有 Pod
不会自动对它们进行网格划分。对于现有的 Pod,添加注释后,您还需要重新创建或更新资源(例如,通过使用kubectl rollout restart
执行 滚动更新)以触发代理注入。
1 2 3 4 5 6
| $ kubectl get pods -n emojivoto NAME READY STATUS RESTARTS AGE emoji-55c59cf485-zc46k 2/2 Running 0 176m vote-bot-57d4c898bb-czzlr 2/2 Running 0 176m voting-87469d4bb-sqc9n 2/2 Running 0 176m web-847cbcb586-8c7mk 2/2 Running 0 176m
|
可以看到每个 Pod 现在都有 2 个容器,相较于之前多了一个 Linkerd 的 sidecar 代理容器。
当应用更新完成后,我们就成功将应用引入到 Linkerd 的网格服务中来了,新增的代理容器组成了数据平面,我们也可以通过下面的命令检查数据平面状态:
1
| $ linkerd -n emojivoto check --proxy
|
监测它的运行
您现在可以查看 Linked
面板并查看 emojivoto
中的所有服务。由于 emojivoto
附带了 load generator
,我们可以通过运行以下命令查看实时流量指标(live traffic metrics
):
1 2 3 4 5 6
| $ linkerd -n emojivoto viz stat deploy NAME MESHED SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99 TCP_CONN emoji 1/1 100.00% 2.2rps 1ms 1ms 1ms 4 vote-bot 1/1 100.00% 0.2rps 1ms 1ms 1ms 1 voting 1/1 78.38% 1.2rps 1ms 1ms 1ms 3 web 1/1 83.58% 2.2rps 2ms 4ms 4ms 3
|
这将显示每个部署的黄金(golden
)指标:
- 成功率(
Success rates
) - 请求率(
Request rates
) - 延迟分布百分位数(
Latency distribution percentiles
)
为了进一步深入,可以使用 top
来实时查看正在调用哪些路径:
1
| linkerd -n emojivoto viz top deploy
|
为了更深入,我们可以使用 tap
显示跨单个 pod
、deployment
甚至 emojivoto
命名空间中的所有内容的请求流:
1
| linkerd -n emojivoto viz tap deploy/web
|
服务配置
Linkerd 在每个服务的基础上提供这些指标,跨越服务的所有请求,无论这些请求是什么。然而,有时需要获得更细粒度的指标。例如前面的 Emojivoto
应用程序中的 Emoji 微服务,前面看到的 Linkerd
报告的指标是在该服务的所有端点上聚合的。在实际场景下面,我们可能还希望看到特定端点的成功率或延迟,例如,一个端点可能对服务特别关键,或者特别慢。
为了解决这个问题,Linkerd 使用了服务配置文件(service profile
)的概念,服务配置文件是一个可选的配置,它通知 Linkerd 对服务的不同类型的请求,按其路由进行分类。路由只是一个端点(对于 gRPC
)或一个 HTTP verb 和端点(对于 HTTP
)。
服务配置文件允许 Linkerd 为服务提供每个路由(pre-route
)而不是每个服务指标。
生成服务配置文件
Linkerd 的服务配置文件是通过实例化一个名为 ServiceProfile
的 Kubernetes CRD 来实现的。 ServiceProfile
会枚举 Linkerd 为该服务期望的路由。
可以手动创建 ServiceProfile
,但也可以自动生成它们。Linkerd CLI 有一个 profile
命令,可以通过几种不同的方式来生成服务配置文件。最常见的方法之一是从服务的现有资源(如 OpenAPI/Swagger
规范或 protobuf
文件)生成它们。
上面的帮助命令输出列出了可用于为 ServiceProfile
资源生成 YAML 的标志,可以看到其中就有一个 --open-api
标志,用于指示 ServiceProfile
资源将从指定的 OpenAPI
或 Swagger
文档来生成服务配置文件,通过 --proto
标志可以指示从指定的 Protobuf
文件生成服务配置文件。
Emojivoto
的 web 服务有一个简单的 Swagger 规范文件,内容如下所示:
1 2 3 4 5 6 7 8
| openapi: 3.0.1 version: v10 paths: /api/list: get: {} /api/vote: get: {}
|
可以利用上面的规范文件来生成一个 ServiceProfile
对象,命令如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| $ linkerd profile -n emojivoto --open-api web.swagger web-svc > web-sp.yaml $ cat web-sp.yaml apiVersion: linkerd.io/v1alpha2 kind: ServiceProfile metadata: creationTimestamp: null name: web-svc.emojivoto.svc.cluster.local namespace: emojivoto spec: routes: - condition: method: GET pathRegex: /api/list name: GET /api/list - condition: method: GET pathRegex: /api/vote name: GET /api/vote
|
上面的资源清单文件就是一个典型的 ServiceProfile
对象的声明方式,spec.routes
用来声明所有的路由规则,每条路由中包含一个 name
和 condition
属性:
name
用来表示路由的名称,用于显示使用。condition
用来描述路由的规范。上例中生成的condition
有两个字段:method
:与请求匹配的 HTTP 方法。pathRegex
:用于匹配路径的正则表达式。在我们的示例中,这些是完全匹配的规则,但通常这些是正则表达式。
除了通过 OpenAPI
可以生成服务配置文件之外,也可以通过 Protobuf
来生成,gRPC 协议使用 protobuf 对请求和响应进行编码和解码,这意味着每个 gRPC 服务也有一个 protobuf 定义。
Emojivoto
应用的 Voting 微服务就包含有 protobuf 定义,文件内容如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
| syntax = "proto3"; option go_package = "github.com/buoyantio/emojivoto/proto";
package emojivoto.v1;
message VotingResult { string Shortcode = 1; int32 Votes = 2; }
message VoteRequest { }
message VoteResponse { }
message ResultsRequest { }
message ResultsResponse { repeated VotingResult results = 1; }
service VotingService { rpc VotePoop (VoteRequest) returns (VoteResponse); rpc VoteJoy (VoteRequest) returns (VoteResponse); rpc VoteSunglasses (VoteRequest) returns (VoteResponse); rpc VoteRelaxed (VoteRequest) returns (VoteResponse); rpc VoteStuckOutTongueWinkingEye (VoteRequest) returns (VoteResponse); rpc VoteMoneyMouthFace (VoteRequest) returns (VoteResponse); rpc VoteFlushed (VoteRequest) returns (VoteResponse); rpc VoteMask (VoteRequest) returns (VoteResponse); rpc VoteNerdFace (VoteRequest) returns (VoteResponse); rpc VoteGhost (VoteRequest) returns (VoteResponse); rpc VoteSkullAndCrossbones (VoteRequest) returns (VoteResponse); rpc VoteHeartEyesCat (VoteRequest) returns (VoteResponse); rpc VoteHearNoEvil (VoteRequest) returns (VoteResponse); rpc VoteSeeNoEvil (VoteRequest) returns (VoteResponse); rpc VoteSpeakNoEvil (VoteRequest) returns (VoteResponse); rpc VoteBoy (VoteRequest) returns (VoteResponse); rpc VoteGirl (VoteRequest) returns (VoteResponse); rpc VoteMan (VoteRequest) returns (VoteResponse); rpc VoteWoman (VoteRequest) returns (VoteResponse); rpc VoteOlderMan (VoteRequest) returns (VoteResponse); rpc VotePoliceman (VoteRequest) returns (VoteResponse); rpc VoteGuardsman (VoteRequest) returns (VoteResponse); rpc VoteConstructionWorkerMan (VoteRequest) returns (VoteResponse); rpc VotePrince (VoteRequest) returns (VoteResponse); rpc VotePrincess (VoteRequest) returns (VoteResponse); rpc VoteManInTuxedo (VoteRequest) returns (VoteResponse); rpc VoteBrideWithVeil (VoteRequest) returns (VoteResponse); rpc VoteMrsClaus (VoteRequest) returns (VoteResponse); rpc VoteSanta (VoteRequest) returns (VoteResponse); rpc VoteTurkey (VoteRequest) returns (VoteResponse); rpc VoteRabbit (VoteRequest) returns (VoteResponse); rpc VoteNoGoodWoman (VoteRequest) returns (VoteResponse); rpc VoteOkWoman (VoteRequest) returns (VoteResponse); rpc VoteRaisingHandWoman (VoteRequest) returns (VoteResponse); rpc VoteBowingMan (VoteRequest) returns (VoteResponse); rpc VoteManFacepalming (VoteRequest) returns (VoteResponse); rpc VoteWomanShrugging (VoteRequest) returns (VoteResponse); rpc VoteMassageWoman (VoteRequest) returns (VoteResponse); rpc VoteWalkingMan (VoteRequest) returns (VoteResponse); rpc VoteRunningMan (VoteRequest) returns (VoteResponse); rpc VoteDancer (VoteRequest) returns (VoteResponse); rpc VoteManDancing (VoteRequest) returns (VoteResponse); rpc VoteDancingWomen (VoteRequest) returns (VoteResponse); rpc VoteRainbow (VoteRequest) returns (VoteResponse); rpc VoteSkier (VoteRequest) returns (VoteResponse); rpc VoteGolfingMan (VoteRequest) returns (VoteResponse); rpc VoteSurfingMan (VoteRequest) returns (VoteResponse); rpc VoteBasketballMan (VoteRequest) returns (VoteResponse); rpc VoteBikingMan (VoteRequest) returns (VoteResponse); rpc VotePointUp2 (VoteRequest) returns (VoteResponse); rpc VoteVulcanSalute (VoteRequest) returns (VoteResponse); rpc VoteMetal (VoteRequest) returns (VoteResponse); rpc VoteCallMeHand (VoteRequest) returns (VoteResponse); rpc VoteThumbsup (VoteRequest) returns (VoteResponse); rpc VoteWave (VoteRequest) returns (VoteResponse); rpc VoteClap (VoteRequest) returns (VoteResponse); rpc VoteRaisedHands (VoteRequest) returns (VoteResponse); rpc VotePray (VoteRequest) returns (VoteResponse); rpc VoteDog (VoteRequest) returns (VoteResponse); rpc VoteCat2 (VoteRequest) returns (VoteResponse); rpc VotePig (VoteRequest) returns (VoteResponse); rpc VoteHatchingChick (VoteRequest) returns (VoteResponse); rpc VoteSnail (VoteRequest) returns (VoteResponse); rpc VoteBacon (VoteRequest) returns (VoteResponse); rpc VotePizza (VoteRequest) returns (VoteResponse); rpc VoteTaco (VoteRequest) returns (VoteResponse); rpc VoteBurrito (VoteRequest) returns (VoteResponse); rpc VoteRamen (VoteRequest) returns (VoteResponse); rpc VoteDoughnut (VoteRequest) returns (VoteResponse); rpc VoteChampagne (VoteRequest) returns (VoteResponse); rpc VoteTropicalDrink (VoteRequest) returns (VoteResponse); rpc VoteBeer (VoteRequest) returns (VoteResponse); rpc VoteTumblerGlass (VoteRequest) returns (VoteResponse); rpc VoteWorldMap (VoteRequest) returns (VoteResponse); rpc VoteBeachUmbrella (VoteRequest) returns (VoteResponse); rpc VoteMountainSnow (VoteRequest) returns (VoteResponse); rpc VoteCamping (VoteRequest) returns (VoteResponse); rpc VoteSteamLocomotive (VoteRequest) returns (VoteResponse); rpc VoteFlightDeparture (VoteRequest) returns (VoteResponse); rpc VoteRocket (VoteRequest) returns (VoteResponse); rpc VoteStar2 (VoteRequest) returns (VoteResponse); rpc VoteSunBehindSmallCloud (VoteRequest) returns (VoteResponse); rpc VoteCloudWithRain (VoteRequest) returns (VoteResponse); rpc VoteFire (VoteRequest) returns (VoteResponse); rpc VoteJackOLantern (VoteRequest) returns (VoteResponse); rpc VoteBalloon (VoteRequest) returns (VoteResponse); rpc VoteTada (VoteRequest) returns (VoteResponse); rpc VoteTrophy (VoteRequest) returns (VoteResponse); rpc VoteIphone (VoteRequest) returns (VoteResponse); rpc VotePager (VoteRequest) returns (VoteResponse); rpc VoteFax (VoteRequest) returns (VoteResponse); rpc VoteBulb (VoteRequest) returns (VoteResponse); rpc VoteMoneyWithWings (VoteRequest) returns (VoteResponse); rpc VoteCrystalBall (VoteRequest) returns (VoteResponse); rpc VoteUnderage (VoteRequest) returns (VoteResponse); rpc VoteInterrobang (VoteRequest) returns (VoteResponse); rpc Vote100 (VoteRequest) returns (VoteResponse); rpc VoteCheckeredFlag (VoteRequest) returns (VoteResponse); rpc VoteCrossedSwords (VoteRequest) returns (VoteResponse); rpc VoteFloppyDisk (VoteRequest) returns (VoteResponse); rpc Results (ResultsRequest) returns (ResultsResponse); }
|
同样可以使用 linkerd profile
命令来生成对应的 ServiceProfile
对象:
1
| $ linkerd profile -n emojivoto --proto Voting.proto voting-svc > voting-sp.yaml
|
此命令的输出结果将比上一个命令的输出多很多,因为 Voting.proto
文件定义了更多路由,可以查看下 voting-sp.yaml
文件,并将其与上面创建的 ServiceProfile
资源进行比较。
此外还可以用另外一种方法来动态生成 ServiceProfile
,Linkerd 可以监控在指定时间段内进入的实时请求,并从中收集路由数据。对于不了解服务提供的内部路由的集群管理员来说,这是一个非常强大的功能,因为 Linkerd 会负责处理请求并将它们写入文件,该特性的底层原理是实时观察请求的 Tap
功能。
现在,让我们使用 linkerd profile
命令监控 emoji
服务 10 秒并将输出重定向到文件,与前面学习的所有命令一样,输出会打印到终端,并且此命令会将输出重定向到文件,因此我们只需运行该命令(并等待 10 秒)一次。
Linkerd Viz 扩展也有自己的配置文件子命令,可以与 Tap
功能一起使用,从实时流量中生成服务配置文件!如下命令所示:
1
| $ linkerd viz profile emoji-svc --tap deploy/emoji --tap-duration 10s -n emojivoto > auto-emoji-sp.yaml
|
当请求发送到 emoji
服务时,这些路由通过 Tap
功能收集。我们也可以使用 Emoji.proto
文件生成服务配置文件,然后去比较使用 --proto
和 --tap
标志创建的 ServiceProfile
资源的定义。
生成的 ServiceProfile
资源清单文件内容如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| cat auto-emoji-sp.yaml apiVersion: linkerd.io/v1alpha2 kind: ServiceProfile metadata: creationTimestamp: null name: emoji-svc.emojivoto.svc.cluster.local namespace: emojivoto spec: routes: - condition: method: POST pathRegex: /emojivoto\.v1\.EmojiService/FindByShortcode name: POST /emojivoto.v1.EmojiService/FindByShortcode - condition: method: POST pathRegex: /emojivoto\.v1\.EmojiService/ListAll name: POST /emojivoto.v1.EmojiService/ListAll
|
如果你需要手动编写服务配置文件,linkerd profile
命令有一个 --template
标志,可以生成 ServiceProfile
资源的脚手架,然后可以使用你的服务的详细信息对其进行更新。
1
| $ linkerd profile --template -n emojivoto web
|
上面的命令会输出包含 ServiceProfile
资源的字段和每个字段的详细说明,如果你需要 ServiceProfile
定义的快速参考,就可以使用 --template
标志!
仪表板中查看Per-Route Metrics
上面了解了如何使用 linkerd profile
命令来生成 ServiceProfile
资源清单文件,现在让我们重新运行生成命令,并直接将生成的 ServiceProfile
对象直接应用到集群中:
1 2 3 4 5 6
| $ linkerd profile -n emojivoto --open-api web.swagger web-svc | kubectl apply -f - serviceprofile.linkerd.io/web-svc.emojivoto.svc.cluster.local created $ linkerd profile -n emojivoto --proto Voting.proto voting-svc | kubectl apply -f - serviceprofile.linkerd.io/voting-svc.emojivoto.svc.cluster.local created $ linkerd viz profile emoji-svc --tap deploy/emoji --tap-duration 10s -n emojivoto | kubectl apply -f - serviceprofile.linkerd.io/emoji-svc.emojivoto.svc.cluster.local created
|
当上面的命令运行成功后,让我们打开 Linkerd 仪表盘来查看下相关指标,我们先导航到 Web Deployment 下查看每个路由的指标。
从上图可以看出来在 ROUTE METRICS
选项卡下面相比默认的 [DEFAULT]
路由多了两个路由,这两个路由就是我们通过 linkerd profile --open-api
命令从 web.swagger
文件中生成的两条路由,每行列中,我们可以看到这两条路由的每条指标数据。在部署 ServiceProfile
对象之前,我们只能看到 web 服务的聚合指标,部署后我们现在可以看到 /api/list
这条路由是 100% 成功的,/api/vote
路由有一些错误。
同样在服务配置文件之前,我们只知道 web 服务正在返回错误,现在我们错误是来自与 /api/vote
路由,另外的 [DEFAULT]
默认路由表示当服务配置文件中没有路由匹配请求时 Linkerd 使用的路由,其会捕获在 ServiceProfile
之前观察到的任何流量。
现在我们再去看看另外的服务配置文件,其中的 Voting
服务比较具有代表性,因为它包含很多路由。在 Linkerd Dashboard 页面上在 emojivoto
命名空间上进入 Voting Deployment,切换到 ROUTE METRICS
选项卡。我们会该服务下有非常多的路由,上面的 web 服务我们知道 /api/vote
路由的请求成功率低于 100%,所有 voting 服务中的每条路由信息都有可能会提供相关的错误信息,由于路由非常多,我们可以直接按照 Success Rate
这一列进行升序排序,正常就可以看到 VoteDoughnut
路由成功率为 0。
命令行查看 Per-Route Metrics
上面我们已经了解了如何通过 Dashboard 来查看 Emojivoto
应用中服务的每个路由指标了,接下来我们再尝试使用 CLI工具 查看每个路由的指标。
linkerd viz routes
命令使用与仪表盘使用的相同指标,让我们看看使用 Linkerd CLI 来查看 emoji
服务的路由,如下所示:
1 2 3 4 5
| $ linkerd viz routes deployment/web --namespace emojivoto ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99 GET /api/list web-svc 100.00% 1.0rps 1ms 3ms 5ms GET /api/vote web-svc 83.33% 1.0rps 2ms 4ms 8ms [DEFAULT] web-svc - - - - -
|
我们可以看到,为 emoji
服务定义的两条路由都是成功的,并且在 1ms 的时间内处理了请求,可以看出这些路由是健康的。还要注意我们的默认路由,标记为 [DEFAULT]
,同样这是 Linkerd 在服务配置文件中没有与请求匹配的路由时使用的路由。
现在我们用同样的方式通过 linkerd viz routes
命令来查看 voting 和 web 服务的路由,如下所示:
1 2 3 4 5 6 7 8 9 10 11
| $ linkerd viz routes deployment/web --namespace emojivoto ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99 GET /api/list web-svc 100.00% 1.0rps 1ms 2ms 3ms GET /api/vote web-svc 83.00% 1.0rps 2ms 3ms 4ms [DEFAULT] web-svc - - - - - $ linkerd viz routes deploy/voting -n emojivoto VoteDancingWomen voting-svc 100.00% 0.0rps 1ms 1ms 1ms VoteDog voting-svc - - - - - VoteDoughnut voting-svc 0.00% 0.1rps 1ms 1ms 1ms VoteFax voting-svc - - - - - VoteFire voting-svc 100.00% 0.1rps 1ms 1ms 1ms
|
voting 服务的输出很长,因为它有很多路由,因此可以通过 grep
命令来查找 VoteDoughnut
路由:linkerd viz routes deploy/voting -n emojivoto | grep VoteDoughnut
(或者可以使用 -o json
标志以及 jq
之类的工具来解析输出)。