前言

为了防止容器调度到资源不足的节点上,可以为容器指定资源最少要求量。

为了防止容器无节制的使用 CPU、内存 等资源,可以为容器指定资源最大允许使用量。

更新历史

资源请求和资源约束

可以为容器指定资源请求量和资源约束量。

资源一般指 CPU、内存。

资源请求量,指容器要求节点分配的最小容量,如果该节点可用容量小于容器要求的请求量,容器将被调度到其他合适节点。

资源约束量,指容器要求节点限制其使用的最大容量,如果容器内存使用量超过内容约束量,容器将被杀掉,如果指定了重启策略,容器杀掉后将被重启。

涉及的参数

约束量
spec.containers[].resources.limits.cpu
spec.containers[].resources.limits.memory
spec.containers[].resources.limits.hugepages-<size>

请求量
spec.containers[].resources.requests.cpu
spec.containers[].resources.requests.memory
spec.containers[].resources.requests.hugepages-<size>

示例

apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: app
    image: images.my-company.example/app:v4
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "password"
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"
  - name: log-aggregator
    image: images.my-company.example/log-aggregator:v6
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

资源单位

K8S 中的一个 cpu 等于云平台上的 1 个 vCPU/核或裸机 Intel 处理器上的 1 个超线程

spec.containers[].resources.requests.cpu 为 0.5 的容器肯定能够获得请求 1 CPU 的容器的一半 CPU 资源。表达式 0.1 等价于表达式 100m, 可以看作 “100 millicpu”。具有小数点(如 0.1)的请求由 API 转换为 100m;最大精度是 1m。

优先考虑使用 100m 的形式。

内存可以使用E、P、T、G、M、K。也可以使用对应的 2 的幂数:Ei、Pi、Ti、Gi、Mi、Ki。

Pod 的资源请求和约束

Pod 的请求量等于内部所有容器的请求量之和。

Pod 的约束量等于内部所有容器的约束量之和。

带资源请求的 Pod 如何调度

调度程序确保所调度的 Pod 的资源请求量小于节点的容量。

即,调度程序将查找到资源可用量充足的,可用量大于请求量的节点来放置 Pod。

带资源约束的 Pod 如何运行 

内存资源约束

如果容器超过其内存最大限制,则可能会被终止。如果容器可重新启动,kubelet 将重新启动容器。

如果一个容器内存使用量超过其内存请求值,那么当节点内存不足时,容器所处的 Pod 可能被逐出。

CPU 资源约束

每个容器可能被允许也可能不被允许使用超过其 CPU 约束的处理时间。 但是,容器不会由于 CPU 使用率过高而被杀死。

spec.containers[].resources.limits.cpu 先被转换为 millicore 值,再乘以 100。其结果就是每 100 毫秒内容器可以使用的 CPU 时间总量。在此期间(100ms),容器所使用的 CPU 时间不会超过它被分配的时间。

默认的配额(quota)周期为 100 毫秒。 CPU配额的最小精度为 1 毫秒。

如何获知集群资源使用情况,需要通过 metrics-server 来获取。

metrics-server

查看是否已安装 metrics-server,以下说明没安装

# kubectl top node
Error from server (NotFound): the server could not find the requested resource (get services http:heapster:)
# kubectl get pods,svc,deployments -n kube-system | grep metrics-server
# 

开始安装 metrics-server

git clone https://github.com/kubernetes-incubator/metrics-server
cd metrics-server/
git checkout release-0.3

修改 metrics-server/deploy/1.8+/metrics-server-deployment.yaml

      containers:
      - name: metrics-server
        #image: k8s.gcr.io/metrics-server/metrics-server:v0.3.6
        image: registry.aliyuncs.com/google_containers/metrics-server-amd64:v0.3.6
        imagePullPolicy: IfNotPresent
        args:
          - --cert-dir=/tmp
          - --secure-port=4443
          - --metric-resolution=30s
          - --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP
          - --kubelet-insecure-tls

参数说明

--metric-resolution=30s:从 kubelet 采集数据的周期;
--kubelet-preferred-address-types:优先使用 InternalIP 来访问 kubelet,这样可以避免节点名称没有 DNS 解析记录时,通过节点名称调用节点 kubelet API 失败的情况(未配置时默认的情况);
--kubelet-insecure-tls:kubelet 的10250端口使用的是https协议,连接需要验证tls证书。--kubelet-insecure-tls不验证客户端证书

安装

kubectl apply -f deploy/1.8+/

再次查看,需要多等一会儿,确保采集到数据。

[root@master01 ~]# kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes"
{"kind":"NodeMetricsList","apiVersion":"metrics.k8s.io/v1beta1","metadata":{"selfLink":"/apis/metrics.k8s.io/v1beta1/nodes"},"items":[]}

[root@master01 1.8+]# kubectl top nodes
NAME       CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%   
master01   163m         4%     2093Mi          56%       
master02   147m         3%     1638Mi          44%       
master03   151m         3%     1609Mi          43%       
work01     141m         3%     1084Mi          29%       
work02     150m         3%     1097Mi          29%       
work03     138m         3%     1471Mi          39%   

[root@master01 1.8+]# kubectl top pod
NAME                                      CPU(cores)   MEMORY(bytes)   
mysql-0                                   15m          202Mi           
mysql-1                                   14m          191Mi           
mysql-2                                   13m          182Mi           
nfs-client-provisioner-7db698bbc9-8ph55   2m           8Mi  

已经可以看到资源使用情况。

实践内存限额

创建一个测试用命名空间

# kubectl create namespace mem-example
namespace/mem-example created

创建一个最大限额 100M,但最少需要 250M 的容器

# cat memory-request-limit-2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: memory-demo-2
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-2-ctr
    image: polinux/stress
    resources:
      requests:
        memory: "50Mi"
      limits:
        memory: "100Mi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]
# kubectl apply -f memory-request-limit-2.yaml
pod/memory-demo-2 created

查看发现创建失败

# kubectl get pod -n mem-example
NAME            READY   STATUS             RESTARTS   AGE
memory-demo-2   0/1     CrashLoopBackOff   3          88s

查看原因

# kubectl get pod -n mem-example -o go-template='{{range.status.containerStatuses}}{{"Container Name: "}}{{.name}}{{"\r\nLastState: "}}{{.lastState}}{{end}}' memory-demo-2
Container Name: memory-demo-2-ctr
LastState: map[terminated:map[containerID:docker://aaf41e exitCode:1 reason:OOMKilled ]]

其中有 reason:OOMKilled,说明超过了内存最大限额,导致了 OOM 然后被杀掉。

结束语

通过 resources.requests 和 resources.limits,可以对资源进行最少容量要求和最大容量限制,确保容器可以运行在资源充足的节点上,同时也不会由于自身占用了过多资源而影响节点上其他容器。

联系我

微信公众号:zuolinux_com

微信扫码关注