在生产环境中,如何设置 Kubernetes 的 Limit 和 Request 对于优化应用程序和集群性能至关重要。

矛盾的是,分布式系统旨在解决应用程序之间的资源共享问题(例如 Kubernetes),但它们面临的挑战之一却是如何正确共享资源。

在以前,应用程序通常被设计为独立运行,并可以使用现有的所有资源。但随着企业应用程序数量的急剧增长和对系统稳定、弹性要求的提升,新格局下,应用程序之间需要共享相同的资源,这使得资源合理配置成了一个硬性要求。

命名空间配额

众所周知,Kubernetes 是允许管理员在命名空间中指定资源 Request 和 Limit 的,这一特性对于资源管理限制非常有用。但它目前还存在一定局限:如果管理员在命名空间中设置了 CPU  Request 配额,那么所有 Pod 也要在其定义中设置 CPU Request,否则就无法被调配资源

下面是一个例子:

apiVersion: v1
kind: ResourceQuota
metadata:
name: mem-cpu-example
spec:
hard:
requests.cpu: 2
requests.memory: 2Gi
limits.cpu: 3
limits.memory: 4Gi

如果我们把这个文件应用于命名空间,它会设置以下限制:

  • 所有 Pod 容器都必须声明对 CPU 和 RAM 的 Request 和 Limit;
  • 所有 CPU Requests 的总和不能超过 2 个内核;
  • 所有 CPU Limits 的总和不能超过 3 个内核;
  • 所有 RAM Requests 的总和不能超过 2 GiB;
  • 所有 RAM Limits 的总和不能超过 4 GiB。

假设我们已经为其他 Pods 分配了 1.9 个内核,开始响应新 Pod 提出的 200m CPU 分配请求,那么由于超过了最大 Request 限制,这个 Pod 会一直保持“Pending”状态,无法被调度。

什么是 Pod Request 和 Limit

下面是一个部署示例:

kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: redis
labels:
name: redis-deployment
app: example-voting-app
spec:
replicas: 1
selector:
matchLabels:
name: redis
role: redisdb
app: example-voting-app
template:
spec:
containers:
- name: redis
image: redis:5.0.3-alpine
resources:
limits:
memory: 600Mi
cpu: 1
requests:
memory: 300Mi
cpu: 500m
- name: busybox
image: busybox:1.28
resources:
limits:
memory: 200Mi
cpu: 300m
requests:
memory: 100Mi
cpu: 100m

假设我们正在运行一个具有 4 个内核和 16GB RAM 节点的集群:

  • Pod 的有效  Request  是 400MiB 的内存和 600 毫核(millicore)的 CPU。我们需要一个具有足够可用可分配空间的节点来调度 Pod;
  • Redis 容器共享的 CPU 资源份额为 500,busybox 容器的 CPU 份额为 100。由于 Kubernetes 会把每个内核分成 1000 个 shares,因此:
    • Redis:1000m*0.5cores≅500m
    • busybox:1000m*0.1cores≅100m
  • 如果 Redis 容器尝试分配超过 600MB 的 RAM,就会被 OOM-killer
  • 如果 Redis 容器尝试每 100ms 使用 100ms 以上的 CPU,那么 Redis 就会受到 CPU 限制(一共有 4 个内核,可用时间为 400ms/100ms),从而导致性能下降;
  • 如果 busybox 容器尝试分配 200MB 以上的 RAM,也会引起 OOM
  • 如果 busybox 容器尝试每 100ms 使用 30ms 以上的 CPU,也会使 CPU 受到限制,从而导致性能下降。

为了发现这些问题,我们应该监控:

节点中的 CPU 和 RAM 使用情况。如果节点内存已满,尽管所有容器都在它们的 Limits 之下,内存压力仍然会触发 OOM-killer。CPU 压力会限制进程并影响性能。

节点中的磁盘空间。如果节点耗尽磁盘,它会释放磁盘空间,而且很可能会驱逐 Pod。

每个容器使用的 CPU 配额百分比。需要注意的是,监控 Pod 的 CPU 使用可能是不够的,Kubernetes 限制的是每个容器,而不是每个 Pod。其他 CPU 指标,如共享 CPU 资源使用情况,只对分配有参考价值,所以如果遇到了性能上的问题,建议不要在这些指标上浪费时间。