简介
Linkerd 是一个k8s的服务网格,通过提供运行时调试、可观察性、可靠性和安全性来保证更简单安全地运行服务。这些功能都不需要对你的代码进行修改。
什么是服务网格?
服务网格是一个专注于处理服务间通信的基础设施层,是应用开发和交付问题的解决方案。
从架构上来看,它就是一束用户空间代理,位置卡在了服务的"next",额外的会有一组管理进程。
那些用户空间代理被称为服务网格的 data plane,那些管理进程成为了服务网格的 control plane。 数据层拦截服务之间的调用并且使用这些调用来做一些事,控制层协调代理之间的行为并提供 API,operator 负责操作和测量整个网格。
数据层/代理是什么? 它们是第 7 层感知 TCP 代理,就像是 haproxy 或 NGINX。不同服务网格选择的代理不同,Linkerd 使用的 Linkerd-proxy 是用 Rust 写的微代理,专门给 Linkerd 使用。其他的服务网格会使用不同的代理——Envoy 是一种普遍的选择。但选择代理已经是实现方面的细节。 数据层/代理做什么? 代理在服务之间被调用,更准确地说,它们的行为包括“代理”和“反向代理”,会处理传入和传出的调用。并且它们实现了一个功能集,关注于服务之间的调用。这些对服务间流量的关注重点是区分服务网格代理和 api 网关或 ingress 代理的重点,后者关注于来自外界对集群的调用。 控制层是什么? 控制层是一组组件,提供那些数据面以协调方式运行所需的任何机制,包括服务发现,TLS 证书颁发,指标聚合等。数据层调用控制层来通知其行为,控制面反过来提供一个 API 来允许用户去修改并检查数据面的行为。 以 Linkerd 控制层&数据层为例,控制层的组件包括小型 Prometheus 实例(聚合代理的指标数据),服务发现组件 destination,证书授权 identity,web 和 cli 终端 public-api。数据层则只是一个简单的 linkerd-proxy,位于一个服务应用的"next"。(这只是一个逻辑图,实际部署可能会得到每个控制面组件的三个副本,成百上千个数据面代理) 在逻辑图中的蓝色盒子是 k8s pod 的边界。linkerd-proxy 跟应用的容器跑在一个 pod 里,称 sidecar 容器。 Sidecar,微服务中“用于端对端通信的、被单独分离出来的“组件
服务网格的更进一步解释 上文中所说的 sidecar 容器是一个代理实例,这个代理实例最终实现了服务网格。它会包含在每一个 service 中(app+proxy=pod)。这个 sidecar 负责处理服务之间的通信、监控以及一些安全方面(这些安全内容可以从服务本体中抽象出来)。这样服务间通信变成了 sidecar proxy 之间通信。
服务网格的一些功能举例: 关于 proxy 可以实现的服务发现功能:当一个实例需要与其他服务进行通讯时,它需要“寻找并发现”另一个健康、可用的服务实例。对应这件事,k8s 维护着一个时刻准备接受请求的实例列表。 关于服务网格可以实现的安全内容:服务网格可以加密和解密服务间的请求与响应,而且这一步操作是独立于服务之外的,这样每个服务内部无需去处理这个加解密步骤了。尽管服务网格在发起请求的时候要创建新的 TCP 连接(详见下文中的附:什么是 Layer 7 proxy),但它可以优先再利用已存在的持续连接来提高性能。除加解密之外,服务网格对于服务间请求也可以进行授权和认证,只发送那些有效的请求给服务。 熔断器模式:隔离不健康的实例,或将实例再次添加进健康实例池。 控制面的工作内容:创建新实例,终止不健康或不需要的实例,服务监控,集成监控与管理,实施应用范围的策略,或将整个应用优雅结束掉。控制面一半被设计成用于连接 api、命令行工具或者一个用于管理整个应用的可视化用户界面。
为什么要用到服务网格?研发人员在专注于开发服务,运维人员负责维护服务网格,运行 app 即可。
附:什么是 Layer 7 proxy 四层负载均衡和七层负载均衡的区别: L4 工作于传输层,负责处理消息的传递,不管消息内容。TCP 就是 HTTP 传输方式的四层协议(Layer 4 Protocol)。L4 的负载均衡只针对由上游服务发送和接收的网络报,而不检查包内的具体内容是什么。L4 可以通过检查 TCP 流中的前几个包来决定是否限制路由。 L7 工作于应用层(顶层),也就是将负载均衡部署在应用层上,处理每条消息中的真正内容(L4 不检查具体内容)。L7 适合 HTTP 这种基于 TCP 传输的方式。一个 L7 负载均衡会终止网络传输并读取消息中的内容,基于其中的内容(比如 URL 和 cookie)来作出负载均衡的决策。之后,L7 负载均衡会建立一个新的 TCP 连接(或选择一个已存在的 TCP 连接通过 keepalives 方式)来选择上游服务并对服务发出请求. L7 的优势:运用缓存的方式来卸载上游服务较慢的连接,显著提高性能。使用 L7 的设备经常被用于反向代理。 L7 举例:L7 让均衡器根据请求的具体信息来进行路由,比如针对图片或视频的静态内容请求被路由到多媒体内容服务器,事物型信息路由到应用服务器。利用 L7 可以建立一个高速调整、高度优化、针对需求可靠且可扩展的服务基础架构或应用传递网络。
服务网格架构的含义 代理功能集是出于被服务间调用而设计的,只有你的应用构建为服务时,服务网格才具有意义。你可以将它与单体应用程序一起食用,但运行单个代理将是一大堆机器,并且功能集不太适用。另一个后果是服务网格将需要大量代理。 事实上,Linkerd 为每个服务的每个实例添加了一个链接器代理(Linkerd-proxy)。其他的一些服务网格的实现是为每个 node/host/vm 添加一个代理,也很多。大量代理的使用本身有几个含义: 无论这些数据面的代理是什么,它们都最好非常快。你正在为每个调用添加两个代理跃点,一个在 client 一个在 server。 代理需要小而轻量,每一个代理都会消耗内存和 cpu,这种消耗会与你的应用线性相关。 你需要一个系统来部署和更新代理,你不会想手动操作它。 至少在 10,000ft 级别,这些就是服务网格的全部了:你部署大量 userspace 代理来向内部和“服务到服务”流量“做一些事”,并且你使用控制面来更改它们的行为并查询它们生成的数据。 回到 Linkerd linkerd 具有三个组件:
一个 UI 一个数据面 一个控制面 运行 linkerd 可以通过:
在本地环境中安装 cli 在集群中安装控制面 将你的服务添加进 linkerd 的数据面 当一个服务带着 linkerd 一同运行,你可以使用 linkerd 的 ui 来检查并操纵这个服务。
linkerd 是如何工作的?(监控与操纵出入服务的流量) linkerd 在每个服务实例“旁边”安装一个轻量级同名代理(app 与 proxy 在同一个 pod 中,proxy 代理了所有出入流量)。这些代理自动处理服务的所有出入流量。因为它们是透明的,这些代理充当高度仪表化的进程外网络堆栈,发送遥测给控制面并从控制面接收信号。这种设计允许 Linkerd 来测量和操纵流量出入你的服务,并不会引入过多的延迟。 为了尽可能地小而轻量且安全,linkerd 的代理由 Rust 编写(rust 容易写出轻量的组件)并为 linkerd 特制。(同时控制层是用 go 写的)
安装 cli curl –proto ‘=https’ –tlsv1.2 -sSfL https://run.linkerd.io/install | sh 结果: Linkerd stable-2.11.1 was successfully installed 🎉
Add the linkerd CLI to your path with:
export PATH=$PATH:/Users/sindweller/.linkerd2/bin
Now run:
linkerd check –pre # validate that Linkerd can be installed linkerd install | kubectl apply -f - # install the control plane into the ’linkerd’ namespace linkerd check # validate everything worked! linkerd dashboard # launch the dashboard 1 2 3 4 5 6 7 8 9 10 11 12 13 验证 linkerd linkerd check –pre 结果: Status check results are √ 1 向你的 k8s cluster 里安装控制面 linkerd install | kubectl apply -f - linkerd install 命令会生成一个 k8s 的 manifest,包含所有核心控制面资源,将此 manifest 导入 kubectl apply 然后指示 Kubernetes 将这些资源添加到您的集群中。 验证控制面 linkerd check 这一步用了相当长的时间。遇到一个报错: × control plane pods are ready No running pods for “linkerd-destination” 1 2 根据报错来检查一下 control 面的 pod: kubectl -n linkerd get po
sindweller@xindeweiladeMacBook-Pro ~ % kubectl -n linkerd get po NAME READY STATUS RESTARTS AGE linkerd-destination-7848d79549-6fdpl 0/4 Init:0/1 0 9m55s linkerd-heartbeat-27406998-7wn72 0/1 ContainerCreating 0 32s linkerd-identity-6b78ff444f-nj6vr 0/2 Init:ImagePullBackOff 0 9m55s linkerd-proxy-injector-6469747db-bhq4s 0/2 Init:0/1 0 9m55s 1 2 3 4 5 6 发现是 img 没有 pull 下来
Warning Failed 2m14s kubelet Error: ImagePullBackOff Normal Pulling 2m1s (x2 over 14m) kubelet Pulling image “cr.l5d.io/linkerd/proxy-init:v1.4.0” 1 2 自己用 docker pull 了一下这个 img cr.l5d.io/linkerd/proxy-init:v1.4.0 成功 pull 下来了。 由于用的 kind 创建的 k8s 集群,所以需要把本地的镜像加载进 kind 里:
sindweller@xindeweiladeMacBook-Pro ~ % kind load docker-image cr.l5d.io/linkerd/proxy-init:v1.4.0 Image: “cr.l5d.io/linkerd/proxy-init:v1.4.0” with ID “sha256:a69eedfa2d4e8f04c60e7420ad0b32d28cc4cb6404350bf39d1a253915bcc7cf” not yet present on node “kind-control-plane”, loading… 1 2 这样的话删除 ns 后再次安装
kubectl delete ns linkerd linkerd install | kubectl apply -f - 1 2 然后使用 kubectl -n linkerd describe po linkerd-destination-d86c59855-rpccx 查看一下 pod 内事件,发现
Normal Pulled 19s kubelet Container image “cr.l5d.io/linkerd/proxy-init:v1.4.0” already present on machine 1 说明本地 pull 的镜像已经加载进 kind 节点里了 在 pull 了一堆 kind 无法直接 pull 的镜像后,check 通过
Status check results are √ 1 安装 demo app demo app 介绍: 这是一个包含 gRPC 和 HTTP 调用来允许用户给他们最喜欢的 emojis 投票的 app。在 emojivoto 的 ns 中安装这个 app:
|
|
1 2 但 linkerd 还并没有起作用,我们需要把这个 app“网格化”。但在此之前,先看一下 emojivoto 的自然状态。我们通过将流量转发到他的 web-svc 服务来做到这一点,这样我们可以将浏览器指向他。运行以下命令来在本地将 web-svc 转发到 8080 端口 kubectl -n emojivoto port-forward svc/web-svc 8080:80 这个时候去访问 http://localhost:8080 就可以看到 emojivoto。(点击巧克力圈会导致一个 404 err,这是故意的,为了之后用 linkerd 来验证这个问题) 6. 通过添加 linkerd 数据面代理来网格化这个 app
|
|
这个命令检索所有在 emojivoto 命名空间内运行的 deploys,通过 linkerd inject 运行它们的 manifests。随后,重新 apply 至 cluster。(linkerd inject 命令只是在向 pod 的 spec 里添加一些 annotations,这些注解指示 linkerd 在创建 pod 时将 porxy 注入到 pod 中。) 现在已经将 linkerd 添加进了一个 app 中。 7. 检查数据面 linkerd -n emojivoto check –proxy 8. 访问 8080
继续探索 linkerd emojivoto 看起来在加上 linkerd 之后并无变化,为了展示 linkerd 实际上做了什么,需要安装一个扩展。linkerd 的核心控制面非常迷你,所以 linkerd 附带扩展,扩展会给 linkerd 添加一些非关键但非常好用的功能,包括一些 dashboard。 于是我们添加一些内容(二选一):
viz 扩展,会安装一个在 cluster 上的指标堆栈 linkerd viz install | kubectl apply -f - 或者 buoyant-cloud 扩展,它连接了一个托管的指标堆栈 curl –proto ‘=https’ –tlsv1.2 -sSfL https://buoyant.cloud/install | sh # get the installer linkerd buoyant install | kubectl apply -f - 1 2 无论安装了哪一个扩展,之后再次检查一下: linkerd check
然后就可以使用 viz 的 dashboard,linkerd viz dashboard & 会看到一个界面:
debug 模式 使用 linkerd 来诊断一些不至于让整个服务崩溃的小问题。
通过将 ns 选取为 emojivoto 来查看关于这个 app 的 deploy 的状态信息。包含:成功率、每秒请求数、延迟分位数。
这里会发现 web 的成功率低于 100%,点击 web,
查看 web deploy 的页面,可以看到这个 web deploy 会从 vote-bot 接收流量(vote-bot 持续生成低水平的实时流量),web deploy 有两个传出依赖——emoji 和 voting。
emoji deploy 处理来自 web 的请求,并且全都成功 voting deploy 会有一些处理失败的请求 deploy 依赖(emoji&voting)中的失败可能是导致 web 返回错误的原因。
继续查看页面的后续部分,会看到一个关于所有出入 web 的流量的实时列表。 在 tools-top 里选择 ns 为 emojivoto 之后点击 start,可以获取出入流量
按照教程中的点击 web 之后下拉也会看到一些 api 调用
从 deployments 的 tcp metrics 点击 web 进入会看到教程中所说的两个失败
因为/api/vote 是一个传入调用而 VoteDoughnut 是一个传出调用,因此这是一个很好的线索,表明这个端点是导致问题的原因!
更深入探索一下原因,可以点击列表右侧的 tap 图标,就可以看到仅匹配此端点的请求的实时列表。可以看到 GRPC status 是 Unknown,这是因为请求失败返回了 GRPC status code 2。
Linkerd 无需任何其他配置即可知道 gRPC 的响应分类!
点击下拉按钮可以获取更多请求细节~
至此,我们拥有修复端点和恢复应用程序整体健康所需的一切
遇到的一些报错
Error running port-forward: unable to listen on any of the requested ports: [{50750 8084}] for linkerd-viz/web-db97ff489-rl28g
Check for linkerd dashboard
running in other terminal sessions, or use the --port
flag.
1
2
这里是重复启动了,ps axu |grep linkerd 一下,杀掉之前的 linkerd dashboard 进程就好了。 2.
|
|
没有启用 tap,按照报错提示的重新启动这个 pod 就好了: kubectl -n emojivoto delete po web-5f86686c4d-q5wlt 然后再次使用 viz 的 top 命令 linkerd viz top deploy/web -n emojivoto 就可以看到出入流量
|
|
正在运行着的 viz dashboard 报错: E0210 16:10:15.826094 48128 portforward.go:400] an error occurred forwarding 50750 -> 8084: error forwarding port 8084 to pod 1a64581654541315cbc6e5dc95e098c668ce871a00a1700c60369959e9cb4930, uid : failed to find sandbox “1a64581654541315cbc6e5dc95e098c668ce871a00a1700c60369959e9cb4930” in store: not found E0210 16:10:26.563891 48128 portforward.go:400] an error occurred forwarding 50750 -> 8084: error forwarding port 8084 to pod 1a64581654541315cbc6e5dc95e098c668ce871a00a1700c60369959e9cb4930, uid : failed to find sandbox “1a64581654541315cbc6e5dc95e098c668ce871a00a1700c60369959e9cb4930” in store: not found 1 2 还是一样,杀掉重启
linkerd 和 istio 的优劣 Istio 也是一个非常流行的服务网格。主要列出以下几点 linkerd 的优势
适配相对较容易,有内置和开箱即用的配置,不需要去进行过多的配置。从这个角度来说,配置灵活性可能导致运维团队的负担。 ingress controller: inkerd 本身不提供 ingress 功能,可以接入任何 ingress controller 设计轻巧,比 Istio 快。 代理功能 HTTP、HTTP/2 和任意 TCP 协议的透明、零配置代理。 自动为 HTTP 和 TCP 流量导出 Prometheus 指标。 透明的零配置 WebSocket 代理。 自动的、可感知延迟的 7 层负载均衡。 非 HTTP 流量的自动的 4 层负载均衡。 按需诊断 tap API。 为什么微服务需要服务网格? 目前的微服务开发模式中,对于每个服务有一些重复性的基础工作,例如:
服务注册 服务发现 得到服务实例后,负载均衡 熔断机制 这些基础工作需要开发人员在项目中用代码解决并实现,即使用库或框架也十分麻烦。 那么是否可以把这些重复性的基础工作从服务中抽离出来?
首先要考虑的是这些工作所处的位置,解决的是什么问题:这些工作集中在处理各个服务之间的通信。
那么我们可以专注于服务间通信,形成单独的组件运行在微服务中。这种组件称为 sidecar。这样做的好处是:微服务中的业务逻辑与服务通信解耦,分离成两个独立运行的组件。
以组件作为服务间通信的解决方案,还存在一个可以优化的地方,即每个微服务的 sidecar 无法通用,如果能将组件拆出来分配给任意微服务使用的话,将构成一个更加通用且简单的解决方案。
因此,在 sidecar 上更进一步,构建一个专注于处理服务间通信的基础设施层——服务网格。app 无需考虑微服务中的通讯部分。在服务网格中,每个微服务至少拥有两个组件:1. 用于处理业务功能的 app 2. 专门处理服务间通信的 proxy/sidecar。
因此,在开发微服务时,研发人员无需考虑服务通信,服务通信由每个微服务的 sidecar 组件完成,而这些 sidecar 组件有专门的项目来接管。例如 Envory HaProxy 和 Nginx(还有 linkerd-proxy)都可以当作 sidecar 来使用。
服务网格使开发人员和运维人员的职责范围更清晰且避免重叠,开发人员关注业务开发,运维团队关注微服务中的 sidecar 就可以了解到微服务的健康情况和各种指标。
总之,服务网格实际上就是处于 TCP/IP 上的一个抽象层,它有点类似 TCP/IP,具备处理网络故障的能力。对于 TCP 来说,是对网络端点间传输字节的机制进行了抽象,而服务网格是对服务节点间请求的路由机制进行了抽象。(服务网格不关心消息体也不关心如何编码)。服务网格的目标是配合 app 的“将某些东西从 a 传送到 b”目标,实现这个目标并处理传送过程中的任何故障。 ———————————————— 版权声明:本文为 CSDN 博主「Sindweller5530」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/Sindweller5530/article/details/122883451