首页 体育 教育 财经 社会 娱乐 军事 国内 科技 互联网 房产 国际 女人 汽车 游戏

Kubernetes 用了,延迟高了 10 倍,问题在哪?

2019-12-18

没想到文章遭到这么多重视,也有不少朋友反映标题具有误导性。我能了解咱们的主意,所以在这儿弄清下本文的写作目的。当咱们团队将事务搬迁至 Kubernetes 之后,一旦呈现问题,总有人觉得“这是搬迁之后的阵痛”,并把锋芒指向 Kubernetes。我信任许多朋友必定听人说过标题里这句话,但终究事实证明犯错的并不是 Kubernetes。虽然文章并不触及关于 Kubernetes 的突破性启示,但我以为内容仍值得各位办理杂乱体系的朋友学习。

近期,我地点的团队将一项微服务搬迁到中心渠道。这套中心渠道绑缚有 CI/CD,依据 Kubernetes 的运转时以及其他功用。这项演习也将作为先头试点,用于辅导未来几个月内别的 150 多项微服务的进一步搬迁。而这全部,都是为了给西班牙的多个首要在线渠道供给支撑。

在将应用程序布置到 Kubernetes 并路由一部分生产流量过去后,状况开端发作改变。Kubernetes 布置中的恳求推迟要比 EC2 上的高出 10 倍。假如不找到解决办法,不光是后续微服务搬迁无法正常进行,整个项目都有遭到抛弃的危险。

为了查明瓶颈,咱们收集了整个恳求途径中的方针。这套架构十分简略,首先是一个 API 网关,担任将恳求署理至运转在 EC2 或许 Kubernetes 中的微服务实例。在 Kubernetes 中,咱们仅代表和 NGINX Ingress 控制器,后端则为运转有依据 Spring 的 JVM 应用程序的 Deployment 方针。

 仿制代码

EC2
+---------------+
|+---------+|
+------- BACKEND ||
|||||
||+---------+|
|+---------------+
+------+|
Public |||
------- ZUUL +--+
traffic |||Kubernetes
+------+|+-----------------------------+
||+-------+ +---------+|
||||xx|||
+------- NGINX +------ BACKEND ||
|||xx|||
|+-------+ +---------+|
+-----------------------------+

问题好像来自后端的上游推迟。将应用程序布置至 EC2 中之后,体系大约需求 20 毫秒就能做出呼应。但在 Kubernetes 中,整个进程却需求 100 到 200 毫秒。

咱们很快排除了随运转时刻改变而或许呈现的可疑方针。JVM 版别彻底相同,并且因为应用程序现已运转在 EC2 容器傍边,所以问题也不会源自容器化机制。别的,负载强度也是无辜的,因为即便每秒只宣布 1 项恳求,推迟相同居高不下。别的,GC 暂停时长简直可以忽略不计。

咱们的一位 Kubernetes 办理员问询这款应用程序是否具有外部依靠项,因为 DNS 解析之前就曾引起过相似的问题,这也是咱们现在找到的或许性最高的假定。

在每一次恳求时,咱们的应用程序都像域中的某个 AWS ElasticSearch 实例宣布 1 到 3 条查询。咱们在容器中增加了一个 shell,用于验证该域名的 DNS 解析时刻是否过长。

来自容器的 DNS 查询成果:

 仿制代码

[root@be-851c76f696-alf8z /]#whiletrue;dodig elastic.spain.adevinta.com | grep time; sleep2; done
;; Query time:22msec
;; Query time:22msec
;; Query time:29msec
;; Query time:21msec
;; Query time:28msec
;; Query time:43msec
;; Query time:39msec

来自运转这款应用程序的 EC2 实例的相同查询成果:

 仿制代码

bash-4.4#whiletrue;dodig elastic.spain.adevinta.com | greptime; sleep2; done
;; Querytime:77msec
;; Querytime:0msec
;; Querytime:0msec
;; Querytime:0msec
;; Querytime:0msec

前者的均匀解析时刻约为 30 毫秒,很明显,咱们的应用程序在其 ElasticSearch 上造成了额定的 DNS 解析推迟。

但这种状况十分古怪,原因有二:

为了承认 DNS 假定,咱们决议剥离 DNS 解析进程,并检查问题是否可以消失。咱们的榜首项测验是让应用程序直接与 ELasticSearch IP 通讯,然后绕过域名机制。这需求改变代码并进行新的布置,即需求在 /etc.hosts 中增加一行代码以将域名映射至其实践 IP:

 仿制代码

34.55.5.111elastic.spain.adevinta.com

经过这种方法,容器可以以近即时方法进行 IP 解析。咱们发现推迟的确有所改进,但间隔方针等待时刻依然相去甚远。虽然 DNS 解析时长有问题,但真实的原因还没有被找到。

咱们决议在容器中进行 tcpdump,以便精确调查网络的运转状况。

 仿制代码

[root@be-851c76f696-alf8z /]# tcpdump -leni any -w capture.pcap

咱们随后发送了多项恳求并下载了捕捉成果,然后运用 Wireshark 进行检查。

DNS 查询部分全部正常。可是,咱们的服务处理各项恳求的方法有些古怪。以下是捕捉成果的截图,显现出在呼应开端之前的恳求接纳状况。

数据包编号显现在榜首列傍边。为了清楚起见,我对不同的 TCP 流填充了不同的色彩。

从数据包 328 开端的绿色部分显现,客户端打开了容器间的 TCP 衔接。在开端的握手之后,数据包 331 将 HTTP GET /v1/…引向咱们的服务,整个进程耗时约 1 毫秒。

来自数据包 339 的灰色部分标明,咱们的服务向 ElasticSearch 实例发送了 HTTP 恳求,整个进程耗费了 18 毫秒。

到这儿,全部看起来还算正常,并且时刻也根本契合全体呼应推迟预期。

但在两次交流之间,蓝色部分占用了 86 毫秒。这到底是怎么回事?在数据包 333,咱们的服务向 /latest/meta-data/iam/security-credentials 发送了一项 HTTP GET 恳求,然后在同一 TCP 衔接上,又向 /latest/meta-data/iam/security-credentials/arn:…发送了另一项 GET 恳求。

咱们进行了验证,发现整个流程中的每项单一恳求都发作了这种状况。在容器内,DNS 解析的确有点慢。可是,导致高推迟的真实原因,在于针对每项独自恳求的 AWS Instance Metadata Service 查询。

两个端点都是 AWS Instance Metadata API 的组成部分。咱们的微服务会在从 ElasticSearch 中读取信息时运用该服务。这两条调用归于授权作业的根本流程,端点经过榜首项恳求发作与实例相关的 IAM 人物。

 仿制代码

/ # curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
arn:aws:iam:: account_id :role/some_role

第二条恳求则向第二个端点查询实例的暂时凭据:

 仿制代码

/ # curl http://169.254.169.254/latest/meta-data/iam/security-credentials/arn:aws:iam:: account_id :role/some_role`
 Code : Success ,
 LastUpdated : 2012-04-26T16:39:16Z ,
 Type : AWS-HMAC ,
 AccessKeyId : ASIAIOSFODNN7EXAMPLE ,
 SecretAccessKey : wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY ,
 Token : token ,
 Expiration : 2017-05-17T15:09:54Z 

客户可以在短时刻内运用这些凭据,且端点会定时检索新凭据。这套模型十分简略:出于安全原因,AWS 常常轮换暂时密钥,但客户端可以将密钥缓存几分钟,然后抵消检索新凭据所带来的功用丢失。

照理来说,整个进程应该由 AWS Java SDK 为咱们处理。但不知道为什么,实践状况并非如此。查找了一遍 GitHub 问题,咱们从 #1921 傍边找到了需求的头绪。

AWS SDK 会在满意以下两项条件之一时改写凭据:

咱们期望检查所获取凭据的实践到期时刻,因而咱们针对容器 API 运转了 cURL 指令——别离指向 EC2 实例与容器。但容器给出的呼应要短得多:正好 15 分钟。现在的问题很明显了:咱们的服务将为榜首项恳求获取暂时凭据。因为有用时刻仅为 15 分钟,因而鄙人一条恳求中,AWS SDK 会首先进行凭据改写,每一项恳求中都会发作相同的状况。

AWS Instance Metadata Service 在规划上首要代 EC2 实例运用,而不太合适 Kubernetes。可是,其为应用程序保存相同接口的机制的确很便利,所以咱们转而运用 KIAM,一款可以运转在各个 Kubernetes 节点上的东西,答应用户将 IAM 人物相关至 Pod 容器,或许说将后者视为 EC2 实例的平等方针。其作业原理是阻拦指向 AWS Instance Metadata Service 的调用,并运用自己的缓存及时接上。从应用程序的视点来看,整个流程与 EC2 运转环境没有差异。

KIAM 刚好为 Pod 供给了周期更短的暂时凭据,因而可以合理做出假定,Pod 的均匀存在周期应该短于 EC2 实例——默许值为 15 分钟。假如将两种默许值放在一同,就会引发问题。供给给应用程序的每一份证书都会在 15 分钟之后到期,但 AWS Java SDK 会对全部剩余时刻缺乏 15 分钟的凭据做出强制性改写。

成果便是,每项恳求都将被逼进行凭据改写,这使每项恳求的推迟提高。接下来,咱们又在 AWS Java SDK 中发现了一项功用恳求,其间也提到了相同的问题。

相比之下,修正作业十分简略。咱们对 KIAM 重新配置以延伸凭据的有用期。在应用了此项改变之后,咱们就可以在不触及 AWS Instance Metadata Service 的状况下开端处理恳求,一起回来比 EC2 更低的推迟水平。

依据咱们的实践搬迁经历,最常见的问题并非源自 Kubernetes 或许该渠道其他组件,与咱们正在搬迁的微服务自身也根本无关。 事实上,大多数问题源自咱们急于把某些组件粗犷地整合在一同。

咱们之前从来没有杂乱体系的整合经历,所以这一次咱们的处理方法比较粗糙,未能充沛考虑到更多活动部件、更大的毛病面以及更高熵值带来的实践影响。

在这种状况下,导致推迟升高的并不是 Kubernetes、KIAM、AWS Java SDK 或许微服务层面的过错决议计划。相反,问题源自 KIAM 与 AWS Java SDK 傍边两项看似彻底正常的默许值。独自来看,这两个默许值都很合理:AWS Java SDK 期望更频频地改写凭据,而 KIAM 设定了较低的默许过期时刻。但在二者结合之后,却发作了病态的成果。 是的,各个组件可以正常独立运转,并不代表它们就能顺畅协作并构成更巨大的体系。

https://srvaroa.github.io/kubernetes/migration/latency/dns/java/aws/microservices/2019/10/22/kubernetes-added-a-0-to-my-latency.html

热门文章

随机推荐

推荐文章