开源路由器
在上一篇文章中 ,我介绍了Skipper ,这是一个开放源代码HTTP路由器和用于服务组合的反向代理。 本文重点介绍Skipper如何增加网络可见性,并描述其对可伸缩应用程序开发人员和他们所运行的基础结构的运营商的优势。
什么是网络可见性?
什么是可见性,我们需要获得什么支持? 慈善专业人士在Twitter上提供了一个很好的答案:
监视适用于操作系统/系统
—慈善专业(@mipsytipsy) ,2017年9月23日
仪器用于编写软件
可观察性是为了理解系统
作为HTTP路由提供程序,Skipper希望为应用程序开发人员提供可见性。 在电子商务零售商Zalando,功能团队的开发人员希望监视他们的系统,以了解故障率(%),吞吐量(每秒请求数或RPS)和延迟(例如p50,p99,p999)。 想要检测其自定义代理的Skipper库用户可以使用Skipper的指标包来检测其代理并创建自定义指标。
操作员需要具有可观察性,才能了解系统在一般时间和特定时间点的行为,因此当有人问“昨天凌晨2:30发生了什么事情”时,他们已经准备就绪。 或“如何处理此请求?” Skipper可以通过增加后端应用程序的可见性来帮助回答这些问题。
监控后端应用程序
出于本文的目的,我将后端应用程序定义为在Skipper HTTP代理后面运行的任意HTTP服务。
运行工作负载的应用程序所有者希望监视四个黄金信号 :延迟,流量,错误和饱和。 像Skipper这样的HTTP路由器在呼叫路径中处于有利位置,可以为后端提供四种(延迟,流量和错误)中的三种。 但实际上,饱和度是一个复杂的目标。 它必须在节点,控制组或应用程序级别进行监视,因此不在本文讨论范围之内。
指标
船长公开了路由和聚合路由的响应延迟,RPS吞吐量以及正常和失败率。 这些是应用程序所有者获得警报所需的基本信号,因此他们可以根据需要采取措施。
主机头或按路由 。 如果使用-serve-host-metrics启动Skipper, 则可以收集和公开按Host标头分组的指标。 如果需要更多详细信息,可以使用-serve-route- metrics按路由,状态码和方法来拆分度量。请记住,没有什么是免费的,拥有更多指标并不总是更好。 您将为在Skipper和时间序列数据库( TSDB )中使用内存使用情况存储和查询指标而付费 。
您还可以选择以Coda Hale还是Prometheus格式显示指标。 Coda Hale指标暴露了pN中预先汇总的延迟,例如p99。 这些预先汇总的指标的一个缺点是,您在所有实例上都无法获得统计上正确的p99。 如果要在所有Skipper实例上计算正确的pN ,我建议使用Prometheus抓取Skipper实例。 然后,您可以查询Prometheus以计算延迟,错误和吞吐量。
潜伏
要获取后端的响应延迟,可以使用图1所示的PromQL查询。该查询将在最后一分钟获取按主机标头划分的p99延迟:
图1:
histogram_quantile
(
0.99,
sum
( rate
( skipper_serve_host_duration_seconds_bucket
{
}
[ 1m
]
)
)
by
( le,host
)
)
交通
要获取吞吐量数据,可以使用图2中所示的查询。此PromQL查询显示了按主机标头拆分的RPS:
图2:
sum ( rate ( skipper_serve_host_duration_seconds_count { } [ 1m ] ) ) by ( host )
失误
您可以使用多个选项来监视Skipper中的错误。 要通过主机标头每秒获取HTTP状态码4xx和5xx错误,请使用图3所示的查询:
图3:
sum
( rate
( skipper_serve_host_duration_seconds_count
{ code=~
"[45].."
}
[ 1m
]
)
)
by
( host
)
/
sum
( rate
( skipper_serve_host_duration_seconds_count
{
}
[ 1m
]
)
)
by
( host
)
如果只想查看错误,请使用图4中的查询:
图4:
sum ( rate ( skipper_serve_host_duration_seconds_count { code=~ "[45].." } [ 10m ] ) ) by ( host )
如果要查看成功率百分比,请使用图5中的查询:
图5:
(
sum
( rate
( skipper_serve_host_duration_seconds_count
{
}
[ 10m
]
)
) by
( host
)
- sum
( rate
( skipper_serve_host_duration_seconds_count
{ code=~
"[45].."
}
[ 10m
]
)
) by
( host
)
) / sum
( rate
( skipper_serve_host_duration_seconds_count
{
}
[ 10m
]
)
) by
( host
)
访问日志
船长还提供访问日志,这非常有价值。 您可以使用enableAccessLog和disableAccessLog筛选器通过状态代码控制单个路由的日志输出。 访问日志还包含可以追溯到各个客户端的数据。 例如,您可以使用unverifiedAuditLog过滤器记录JSON Web令牌(JWT)的一部分,这些部分将识别客户端。 这有助于应用程序所有者了解哪个客户端具有哪种访问模式。
Skipper支持JSON或Apache 2样式访问日志格式的结构化日志。 Apache格式非常适合人类阅读,并且可以使用巨大的生态系统。 如果要使用计算机处理日志,则可能要使用JSON格式。
通常,最终用户网站(例如网上商店)会使用多个应用程序或服务。 为了进行调查,您希望查看单个用户事务的所有日志,以便您可以了解错误并在后台进行修复。 在所有应用程序的日志中都包含业务跟踪ID怎么样? 在Zalando,我们使用flowID标识符实现了事件日志,该标识符作为HTTP标头传递。 船长有一个flowID过滤器,还会将该值写入您的访问日志中。 这使您能够轻松地查找一项业务交易的所有访问日志,而无需使用诸如分布式跟踪之类的更新工具包。
分布式跟踪
分布式跟踪在现代可见性工具包中很重要。 我们将此术语用于OpenTelemetry提供的生态系统,以前称为OpenTracing和OpenCensus 。
分布式跟踪提供了完全不同的分布式系统视图。 例如,您可以获得如图6所示的瀑布图类型。该图显示了执行不同操作的多个服务,所有服务均以持续时间来衡量。 左侧的初始HTTP GET /调度范围打开了另外两个范围: HTTP GET / customer调用和Driver :: findNearest Redis操作。
这有很多知名度!
您可以在下面找到有关Skipper的分布式跟踪支持的更多详细信息。
运作方式
分布式服务的运营商希望了解边缘代理的运行方式。 他们想知道Skipper是否健康,是否导致应用程序延迟以及(如果如此)在浪费时间。 他们还希望区分后端问题和HTTP路由器问题,以减少平均修复时间。
大多数应用程序应提供诸如内存和CPU使用率之类的核心指标,但这对于服务整个业务骨干的重要组件来说还远远不够—后端延迟,RPS吞吐量,错误率,连接状态,队列状态和重启非常重要。也很重要 Skipper用Go编写,它公开了一些有趣的运行时指标 ,例如内存,垃圾回收详细信息,线程数和goroutine。 所有这些指标都在支持侦听器上导出,默认为:9911 / metrics 。
图7显示了一个Skipper仪表板,其中包含一组集群范围的图形。 第一行显示吞吐量,后端延迟和有关进程并发的数据。 这些图显示了重要的群集模式以及HTTP路由和后端是否健康。 硬件更改可能会更改Go运行时正在使用的线程数或每个实例的计算吞吐量。 在第二行中,您可以查看HTTP状态代码2xx,4xx和5xx,以全面了解错误并了解什么是“正常”的。
图7:
图8显示了其他运行时和容量的详细信息。 该图显示了按主机标头拆分的请求,使您可以识别对应用程序的访问模式。
图8:
图9的底部显示了两个不同容器的CPU使用率。 顶部显示过滤器和LIFO(后进先出)队列,需要更多说明。 例如,如果用户开始使用compress()过滤器,则可能会看到响应过滤器速度变慢和CPU使用率增加,因为现在您将CPU用于压缩算法,而不仅用于传输数据包。 如果开发人员开始使用可调用诸如webhook()或oauth *之类的外部API的身份验证过滤器,则请求过滤器运行时还将计入这些端点的往返次数。 此信息显示观察到的应用程序延迟是否是更全局性的副作用,例如缓慢的tokeninfo端点。 图9显示在17:00,有关于tokeninfo CPU使用率的新指标。 我将tokeninfo auth端点部署为辅助工具,这减少了过滤器延迟
以及船长的CPU使用率。
图9:
分布式跟踪
如果回头看一下图6中的瀑布图,则可以看到整个应用程序中的请求流。 该信息由跟踪库提供。 Skipper当前支持跟踪提供程序Instana , Jaeger和Lightstep ,因此您可以选择使用SaaS提供程序或开源版本。
分布式跟踪提供程序提供的跟踪效果很好,但是如果您仔细研究细节,则可以获得的收益远远超过仅从瀑布图获得的收益。
在分布式跟踪中,跟踪由连接的跨度组成。 跨度具有服务,操作和持续时间,但是它们还可以包含标签和日志之类的元数据。 使用Skipper,您可以设置流程全局标签,例如环境,主机名,集群名称或云提供商。 这对于过滤和分组跟踪很有用。
船长为每个操作提供五个跨度:
- 入口:父范围,HTTP处理程序(包括路由查找)
- 请求过滤器:处理请求过滤器
- 身份验证过滤器:与您的授权提供者进行交互
- 代理:包装后端调用
- 响应过滤器:处理响应过滤器
例如,在图10的“ 请求过滤器”跨度中,您可以在日志中看到运行路由的所有单个过滤器所花费的时间。
图10:
在“ 代理”跨度中,日志显示拨号 ,TCP / IP连接和TLS握手,到后端的HTTP往返以及到客户端的标头和数据流所花费的时间。 如图11所示,即使没有检测到的后端,后端也大约花费了90ms,而Dial_context ,TCP / IP和TLS握手花费了1.04ms。
图11:
如果您需要检测Go应用程序,则可以使用Skipper HTTP客户端 ,该客户端在客户端范围内提供详细的日志。 例如,图12显示了正在使用Client的tokeninfo和auth过滤器范围:
图12:
可以在Skipper的OpenTracing文档中找到更多详细信息。
路由表
像任何其他HTTP路由器一样,Skipper具有一个路由表,或更确切地说,还有一个路由树(如我之前的文章中所述 )。 使用树形结构可以扩展路由数量,并在查找期间获得良好的性能。 这使Skipper可以在一个实例中运行超过600,000条路线。
作为操作员,您有时必须调查路由问题。 不必在配置源上工作,而可以在线查看路由树,那将是很好的选择。 使用-support-listener ,Skipper使您能够转储实例的当前路由表 。 图13显示了如何转储部分路由表。 在这种情况下,使用offset = 10和limit = 1只会打印第十条路线:
图13:
% curl "localhost:9911/routes?offset=10&limit=1"
kube_default__foo__foo_teapot_example_org_____foo
: Host
( /^foo
[ .
] teapot
[ .
] example
[ .
] org$/
) && PathSubtree
(
"/"
)
-> enableAccessLog
( 4, 5
)
-> lifo
( 2000, 20000,
"3s"
)
-> setRequestHeader
(
"X-Foo" ,
"hello-world"
)
-> <roundRobin,
"http://10.2.0.225:9090" ,
"http://10.2.1.244:9090" >;
调试请求
我的前一篇文章表明,Skipper 筛选器可以更改传递到后端的请求。 但是,您如何调查外发请求? 人们经常尝试使用调试日志或通过SSH进入计算机,然后使用tcpdump转储所有内容以查找传出的请求。
船长提供了一种更好的方法来检查转换后的请求。 通过使用调试侦听器-debug-listener =:9922 ,Skipper可以显示有关传入和传出请求的信息。 在图14中,您可以看到传出请求应用了X-Foo:hello-world请求标头,该标头是由图13中的过滤器setRequestHeader(“ X-Foo”,“ hello-world”)添加的 。
图14:
% curl -s http://127.0.0.1:9922/ -H"Host: foo.teapot.example.org" | jq .
{
"route_id"
:
"kube_default__foo__foo_teapot_example_org_____foo" ,
"route"
:
"Host(/^foo[.]teapot[.]example[.]org$/) && PathSubtree(\" /\
") -> enableAccessLog(4, 5) -> lifo(2000, 20000, \" 3s\
") -> setRequestHeader(\" X-Foo\
", \" hello-world\
") -> <roundRobin, \" http://10.2.0.225:9090\
", \" http://10.2.1.244:9090\
">" ,
"incoming"
:
{
"method"
:
"GET" ,
"uri"
:
"/" ,
"proto"
:
"HTTP/1.1" ,
"header"
:
{
"Accept"
:
[
"*/*"
] ,
"User-Agent"
:
[
"curl/7.49.0"
]
} ,
"host"
:
"foo.teapot.example.org" ,
"remote_address"
:
"127.0.0.1:32992"
} ,
"outgoing"
:
{
"method"
:
"GET" ,
"uri"
:
"" ,
"proto"
:
"HTTP/1.1" ,
"header"
:
{
"Accept"
:
[
"*/*"
] ,
"User-Agent"
:
[
"curl/7.49.0"
] ,
"X-Foo"
:
[
"hello-world"
]
} ,
"host"
:
"foo.teapot.example.org"
} ,
"response_mod"
:
{
"header"
:
{
"Server"
:
[
"Skipper"
]
}
} ,
"filters"
:
[
{
"name"
:
"enableAccessLog" ,
"args"
:
[
4,
5
]
} ,
{
"name"
:
"lifo" ,
"args"
:
[
2000,
20000,
"3s"
]
} ,
{
"name"
:
"setRequestHeader" ,
"args"
:
[
"X-Foo" ,
"hello-world"
]
}
] ,
"predicates"
:
[
{
"name"
:
"PathSubtree" ,
"args"
:
[
"/"
]
}
]
}
加起来
本文介绍了Skipper如何使您能够提高HTTP路由系统的可见性。 希望这能为您提供提高可观察性所需的信息,但是如果您有任何疑问,请在下面的评论中分享。
开源路由器