--- title: 为容器管理计算资源 content_template: templates/concept weight: 20 feature: title: 自动装箱 description: > 根据资源需求和其他约束自动放置容器,同时不会牺牲可用性,将任务关键工作负载和尽力服务工作负载进行混合放置,以提高资源利用率并节省更多资源。 --- {{% capture overview %}} 当您定义 [Pod](/docs/user-guide/pods) 的时候可以选择为每个容器指定需要的 CPU 和内存(RAM)大小。当为容器指定了资源请求后,调度器就能够更好的判断出将容器调度到哪个节点上。如果您还为容器指定了资源限制,Kubernetes 就可以按照指定的方式来处理节点上的资源竞争。关于资源请求和限制的不同点和更多资料请参考 [Resource QoS](https://git.k8s.io/community/contributors/design-proposals/resource-qos.md)。 {{% /capture %}} {{% capture body %}} ## 资源类型 *CPU* 和*内存*都是*资源类型*。资源类型具有基本单位。CPU 的单位是核心数,内存的单位是字节。 CPU和内存统称为*计算资源*,也可以称为*资源*。计算资源的数量是可以被请求、分配、消耗和可测量的。它们与 [API 资源](/docs/concepts/overview/kubernetes-api/) 不同。 API 资源(如 Pod 和 [Service](/docs/concepts/services-networking/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* 为单位。 Kubernetes 中的一个 cpu 等于: - 1 AWS vCPU - 1 GCP Core - 1 Azure vCore - 1 *Hyperthread* 在带有超线程的裸机 Intel 处理器上 允许浮点数请求。具有 `spec.containers[].resources.requests.cpu` 为 0.5 的容器保证了一半 CPU 要求 1 CPU的一半。表达式 `0.1` 等价于表达式 `100m`,可以看作 “100 millicpu”。有些人说成是“一百毫 cpu”,其实说的是同样的事情。具有小数点(如 `0.1`)的请求由 API 转换为`100m`,精度不超过 `1m`。因此,可能会优先选择 `100m` 的形式。 CPU 总是要用绝对数量,不可以使用相对数量;0.1 的 CPU 在单核、双核、48核的机器中的意义是一样的。 ## 内存的含义 内存的限制和请求以字节为单位。您可以使用以下后缀之一作为平均整数或定点整数表示内存:E,P,T,G,M,K。您还可以使用两个字母的等效的幂数:Ei,Pi,Ti ,Gi,Mi,Ki。例如,以下代表大致相同的值: ```shell 128974848, 129e6, 129M, 123Mi ``` 下面是个例子。 以下 Pod 有两个容器。每个容器的请求为 0.25 cpu 和 64MiB(226 字节)内存,每个容器的限制为 0.5 cpu 和 128MiB 内存。您可以说该 Pod 请求 0.5 cpu 和 128 MiB 的内存,限制为 1 cpu 和 256MiB 的内存。 ```yaml 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" ``` ## 具有资源请求的 Pod 如何调度 当您创建一个 Pod 时,Kubernetes 调度程序将为 Pod 选择一个节点。每个节点具有每种资源类型的最大容量:可为 Pod 提供的 CPU 和内存量。调度程序确保对于每种资源类型,调度的容器的资源请求的总和小于节点的容量。请注意,尽管节点上的实际内存或 CPU 资源使用量非常低,但如果容量检查失败,则调度程序仍然拒绝在该节点上放置 Pod。当资源使用量稍后增加时,例如在请求率的每日峰值期间,这可以防止节点上的资源短缺。 ## 具有资源限制的 Pod 如何运行 当 kubelet 启动一个 Pod 的容器时,它会将 CPU 和内存限制传递到容器运行时。 当使用 Docker 时: - `spec.containers[].resources.requests.cpu` 的值将转换成 millicore 值,这是个浮点数,并乘以 1024,这个数字中的较大者或 2 用作 `docker run` 命令中的[ `--cpu-shares`](https://docs.docker.com/engine/reference/run/#/cpu-share-constraint) 标志的值。 - `spec.containers[].resources.limits.cpu` 被转换成 millicore 值。被乘以 100000 然后 除以 1000。这个数字用作 `docker run` 命令中的 [`--cpu-quota`](https://docs.docker.com/engine/reference/run/#/cpu-quota-constraint) 标志的值。[`--cpu-quota` ] 标志被设置成了 100000,表示测量配额使用的默认100ms 周期。如果 [`--cpu-cfs-quota`] 标志设置为 true,则 kubelet 会强制执行 cpu 限制。从 Kubernetes 1.2 版本起,此标志默认为 true。 {{< note >}} 默认配额限制为 100 毫秒。 CPU配额的最小单位为 1 毫秒。 {{}} - `spec.containers[].resources.limits.memory` 被转换为整型,作为 `docker run` 命令中的 [`--memory`](https://docs.docker.com/engine/reference/run/#/user-memory-constraints) 标志的值。 如果容器超过其内存限制,则可能会被终止。如果可重新启动,则与所有其他类型的运行时故障一样,kubelet 将重新启动它。 如果一个容器超过其内存请求,那么当节点内存不足时,它的 Pod 可能被逐出。 容器可能被允许也可能不被允许超过其 CPU 限制时间。但是,由于 CPU 使用率过高,不会被杀死。 要确定容器是否由于资源限制而无法安排或被杀死,请参阅[疑难解答](#troubleshooting) 部分。 ## 监控计算资源使用 Pod 的资源使用情况被报告为 Pod 状态的一部分。 如果为集群配置了 [可选监控](http://releases.k8s.io/{{< param "githubbranch" >}}/cluster/addons/cluster-monitoring/README.md),则可以从监控系统检索 Pod 资源的使用情况。 ## 疑难解答 ### 我的 Pod 处于 pending 状态且事件信息显示 failedScheduling 如果调度器找不到任何该 Pod 可以匹配的节点,则该 Pod 将保持不可调度状态,直到找到一个可以被调度到的位置。每当调度器找不到 Pod 可以调度的地方时,会产生一个事件,如下所示: ```shell 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),类似的错误消息也可能会导致失败。一般来说,如果有这种类型的消息而处于 pending 状态,您可以尝试如下几件事情: - 向集群添加更多节点。 - 终止不需要的 Pod,为待处理的 Pod 腾出空间。 - 检查 Pod 所需的资源是否大于所有节点的资源。 例如,如果全部节点的容量为`cpu:1`,那么一个请求为 `cpu:1.1`的 Pod 永远不会被调度。 您可以使用 `kubectl describe nodes` 命令检查节点容量和分配的数量。 例如: ```shell kubectl describe nodes e2e-test-node-pool-4lw4 ``` ``` Name: e2e-test-node-pool-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 请求超过 1120m CPU 或者 6.23Gi 内存,节点将无法满足。 通过查看 `Pods` 部分,您将看到哪些 Pod 占用的节点上的资源。 Pod 可用的资源量小于节点容量,因为系统守护程序使用一部分可用资源。 [NodeStatus](/docs/resources-reference/{{< param "version" >}}/#nodestatus-v1-core) 的 `allocatable` 字段给出了可用于 Pod 的资源量。 有关更多信息,请参阅 [节点可分配资源](https://git.k8s.io/community/contributors/design-proposals/node-allocatable.md)。 可以将 [资源配额](/docs/concepts/policy/resource-quotas/) 功能配置为限制可以使用的资源总量。如果与 namespace 配合一起使用,就可以防止一个团队占用所有资源。 ## 我的容器被终止了 您的容器可能因为资源枯竭而被终止了。要查看容器是否因为遇到资源限制而被杀死,请在相关的 Pod 上调用 `kubectl describe pod`: ```shell 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` 意味着 Pod 中的 `simmemleak` 容器被终止并重启了五次。 您可以使用 `kubectl get pod` 命令加上 `-o go-template=...` 选项来获取之前终止容器的状态。 ```shell 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。 ## 本地临时存储 Kubernetes版本1.8引入了新资源_ephemeral-storage_,用于管理本地临时存储。 在每个Kubernetes节点中,kubelet的根目录(默认为 /var/lib/kubelet)和日志目录( /var/log )存储在节点的根分区上。 Pods还通过emptyDir卷,容器日志,镜像层和容器可写层共享和使用此分区。 该分区是“临时”分区,应用程序无法从该分区获得任何性能SLA(例如磁盘IOPS)。 本地临时存储管理仅适用于根分区。 图像层和可写层的可选分区超出范围。 {{< note >}} 如果使用可选的运行时分区,则根分区将不保存任何镜像层或可写层。 {{< /note >}} ### 本地临时存储的请求和限制设置 Pod 的每个容器可以指定以下一项或多项: * `spec.containers[].resources.limits.ephemeral-storage` * `spec.containers[].resources.requests.ephemeral-storage` 对“临时存储”的限制和请求以字节为单位。您可以使用以下后缀之一将存储表示为纯整数或小数形式:E,P,T,G,M,K。您还可以使用2的幂次方:Ei,Pi,Ti,Gi,Mi,Ki。例如,以下内容表示的值其实大致相同: ```shell 128974848, 129e6, 129M, 123Mi ``` 例如,以下Pod具有两个容器。每个容器都有一个2GiB的本地临时存储请求。每个容器的本地临时存储限制为4GiB。因此,该Pod要求本地临时存储空间为4GiB,存储空间限制为8GiB。 ```yaml 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" ``` ### 如何调度临时存储请求的 Pod 创建Pod时,Kubernetes调度程序会选择一个节点来运行Pod。每个节点都可以为Pod提供最大数量的本地临时存储。 有关更多信息,请参见[节点可分配](/docs/tasks/administer-cluster/reserve-compute-resources/#node-allocatable)。 调度程序会确保调度的容器的资源请求的总和小于节点的容量。 ### 具有临时存储限制的 Pod 如何运行 对于容器级隔离,如果容器的可写层和日志使用量超出其存储限制,则将驱逐Pod。对于 pod 级别的隔离,如果来自所有容器的本地临时存储使用量以及 Pod 的 emptyDir 卷的总和超过限制,则将驱逐Pod。 ### 监控临时存储消耗 使用本地临时存储时,kubelet 会持续对本地临时存储时进行监视。 通过定期扫描,来监视每个 emptyDir 卷,日志目录和可写层。 从Kubernetes 1.15开始,作为集群操作员的一个选项,可以通过[项目配额](http://xfs.org/docs/xfsdocs-xml-dev/XFS_User_Guide/tmp/en-US/html/xfs-quotas.html) 来管理 emptyDir 卷(但是不包括日志目录或可写层)。 项目配额最初是在XFS中实现的,最近又被移植到ext4fs中。 项目配额可用于监视和执行; 从Kubernetes 1.15开始,它们可用作Alpha功能仅用于监视。 配额比目录扫描更快,更准确。 将目录分配给项目时,在该目录下创建的所有文件都将在该项目中创建,内核仅需跟踪该项目中的文件正在使用多少块。 如果创建并删除了文件,但是文件描述符已打开,它将继续占用空间。 该空间将由配额跟踪,但目录扫描不会检查。 Kubernetes使用从1048576开始的项目ID。正在使用的ID注册于 `/etc/projects` 和 `/etc/projid`。 如果此范围内的项目ID用于系统上的其他目的,则这些项目ID必须在 `/etc/projects` 和 `/etc/projid` 中注册,以防止Kubernetes使用它们。 要启用项目配额,集群操作员必须执行以下操作: * 在kubelet配置中启用 `LocalStorageCapacityIsolationFSQuotaMonitoring = true` 功能。 在Kubernetes 1.15中默认为 false,因此必须显式设置为 true。 * 确保根分区(或可选的运行时分区)是在启用项目配额的情况下构建的。 所有 XFS 文件系统都支持项目配额,但是 ext4 文件系统必须专门构建。 * 确保在启用了项目配额的情况下挂载了根分区(或可选的运行时分区)。 #### 在启用项目配额的情况下构建和挂载文件系统 XFS文件系统在构建时不需要任何特殊操作; 它们是在启用项目配额的情况下自动构建的。 Ext4fs文件系统必须在启用了配额的情况下构建,然后必须在文件系统中启用它们: ``` % sudo mkfs.ext4 other_ext4fs_args... -E quotatype=prjquota /dev/block_device % sudo tune2fs -O project -Q prjquota /dev/block_device ``` 要挂载文件系统,ext4fs 和 XFS 都需要在 `/etc/fstab` 中设置 `prjquota` 选项: ``` /dev/block_device /var/kubernetes_data defaults,prjquota 0 0 ``` ## 拓展资源 拓展资源是 `kubernetes.io` 域名之外的标准资源名称。它们允许集群管理员做分发,而且用户可以使用非Kubernetes内置资源。 使用扩展资源需要两个步骤。 首先,集群管理员必须分发拓展资源。 其次,用户必须在 Pod 中请求拓展资源。 ```shell curl --header "Content-Type: application/json-patch+json" \ --request PATCH \ --data '[{"op": "add", "path": "/status/capacity/example.com~1foo", "value": "5"}]' \ http://k8s-master:8080/api/v1/nodes/k8s-node-1/status ``` ### 管理拓展资源 #### 节点级拓展资源 节点级拓展资源绑定到节点。 ##### 设备插件托管资源 有关如何在每个节点上分发设备插件托管资源的信息,请参阅[设备插件](/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/)。 ##### 其他资源 为了发布新的节点级拓展资源,集群操作员可以向API服务器提交 `PATCH` HTTP 请求, 以在 `status.capacity` 中为集群中的节点指定可用数量。 完成此操作后,节点的 `status.capacity` 将包含新资源。 由kubelet异步使用新资源自动更新 `status.allocatable` 字段。 请注意,由于调度程序在评估Pod适合性时使用节点的状态 `status.allocatable` 值, 因此在用新资源修补节点容量和请求在该节点上调度资源的第一个Pod之间可能会有短暂的延迟。 **示例:** 这是一个示例,显示了如何使用 `curl` 进行HTTP请求,该请求在主节点为 `k8s-master` 的子节点 `k8s-node-1` 上通告五个 `example.com/foo` 资源。 ```shell curl --header "Content-Type: application/json-patch+json" \ --request PATCH \ --data '[{"op": "add", "path": "/status/capacity/example.com~1foo", "value": "5"}]' \ http://k8s-master:8080/api/v1/nodes/k8s-node-1/status ``` {{< note >}} 在前面的请求中,`~1` 是 Patch 路径中字符 `/` 的编码。 JSON-Patch中的操作路径值被解释为JSON-Pointer。 有关更多详细信息,请参见 [IETF RFC 6901, section 3](https://tools.ietf.org/html/rfc6901#section-3). {{< /note >}} #### 集群级扩展资源 群集级扩展资源不绑定到节点。 它们通常由调度程序扩展程序管理,这些程序处理资源消耗和资源配额。 您可以在[调度程序策略配置](https://github.com/kubernetes/kubernetes/blob/release-1.10/pkg/scheduler/api/v1/types.go#L31)中指定由调度程序扩展程序处理的扩展资源。 **示例:** 通过调度程序策略的以下配置,指示群集级扩展资源 "example.com/foo" 由调度程序扩展程序处理。 - 仅当Pod请求 "example.com/foo" 时,调度程序才会将 Pod 发送到调度程序扩展程序。 - `ignoredByScheduler` 字段指定调度程序不在其 `PodFitsResources` 字段中检查 "example.com/foo" 资源。 ```json { "kind": "Policy", "apiVersion": "v1", "extenders": [ { "urlPrefix":"", "bindVerb": "bind", "managedResources": [ { "name": "example.com/foo", "ignoredByScheduler": true } ] } ] } ``` ### 消耗扩展资源 就像 CPU 和内存一样,用户可以使用 Pod 的扩展资源。 调度程序负责核算资源,因此不会同时将过多的可用资源分配给 Pod。 {{< note >}} 扩展资源取代了 Opaque 整数资源。 用户可以使用保留字 `kubernetes.io` 以外的任何域名前缀。 {{< /note >}} 要在Pod中使用扩展资源,请在容器规范的 `spec.containers[].resources.limits` 映射中包含资源名称作为键。 {{< note >}} 扩展资源不能过量使用,因此如果容器规范中存在请求和限制,则它们必须一致。 {{< /note >}} 仅当满足所有资源请求(包括 CPU ,内存和任何扩展资源)时,才能调度 Pod。 只要资源请求无法满足,则 Pod 保持在 `PENDING` 状态。 **示例:** 下面的 Pod 请求2个 CPU 和1个"example.com/foo"(扩展资源)。 ```yaml apiVersion: v1 kind: Pod metadata: name: my-pod spec: containers: - name: my-container image: myimage resources: requests: cpu: 2 example.com/foo: 1 limits: example.com/foo: 1 ``` ## 计划改进 在 kubernetes 1.5 版本中仅允许在容器上指定资源量。计划改进对所有容器在 Pod 中共享资源的计量, 如 [emptyDir volume](/docs/concepts/storage/volumes/#emptydir)。 在 kubernetes 1.5 版本中仅支持容器对 CPU 和内存的申请和限制。计划增加新的资源类型,包括节点磁盘空间资源和一个可支持自定义 [资源类型](https://github.com/kubernetes/community/blob/{{< param "githubbranch" >}}/contributors/design-proposals/resources.md) 的框架。 Kubernetes 通过支持通过多级别的 [服务质量](http://issue.k8s.io/168) 来支持资源的过度使用。 在 kubernetes 1.5 版本中,一个 CPU 单位在不同的云提供商和同一云提供商的不同机器类型中的意味都不同。例如,在 AWS 上,节点的容量报告为 [ECU](http://aws.amazon.com/ec2/faqs/),而在 GCE 中报告为逻辑内核。我们计划修改 cpu 资源的定义,以便在不同的提供商和平台之间保持一致。 {{% /capture %}} {{% capture whatsnext %}} * 获取将 [分配内存资源给容器和 Pod ](/docs/tasks/configure-pod-container/assign-memory-resource/) 的实践经验 * 获取将 [分配 CPU 资源给容器和 Pod ](/docs/tasks/configure-pod-container/assign-cpu-resource/) 的实践经验 * [容器](/docs/reference/generated/kubernetes-api/{{< param "version" >}}/#container-v1-core) * [资源需求](/docs/resources-reference/{{< param "version" >}}/#resourcerequirements-v1-core) {{% /capture %}}