本文介绍: 你好,我是俞凡,在Motorola做过研发,现在在Mavenir做技术工作,对通信网络、后端架构、云原生、DevOps、CICD、区块链、AI等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习。根据我的经验,”边缘情况”是”我们开发过程没有看到的东西”的同义词,这意味着在某些生产环境中会出现不稳定的容器。总之,在编写探针时牢记目标,并确保快速可靠的运行,在对kubelet响应中以尽可能(如果有的话)精确的方式提供清晰的信息。,我建议保持默认值(1秒)。

Kubernetes集群中维护容器状态更像是一种艺术,而不是科学。原文: The Art and Science of Probing a Kubernetes Container[1]

Kubernetes集群中维护容器状态更像是一种艺术,而不是科学。

本文将带你深入理解容器探测[2],并特别关注相对较新的启动探测。在此过程中,通过文中的推荐链接可以进一步了解相关领域,以实现文中的各种建议。

启动……不对……是在Kubernetes集群请求启动新容器相对简单: 只需要集群提供一个pod规范[3],尤其是封装了各种工作负载[4]资源(比如Deployment[5]Job[6])的pod模板[7]。在接收pod规范后,kubescheduler[8]将为pod分配一个节点然后节点kubelet[9]负责启动pod中的容器。

pod遵循明确的生命周期[10],其中就允许kubelet探测pod容器,以确保始终都有响应。探测器遵循如下契约: pod容器通告端点,kubelet从端点轮询其内部不同状态

简单来说,有三种类型的探针来表示容器的内部状态:

图1: 容器探测和pod生命周期之间的关系
图1: 容器探测和pod生命周期之间的关系

Readiness probes

如果容器未能响应readiness探测,kubelet将从服务负载均衡器中删除容器,将流量从容器中转移。在这种情况下,开发人员希望其他地方的副本可以处理流量

readiness探测的设计比较简单,只需要考虑依赖状态和容器中的资源使用情况:

要做:

不要做:


Liveness probes

如果容器不能连续响应此探测,kubelet终止容器。具体是”终止“还是”重启”,取决于pod的重启策略[11]

众所周知,对这些探针进行正确编码非常困难,因为探针开发人员的目标是预测意外情况,比如进程中的bug可能会使整个容器处于不可恢复状态这样的情况。

如果探测过于宽松,容器可能会处于无响应状态而不会被终止,从而减少了可以提供服务的pod副本数量。

如果探测过于严格,容器可能会一直被不必要的终止,这种情况在间歇性发生时很难察觉,当你排查问题时,pod可能看起来很健康

要做:

不要做:

  • readiness不是liveness: 不要检查依赖关系的可用性,如果失败,这是readiness探针的责任,终止pod不太可能有帮助。
  • 不要重用readiness条件: 经常看到容器探测使用相同的端点,但在
    failureThreshold
    periodSeconds使用不同的阈值liveness探测的关注点不同于readiness探测的关注点。容器可能由于外部因素而无法处理流量,而liveness探测使用readiness探测相同的端点,可能会告诉kubelet终止容器,从而加剧问题
  • 超时时间超过readiness探测: 无论是查看
    failureThreshold
    periodSeconds还是考虑容器中系统资源的可用性,都要确保liveness探测的超时范围超过readiness探测的超时范围。例如,最大超时时间(
    initialDelaySeconds + failureThreshold * periodSeconds)比readiness探测的最大超时时间更短会造成容器在仍然为远程请求服务的情况下被过早终止。
  • 不要过于保守: 在有疑问时,宁可宽大处理,也要为
    failureThreshold
    periodSeconds
    initialDelaySeconds设置更高的值,给容器足够的自由度来报告生存状态。一个不错的经验法则是使用比readiness探针的最大超时时间长两倍或更长的总超时时间。意外挂起进程是一种边缘情况,比响应缓慢的进程更罕见,liveness探测应该支持最常见的情况。
图2: liveness探测应该比readiness探测的时间更长。
图2: liveness探测应该比readiness探测的时间更长。

Startup probes

startup探针是容器探针中相对较新的成员,2020年底在Kubernetes 1.20[13]中达到了GA。注意,这要归功于我的同事,博学的Nathan Brophy[14],他指出这个功能Kubernetes 1.18尽管还处于测试阶段,但已经默认可用[15]了。

startup探测在容器生命周期中为需要大量时间才能准备就绪的容器创建了一个”缓冲区“。

在过去,在没有startup探测的情况下,开发人员使用初始化容器[16],并为readiness探测和liveness探测设置较长的initialDelaySeconds值,每一种都有自己的权衡:

考虑现有liveness探测和readiness探测是否需要相对较长的时间来启动,并将较大的initialDelaySeconds替换为等效的startup探测。

例如,下面的容器规范:

spec:
  containers:
  - name: myslowstarter
    image: ...
    ...
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 600
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 600
      periodSeconds: 20

可以通过将延迟挪到startup探测中来显著改善,如下例所示:

spec:
  containers:
  - name: myslowstarter
    image: ...
    ...
    readinessProbe:
      tcpSocket:
        port: 8080
      # i̵n̵i̵t̵i̵a̵l̵D̵e̵l̵a̵y̵S̵e̵c̵o̵n̵d̵s̵:̵ ̵6̵0̵0̵
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 8080
      # i̵n̵i̵t̵i̵a̵l̵D̵e̵l̵a̵y̵S̵e̵c̵o̵n̵d̵s̵:̵ ̵6̵0̵0̵
      periodSeconds: 20
    startupProbe:
      tcpSocket:
        port: 8080
      failureThreshold: 60
      periodSeconds: 10

在第一个示例中,kubelet在评估readiness和liveness之前等待600秒。相比之下,在第二个示例中,kubelet以10秒的间隔检查最多60次,从而使容器能够在满足启动条件时立即启动。

startup探测中频繁检查的隐藏好处是,允许开发人员failureThresholdperiodSeconds设置较高的值,而不用担心减慢容器启动速度。相反,死板的设置initialDelaySeconds开发人员施加了压力,忽略了边缘情况,只能通过设置较低的值才能让整个应用更快启动。根据我的经验,”边缘情况”是”我们开发过程中没有看到的东西”的同义词,这意味着在某些生产环境中会出现不稳定的容器。

根据经验,如果readiness探测和liveness探测中的initialDelaySeconds字段超过failureThreshold * periodSeconds字段指定的总时间,则使用startup探测。另一条经验法则是,readiness探测或liveness探测中initialDelaySeconds的时间如果超过60秒,就说明应用将受益于使用startup探测。

速度就是一切

在看到本文的建议后,你可能不可避免的会问下面的问题:

“那么我应该用什么来设置探针呢?”

我们通常希望readiness探针比较敏感,并且在容器开始难以响应请求时就报告失败。另一方面,希望liveness探针稍微宽松一点,只在代码失去对有效内部状态的控制时才报告失败。

对于timeoutSeconds,我建议保持默认值(1秒)。这个建议建立在另一个建议之上,即通过用于评估响应kubelet请求的线程之外的探针的响应。使用较高的值将扩大窗口,集群会将流量路由到无法处理请求的容器。

对于periodSecondsfailureThreshold组合,在相同间隔内进行更多检查往往比较少检查更准确。假设我们遵循了将容器状态与响应请求的线程分开评估的建议,那么更频繁的检查不会给容器增加显著的开销。

注意CPU限制

不同的集群,不同的速度。

探测(尤其是liveness探测)的一个常见问题是,假设集群总是按照要求为容器提供足够的CPU,另一个常见错误假设集群总是能够精确观察到部分请求。

从hypervisor和承载工作节点的VM开始,一直到pod规范中的CPU限制[17],容器有无数原因可以以不同速度运行同一段代码

以下是最容易让开发者措手不及的因素:

  • hypervisor中的cpu复用: IaaS供应商共享虚拟机,即使硬件网络速度相同,也会偶尔受到”邻居”的影响,从而导致CPU使用量激增。现代hypervisors非常擅长补偿这样的突发状况,甚至会采取节流措施。尽管如此,IaaS还是可能会超卖CPU,并假设不会同时出现流量突发状况。
  • 无穷小的CPU请求: 将CPU分配给容器的CPU限制设置为20ms似乎是一个负责任的、有意识的决定,因为容器很少进行任何处理。然而,在现实世界中,工作节点的vCPU并不只有完整vCPU大小的2%。工作节点试图通过在短时间内为容器提供整个vCPU来模拟这个微小的vCPU,从而导致对容器CPU分配产生碎片。因此,容器可能在短时间内运行比所请求的更多的CPU时间,然后暂停比预期更长的时间。

了解一些关于IaaS提供商的硬件特性和超卖设置的知识,可以帮助我们决定将安全乘数添加timeoutSecondsfailureThresholdperiodSeconds等设置中。在为探针,特别是liveness探针设置时,请记住两个因素。根据所了解的内容,还可以重新考虑CPU requests和limits的设置,以便探针有足够的处理能力及时响应请求。

图3: 由于严格的CPU限制而终止正常运行的容器
图3: 由于严格的CPU限制而终止正常运行的容器
结论

本文提供了一系列建议来提高容器探测的精度和性能,使容器能够更快启动并运行更长时间。

下一步是仔细分析容器中运行的内容,并研究在不同集群和条件下的实际运行时行为模拟依赖关系失败或者降低系统资源可用性。使用kubectl及其格式化过滤内容功能,是查找重新启动多次以及探测不充分的容器的好方法,在The art and science of probing a Kubernetes container, part 2: kubectl queries[18]这篇文章中有更多相关技术内容。将PromQL与Kubernetes指标结合使用可以用各种时间序列图表进一步扩展技术,这也是那篇文章主题

总之,在编写探针时牢记目标,并确保快速可靠的运行,在对kubelet的响应中以尽可能(如果有的话)精确的方式提供清晰的信息然后,信任集群会以最佳方式处理数据,确保容器对其客户端提供最大的可用性


你好,我是俞凡,在Motorola做过研发,现在在Mavenir做技术工作,对通信网络、后端架构、云原生、DevOps、CICD、区块链、AI等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习
微信公众号:DeepNoMind

参考资料

[1]

The Art and Science of Probing a Kubernetes Container: https://dnastacio.medium.com/the-art-and-science-of-probing-a-kubernetes-container-db1f16539080


[2]

Pod Probe: https://kubernetes.io/docs/reference/kubernetes-api/workloadresources/pod-v1/#Probe


[3]

Pod Specification: https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1


[4]

Workload Resource: https://kubernetes.io/docs/reference/kubernetes-api/workload-resources


[5]

Deployment: https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/deployment-v1


[6]

Job: https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/job-v1


[7]

Pod Template: https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-template-v1/#PodTemplateSpec


[8]

kube-scheduler: https://kubernetes.io/docs/reference/commandlinetoolsreference/kube-scheduler


[9]

kubelet: https://kubernetes.io/docs/reference/commandlinetoolsreference/kubelet


[10]

Pod Lifecycle: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle


[11]

Pod Restart Policy: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy


[12]

Kubernetes Components: https://kubernetes.io/docs/concepts/overview/components


[13]

Kubernetes 1.20: https://kubernetes.io/blog/2020/12/08/kubernetes-1-20-release-announcement


[14]

Nathan Brophy: https://www.linkedin.com/in/nathan-brophy-905a16171


[15]

Feature Gates: https://kubernetes.io/docs/reference/commandlinetoolsreference/feature-gates


[16]

Init Container: https://kubernetes.io/docs/concepts/workloads/pods/init-containers


[17]

Resource Management for Pods and Containers: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers


[18]

The art and science of probing a Kubernetes container, part 2: kubectl queries: https://sourcepatch.blogspot.com/2021/12/6-kubectl-queries-for-validating.html


– END –

本文 mdnice 平台发布

原文地址:https://blog.csdn.net/yuff100/article/details/134533176

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_8853.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注