如何在Kubernetes中为容器和Pod分配内存资源

为容器和Pod分配内存资源

Posted by Brian on Monday, September 18, 2023

环境要求

需要安装 metrics-server

helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/

在 values.yaml 中增加:如下配置:

helm show values metrics-server/metrics-server  > ~/values.yam
defaultArgs:
  - --cert-dir=/tmp
  - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
  - --kubelet-use-node-status-port
  - --metric-resolution=15s
  - --kubelet-insecure-tls 

--kubelet-insecure-tls 不验证Kubelets提供的服务证书的CA,否则会报“cannot validate certificate for 192.168.130.100 because it doesn’t contain any IP SANs”

helm upgrade --install metrics-server metrics-server/metrics-server

内存请求和限制的目的

Kubernetes 默认是不限制Pod使用使用该节点的内存。如果该节点发生内存不足的情况。如果没有设置内存限制的话那么是很有可能被杀死一些我们不想被杀死的服务的。内存限制可以有效的利用集群节点上可用的内存资源。同时在一些突发情况的时候能将内存限制在一个合理的期间。比如Redis 服务。如果没有进行限制突然的流量高峰进来。导致内存吃紧。那么Kubernenets 会杀死一些不使用的Pod来释放资源。这种行为我认为是不合理的。因为不确定系统要杀死那些服务,有可能会导致其它的服务不可用,一切变得不可预测。如果能将该行为限制在内存不足时只影响这一个服务的话我认为是合理的。毕竟影响一个跟影响一片这就不用说了。Kubernetes 默认遵循以下两种情况之一:

  • 容器可无限制地使用内存。容器可以使用其所在节点所有的可用内存, 进而可能导致该节点调用 OOM Killer。 此外,如果发生 OOM Kill,没有资源限制的容器将被杀掉的可行性更大。
  • 运行的容器所在命名空间有默认的内存限制,那么该容器会被自动分配默认限制。 集群管理员可用使用 LimitRange 来指定默认的内存限制。

内存单位

内存的基本单位是 byte。以下是可以使用的后缀E、P、T、G、M、K、Ei、Pi、Ti、Gi、Mi、Ki。

Example:

128974848, 129e6, 129M, 123Mi, 100Gi, 1T

创建命名空间

创建一个命名空间,以便将本练习中创建的资源与集群的其余部分隔离。

kubectl create namespace mem-example

指定内存请求和限制

要指定内存请求和限制,只需要在清单文件中增加 resources.requestsresources.limits即可。以下示例将创建一个请求 100MiB 内存,且内存限制在 200 MiB。清单文件(memory-request-limit.yaml)如下 :

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-ctr
    image: ghcr.io/colinianking/stress-ng
    resources:
      requests:
        memory: "100Mi"
      limits:
        memory: "200Mi"
    command: ["stress-ng"]	
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]

--vm-bytes 表示容器启动时尝试分配 150 MiB 内存。

创建Pod:

kubectl apply -f memory-request-limit.yaml

验证Pod

kubectl get pod memory-demo -n mem-example

# Output:
NAME          READY   STATUS    RESTARTS   AGE
memory-demo   1/1     Running   0          7s

查看Pod相关信息

kubectl get pod memory-demo --output=yaml -n mem-example

# Output:
...
    resources:
      limits:
        memory: 200Mi
      requests:
        memory: 100Mi
...

输出结果显示:该 Pod 中容器的内存请求为 100 MiB,内存限制为 200 MiB。

运行 kubectl top 命令,获取该 Pod 的指标数据:

kubectl top pod memory-demo -n mem-example
# Output:
NAME          CPU(cores)   MEMORY(bytes)
memory-demo   795m         152Mi

可以看到内存使用了 152 Mb,大于请求的 100 Mi 小于限制的 200 Mi。

删除Pod

kubectl delete -f memory-request-limit.yaml 

超过容器限制的内存

当节点拥有足够的可用内存时,容器可以使用其请求的内存。 但是,容器不允许使用超过其限制的内存。 如果容器分配的内存超过其限制,该容器会成为被终止的候选容器。 如果容器继续消耗超出其限制的内存,则终止容器。 如果终止的容器可以被重启,则 kubelet 会重新启动它,就像其他任何类型的运行时失败一样。

接下来创建一个Pod,尝试分配超出其内存限制。内存请求为 50 Mi ,限制为 100 Mi。尝试分配 250 Mi。

memory-request-limit1.yaml

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo-2
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-ctr
    image: ghcr.io/colinianking/stress-ng
    resources:
      requests:
        memory: "50Mi"
      limits:
        memory: "100Mi"
    command: ["stress-ng"]	
    args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]

在配置文件的 args 部分中,你可以看到容器会尝试分配 250 MiB 内存,这远高于 100 MiB 的限制。

创建Pod

kubectl create -f memory-request-limit1.yaml

查看Pod

kubectl get pod memory-demo-2 -n mem-example

#Output: NAME            READY   STATUS    RESTARTS   AGE
memory-demo-2   1/1     Running   0          37s

由于我是ARM的机器,不知道为什么 stress-ng 无法实现分配内存超过限制的内存。有机会在到AMD的机器上试试看。

超过整个节点的内存

内存请求和限制是与容器关联的,但将 Pod 视为具有内存请求和限制,也是很有用的。 Pod 的内存请求 = sum( Pod 中所有容器的内存请求)。 同理,Pod 的内存限制是 Pod 中所有容器的内存限制之和。

Pod 的调度基于请求。只有当节点拥有足够满足 Pod 内存请求的内存时,才会将 Pod 调度至节点上运行。

下面这个例子演示请求内存超过了集群内任何一个节点的所拥有的内存。

memory-request-limit2.yaml

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo-3
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-ctr
    image: ghcr.io/colinianking/stress-ng
    resources:
      requests:
        memory: "1000Gi"
      limits:
        memory: "1000Gi"
    command: ["stress-ng"]	
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]

创建Pod

kubectl create -f memory-request-limit2.yaml

查看Pod

kubectl get pod memory-demo-3 -n mem-example

#Output: 
NAME            READY   STATUS    RESTARTS   AGE
memory-demo-3   0/1     Pending   0          20s

输出结果显示:Pod 处于 PENDING 状态。 这意味着,该 Pod 没有被调度至任何节点上运行,并且它会无限期的保持该状态。

查看关于 Pod 的详细信息,包括事件:

kubectl describe pod memory-demo-3 -n mem-example

#Output:
......
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  72s   default-scheduler  0/3 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }, 2 Insufficient memory. preemption: 0/3 nodes are available: 1 Preemption is not helpful for scheduling, 2 No preemption victims found for incoming pod..

输出结果显示:由于节点内存不足,该容器无法被调度。

清理

删除命名空间。下面的命令会删除你根据这个任务创建的所有 Pod:

kubectl delete namespace mem-example