简介
Kubernetes 是一套功能强大的工具,可用于管理自动可扩展、高可用性的分布式云原生应用程序,但很多人都会犯一些常见错误。
在本文中,我们将探讨使用 Kubernetes 时最常见的一些误区,并提供如何避免这些误区的提示。
不设置资源请求
这绝对是最值得关注的问题之一。CPU 请求通常要么未设置,要么设置得很低(这样我们就能在每个节点上安装大量 pod),从而导致节点超负荷运行。在需求量大的时候,节点的 CPU 会被充分利用,而我们的工作负载只能获得 “它所要求的”,因此 CPU 会被节流,导致应用程序延迟增加、超时等。
以下是一些示例情况,请不要轻易尝试:
BestEffort
resources: {}
CPU 性能极低
resources:
requests:
cpu: "1m"
另一方面,即使节点的 CPU 没有被充分利用,CPU 限制也会不必要地限制 pod 的运行,这同样会导致延迟增加。
关于 Linux 内核中的 CPU CFS 配额和基于 CPU 设置的 CPU 节流以及关闭 CFS 配额的问题,有一个公开的讨论, 即 CPU 限制可能导致更多问题,而不是解决问题。
因此,不要过度占用内存,并使用保证 QoS(服务质量)将内存请求设置为等于限制,就像下面的例子一样。
Burstable(更有可能经常被 OOMkilled)
resources:
requests:
memory: "128Mi"
cpu: "500m"
limits:
memory: "256Mi"
cpu: 2
Guaranteed
resources:
requests:
memory: "128Mi"
cpu: 2
limits:
memory: "128Mi"
cpu: 2
那么,在设置资源时,有什么可以帮助您呢?
您可以使用 metrics-server,查看 pod(以及其中的容器)当前的 CPU 和内存使用情况。您很可能已经在运行它了。只需运行以下命令即可:
kubectl top pods
kubectl top pods --containers
kubectl top nodes
不过,这些显示的只是当前的使用情况。虽然可以大致了解数字,但您最终还是希望及时看到这些使用指标(比如助于解决以下问题:在高峰期、昨天早上等时间段的 CPU 使用率是多少)。为此,您可以使用 Prometheus、DataDog 和其他许多工具。它们只需从指标服务器摄取指标并存储起来,然后就可以查询和绘制图表了。
VerticalPodAutoscaler 可以帮助您将这一手动过程自动化——及时查看 CPU/内存使用情况,并根据这些情况重新设置新的请求和限制。
省略健康检查
将服务部署到 Kubernetes 时,健康检查在维护服务方面发挥着重要作用。在 Kubernetes 环境中,健康检查的利用率非常低。通过健康检查,您可以密切关注 pod 及其容器的健康状况。
Kubernetes 有三种主要工具可用于健康检查:
- 配置存活检查(Liveness Check)允许 Kubernetes 检查应用程序是否存活。每个节点上运行的 Kubelet 代理都会使用存活探针来确保容器按预期运行。
- 就绪检查 (Readiness Checks) 在容器的整个生命周期内运行。Kubernetes 使用该探针了解容器何时准备好接受流量。
- 启动探针(Startup Probe)确定容器应用何时成功启动。如果启动检查失败,就会重新启动 pod。
使用 latest 标签
latest 标签普遍没有说明性,难以使用。关于在生产环境中使用 docker 镜像:latest 标签,Kubernetes 文档说得很清楚:
在生产环境中部署容器时,应避免使用 :latest 标签,因为它很难跟踪运行的是哪个版本的镜像,也很难回滚。
我觉得最近我们已经很少看到这种情况了,因为我们中的很多人都经历过这种情况很多次,所以我们不再使用 :latest
了,每个人都开始有了固定版本。
权限过高的容器
过度授权的容器是指被赋予过多权限的容器,如访问普通容器无法访问的资源。这是开发人员在使用 Kubernetes 时常犯的错误,而且会带来安全风险。
例如,在 Docker 容器内运行 Docker 守护进程就是特权容器的一个例子,它不一定安全。为了避免这种情况,建议避免为容器赋予 CAP_SYS_ADMIN 能力,因为它占所有内核漏洞的 25% 以上。
此外,避免赋予容器完全权限和赋予容器的主机文件系统权限也很重要。这意味着可以利用容器替换恶意二进制文件,从而入侵整个主机。
为防止容器权限过高,必须仔细配置权限设置,切勿以高于所需的权限运行进程。并且,使用监控和日志来检测和解决问题也很重要。
缺乏监控和日志记录
Kubernetes 环境中缺乏监控和日志记录会对其安全性和整体性能造成损害。日志记录和监控不足会给事件调查和响应工作带来挑战,从而难以有效地发现和解决问题。
一个常见的陷阱是,由于缺乏相关日志或指标,无法找到 Kubernetes 平台和应用程序中的故障点。
要解决这个问题,必须设置适当的监控和日志工具,如 Prometheus、Grafana、Fluentd 和 Jaeger,以收集、分析和可视化指标、日志和跟踪,深入了解 Kubernetes 环境的性能和健康状况。
通过实施强大的监控和日志记录实践,企业可以有效地关联信息,获得更深入的见解,并克服与 Kubernetes 环境的动态和复杂性相关的挑战。
有对象的默认命名空间
对 Kubernetes 中的所有对象使用默认命名空间会带来组织和管理方面的挑战。default 命名空间是默认创建服务和应用程序的地方,除非明确指定,否则它也是活动命名空间。
完全依赖默认命名空间会导致集群内的不同组件或团队缺乏隔离和组织。这会导致资源管理、访问控制和可见性方面的困难。为避免这种情况,建议为不同项目、团队或应用程序创建自定义命名空间,以便在 Kubernetes 集群内实现更好的组织、资源分配和访问控制。
通过利用多个命名空间,用户可以有效地划分和管理资源,提高 Kubernetes 环境的整体运行效率和安全性。
缺少安全配置
部署应用程序时,应始终牢记安全性。那么,在安全方面有哪些最重要的事项需要考虑呢?例如,使用集群外部可访问的端点、不保护机密、不考虑如何安全运行有权限的容器等。
Kubernetes 安全是任何 Kubernetes 部署不可或缺的一部分。安全挑战包括:
- 授权:身份验证和授权对于控制 Kubernetes 集群中的资源访问至关重要。
- 网络:Kubernetes 网络涉及管理覆盖网络和服务端点,以确保容器之间的流量在集群内安全路由。
- 存储:集群中存储的安全包括确保数据不会被未经授权的用户或进程访问,并确保数据安全。
Kubernetes API 服务器有一个 REST 接口,可访问存储的所有信息。这意味着,用户只需向 API 发送 HTTP 请求,即可访问 API 中存储的任何信息。为防止未经身份验证的用户访问这些数据,您需要使用用户名/密码或基于令牌的身份验证等支持的方法为 API 服务器配置身份验证。
这不仅关系到集群本身的安全,还关系到集群上的机密和配置的安全。为了保护集群免受漏洞威胁,您需要在集群上配置一套安全控制。
使用基于角色的访问控制(RBAC)来保护 Kubernetes 集群就是这样一种强大的安全控制:基于角色的访问控制可用于保护 Kubernetes 集群的安全,方法是根据分配给用户的角色限制对资源的访问。这些角色可以配置为 “管理员”或 “操作员”。
管理员角色拥有完整的访问权限,而操作员角色对集群内的资源拥有有限的权限。通过这种方式,我们可以控制和管理访问集群的任何人。
缺少 poddisruptionbudget
您在 kubernetes 上运行生产工作负载。您的节点和集群时常需要升级或退役。PodDisruptionBudget (pdb) 是集群管理员和集群用户之间的服务保证 API。
请务必创建 pdb,以避免因节点耗尽而造成不必要的服务中断。
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: db-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: database
这样, 作为集群用户,您可以告诉集群管理员,无论您要做什么都希望至少有两个副本始终可用。
pod 的自我防瘫痪功能
例如,当运行某个部署的 3 个 pod 副本时,节点宕机,所有副本也会随之宕机。为什么?
这是因为,您不能指望 Kubernetes 调度器为您的 pod 强制执行 anti-affinite。您必须明确定义它们。
......
labels:
app: db
......
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: "app"
operator: In
values:
- db
topologyKey: "kubernetes.io/hostname"
就是这样。这将确保 pod 被调度到不同的节点上(仅在调度时检查,而不是在执行时,因此 requiredDuringSchedulingIgnoredDuringExecution)
。
我们说的是不同节点名称上的 podAntiAffinity 而不是不同的可用性区域,即 topologyKey:"kubernetes.io/hostname
。如果您真的需要适当的 HA,请深入了解这一主题。
每个 HTTP 服务的负载均衡
您的集群中可能有更多的 HTTP 服务,而您希望将这些服务对外公开。
如果您将 kubernetes 服务作为 type:LoadBalancer
公开,其控制器(特定于供应商)将提供并调节外部 LoadBalancer,而这些资源可能会变得昂贵(外部静态 IPv4 地址、按秒计价......),因为您需要创建很多这样的资源。
在这种情况下,共享一个外部负载均衡可能更有意义,您可以将您的服务作为 type:NodePort
公开。或者,部署类似 nginx-ingress-controller(或 traefik 或 Istio)的东西,将其作为暴露给外部负载平衡器的单一 NodePort 端点,并根据 kubernetes ingress 资源在集群中路由流量。
集群内其他相互对话的(微)服务可通过 ClusterIP 服务和开箱即用的 DNS 服务发现进行对话。
注意:不要使用他们的公共 DNS/IP,因为这可能会影响他们的延迟和云成本。
未感知集群自动扩展
在集群中添加和移除节点时,不应考虑一些简单的指标,如这些节点的 CPU 利用率。在调度 pod 时,您需要根据大量调度约束条件(如 pod 和节点亲和性、污点和容忍度、资源请求、QoS 等)来做出决定。如果外部自动调度器不了解这些约束条件,可能会造成麻烦。
试想一下,有一个新的 pod 需要调度,但所有可用的 CPU 都被请求了,该 pod 被卡在待定状态。外部自动调节器会看到当前使用的 CPU 平均值(未请求),因此不会扩展(不会添加另一个节点)。pod 无法调度。
向内扩展(从集群中移除一个节点)总是比较困难。假设您有一个有状态的 pod(附加了持久卷),由于持久卷通常是属于特定可用性区域的资源,不会在区域内复制,因此您自定义的 autoscaler 会移除带有此 pod 的节点,而调度器无法将其调度到其他节点上,因为它受到唯一带有持久磁盘的可用性区域的限制。pod 再次卡在待处理状态。
总结
总之,Kubernetes 是管理容器化应用程序的强大工具,但它也有自己的一系列挑战。要避免常见错误和陷阱,必须密切关注与 Kubernetes 的交互,并了解它与已部署服务的交互方式之间的差异。
不要指望一切都能自动运行,要投入一些时间让您的应用程序成为云原生的。通过避免这些错误,您可以高效地进行 Kubernetes 部署,并提高 Kubernetes 环境的稳定性、性能和安全性。