使用Kubernetes时最常见的10个错误

2020-05-17 23:19:39

在我们多年使用Kubernetes的经验中,我们有机会看到相当多的集群(无论是托管的还是非托管的-在GCP、AWS和Azure上),我们看到一些错误正在重复。这没什么丢人的,我们也做过大部分了!

我会试着展示我们经常看到的那些,并谈谈如何修复它们。

CPU请求通常要么没有设置,要么设置得非常低(以便我们可以在每个节点上容纳大量Pod),因此节点会超负荷使用。在需求高的时候,节点的CPU被充分利用,我们的工作负载只得到“它所请求的”,并且CPU受到限制,从而导致应用程序延迟、超时等增加。

另一方面,设置CPU限制可能会不必要地限制Pod,即使节点的CPU没有得到充分利用,这也会导致延迟增加。关于Linux内核中的CPU CFS配额和基于设置CPU限制和关闭CFS配额的CPU限制,有一个公开的讨论。CPU限制可能会导致比解决的问题更多的问题。请在下面的链接中查看更多信息。

内存过量会给您带来更多麻烦。达到CPU限制会导致节流,达到内存限制会杀死Pod。见过Oomkill吗?是的,那就是我们正在谈论的那个。想要将这种情况发生的频率降到最低吗?不要过度使用内存,并使用有保证的QoS(服务质量)将内存请求设置为等于Limit,如下例所示。阅读亨宁·雅各布斯(Zalando)演讲中有关该主题的更多信息。

您可以使用metrics-server查看Pod(以及其中的容器)的当前CPU和内存使用情况。很有可能,你已经在运行它了。只需运行以下命令:

但是,这些只显示当前的使用情况。对这些数字有一个大致的概念是很棒的,但是您最终还是想要及时看到这些使用情况指标(来回答这样的问题:CPU使用率最高时是多少,昨天早上等等)。为此,您可以使用普罗米修斯、DataDog和许多其他软件。它们只需从Metrics-Server获取指标并将其存储,然后您就可以对其进行查询和绘制图表。

VerticalPodAutoscaler可以帮助您自动完成这一手动过程-及时查看CPU/内存使用情况,并根据此重新设置新的请求和限制。

有效利用您的计算机并非易事。这就像一直在玩俄罗斯方块。如果您发现自己在平均利用率较低(比方说~10%)的情况下为计算支付了大量费用,您可能需要查看基于AWS Fargate或Virtual Kubelet的产品,这些产品更多地采用了对您来说可能更便宜的无服务器/按使用付费计费模式。

默认情况下,没有指定活跃度和就绪探测。有时候会一直这样,…。

但是,当出现不可恢复的错误时,您的服务如何重新启动呢?负载均衡器如何知道特定的Pod可以开始处理流量?还是要处理更多的交通问题?

就绪探测失败时会断开故障pod与kubernetes服务的连接(您可以在kubectl get端点中检查这一点),并且在探测再次成功之前不会向其发送更多流量。

人们通常认为就绪探测只在开始时运行,以告知Pod何时准备就绪,可以开始为流量提供服务。但这只是它的一个用例。

另一个是判断在吊舱的生命周期中,吊舱是否变得太热,处理太多的流量(或昂贵的计算),这样我们就不会让她做更多的工作,让她冷静下来,那么就绪探测就会成功,我们就会再次开始发送更多的流量。在这种情况下(当就绪探测失败时),活性探测也失败将非常适得其反。为什么要重新启动一个健康的、做大量工作的吊舱呢?

有时没有定义任何一个探测器比错误定义它们要好。如上所述,如果活跃度探测等于就绪探测,那么您的麻烦就大了。您可能希望从只定义就绪探测开始,因为活跃性探测是危险的。

如果您的任何共享依赖项关闭,请不要使任何一个探测失败,这将导致所有Pod的级联故障。你这是在搬起石头砸自己的脚。

您的集群中可能有更多想要向外界公开的http服务。

如果您将kubernetes服务公开为负载均衡器类型:负载均衡器,则其控制器(特定于供应商)将提供并协调外部负载均衡器(不一定是L7负载均衡器,更有可能是L4lb),并且这些资源可能会变得昂贵(外部静态IPv4地址、计算、每秒定价…)。因为你创造了很多这样的东西。

在这种情况下,共享一个外部负载均衡器可能更有意义,并且您可以将服务公开为类型:NodePort。或者更好的做法是,部署nginx-inress-Controller(或traefik)之类的东西作为向外部负载均衡器公开的单个NodePort端点,并基于Kubernetes入口资源在集群中路由流量。

相互通信的其他群集内(微)服务可以通过ClusterIP服务和开箱即用的DNS服务发现进行通信。注意不要使用他们的公共DNS/IP,因为这可能会影响他们的延迟和云成本。

在集群中添加和删除节点时,不应该考虑一些简单的指标,如这些节点的CPU利用率。调度Pod时,您需要根据大量的调度约束做出决定,例如Pod&;节点亲和度、污染和容忍度、资源请求、QoS等。拥有一个不理解这些约束的外部自动定标器可能会很麻烦。

假设有一个新的Pod需要调度,但是请求了所有可用的CPU,并且Pod处于挂起状态。外部自动定标器可以看到当前使用的平均CPU(未请求),并且不会向外扩展(不会添加其他节点)。吊舱将不会被安排。

扩展(从群集中删除节点)总是比较困难。假设您有一个有状态Pod(附加了永久卷),由于永久卷通常是属于特定可用区且不在该区域复制的资源,因此您的自定义自动伸缩器会删除其中包含此Pod的节点,并且计划程序无法将其调度到另一个节点上,因为它受唯一包含永久磁盘的可用区的限制。Pod再次处于挂起状态。

社区广泛使用群集自动伸缩器,它在您的群集中运行,并与大多数主要的公共云供应商API集成,理解所有这些限制,并在上述情况下进行横向扩展。它还将确定它是否可以优雅地扩展,而不影响我们设置的任何约束,并为您节省计算成本。

不要将IAM用户与计算机和应用程序的永久机密一起使用,而不是使用角色和服务帐户生成临时机密。

我们经常看到它-在应用配置中硬编码访问和密钥,当您手头有Cloud IAM时从不轮换密钥。在适当的情况下使用IAM角色和服务帐户,而不是用户。

跳过kube2iam,直接使用服务帐户的IAM角色,如Štěpán vRANä这篇博文中所述。

此外,当服务帐户或实例配置文件不需要admin和cluster-admin权限时,请不要授予它们ADMIN和CLUSTER-ADMIN权限。这有点困难,特别是在K8的RBAC中,但仍然值得努力。

例如,运行某个部署的3个Pod副本时,节点出现故障,所有副本也随之关闭。哈?。所有副本都在一个节点上运行吗?库伯内斯不是应该有魔力并提供HA吗?!

你不能指望Kubernetes调度程序对你的豆荚实施反亲和。您必须明确定义它们。

//省略简明标签:app:zk//省略简写关联:podAntiAffity:requiredDuringSchedulingIgnoredDuringExecution:-labelSelector:MatchExpressions:-Key:";app";Operator:in值:-ZK topologyKey:";kubernetes.io/hostname";

就是这样。这将确保将Pod调度到不同的节点(这仅在调度时进行检查,而不是在执行时进行检查,因此requiredDuringSchedulingIgnoredDuringExecution).

我们谈论的是不同节点名称(topologyKey:";kubernetes.io/hostname";)上的podAntiAffity,而不是不同的可用区。如果您确实需要适当的HA,请深入研究此主题。

您在Kubernetes上运行生产工作负载。您的节点&;群集必须不时升级或停用。PodDisruptionBudget(PDB)是一种用于集群管理员和集群用户之间的服务保证的API。

作为群集用户,您可以告诉群集管理员:“嘿,我这里有这个动物园管理员服务,不管您要做什么,我都希望至少有两个副本始终可用。”

人们似乎期望,如果他们将非生产工作负载分离到一个名称空间,并将生产工作负载分离到生产命名空间,那么一个工作负载永远不会影响另一个工作负载。可以实现一定程度的公平性(资源请求和限制、配额、优先级、类)和隔离(亲和度、容忍度、污点(或节点选择器)),以便在数据平面中“物理”分离工作负载,但这种分离相当复杂。

如果您需要将两种类型的工作负载放在同一群集中,则必须承担复杂性。如果您不需要它,并且拥有另一个集群对您来说相对便宜(如在公共云中),请将其放在不同的集群中,以实现更强的隔离级别。

经常看到这一点,所有流量在集群内都被路由到NodePort服务,该服务在默认情况下具有External alTrafficPolicy:cluster。这意味着在集群中的每个节点上都会打开NodePort,这样您就可以使用它们中的任何一个来与所需的服务(Pod集)通信。

NodePort服务所针对的实际Pod通常只在这些节点的子集上运行。这意味着,如果我与没有运行Pod的节点交谈,它会将流量转发到另一个节点,从而导致额外的网络跳跃和延迟增加(如果节点位于不同的AZ/数据中心,延迟可能会相当高,并且需要额外的出口成本)。

在Kubernetes服务上设置External alTrafficPolicy:local不会在每个节点上打开该NodePort,而只会在实际运行Pod的节点上打开该NodePort。如果您使用对其端点进行健康检查的外部负载均衡器(如AWS ELB所做的那样),它将开始仅将流量发送到其应该去往的节点,从而改善您的延迟、计算开销、出口账单和健全性。

有可能会将traefik或nginx-inress-Controller公开为NodePort(或LoadBalancer,它也使用NodePort)来处理入口http流量路由,此设置可以极大地减少此类请求的延迟。

您将服务器从称为Anton、HAL9000和Colossus转变为为您的节点生成随机ID,但是您开始使用名称来称呼您的集群吗?

您知道您是如何从Kubernetes的概念验证开始的,将集群命名为“测试”,并且仍然在生产中使用它,每个人都不敢碰它吗?(真实故事)。

宠物群集并不好玩,您可能需要不时考虑删除您的群集,练习灾难恢复,并照顾好您的控制层。害怕触摸控制面不是好兆头。etcd死了吗?嗯,你有大麻烦了。

另一方面,摸得太多也不好。随着时间的推移,控制平面变慢时,您可能会创建大量对象而从不旋转它们(在使用默认设置的HELM时非常常见,默认设置不会在configmap/secrets中旋转其状态,并且最终在控制平面中有数千个对象),或者您经常从Kube-API中抓取和编辑大量内容(用于自动缩放、CICD、监视、来自事件的日志、控制器等)。

此外,请检查您提供“SLA”/SLO和担保的托管Kubernetes。供应商可能会保证控制平面(或其子组件)的可用性,但不能保证您发送给它的请求的P99延迟。换句话说,您可能会在10分钟内获得kubectl获取节点并获得正确答案,但这仍然不违反服务保证。

这个是经典的。我觉得最近我不经常看到这种情况,因为我们中的很多人都被烧伤了太多次,我们停止使用:LATEST,开始固定版本。耶!

不要期望一切都能自动运转--库伯内斯不是灵丹妙药。一个糟糕的应用程序即使在Kubernetes上也会是糟糕的应用程序(实际上,甚至可能比糟糕的应用程序还要糟糕)。如果您不小心,最终可能会导致非常复杂、压力大且速度慢的控制平面,并且没有灾难恢复策略。不要期待开箱即用的多租户和高可用性。花点时间让你的应用程序成为本地云。