系列目录
当你编排一个 pod 的时候, 你也可以可选地指定每个容器需要多少 CPU 和多少内存(RAM). 当容器请求特定的资源时, 调度器可以更好地根据资源请求来确定把 pod 调度到哪个节点上. 当容器请求限制特定资源时, 特定节点会以指定方式对容器的资源进行限制.
对于资源请求和资源限制的区别, 可以查看 QoS
资源类型
CPU 和 RAM 都是资源类型, 资源类型有一个基本单位. CPU 资源通过单位核数来指定, 内存通过单位字节来指定. 它们和 API 资源不同. API 资源, 例如 pod 和 service 是 kubernetes API server 可以读取和修改的对象.
pod 和容器的资源请求与资源限制
pod 里的每一个容器都可以指一个或多个以下内容:
- spec.containers[].resources.limits.CPU
- spec.containers[].resources.limits.memory
- spec.containers[].resources.requests.CPU
- spec.containers[].resources.requests.memory
尽管请求和限制只能通过单个的容器来指定, 但是我们通常说 pod 的资源请求和资源限制, 这样更方便. Pod 对于某一特定资源的请求 (或限制) 是 pod 里单个容器请求资源的和.
CPU 的意义
对 CPU 资源的请求和限制用单位 CPU 来衡量, 一个 CPU 在 kubernetes 里和以下任意一个是等价的:
- 1 AWS vCPU
- 1 GCP Core
- 1 Azure vCore
- 1 IBM vCPU
在祼机因特尔超线程处理器上一个超线程
带小数的请求也是允许的. 一个指定 spec.containers[].resources.requests.CPU 值为 0.5 的容器会被确保分配 1 个 CPU 的一半资源. 值 0.1 和 100m 是等价的, 可以读作 100 millicpu, 也有人读作 100 millicores, 实际上意义是一样的. 请求资源的值为 0.1 将会被 API 转换为 100m, 请求的精度比 1m 更小是不允许的, 因此 100m 一个较优的选择. CPU 资源的请求总是绝对量, 永远不会是一个相对值, 值是 0.1 时对于单核 CPU, 双核 CPU 或者甚至 48 核 CPU 都是一样的.
内存的意义
对内存资源的请求 / 限制用字节来衡量. 你可以使用一个纯整数或者定点整数带上 E, P, T, G, M, K 这些后缀, 你也可以使用 2 的幂次方的: Ei, Pi, Ti, Gi, Mi, Ki 来表示, 比如以下表示的量相等
128974848, 129e6, 129M, 123Mi
以下定义的 pod 包含两个容器, 每一个都请求 0.25cpu 和 64MiB (226 字节)的内存. 每一个都限制 0.5cpu 和 128MIB 内存. 你可以说 pod 请求 0.5cpu 和 128MiB 内存, 限制 1cpu 和 256MiB 内存
前面说过, pod 的资源请求 / 限制是 pod 里的容器资源请求 / 限制的和
- apiVersion: v1
- kind: Pod
- metadata:
- name: frontend
- spec:
- containers:
- - name: db
- image: MySQL
- env:
- - name: MYSQL_ROOT_PASSWORD
- value: "password"
- resources:
- requests:
- memory: "64Mi"
- CPU: "250m"
- limits:
- memory: "128Mi"
- CPU: "500m"
- - name: wp
- image: WordPress
- resources:
- requests:
- memory: "64Mi"
- CPU: "250m"
- limits:
- memory: "128Mi"
- CPU: "500m"
kubernetes 如何调度有资源请求的 pod
当你创建一个 pod,kubernetes 会选择一个节点来运行. 对于每种资源类型, 不论是 CPU 还是内存, 节点可以提供给 pod 的都是有上限的. 对于每种资源, 调度器都会确保被调度的 pod 的请求的总和小于节点的承载能力. 请注意即便节点上的 CPU 或者内存的使用率非常低, 但是如果容量检测失败, 调度器仍然可能拒绝把某一 pod 放到指定节点上. 这确保后来资源使用增多时不会出现资源短缺的情况.
有资源限制的 pod 如何运行
当 kubelete 启动了 pod 的容器, 它会把 CPU 和内存的限制传入到容器的 runtime 里
当使用的是 docker 时:
spec.containers[].resources.requests.CPU 的值被转换为核数值, 它也可能是小数, 乘 1024. 这个值和 2 两者较大的一个会被作为 --CPU-shares 标识的值传入 docker run 命令
spec.containers[].resources.limits.CPU 被转换为 millicore 值并且乘以 100. 结果值是容器每 100 毫秒可用的 CPU 时间的总和.
注意默认的时间段配额是 100 毫秒, 最小配额是 1 毫秒.
spec.containers[].resources.limits.memory
被转换为整数, 然后作为 --momory 标识的值传入 docker run 命令.
如果一个容器超过了它的资源限制, 则它可能会被终止. 如果它是可重启的(根据 pod 编排时重启策略), 则 kubelete 会重启它, 就像对待其它运行时错误一样.
一个容器可能会被允许或者不被允许超过超过它的 CPU 限制. 但是超过 CPU 资源限制时它不会被杀掉.
问题处理
pod 挂起, event 消息是: failedScheduling
如果调度器找不到任何可以放置 pod 的节点, pod 一直是未调度状态找到一个合适的节点. 调度器每当找不到合适节点来放置 pod, 就会产生一个事件, 信息如下
- kubectl describe pod frontend | grep -A 3 Events
- Events:
- FirstSeen LastSeen Count From Subobject PathReason Message
- 36s 5s 6 {scheduler } FailedScheduling Failed for reason PodExceedsFreeCPU and possibly others
上面的示例中, 由于节点的 CPU 资源不足, 名称为 frontend 的 pod 调度失败. 类似的错误提示也可能是由于内存资源不足(PodExceedsFreeMemory). 一般地, 如果类似这样的消息出现, 可以进行以下尝试:
集群中添加更多节点
终止一些非必须进程来为挂起的 pod 腾出资源
检测确保 pod 小于 node, 比如节点的容量是 CPU:1, 如果 pod 请求的是 CPU:1.1 将永远不会被调度.
你可以通过 kubectl describe nodes 命令来查看节点的容量以及可分配的量:
- kubectl describe nodes e2e-test-minion-group-4lw4
- Name: e2e-test-minion-group-4lw4
- [ ... lines removed for clarity ...]
- Capacity:
- CPU: 2
- memory: 7679792Ki
- pods: 110
- Allocatable:
- CPU: 1800m
- memory: 7474992Ki
- pods: 110
- [ ... lines removed for clarity ...]
- Non-terminated Pods: (5 in total)
- Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits
- --------- ---- ------------ ---------- --------------- -------------
- kube-system Fluentd-gcp-v1.38-28bv1 100m (5%) 0 (0%) 200Mi (2%) 200Mi (2%)
- kube-system kube-dns-3297075139-61lj3 260m (13%) 0 (0%) 100Mi (1%) 170Mi (2%)
- kube-system kube-proxy-e2e-test-... 100m (5%) 0 (0%) 0 (0%) 0 (0%)
- kube-system monitoring-influxdb-grafana-v4-z1m12 200m (10%) 200m (10%) 600Mi (8%) 600Mi (8%)
- kube-system node-problem-detector-v0.1-fj7m3 20m (1%) 200m (10%) 20Mi (0%) 100Mi (1%)
- Allocated resources:
- (Total limits may be over 100 percent, i.e., overcommitted.)
- CPU Requests CPU Limits Memory Requests Memory Limits
- ------------ ---------- --------------- -------------
- 680m (34%) 400m (20%) 920Mi (12%) 1070Mi (14%)
通过上面的输出, 你可以看到如果有一个 pod 请求或者 6.23Gi 内存, 则它不适合调度到此节点上.
通过查看 pods 部分, 你可以看到你可以看到哪些 pod 在本节点上占用了资源
可供 pod 使用的资源数量小于节点的实际容量, 这是因为系统守护进程按比例使用可用资源. allocatable 字段里的数量是 pod 实际可用的资源数量, 详情请查看可用资源
容器终止
pod 可能由于资源不足被终止, 你可以使用 kubectl describe pod 来检测 pod 是否由于资源达到上限被杀死
- kubectl describe pod simmemleak-hra99
- Name: simmemleak-hra99
- Namespace: default
- Image(s): saadali/simmemleak
- Node: kubernetes-node-tf0f/10.240.216.66
- Labels: name=simmemleak
- Status: Running
- Reason:
- Message:
- IP: 10.244.2.75
- Replication Controllers: simmemleak (1/1 replicas created)
- Containers:
- simmemleak:
- Image: saadali/simmemleak
- Limits:
- CPU: 100m
- memory: 50Mi
- State: Running
- Started: Tue, 07 Jul 2015 12:54:41 -0700
- Last Termination State: Terminated
- Exit Code: 1
- Started: Fri, 07 Jul 2015 12:54:30 -0700
- Finished: Fri, 07 Jul 2015 12:54:33 -0700
- Ready: False
- Restart Count: 5
- Conditions:
- Type Status
- Ready False
- Events:
- FirstSeen LastSeen Count From SubobjectPath Reason Message
- Tue, 07 Jul 2015 12:53:51 -0700 Tue, 07 Jul 2015 12:53:51 -0700 1 {scheduler } scheduled Successfully assigned simmemleak-hra99 to kubernetes-node-tf0f
- Tue, 07 Jul 2015 12:53:51 -0700 Tue, 07 Jul 2015 12:53:51 -0700 1 {kubelet kubernetes-node-tf0f} implicitly required container POD pulled Pod container image "k8s.gcr.io/pause:0.8.0" already present on machine
- Tue, 07 Jul 2015 12:53:51 -0700 Tue, 07 Jul 2015 12:53:51 -0700 1 {kubelet kubernetes-node-tf0f} implicitly required container POD created Created with docker id 6a41280f516d
- Tue, 07 Jul 2015 12:53:51 -0700 Tue, 07 Jul 2015 12:53:51 -0700 1 {kubelet kubernetes-node-tf0f} implicitly required container POD started Started with docker id 6a41280f516d
- Tue, 07 Jul 2015 12:53:51 -0700 Tue, 07 Jul 2015 12:53:51 -0700 1 {kubelet kubernetes-node-tf0f} spec.containers{simmemleak} created Created with docker id 87348f12526a
上面的例子中, Restart Count:5 表示 simmemleak 容器中止并且被重启了五次.
你可以通过 kubectl get pod 加上 - o go-template=... 选项来获取上次终止的容器的状态
- kubectl get pod -o go-template='{{range.status.containerStatuses}}{{"Container Name: "}}{{.name}}{{"\r\nLastState: "}}{{.lastState}}{{end}}' simmemleak-hra99
- Container Name: simmemleak
- LastState: map[terminated:map[exitCode:137 reason:OOM Killed startedAt:2015-07-07T20:58:43Z finishedAt:2015-07-07T20:58:43Z containerID:docker://0e4095bba1feccdfe7ef9fb6ebffe972b4b14285d5acdec6f0d3ae8a22fad8b2]]
你可以看到容器由于 reason:OOM Killed 错误被中止, OOM 表示 out of memory(内存溢出)
Local ephemeral storage(本地暂存容量)
此功能在 1.14 版本中为 beta 状态
Kubernetes 在 1.8 的版本中引入了一种类似于 CPU, 内存的新的资源模式: ephemeral-storage, 并且在 1.10 的版本在 kubelet 中默认打开了这个特性. ephemeral-storage 是为了管理和调度 Kubernetes 中运行的应用的短暂存储. 在每个 Kubernetes 的节点上, kubelet 的根目录 (默认是 / var/lib/kubelet) 和日志目录 (/var/log) 保存在节点的主分区上, 这个分区同时也会被 Pod 的 EmptyDir 类型的 volume, 容器日志, 镜像的层, 容器的可写层所占用. ephemeral-storage 便是对这块主分区进行管理, 通过应用定义的需求 (requests) 和约束 (limits) 来调度和管理节点上的应用对主分区的消耗.
在节点上的 kubelet 启动的时候, kubelet 会统计当前节点的主分区的可分配的磁盘资源, 或者你可以覆盖节点上 kubelet 的配置来自定义可分配的资源. 在创建 Pod 时会根据存储需求调度到满足存储的节点, 在 Pod 使用超过限制的存储时会对其做驱逐的处理来保证不会耗尽节点上的磁盘空间.
如果运行时指定了别的独立的分区, 比如修改了 docker 的镜像层和容器可写层的存储位置 (默认是 / var/lib/docker) 所在的分区, 将不再将其计入 ephemeral-storage 的消耗.
对 Local ephemeral storage 的请求 / 限制
所有的 container 可以指定以下一个或多个选项
- spec.containers[].resources.limits.ephemeral-storage
- spec.containers[].resources.requests.ephemeral-storage
对 ephemeral-storage 资源的请求 / 限制通过字节来衡量. 同上面内存的请求 / 限制方式一样, 这里不再细述.
比如以下 pod 包含两个容器, 每一个都对 Local ephemeral storage 的请求为 2GiB, 限制为 4GiB. 此时, 整个 pod 对资源的请求为 4GiB, 对资源的限制为 8GiB
- apiVersion: v1
- kind: Pod
- metadata:
- name: frontend
- spec:
- containers:
- - name: db
- image: MySQL
- env:
- - name: MYSQL_ROOT_PASSWORD
- value: "password"
- resources:
- requests:
- ephemeral-storage: "2Gi"
- limits:
- ephemeral-storage: "4Gi"
- - name: wp
- image: WordPress
- resources:
- requests:
- ephemeral-storage: "2Gi"
- limits:
- ephemeral-storage: "4Gi"
对有 ephemeral-storage 请求的 pod 如何调度
当你创建一个 pod,kubernetes 调度器选择一个节点来运行它. 每个节点都有可供 pod 使用的最大 Local ephemeral storage 最大量, 详细信息请查看 Node Allocatable
调度器保证被调度的容器资源总和小于节点的容量
带有 Local ephemeral storage 的 pod 如何运行
对于容器级别的隔离, 如果容器的可写层 ( writable layer) 和日志 (log) 超出了容量限制, 容器所在的 pod 将会被驱离; 对于 pod 级别的隔离, 如果 pod 里所有容器使用的总 Local ephemeral storage 和 pod 的 emptydir 存储卷超过限制, pod 将会被驱离.
示例
- apiVersion: v1
- kind: Pod
- metadata:
- name: teststorage
- labels:
- App: teststorage
- spec:
- containers:
- - name: busybox
- image: busybox
- command: ["bash", "-c", "while true; do dd if=/dev/zero of=$(date'+%s').out count=1 bs=10MB; sleep 1; done"] # 持续写入文件到容器的 rootfs 中
- resources:
- limits:
- ephemeral-storage: 100Mi #定义存储的限制为 100M
- requests:
- ephemeral-storage: 100Mi
测试这个 Pod 就能发现在容器写入超过存储限制时就会被驱逐掉了:
- /tmp kubectl apply -f pod.YAML
- pod "teststorage" created
- /tmp kubectl get pod -w
- NAME READY STATUS RESTARTS AGE
- teststorage 0/1 ContainerCreating 0 3s
- teststorage 1/1 Running 0 7s
- teststorage 0/1 Evicted 0 1m
查看 kubernetes 的 pod 的事件也能看到它是由于超过了限制的 ephemeral-storage 被驱逐掉:
- Events:
- Type Reason Age From Message
- ---- ------ ---- ---- -------
- Normal Scheduled 2m default-scheduler Successfully assigned teststorage to minikube
- Normal SuccessfulMountVolume 2m kubelet, minikube MountVolume.SetUp succeeded for volume "default-token-l7wp9"
- Normal Pulling 2m kubelet, minikube pulling image "busybox"
- Normal Pulled 2m kubelet, minikube Successfully pulled image "busybox"
- Normal Created 2m kubelet, minikube Created container
- Normal Started 2m kubelet, minikube Started container
- Warning Evicted 1m kubelet, minikube pod ephemeral local storage usage exceeds the total limit of containers {{104857600 0} {<nil>} 100Mi BinarySI}
- Normal Killing 1m kubelet, minikube Killing container with id docker://busybox:Need to kill Pod
来源: https://www.cnblogs.com/tylerzhou/p/11027599.html