竹笋

首页 » 问答 » 环境 » 使用Kubernetes最容易犯的10个
TUhjnbcbe - 2024/4/11 18:07:00
白癜风早期能除根吗 https://disease.39.net/bjzkbdfyy/171008/5745578.html

在多年使用Kubernetes的过程中,我们接触了相当多的K8s集群,同样也犯了许多错误。本文就介绍了那些最容易也最常犯的10个错误,并讨论了要如何解决。

资源请求和限制

这绝对是犯错榜单的第一名。设置CPU请求有两种常见错误:不设置或者设置的很低。虽然这样可以在每个节点上容纳更多的Pod,但会导致节点的过度使用。在高需求时期,节点的CPU会被完全占用,工作负载获得的请求资源会受到CPU限制,从而导致应用程序延迟、超时等情况。

不设置CPU请求的配置:

resources:{}

CPU请求设置很低的配置:

resources:

另一方面,即使节点的CPU没有充分利用,如果设置了不必要的CPU限制同样会限制Pod,这也会导致延迟增加。

内存的过量使用一样会带来许多问题。达到CPU限制值会导致延迟,而达到内存限制值,Pod会被直接杀死,这就像是OOMkill,一个内存不足时会自动杀死进程的机制。如果不想发生这样的事情,就不要过度使用内存,而应该使用GuaranteedQoS,设置内存请求值等于限制值。

BurstableQoS下的资源设置:

resources:

GuaranteedQoS下的资源设置:

resources:

在设置资源时,我们可以使用metrics-server查看容器当前CPU和内存的使用情况。如果它已经在服务器端运行,可以运行以下命令:

kubectltoppods

通过显示的当前使用情况,我们就可以大致了解资源情况了。如果想要及时查看情况指标,例如峰值,昨天早晨的CPU使用情况等,我们可以使用Prometheus、DataDog等。它们会从metrics-server中获取指标并进行存储,然后我们就可以查询或绘制图形。另外,VerticalPodAutoscaler工具可以帮助我们自动化地查看CPU、内存的使用情况,并根据情况重新设置新的请求值和限制。

liveness和readiness探针的设置

默认情况下,系统不会设置liveness和readiness探针。K8s强大的自愈能力有时候可以让容器一直工作下去。但如果出现不可恢复的错误时,服务要如何重新启动?负载均衡器如何判断Pod是否可以开始处理流量,是否可以继续处理更多流量?

很多人不知道liveness和readiness探针之间的区别:

如果对Pod的liveness探测失败,会重新启动该Pod如果对Pod的readiness探测失败,会将Pod和Kubernetes断开连接(可以使用kubectlgetendpoints进行检查),并且在下次探测成功之前,都不会发送流量注意,这两种探针要在Pod整个生命周期中运行。

很多人只知道readiness的一个应用场景:readiness探针在容器启动时运行,以告知K8s服务Pod何时就绪,可以开始为流量提供服务。

它的另一个应用场景是告诉用户,在Pod的生命周期内,Pod有没有因为太“热”而无法处理过多的流量,需要减少工作“冷静”一下。直到readiness探测成功时,我们再继续给Pod发送更多流量。在这种情况下,readiness探测失败就会适得其反,因为我们不需要重新启动这个运行状况良好的Pod。

有时候,不配置任何探针会比配置错误探针要好。就像上面说的,如果将liveness探针配置成和readiness探针一样,那么会导致很多问题。一开始建议仅配置readiness探针,因为liveness探针很危险。

如果有一个和其他Pod有共享依赖项的Pod被关闭,我们就要保证这个Pod的任何一个探针都不能失败,否则将导致所有Pod的级联失效,这就像是搬起石头砸自己的脚。

HTTP服务的负载均衡器

我们可能在集群中有更多的HTTP服务,并对外开放。如果将Kubernetes服务设置为type:LoadBalancer,那么其控制器将提供并配置一个外部负载均衡器(不一定是L7负载均衡器,有可能是L4负载均衡器),并且这些资源(外部静态IPv4地址、计算硬件等)可能会变得很贵,因为创建了很多这样的服务。

在这种情况下,我们将外部访问方式设置为type:NodePort,并共享一个外部负载均衡器会更好。另外,还有一个其他不错的方法是部署一个类似Nginx-ingress-controller(或traefik)的东西作为暴露给外部负载均衡器的单个NodePortendpoint,并根据Kubernetesingressresource配置在集群中分配路由流量。

集群内的(微)服务可以通过ClusterIP服务和DNSServiceDiscovery进行通信。注意不要使用公共DNS/IP,这会影响延迟并增加云成本。

无K8s感知的集群自动伸缩

在集群中添加节点或从集群中删除节点时,不应只考虑节点CPU使用率等简单指标。在调度Pod时,我们需要根据许多调度约束(例如Pod和节点的亲和力、taints、tolerations、资源请求、QoS等)来决定。

假设有一个新的Pod要调度,但是所有可用的CPU都被占用,并且Pod处于Pending状态。外部自动伸缩器看到当前使用的CPU平均使用率非常高,就不会扩展(不会将Pod添加为节点),也就是说该Pod不会被调度。

扩展(从集群中删除节点)总是会更加困难。假设有一个有状态的Pod已连接持久卷,由于持久卷通常属于特定的可用性区域,并且不能在该区域中复制,因此自定义自动伸缩器删除带有该Pod的节点时,调度程序会无法将其调度到另一个节点上,因为它受到持久卷唯一可用性区域的限制,Pod会再次卡在Pending状态。

K8s社区广泛使用集群自动伸缩器,它运行在集群中,并与大多数主要公有云供应商的API集成,可以理解这些限制,并且在上述情况下进行扩展。另外,它还会确定是否可以在不影响设置的情况下正常扩展,以节省计算成本。

不使用IAM、RBAC的功能

不要将具有永久secret的IAMUser用于机器和应用程序,而应该使用角色(role)和服务帐号(serviceaccount)生成的临时secret。

我们经常看到这种情况:在应用程序配置中对访问权限和密钥进行硬编码,使用CloudIAM时就永远不轮换secret。我们应该在适当的地方使用IAMRoles和服务帐户代替IAMUser。

跳过kube2iam,直接使用服务帐户的IAMRoles。

apiVersion:v1

另外,在不是必要时,千万不要将admin和cluster-admin的权限给予服务帐户或实例配置文件。

Pod亲和性

运行某个部署的3个Pod副本时,如果该节点下线,所有副本都将会随之下线。我们不能指望Kubernetes调度器对Pod强加亲和性设置,而要自己明确定义它们。

//omittedforbrevity

这样可以确保将Pod调度在不同的节点上(仅在调度时间,而不是在执行时间进行检查,因此要设置requiredDuringSchedulingIgnoredDuringExecution)。

没有PodDisruptionBudget

在Kubernetes上运行生产工作负载时,节点和集群必须不时地升级或停用。PodDisruptionBudget(PDB)是一种API,为集群管理员和集群用户提供服务保证。

确保创建PDB以避免由于节点停用而造成不必要的服务中断。

apiVersion:policy/v1beta1

作为集群用户,我们就可以告诉集群管理员:“嘿,我这里有个zookeeper服务,无论要做什么,都至少要有2个副本始终可用。”

共享集群中太多租户或环境

Kubernetes命名空间不提供任何强隔离。大家通常认为,如果将非生产工作负载分离到一个命名空间,然后再将生产工作负载分离到另一个命名空间,那么二者就永远不会互相影响,这样就可以实现某种程度的公平分配,比如资源的请求和限制、配额、优先级等,并实现隔离(比如affinities、tolerations、taints或nodeselectors),进而“物理地”分离数据平面上的负载,但这种分离是相当复杂的。

如果需要在同一集群中同时使用两种类型的工作负载,我们就必须承担这种复杂性。如果不需要这样,并且再用一个集群成本更低时(例如在公有云中),那么最好将其放在另一个集群中以实现更高的隔离级别。

externalTrafficPolicy:Cluster

我们经常看到这种情况,所有流量都在集群内路由到NodePort服务上,该服务默认externalTrafficPolicy:Cluster,这意味着集群中的每个节点都打开了NodePort,这样可以使用任何一个与所需的服务(一组Pod)进行通信。

通常,NodePort服务为针对的Pod仅运行在那些节点的子集上。这意味着,如果与未运行Pod的节点通信,它会将流量转发到另一个节点,从而导致额外的网络跳转并增加延迟。

在Kubernetes服务上设置

externalTrafficPolicy:Local

后就不会在每个节点上打开NodePort,只会在实际运行Pod的节点上打开。如果使用外部负载均衡器对其终端节点进行检查,它会仅将流量发送到应该去往的那些节点,可以改善延迟并减少计算开销和出口成本。

把集群当宠物,控制平面压力大

大家有没有这样的经历:给服务器取一些奇怪的名字;给节点随机生成ID;亦或者因为一开始用Kubernetes做验证,所以给集群取名“testing”,结果到生产环境还在使用“testing”名字。

把集群当宠物可不是开玩笑的,我们需要不时地删除集群,演练灾难恢复,并管理控制平面。另一方面,过多地使用控制平面也不是一件好事。随着时间的流逝,控制平面变慢了,很可能就是我们创建了很多对象但没有轮换它们。

总结

不要指望一切都会自动解决,Kubernetes并不是万能的。即使在Kubernetes上,糟糕的应用一样是糟糕的应用,一不小心,就可能会导致很多复杂问题。希望本文总结的十个常见错误,可以对你带来帮助。

1
查看完整版本: 使用Kubernetes最容易犯的10个