K8S实战(十八)| 容器资源分配和资源限制

前言

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

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

更新历史

资源请求和资源约束

能够为容器指定资源请求量和资源约束量。mysql

资源通常指 CPU、内存。linux

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

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

涉及的参数sql

约束量
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>

示例docker

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 个超线程api

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

微信扫码关注