运营容器化的基础设施带来了一系列新的挑战。我们需要增强容器、评估 API 端点的性能以及识别出基础设施中的有害部分。Istio 服务网格可在不修改代码的情况下实现 API 增强,并且不会带来服务延迟。这篇文章将介绍如何全面了解 Kubernetes 基础设施以及如何在基于容器的基础设施中使用 Istio 服务网格。
Istio 概览
Istio 是用于 Kubernetes 的服务网格,负责处理服务之间的通信,就像网络路由软件处理 TCP/IP 流量那样。除了 Kubernetes 之外,Istio 还可以与基于 Docker 和 Consul 的服务发生交互。它与已经存在了一段时间的 LinkerD 有点相似。
Istio 是由来自谷歌、IBM、思科和 Lyft Envoy 的团队共同开发的一个开源项目。Istio 最近刚满一岁,却已经被大规模部署到生产环境中。在写这篇文章时 (2018 年 1 月 10 日),Istio 版本为 0.8。
那么,如何在 Kubernetes 生态系统中使用 Istio?可以这么说,Kubernetes 充当的是数据平面,Istio 则充当控制平面。Kubernetes 负责承载应用程序流量,进行容器的编配、部署和扩展。Istio 则负责路由应用程序流量,处理策略执行、流量管理和负载均衡问题。它还聚合遥测元素,如指标、日志和跟踪。Istio 是基于容器基础设施的协管员和警报员。
上图描绘了服务网格的架构。Istio 为每项服务运行一个 Envoy 边车代理。Envoy 代理通过 GRPC 调用将入站请求转发至 Istio Mixer 服务。然后,Mixer 应用流量管理规则来聚合遥测元素。Mixer 是 Istio 的大脑。运维人员可以通过编写 YAML 文件来指定 Envoy 如何重定向流量,还可以指定将哪些遥测元素推送到监控系统和观测系统。我们可以在运行时根据具体情况应用相应的规则,无需重新启动任何 Istio 组件。
Istio 支持多种适配器,用于将数据发送到各种监控工具,如 Prometheus、Circonus 或 Statsd。我们也可以同时启用 Zipkin 和 Jaeger 跟踪,生成图形以便对服务进行可视化。
Istio 的部署非常简单。大概七八个月之前,我们必须通过一系列 kubectl 命令将 Istio 安装到 Kubernetes 群集上。现在当然也可以这么做,不过有另一种更加简便的方式,就是在谷歌云平台上点击几下鼠标即可部署启用了 Istio 的 Kubernetes 群集,包括监控、跟踪和示例应用程序。
另一个好处是,我们可以在不要求开发人员对他们的服务进行增强的情况下收集服务数据。这样可以减少维护工作、消除代码中的失败点。它还提供了与供应商无关的接口,降低了被供应商锁定的可能性。
借助 Istio,我们可以部署单个服务的不同版本,并权衡它们之间的流量。Istio 本身使用了多个不同的 pod,如下所示:
> kubectl get pods -n istio-system NAME READY STATUS RESTARTS AGE istio-ca-797dfb66c5 1/1 Running 0 2m istio-ingress-84f75844c4 1/1 Running 0 2m istio-egress-29a16321d3 1/1 Running 0 2m istio-mixer-9bf85fc68 3/3 Running 0 2m istio-pilot-575679c565 2/2 Running 0 2m grafana-182346ba12 2/2 Running 0 2m prometheus-837521fe34 2/2 Running 0 2m
Istio 实际上不是轻量级的。Istio 的强大功能和灵活性带来了一些运营成本。但是,如果应用程序中包含多个微服务,那么应用程序容器很快会表现出比系统配置容器更大的优势。
服务水平目标
关于服务水平目标的概述将为我们如何衡量服务健康状况奠定基础。服务水平协议(Service Level Agreement,SLA)的概念已经存在了至少十年。而直到最近,与服务水平目标(Service Level Objective,SLO)和服务水平指示器(Service Level Indicator,SLI)相关的网络内容才出现爆发式的增长。
除了谷歌的那本“Site Reliability Engineering”之外,还有两本有关 SLO 的新书即将问世。“The Site Reliability Workbook”有专门的章节介绍了 SLO,而 Circonus 创始人兼首席执行官 Theo Schlossnagle 在“Seeking SRE”的一个章节中对 SLO 的目标进行了定义。建议观看由 Seth Vargo 和 Liz Fong Jones 呈献的 YouTube 视频“SLIs,SLOs,SLAs,oh my!” (链接: https://youtu.be/tEylFyxbDLE ),以便深入了解 SLI、SLO 和 SLA 之间的差别。
总的来说:SLI 驱动 SLO,SLO 通知 SLA。
SLI 是衡量服务健康状况的指标。例如,我们可以有这样的一个 SLI,它表示在过去 5 分钟内,95% 的主页面请求延迟应小于 300 毫秒。
SLO 是 SLI 的目标。我们对 SLI 进行扩展,用以量化我们对服务在给定时间间隔内执行情况的期望。以上面的 SLI 为例,我们可以说,我们希望这个 SLI 设定的标准在下一年可以达到 99.9%。
SLA 是企业与客户之间的协议,定义了未能满足 SLO 的后果。一般来说,SLA 所依据的 SLO 比内部 SLO 更宽松,因为我们希望内部目标比外部目标更为严格。
RED 仪表盘
怎样的 SLI 组合最适合用于量化主机和服务的健康程度?在过去几年中,出现了一些新兴的标准。其中最有影响力的是 USE 方法、RED 方法和谷歌 SRE 手册中讨论的“四个黄金信号”。
USE 方法由 Brendan Gregg 提出,旨在根据利用率、饱和度和错误指标量化系统主机的健康状况。对于像 CPU 这样的产品,我们可以使用常用的指标,如用户、系统和闲置百分比。我们可以使用平均负载和运行队列来衡量饱和度。UNIX 的 perf 分析器是用来测量 CPU 错误事件的一个很好的工具。
几年前,Tom Wilkie 提出了 RED 方法。借助 RED 方法,我们对请求率、请求错误和请求持续时间进行监控。谷歌 SRE 手册讨论了如何使用延迟、流量、错误和饱和度这四个指标。这“四个黄金信号”主要针对服务健康,与 RED 方法类似,只是多了饱和度这一项。但在实践中,可能难以量化服务饱和度。
那么,我们如何监控容器?容器存活期短,通过直接监控它们来识别服务健康状况将带来很多复杂的问题,例如高基数问题。通过聚合这些容器的输出来监控它们会更容易和有效。如果一个服务是健康的,我们就不会在乎容器的行为是否正常。我们的编配框架会捕获到这个容器,并用新的容器取而代之。
让我们来看看如何将 Istio 的 SLI 作为 RED 仪表盘的一部分。首先看看 Istio 都提供了哪些遥测元素:
- 基于响应码的请求数
- 请求时长
- 请求大小
- 响应大小
- 连接收到的字节
- 连接发送的字节
- 连接时长
- 基于模板的元数据(度量标签)
Istio 提供了几个有关收到的请求、响应延迟和连接信息的指标。请注意上述列表中的前两项,我们希望将它们包含在 RED 仪表盘中。
Istio 还赋予我们添加度量标签的能力,就是所谓的维度。我们可以按照主机、集群等对遥测元素进行拆解。我们可以通过计算请求数的一阶导数来获得每秒请求速率。我们还可以通过计算不成功请求数的导数来获得错误率。Istio 还为我们提供了每个请求的延迟,因此我们可以记录每个服务请求需要多少时间才能处理完。
另外,Istio 还为我们提供了一个 Grafana 仪表盘,它包含了我们想要的部分:
上面截图中以红色圈起来的部分是我们想要的组件。左上角是每秒操作请求速率,右上角是每秒失败请求数,底下是响应时间。现在让我们来仔细看看圈出的那几个指标:
这个截图显示了仪表盘的速率组件。我们统计返回 200 响应码的请求数,并绘制成图形。
Istio 仪表盘为返回 5xx 错误码的响应做了类似的操作。在上面的截图中,可以看到,它按照摄入控制器或应用程序产品页的错误来区分错误。
这个截图显示了请求持续时间,提供了丰富的有关服务健康状况的信息。这些数据由 Prometheus 监控系统提供,因此我们可以看到请求时间百分位,包括中位数、90th、95th 和 99th 这些百分位。
这些百分位为我们提供了有关服务执行情况的全面指示。但是,这种方法也存在一些缺陷。在活动不是很活跃的时候,由于样本数量有限,这些百分位可能会大幅偏离,给我们带来误导。这种方法可能出现的其他问题:
持续时间问题:
- 百分位是基于固定时间窗口的聚合指标。
- 无法为群集健康重新聚合百分位。
- 不能计算百分位平均数(这是一个常见的错误)。
- 这种方法存储的聚合是输出,而不是输入。
- 很难用这种方法测量集群 SLI。
百分位通常比平均数提供更深刻的洞见,因为它们使用多个数据点来表示数值范围。但与平均数一样,百分位是一种聚合度量。它们是基于固定时间窗口对固定数据集计算得出的。如果我们要计算某个集群成员的持续时间百分比,就不能将其与另一个集群成员合并,因为这样得到的是整个集群的聚合指标。
我们不能计算百分位的平均数,除非产生这些百分位的分布几乎是一样的,但这种情况很少见。如果你只有百分位,而不是源数据,那么就不知道它是不是这种情况。这是一个鸡生蛋和蛋生鸡的问题。
这也意味着,如果你只是基于百分比衡量单个集群成员的性能,就会因缺少聚合而无法为整个服务设置 SLI。
由于在固定时间窗口内只有 4 个延迟数据点,我们只能在有限的范围内设置有意义的 SLI。因此,在你使用基于百分位的持续时间指标时,必须问自己,你的 SLI 是否真的足够好。我们可以借助数学来设定更好的 SLI,从而全面了解服务的性能和健康状况。
直方图遥测
上面是以微秒为单位显示服务延迟数据的一个直方图。样本数量位于 Y 轴上,样本值(微秒级延迟)位于 X 轴上。这是我们在 Circonus 开发的开源直方图,也请参考 C 语言( https://github.com/circonus-labs/libcircllhist )和 Golang( https://github.com/circonus-labs/circonusllhist )的直方图开源实现。还有其他一些开源的直方图实现,如 Ted Dunning 的 t-digest 直方图( https://github.com/tdunning/t-digest )和 HDR 直方图( http://hdrhistogram.org/ )。
Envoy 项目最近采用了 Circonus 开发的 C 语言版本的对数线性直方图库,因此可以显示所收集数据的分布情况。
直方图是可以合并的,只要 bin 的边界相同,任何两个或多个直方图都可以合并在一起。这意味着我们可以将多个分布组合在一起。可合并的度量指标对于监控和可观测性来说非常有用。我们因此可以合并来自相似资源(如服务成员)的输出,并获得聚合的服务指标。
如上图所示,在这个对数线性直方图中,每 10 次幂对应 90 个 bin。从图中可以看到 100K 到 1M 个之间的 90 个 bin。对于每 10 次幂,bin 的大小就以 10 的倍数增长。这样我们就能够以较高的相对精度记录各种数值,而不需要提前知道数据的分布情况。如果我们将一些百分位重叠起来会是什么样子:
可以看到,现在有平均值、50th 百分位(也称为中位数)和 90th 百分位。90th 百分位是指 90%样本低于该值。
现在回到之前的 SLI 示例,如果按照这种格式来显示延迟数据,就可以通过将直方图合并在一起来获得 5 分钟的数据视图,然后计算出该分布的 90th 百分位,从而得到服务的 SLI。如果它低于 1,000 毫秒,就达到了我们的目标。
上面截图中的 RED 仪表盘包含了四个百分位:50th、90th、95th 和 99th。我们也可以重叠这些百分位。即使在没有数据的情况下,我们也可以看到请求分布的大致轮廓,当然,这里需做出很多假设。要想知道仅基于几个百分位做出的假设将给我们造成怎样的误导,可以看看这个带有其他模式的分布:
这个直方图显示了两种不同模式的分布。左边的模式表示快速响应,可能是因为使用了缓存,而右边的表示从磁盘上读取数据再做出响应。仅使用四个百分位来衡量延迟几乎不可能辨别这种分布。可见,百分位会隐藏掉一些复杂性。现在让我们来看看具有两种以上模式的分布:
这个分布至少有四种模式。如果我们在全分布上运行数学运算,可以找到 20 多种模式。那么我们需要记录多少个百分位才能获得一个近似于上面这样的延迟分布呢?又比如下面这个分布呢?
由很多服务组成的复杂系统将生成无法用百分位准确表示的延迟分布,我们必须记录整个延迟分布才能完整地表示它。这就是为什么要将数据的完整分布放在直方图中,并根据实际需要计算出百分位,而不只是保存几个百分位。
这种直方图显示了固定时间窗口上的分布。我们可以存储多个分布,以了解它随时间变化的情况,如下所示:
这是一个热图,代表一组随时间变化的直方图。想象一下,这个热图中的每一列都有一个单独的条形图,用颜色来表示每个 bin 的高度。这是一个集群(包含 10 个负载均衡器)响应延迟在 Grafana 中的可视化。我们因此能够深入了解整个集群一周之内的行为,这里包含了 100 万个数据样本。这里的中位数大约在 500 微秒左右,以红色条形表示。
上面是另一种类型的热图,其中使用饱和度表示每个 bin 的“高度”(块颜色越深表示越“饱和”)。此外,这次我们在热图上重叠了随时间变化的百分位。百分位是可靠的度量指标,而且非常有用,但它们本身并不能发挥这种作用。我们可以看到 90 以上的百分位将如何随着延迟分布向上移动而增加。
让我们来看看这些时间段分布图,看看是否可以生成比样本仪表盘包含更多信息的东西:
上面的截图是修改后的 RED 仪表盘,显示了基于分布的延迟数据。左下角显示了固定时间窗口的延迟直方图。在它的右边,我们使用热图将分布分解成更小的时间窗口。利用 RED 仪表盘的布局,我们只需要借助几个信息面板就可以全面了解我们的服务是如何运作的。这个仪表盘是使用 Grafana 实现的,后台使用了 IRONdb 时间序列数据库,该数据库在本地存储延迟数据,用于构建对数线性直方图。
我们可以进一步扩展这个 RED 仪表盘,并将 SLI 重叠到上面:
对于速率面板,我们的 SLI 可能会保持最低水平的每秒请求数,每秒错误数也可能保持在某个值之下。正如我们之前研究过的 SLI,我们可能希望整个服务的 99th 百分位在固定时间窗口内保持一定的延迟。我们可以借助这些直方图来设置有意义的 SLI。现在我们还有很多工作要做,而且可以更好地审查我们的数据。
问正确的问题
我们已经把所有的东西都放在了一起,并看到了如何利用 Istio 从服务中获取有意义的数据,现在让我们看看可以解决哪些问题。
我们都希望能够解决技术问题,但不是每个人都专注于此。业务人员会问业务方面的问题,所以你要能够回答这些问题。接下来让我们看看已经组装好的工具将怎样解决业务人员向 SRE 提出的问题:
示例问题:
- 在周二的大促活动后,有多少用户因为速度变慢而感到不愉快?
- 我们的结帐服务是过度配置了还是配置不足?
先看第一个例子。每个人可能都有过访问龟速网站的体验。假设一次市场大推广导致流量暴增,性能下降,用户抱怨网站速度太慢。我们如何量化速度有多慢?有多少用户感到不愉快?假设市场营销部门想知道这些问题的答案,然后就可以向受影响的用户发送 10%折扣的电子邮件,同时希望避免同样问题再次发生。我们定义了一个 SLI,假设用户注意到速度变慢,并在请求超过 500 毫秒时感到生气。我们如何基于这个 500 毫秒的 SLI 计算有多少用户感到生气?
首先,我们需要记录请求延迟,然后将它们绘制成热图。我们可以使用分布数据来计算超过 500 毫秒 SLI 的请求的百分比(借助反向百分位),然后将其乘以该时间窗口中的请求总数,最后将随时间变化的结果重叠在热图上:
在上面的截图中,我们已经在热图上圈出了速度变慢的部分。增加的延迟分布足以告诉我们速度变慢了。图中的线表示受影响的请求总数随时间变化的情况。
在这个例子中,有 400 万个请求达不到我们的 SLI。右边的两处速度变慢不是很明显,因为它们的幅度较小,但每处都有 200 万个请求达不到 SLI。
我们可以继续进行这类数学分析,因为数据被存储为分布,而不只是聚合的百分位。
现在让我们来考虑另一个问题,即我们的服务是配置不足还是配置过度?
答案通常是“视情况而定”。除了碰上特殊日子,负载在一天中和一周中的每一天也都有所不同。在我们搞清楚系统在特定负载下的行为之前,我们所知道的就是这些。现在我们来做一些简单的数学运算,使用延迟来可视化系统是如何运行的:
上面的可视化显示了延迟分布随时间变化的情况,包括低于 25 毫秒的请求数、25 毫秒到 100 毫秒的请求数、100 毫秒到 250 毫秒的请求数、250 毫秒到 1000 毫秒的请求数以及超过 1000 毫秒的请求数。它们按照颜色进行分组,绿色表示快速请求,红色表示慢请求。
这种可视化告诉了我们什么?我们可以看到,服务请求在一开始非常快,几分钟后快请求的百分比下降,大约 10 分钟后慢请求的百分比增加。这种模式在两次流量会话中重复出现。这又告诉了我们什么?这表明,最初服务过度配置,但在随后的 10 到 20 分钟内配置不足。看起来,这里很适合使用自动伸缩。
我们也可以将这种类型的可视化添加到 RED 仪表盘中。这种类型的数据对业务利益相关者来说非常有用,而且他们不需要掌握大量的技术知识就可以了解它们对业务的影响。
结论
我们应该监控服务,而不是容器。服务是长期存活的实体,而容器不是。用户并不关心容器如何运行,他们只关心服务如何运行。
我们应该记录分布而不是聚合,不过要能够从这些分布中生成聚合。聚合是非常有价值的信息来源。但它们是不可合并的,因此不适用于统计分析。
Istio 提供了很多现成的东西,我们没有必要去修改我们的代码,也没必要从头开始构建高质量的应用程序框架。
使用数学方法提出并回答有关服务的问题。当我们通过解决重要的业务问题让系统变得更加可靠时,其实是实现了组织的目标。
感谢张婵对本文的审校。
评论 1 条评论