背景
Kerberos 是一种网络认证协议,其设计目标是通过密钥系统为客户端、服务器端的应用程序提供强大的认证服务。
作为一种可信任的第三方认证服务,Kerberos 是通过传统的密码技术(如:共享密钥)执行认证服务的,被 Client 和 Server 同时信任。KDC 是对该协议中第三方认证服务的一种具体实现,一直以来都是美团数据平台的核心服务之一,在 Hive、HDFS、YARN 等开源组件的权限认证方面有着广泛的应用。该服务将认证的密钥事先部署在集群的节点上,集群或者新节点启动时,相应节点使用密钥得到认证。只有被认证过节点才能被集群所接纳。企图冒充的节点由于没有相关密钥信息,无法与集群内部的节点通信,从而有效的确保了数据的安全性、节点的可信赖性。
但随着平台业务的快速增长,当前线上 KDC 的处理能力不足和不能可靠监控的问题被凸显的日益严重:线上单台 KDC 服务器最大承受 QPS 是多少?哪台 KDC 的服务即将出现压力过大的问题?为什么机器的资源非常空闲,KDC 的压力却会过大?如何优化?优化后瓶颈在哪儿?如何保证监控指标的全面性、可靠性和准确性?这都是本文需要回答的问题。从本次优化工作达成的最终结果上来看,单台服务器每秒的处理性能提升 16 倍左右,另外通过共享内存的方式设计了一个获取 KDC 各项核心指标的接口,使得服务的可用性进一步提升。
为方便大家,表 1 总结并解释了本文中后续会涉及到一些专业名词的简称:
表 1 专业名词解释
图 1 为美团数据平台 KDC 当前服务架构,目前整个 KDC 服务部署在同一个 IDC。
图 1 KDC 主体流程图
KDC 原理介绍
Client、 KDC 和 Server 在认证阶段主要有 Client 和 KDC 的 AS、Client 和 KDC 的 TGS 以及 Client 和 Server 的交互三个过程,下文将详细介绍三个过程的交互,其中图 2 中的步骤 1 和 2、3 和 4、5 和 6 分别对应下文的 A、B、C 三部分:
图 2 KDC 原理图
A. Client 和 AS 的交互
用户以明文的形式发送自己的信息、以及想要申请的 TGT Principal(默认为 KDC 的 TGT:krbtgt/REALM@REALM)等信息给 AS 服务;
AS 服务验证该用户信息存在数据库中后,给客户端返回两大块信息:
使用用户的密钥加密其申请的 TGT 和一个 Session Key 返回用户,用户得到加密信息后,使用自己密钥解密得到其申请的 TGT 和 Session Key(后续都简称 SKCandK)。
以 KDC 自身密钥加密用户申请的 TGT、SKCandK、用户自己信息等;简称{TGT}Ktgs,该部分信息被 Client 保存在本地。
B. Client 和 TGS 的交互
Client 访问 TGS 获取访问网路中某一 Server 的 ticket 请求。Client 端会把第一部分中保存在本地的{TGT}Ktgs和用 SKCandK加密的客户端信息发送 KDC 的 TGS 模块;
TGS 收到请求后,检查请求 Server 存在数据库中后,用自己的密钥解密得到 TGT 中的 SKCandK,然后便可以解密得到用户的信息并验证其合法性;通过后,TGS 会生成一个新的 Session Key,简称 SKCandS;同时返回两部分信息:
用 SKCandK加密的 SKCandS、能够访问 Service 的 ticket 等信息。
用 Service 密钥加密的 Client info、SKCandS等信息,简称{ TService }KService。
C. Client 和 Server 的交互
Client 拿到访问 Service 的 ticket 后,向 Service 发起请求,同时将两部分信息发送给 Server:1)通过 SKCandS加密的 Client info 等信息。2)第二部分 TGS 返回客户端的{ TService }KService;
Server 端收到 Client 的请求信息后,用自己的密钥解密获取到 TService信息,就能够解密 SKCandS加密的客户端信息,和 TService中的客户端信息进行对比,通过后,整个 KDC 认证过程结束,Client 和 Service 进行正常的服务通信。
主要优化工作
通过对 KDC 原理的分析,很容易判断只有前两部分才可能直接给 KDC 服务带来压力,因此本文涉及到的工作都将围绕上一部分的前两个环节展开分析。本次优化工作采用 Grinder 这一开源压测工具,分别对 AS、TGS 两个请求过程,采用相同机型(保证硬件的一致性)在不同场景下进行了压力测试。
优化之前,线上 KDC 服务启动的单进程;为最低风险的完成美团和点评数据的融合,KDC 中 keytab 都开启了 PREAUTH 属性;承载 KDC 服务的部分服务器没有做 RAID。KDC 服务出现故障时,机器整体资源空闲,怀疑是单进程的处理能力达到上限;PREAUTH 属性进一步保证提升了 KDC 服务的安全性,但可能带来一定的性能开销;如果线上服务器只加载了少量的 keytab 信息,那么没有被加载到内存的数据必然读取磁盘,从而带来一定的 IO 损耗。
因此本文中,对以下三个条件进行变动,分别进行了测试:
对承载 KDC 服务的物理机型是否做 RAID10;
请求的 keytab 在库中是否带有 PRAUTH 属性;
KDC 是否启动多进程(多进程设置数目和物理机核数一致)。(实际测试工作中进行了多次测试)
A. Client 和 AS 交互过程的压测
表 2 为 AS 压测的一组平均水平的测试数据,使用的物理机有 40 核,因此多进程测试启动 40 个进程。
表 2 AS 压测
分析表 2 中的数据,很容易提出如下问题从而需要进一步探索:
比较表 2 中第一行和第二行、第三行和第四行,主机做不做 RAID 为什么对结果几乎无影响?
该四组(测试结果为 49、53、100 和 104 所在表 2 中的行)数据均在达到处理能力上限一段时间后产生认证失败,分析机器的性能数据,内存、网卡、磁盘资源均没有成为系统的瓶颈,CPU 资源除了某个 CPU 偶尔被打满,其他均很空闲。分析客户端和服务端的认证日志,服务端未见明显异常,但是客户端发现大量的 Socket Timeout 错误(测试设置的 Socket 超时时间为 30s)。由于测试过程中,客户端输出的压力始终大于 KDC 的最大处理能力,导致 KDC 端的 AS 始终处于满负荷状态,暂时处理不了的请求必然导致排队;当排队的请求等待时间超过设置的 30s 后便会开始超时从而认证出错,且伴随机器某一 CPU 被打满(如图 3)。 显然 KDC 单进程服务的处理能力已经达到瓶颈且瓶颈存在单核 CPU 的处理能力,从而决定向多进程方向进行优化测试。
图 3 单进程 KDC 打满某一 CPU
图 4 为本次压力测试的一个通用模型,假设 KDC 单位时间内的最大处理能力是 A,来自客户端的请求速率稳定为 B 且 B>A ;图中黄色区域为排队的请求数,当某一请求排队超过 30s,便会导致 Socket Timedout 错误。
图 4 AS 处理能力和 Client 压力模型
比较表 2 中第 1 和 3 行、第 2 和 4 行、第 7 和 8 行相比,为什么有 PREAUTH 属性的认证 QPS 大致是无该属性处理能力的一半?
如果 Client 的 keytab 在 KDC 的库中不带有 PREAUTH 这一属性,Client 发送请求,KDC 的 AS 模块验证其合法性之后返回正确的结果;整个过程只需要两次建立链接进行交互便可完成。如果带有 PREAUTH 属性,意味着该 keytab 的认证启动了 Kerberos 5 协议中的 pre-authentication 概念:当 AS 模块收到 Client 的请求信息后;故意给 Client 返回一个错误的请求包,Client 会“领悟到”这是 KDC 的 AS 端需要进行提前认证;从而 Client 获取自己服务器的时间戳并用自己的密钥加密发送 KDC,KDC 解密后和自身所在服务器的时间进行对比,如果误差在能容忍的范围内;返回给 Client 正确的 TGT 响应包;过程如图 5 所示。
图 5 库中 keytab 有无 preauth 属性的区别
根据对问题 2 的分析,表 2 中第 5 和 7 行的值的比例应该近似为 1:2,为什么第 5 行的值只有 115,结果和理论差距如此之大?
KDC 的库中对客户端的 keytab 开启 PREAUTH 属性,客户端每认证一次,KDC 需要将该次认证的时间戳等信息写到本次磁盘的 BDB 数据库的 Log 中;而关闭 PREAUTH 属性后,每次认证只需要从库中读取数据,只要给 BDB 数据库分配的内存足够大,就可以最大程度的减少和本次磁盘的交互。KDC40 进程且开启 PRAUTH,其 AS 处理能力的 QPS 只有 115,分析机器性能的相关指标,发现瓶颈果然是单盘的 IO,如图 6 所示。使用 BDB 提供的工具,查看美团数据平台 KDC 服务的 BDB 缓存命中率为 99%,如图 7 所示:
图 6 无 RAID 多 KDC 进程服务器磁盘 IO
图 7 美团 KDC 缓存命中率
KDC AS 处理能力在多进程做 RAID 条件下,有无 preauth 属性,KDC 服务是否有瓶颈?如果有在哪里?
经多次实验,KDC 的 AS 处理能力受目前物理机 CPU 处理能力的限制,图 8 为有 PREAUTH 属性的 CPU 使用情况截图,无 PREAUTH 结果一致。
图 8 40 进程有 PREAUTH,AS 对 CPU 资源的使用情况
B. Client 和 TGS 交互过程的压测
表 3 为 TGS 压测的一组平均水平的测试数据:
表 3 TGS 压测
分析表 3 中的数据,可以发现 KDC 对 TGS 请求的处理能力和主机是否做 RAID 无关,结合 KDC 中 TGS 的请求原理,就较容易理解在 BDB 缓存命中率足够高的条件下,TGS 的请求不需要和本次磁盘交互;进一步做实验,也充分验证了这一点,机器的磁盘 IO 在整个测试过程中,没有大的变化,如图 9 所示,操作系统本身偶尔产生的 IO 完全构不成 KDC 的服务瓶颈。KDC 单进程多进程的对比,其处理瓶颈和 AS 一致,均受到 CPU 处理能力的限制(单进程打满某一 CPU,多进程几乎占用整台机器的 CPU 资源)。从 Kerberos 的设计原理分析,很容易理解,无论 KDC 库中的 keytab 是否带有 PREAUTH 属性,对 TGS 的处理逻辑几乎没有影响,压测的数据结果从实际角度验证了这一点。
图 9 TGS 压测,IO 资源的使用情况
C. 其他问题
Client 和 KDC 的交互,支持 TCP 和 UDP 两种协议。在网络环境良好的情况下,两种协议的 KDC 的测试结果理论上和实际中几乎一致。但是在原生代码中,使用 TCP 协议,在客户端给 KDC 造成一定压力持续 6s 左右,客户端开始认证出错,在远未达到超时时限的情况下,Client 出现了socket reset
类的错误。KDC 查看内核日志,发现大量possible SYN flooding on port 8089(KDC的服务端口). Sending cookies
,且通过netstat -s
发现机器的xxxx times the listen queue of a socket overflowed
异常增高,种种现象表明可能是服务端的半连接队列、全连接队列中的一个或者全部被打满。主要原理如图 10 所示:
图 10 半连接、全连接原理图
发现 KDC 服务所在服务器:半队列/proc/sys/net/ipv4/tcp_max_syn_backlog为2048
。
全队列:1)系统参数/proc/sys/net/core/somaxconn=65535
,查看代码listen()
函数的传入值为 5。
故而判断 TCP 的瓶颈在于全队列,因此目标为将listen
函数的第二个backlog
参数变成可控可传入。
KDC 可监控的设计和实现
开源社区对 Kerberos 实现的 KDC 完全没有对外暴露可监控的接口,最初线上的场景主要通过检索 Log 进行相关指标的监控,在统计服务 QPS、各种错误的监控等方面,存在准确准确监控难的尴尬局面。为了实现对 KDC 准确、较全面的监控,对 KDC 进行了二次开发,设计一个获取监控指标的接口。对监控的设计,主要从以下三个方面进行了考虑和设计。
A. 设计上的权衡
监控的设计无论在什么场景下,都应该尽可能的不去或者最小程度的影响线上的服务,本文最终采用建立一块共享内存的方式,记录各个 KDC 进程的打点信息,实现的架构如图 11 所示。每个 KDC 进程对应共享内存中的一块区域,通过 n 个数组来存储 KDC n 个进程的服务指标:当某个 KDC 进程处理一个请求后,该请求对监控指标的影响会直接打点更新到其对应的 Slot 数组中。更新的过程不受锁等待更新的影响,KDC 对监控打点的调用仅仅是内存块中的更新,对服务的影响几乎可以忽略不计。相比其他方式,在实现上也更加简单、易理解。
纪录每个 KDC 进程的服务情况,便于准确查看每个进程的对请求的处理情况,有助于定位问题多种情况下出现的异常,缩短故障的定位时间。例如:能够准确的反应出每个进程的请求分布是否均匀、请求处理出现异常能够定位到具体是某个进程出现异常还是整体均有异常。
图 11 KDC 监控设计的整体架构
B. 程序的可扩展性
任何指标的采集都是随着需求进行变更的,如果程序设计上不具有良好的扩展性,会后续的指标扩展带来很大的困扰。第一版 KDC 监控指标的采集只区分请求的成功与失败两种类型,美团数据平台 KDC 库中所有的 keytab 都具有 PREAUTH 属性。根据上文可知,去掉 PREAUTH 属性后,AS 请求的 QPS 能够提升一倍。后续随着服务规模的进一步增长,如果 AS 请求的处理能力逐步成为瓶颈,会考虑去掉 PREAUTH 属性。为了准确监控去掉 PREAUTH 属性这一过程是否有、有多少请求出现错误,需要扩展一个监控指标,因此有了 KDC 监控的第二版。整个过程只需要修改三个地方,完成两个功能的实现:
添加指标 ;
打点逻辑的添加。
整个修改过程简单明了,因此,该 KDC 监控程序的设计具有非常好的扩展性。图 12 为监控指标的罗列和注释:
图 12 KDC 监控指标及含义
C. 接口工具 kstat 的设计
获取 KDC 监控指标的接口工具主要分为两种:
获取当前每个 KDC 进程对各个指标的累积值,该功能是为了和新美大的监控平台 Falcon 结合,方便实现指标的上报实现累加值和分钟级别速率值的处理;
获取制定次数在制定时间间隔内每个进程监控指标的瞬时速率,最小统计间隔可达秒级,方便运维人员登陆机器无延迟的查看当前 KDC 的服务情况,使其在公司监控系统不可用的情况下分析服务的当前问题。具体使用见图 13 。
图 13 kstat 的使用帮助和两种功能使用样例
总结
通过本次对 KDC 服务的压测实验和分析,总结出 KDC 最优性能的调整方案为:
KDC 服务本身需要开启多进程和以充分利用多核机器的 CPU 资源,同时确保 BDB 的内存资源足够,保证其缓存命中率达到一定比例(越高越好,否则查询库会带来大量的磁盘读 IO);
选择的物理机要做 RAID,否则在库中 keytab 带有 PREAUTH 属性的条件下,会带来大量的写,容易导致磁盘成为 KDC 的性能瓶颈。通过建立一块共享内存无锁的实现了 KDC 多进程指标的收集,加上其良好的扩展性和数据的精确性,极大的提高了 KDC 服务的可靠性。
相比原来线上单进程的处理能力,目前单台服务器的处理性能提升 10+倍以上。本次工作没有详细的论述 TCP 协议中半队列、全队列的相关参数应该如何设定才能达到最优,和服务本身结合到一起,每个参数的变更带来的影响具体是啥?考虑到 TCP 本身的复杂性,我们将在未来的文章中详细讨论这个问题。
参考文档
作者简介
鹏飞,美团基础数据部数据平台大数据 SRE 组,离线计算组 SRE 负责人,2015 年 11 月加入美团。
评论