Kubernetes 给容器和Pod分配内存资源

这篇教程指导如何给容器分配申请的内存和内存限制。我们保证让容器获得足够的内存 资源,但是不允许它使用超过限制的资源。

Before you begin

You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. If you do not already have a cluster, you can create one by using Minikube.

你的集群里每个节点至少必须拥有300M的内存。

这个教程里有几个步骤要求Heapster , 但是如果你没有Heapster的话,也可以完成大部分的实验,就算跳过这些Heapster 步骤,也不会有什么问题。

检查看Heapster服务是否运行,执行命令:

kubectl get services --namespace=kube-system

如果Heapster服务正在运行,会有如下输出:

NAMESPACE    NAME      CLUSTER-IP    EXTERNAL-IP  PORT(S)  AGE
kube-system  heapster  10.11.240.9   <none>       80/TCP   6d

创建一个命名空间

创建命名空间,以便你在实验中创建的资源可以从集群的资源中隔离出来。

kubectl create namespace mem-example

配置内存申请和限制

给容器配置内存申请,只要在容器的配置文件里添加resources:requests就可以了。配置限制的话, 则是添加resources:limits。

本实验,我们创建包含一个容器的Pod,这个容器申请100M的内存,并且内存限制设置为200M,下面 是配置文件:

memory-request-limit.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: memory-demo
spec:
  containers:
  - name: memory-demo-ctr
    image: vish/stress
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"
    args:
    - -mem-total
    - 150Mi
    - -mem-alloc-size
    - 10Mi
    - -mem-alloc-sleep
    - 1s

在这个配置文件里,args代码段提供了容器所需的参数。-mem-total 150Mi告诉容器尝试申请150M 的内存。

创建Pod:

kubectl create -f https://k8s.io/docs/tasks/configure-pod-container/memory-request-limit.yaml --namespace=mem-example

验证Pod的容器是否正常运行:

kubectl get pod memory-demo --namespace=mem-example

查看Pod的详细信息:

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

这个输出显示了Pod里的容器申请了100M的内存和200M的内存限制。

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

启动proxy以便我们可以访问Heapster服务:

kubectl proxy

在另外一个命令行窗口,从Heapster服务获取内存使用情况:

curl http://localhost:8001/api/v1/proxy/namespaces/kube-system/services/heapster/api/v1/model/namespaces/mem-example/pods/memory-demo/metrics/memory/usage

这个输出显示了Pod正在使用162,900,000字节的内存,大概就是150M。这很明显超过了申请 的100M,但是还没达到200M的限制。

{
 "timestamp": "2017-06-20T18:54:00Z",
 "value": 162856960
}

删除Pod:

kubectl delete pod memory-demo --namespace=mem-example

超出容器的内存限制

只要节点有足够的内存资源,那容器就可以使用超过其申请的内存,但是不允许容器使用超过其限制的 资源。如果容器分配了超过限制的内存,这个容器将会被优先结束。如果容器持续使用超过限制的内存, 这个容器就会被终结。如果一个结束的容器允许重启,kubelet就会重启他,但是会出现其他类型的运行错误。

本实验,我们创建一个Pod尝试分配超过其限制的内存,下面的这个Pod的配置文档,它申请50M的内存, 内存限制设置为100M。

memory-request-limit-2.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: memory-demo-2
spec:
  containers:
  - name: memory-demo-2-ctr
    image: vish/stress
    resources:
      requests:
        memory: 50Mi
      limits:
        memory: "100Mi"
    args:
    - -mem-total
    - 250Mi
    - -mem-alloc-size
    - 10Mi
    - -mem-alloc-sleep
    - 1s

在配置文件里的args段里,可以看到容器尝试分配250M的内存,超过了限制的100M。

创建Pod:

kubectl create -f https://k8s.io/docs/tasks/configure-pod-container/memory-request-limit-2.yaml --namespace=mem-example

查看Pod的详细信息:

kubectl get pod memory-demo-2 --namespace=mem-example

这时候,容器可能会运行,也可能会被杀掉。如果容器还没被杀掉,重复之前的命令直至 你看到这个容器被杀掉:

NAME            READY     STATUS      RESTARTS   AGE
memory-demo-2   0/1       OOMKilled   1          24s

查看容器更详细的信息:

kubectl get pod memory-demo-2 --output=yaml --namespace=mem-example

这个输出显示了容器被杀掉因为超出了内存限制。

lastState:
   terminated:
     containerID: docker://65183c1877aaec2e8427bc95609cc52677a454b56fcb24340dbd22917c23b10f
     exitCode: 137
     finishedAt: 2017-06-20T20:52:19Z
     reason: OOMKilled
     startedAt: null

本实验里的容器可以自动重启,因此kubelet会再去启动它。输入多几次这个命令看看它是怎么 被杀掉又被启动的:

kubectl get pod memory-demo-2 --namespace=mem-example

这个输出显示了容器被杀掉,被启动,又被杀掉,又被启动的过程:

stevepe@sperry-1:~/steveperry-53.github.io$ kubectl get pod memory-demo-2 --namespace=mem-example
NAME            READY     STATUS      RESTARTS   AGE
memory-demo-2   0/1       OOMKilled   1          37s
stevepe@sperry-1:~/steveperry-53.github.io$ kubectl get pod memory-demo-2 --namespace=mem-example
NAME            READY     STATUS    RESTARTS   AGE
memory-demo-2   1/1       Running   2          40s

查看Pod的历史详细信息:

kubectl describe pod memory-demo-2 --namespace=mem-example

这个输出显示了Pod一直重复着被杀掉又被启动的过程:

... Normal  Created   Created container with id 66a3a20aa7980e61be4922780bf9d24d1a1d8b7395c09861225b0eba1b1f8511
... Warning BackOff   Back-off restarting failed container

查看集群里节点的详细信息:

kubectl describe nodes

输出里面记录了容器被杀掉是因为一个超出内存的状况出现:

Warning OOMKilling  Memory cgroup out of memory: Kill process 4481 (stress) score 1994 or sacrifice child

删除Pod:

kubectl delete pod memory-demo-2 --namespace=mem-example

配置超出节点能力范围的内存申请

内存的申请和限制是针对容器本身的,但是认为Pod也有容器的申请和限制是一个很有帮助的想法。 Pod申请的内存就是Pod里容器申请的内存总和,类似的,Pod的内存限制就是Pod里所有容器的 内存限制的总和。

Pod的调度策略是基于请求的,只有当节点满足Pod的内存申请时,才会将Pod调度到合适的节点上。

在这个实验里,我们创建一个申请超大内存的Pod,超过了集群里任何一个节点的可用内存资源。 这个容器申请了1000G的内存,这个应该会超过你集群里能提供的数量。

memory-request-limit-3.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: memory-demo-3
spec:
  containers:
  - name: memory-demo-3-ctr
    image: vish/stress
    resources:
      limits:
        memory: "1000Gi"
      requests:
        memory: "1000Gi"
    args:
    - -mem-total
    - 150Mi
    - -mem-alloc-size
    - 10Mi
    - -mem-alloc-sleep
    - 1s

创建Pod:

kubectl create -f https://k8s.io/docs/tasks/configure-pod-container/memory-request-limit-3.yaml --namespace=mem-example

查看Pod的状态:

kubectl get pod memory-demo-3 --namespace=mem-example

输出显示Pod的状态是Pending,因为Pod不会被调度到任何节点,所有它会一直保持在Pending状态下。

kubectl get pod memory-demo-3 --namespace=mem-example
NAME            READY     STATUS    RESTARTS   AGE
memory-demo-3   0/1       Pending   0          25s

查看Pod的详细信息包括事件记录

kubectl describe pod memory-demo-3 --namespace=mem-example

这个输出显示容器不会被调度因为节点上没有足够的内存:

Events:
  ...  Reason            Message
       ------            -------
  ...  FailedScheduling  No nodes are available that match all of the following predicates:: Insufficient memory (3).

内存单位

内存资源是以字节为单位的,可以表示为纯整数或者固定的十进制数字,后缀可以是E, P, T, G, M, K, Ei, Pi, Ti, Gi, Mi, Ki.比如,下面几种写法表示相同的数值:alue:

128974848, 129e6, 129M , 123Mi

删除Pod:

kubectl delete pod memory-demo-3 --namespace=mem-example

如果不配置内存限制

如果不给容器配置内存限制,那下面的任意一种情况可能会出现:

  • 容器使用内存资源没有上限,容器可以使用当前节点上所有可用的内存资源。
  • 容器所运行的命名空间有默认内存限制,容器会自动继承默认的限制。集群管理员可以使用这个文档 LimitRange来配置默认的内存限制。

内存申请和限制的原因

通过配置容器的内存申请和限制,你可以更加有效充分的使用集群里内存资源。配置较少的内存申请, 可以让Pod跟任意被调度。设置超过内存申请的限制,可以达到以下效果:

  • Pod可以在负载高峰时更加充分利用内存。
  • 可以将Pod的内存使用限制在比较合理的范围。

清理

删除命名空间,这会顺便删除命名空间里的Pod。

kubectl delete namespace mem-example

译者:NickSu86 原文链接

K8S中文社区微信公众号

Kubernetes 给容器和Pod分配CPU资源

这个教程指导如何给容器分配请求的CPU资源和配置CPU资源限制,我们保证容器可以拥有 所申请的CPU资源,但是并不允许它使用超过限制的CPU资源。

Before you begin

You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. If you do not already have a cluster, you can create one by using Minikube.

集群里的每个节点至少需要1个CPU。

这篇教程里的少数步骤可能要求你的集群运行着Heapster 如果你没有Heapster,也可以完成大部分步骤,就算跳过Heapster的那些步骤,也不见得会有什么问题。

判断Heapster服务是否正常运行,执行以下命令:

kubectl get services --namespace=kube-system

如果heapster正常运行,命令的输出应该类似下面这样:

NAMESPACE    NAME      CLUSTER-IP    EXTERNAL-IP  PORT(S)  AGE
kube-system  heapster  10.11.240.9   <none>       80/TCP   6d

创建一个命名空间

创建一个命名空间,可以确保你在这个实验里所创建的资源都会被有效隔离, 不会影响你的集群。

kubectl create namespace cpu-example

声明一个CPU申请和限制

给容器声明一个CPU请求,只要在容器的配置文件里包含这么一句resources:requests就可以, 声明一个CPU限制,则是这么一句resources:limits.

在这个实验里,我们会创建一个只有一个容器的Pod,这个容器申请0.5个CPU,并且CPU限制设置为1. 下面是配置文件:

cpu-request-limit.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: cpu-demo
spec:
  containers:
  - name: cpu-demo-ctr
    image: vish/stress
    resources:
      limits:
        cpu: "1"
      requests:
        cpu: "0.5"
    args:
    - -cpus
    - "2"

在这个配置文件里,整个args段提供了容器所需的参数。 -cpus "2"代码告诉容器尝试使用2个CPU资源。

创建Pod:

kubectl create -f https://k8s.io/docs/tasks/configure-pod-container/cpu-request-limit.yaml --namespace=cpu-example

验证Pod的容器是否正常运行:

kubectl get pod cpu-demo --namespace=cpu-example

查看Pod的详细信息:

kubectl get pod cpu-demo --output=yaml --namespace=cpu-example

输出显示了这个Pod里的容器申请了500m的cpu,同时CPU用量限制为1.

resources:
  limits:
    cpu: "1"
  requests:
    cpu: 500m

启用proxy以便访问heapster服务:

kubectl proxy

在另外一个命令窗口里,从heapster服务读取CPU使用率。

curl http://localhost:8001/api/v1/proxy/namespaces/kube-system/services/heapster/api/v1/model/namespaces/cpu-example/pods/cpu-demo/metrics/cpu/usage_rate

输出显示Pod目前使用974m的cpu,这个刚好比配置文件里限制的1小一点点。

{
 "timestamp": "2017-06-22T18:48:00Z",
 "value": 974
}

还记得我吗设置了-cpu "2", 这样让容器尝试去使用2个CPU,但是容器却只被运行使用1一个, 因为容器的CPU使用被限制了,因为容器尝试去使用超过其限制的CPU资源。

注意: 有另外一个可能的解释为什么CPU会被限制。因为这个节点可能没有足够的CPU资源,还记得我们 这个实验的前提条件是每个节点都有至少一个CPU,如果你的容器所运行的节点只有1个CPU,那容器就无法 使用超过1个的CPU资源,这跟CPU配置上的限制以及没关系了。

CPU 单位

CPU资源是以CPU单位来计算的,一个CPU,对于Kubernetes而言,相当于:

  • 1 AWS vCPU
  • 1 GCP Core
  • 1 Azure vCore
  • 1 Hyperthread on a bare-metal Intel processor with Hyperthreading

小数值也是允许的,一个容器申请0.5个CPU,就相当于其他容器申请1个CPU的一半,你也可以加个后缀m 表示千分之一的概念。比如说100m的CPU,100豪的CPU和0.1个CPU都是一样的。但是不支持精度超过1M的。

CPU通常都是以绝对值来申请的,绝对不能是一个相对的数值;0.1对于单核,双核,48核的CPU都是一样的。

删除Pod:

kubectl delete pod cpu-demo --namespace=cpu-example

请求的CPU超出了节点的能力范围

CPU资源的请求和限制是用于容器上面的,但是认为POD也有CPU资源的申请和限制,这种思想会很有帮助。 Pod的CPU申请可以看作Pod里的所有容器的CPU资源申请的总和,类似的,Pod的CPU限制就可以看出Pod里 所有容器的CPU资源限制的总和。

Pod调度是基于请求的,只有当Node的CPU资源可以满足Pod的需求的时候,Pod才会被调度到这个Node上面。

在这个实验当中,我们创建一个Pod请求超大的CPU资源,超过了集群里任何一个node所能提供的资源。 下面这个配置文件,创建一个包含一个容器的Pod。这个容器申请了100个CPU,这应该会超出你集群里 任何一个节点的CPU资源。

cpu-request-limit-2.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: cpu-demo-2
spec:
  containers:
  - name: cpu-demo-ctr-2
    image: vish/stress
    resources:
      limits:
        cpu: "100"
      requests:
        cpu: "100"
    args:
    - -cpus
    - "2"

创建Pod:

kubectl create -f https://k8s.io/docs/tasks/configure-pod-container/cpu-request-limit-2.yaml --namespace=cpu-example

查看Pod的状态:

kubectl get pod cpu-demo-2 --namespace=cpu-example

这个输出显示Pod正处在Pending状态,那是因为这个Pod并不会被调度到任何节点上,所以它会 一直保持这种状态。

kubectl get pod cpu-demo-2 --namespace=cpu-example
NAME         READY     STATUS    RESTARTS   AGE
cpu-demo-2   0/1       Pending   0          7m

查看Pod的详细信息,包括记录的事件:

kubectl describe pod cpu-demo-2 --namespace=cpu-example

这个输出显示了容器无法被调度因为节点上没有足够的CPU资源:

Events:
  Reason			Message
  ------			-------
  FailedScheduling	No nodes are available that match all of the following predicates:: Insufficient cpu (3).

删除Pod:

kubectl delete pod cpu-demo-2 --namespace=cpu-example

如果不指定CPU限额呢

如果你不指定容器的CPU限额,那下面所描述的其中一种情况会出现:

  • 容器使用CPU资源没有上限,它可以使用它运行的Node上所有的CPU资源。
  • 容器所运行的命名空间有默认的CPU限制,这个容器就自动继承了这个限制。集群管理可以使用 限额范围 来指定一个默认的CPU限额。

设置CPU申请和限制的动机

通过配置集群里的容器的CPU资源申请和限制,我们可以更好的利用集群中各个节点的CPU资源。 保持Pod的CPU请求不太高,这样才能更好的被调度。设置一个大于CPU请求的限制,可以获得以下 两点优势:

  • Pod 在业务高峰期能获取到足够的CPU资源。
  • 能将Pod在需求高峰期能使用的CPU资源限制在合理范围。

清理

删除你的命名空间:

kubectl delete namespace cpu-example

译者:NickSu86 原文链接

K8S中文社区微信公众号

Kubernetes 对计算、存储和网络资源进行监控的工具

理解应用在部署过程中如何运转,对提供可靠的服务和对应用进行扩展是至关重要的。在 Kubernetes 集群中,可以在多个层级对应用进行监测:容器级, pod 级service 级, 以及整个集群级。作为 Kubernetes 的一部分,我们从所有这些层面向用户提供他们运行的应用的资源详细使用信息。这样可以使用户对应用如何运行及何处可能发现瓶颈有更深刻的洞见。在Heapster项目中,提供了一个 Kubernetes 上的基础监控平台。

概述

Heapster 汇聚整个集群范围内的所有监控和事件数据。当前,Heapster 原生支持 Kubernetes 并且可以在所有 Kubernetes 安装中运行。 与普通 Kubernetes 应用类似, Heapster 在集群中以 pod 形式运行。Heapster pod 自动发现集群中所有节点并从节点上运行的 Kubernetes 代理进程 (Kubelet) 中查询资源使用信息。 Kubelet 则从 cAdvisor 获取数据。Heapster将收集的信息按照 pod 及其相关标签进行分组,然后将其推送到用于存储和可视化的后端中。当前支持的后端包括 InfluxDB (使用 Grafana 进行可视化),Google Cloud Monitoring ,和许多其它的后端(详情参见)。监控服务整体架构可参见下图:

监控服务整体架构图

接下来让我们详细了解一下其他组件。

cAdvisor

cAdvisor 是一款开源的容器资源使用和性能分析代理。它是专门为容器构建的并且原生支持 Docker 容器。在 Kubernetes 中,cAdvisor 被集成在 Kubelet 里。它自动发现本机运行的所有容器并且收集 CPU,内存,文件系统和网络资源的使用统计。它也可以通过分析本机运行的 ‘root’ 容器,来提供本机整体的资源使用统计。

在大多数 Kubernetes 集群中,cAdvisor 使用本机的4194端口对外提供了一个查看本机容器监控数据的简单UI。下面是 cAdvisor 展示整机资源使用状况的界面的部分快照:

cAdvisor

Kubelet

Kubelet 是连接 Kubernetes mater 和 nodes 的桥梁。它管理着本机运行的 pod 和 容器。Kubelet 从 cAdvisor 获取 pod 中每个容器所用资源的统计信息,然后将该 pod 中所有容器的统计信息进行汇聚,通过 REST API 开放给外部使用。

存储后端

InfluxDB 和 Grafana

Grafana 搭配 InfluxDB 的组合是开源世界十分流行的监控方案。InfluxDB 提供了非常易用的 API 来读写时间序列数据。在大多数 Kubernetes 集群中,Heapster 默认使用它作为存储后端。详细的安装指南请参见这里。InfluxDB 和 Grafana 以 Pod 形态运行并提供相应的 Kubernetes service 给 Heapster 使用。

Grafana 容器提供 WEB UI 服务,该 UI 提供了配置方便的仪表盘接口。默认的 Kubernetes 仪表盘包含了监控集群整体资源使用和集群内所有 pod 资源使用的样例仪表盘,您可以方便的基于它进行定制或扩展。请在这里查看 InfluxDB 的存储 schema。

下面的视频展示了如何使用 heapster, InfluxDB 和 Grafana 来监控 Kubernetes 集群:

如何使用 heapster, InfluxDB 和 Grafana 来监控 Kubernetes 集群

这是一个默认Kubernetes Grafana 仪表盘的快照,它展示了整个集群、单个 pod 和容器的 CPU 与内存使用情况:

默认Kubernetes Grafana 仪表盘快照

Google Cloud 监控

Google Cloud 监控是一项托管监控服务,它允许您基于应用的重要指标进行可视化和报警。可以配置 Heapster 将收集到的指标数据自动向 Google Cloud 监控推送。推送的指标信息将出现在 云监控控制台 。该存储后端是最易于安装和维护的。在监控控制台上您可以轻松的使用导出的数据来创建和定制仪表盘。

下面的视频展示了如何安装和运行基于 Heapster 的 Google Cloud 监控:

如何安装和运行基于 Heapster 的 Google Cloud 监控

下面是一个Google Cloud 监控仪表盘,它展示了整个集群的资源使用情况。

Google Cloud 监控仪表盘

动手试试吧

现在您已经对 Heapster 有了一定的了解,大胆在您的集群中尝试一下吧!在 GitHub 上有Heapster 仓库。其中有安装 Heapster 和对应存储后端的详细指导。Heapster 在大多数 Kubernetes 集群上是默认运行的,所以您的集群里可能已经有了哦!随时欢迎您的反馈,如果在使用过程中遇到任何麻烦,请通过故障处理通道 知会我们。


作者: Vishnu Kannan 与 Victor Marmol, 谷歌软件工程师. 该文最早发布在 Kubernetes 博客.

译者:xuyang02965 原文链接

K8S中文社区微信公众号

Kubernetes 为系统守护进程预留计算资源

Kubernetes 的节点可以按照 Capacity 调度。默认情况下 pod 能够使用节点全部可用容量。这是个问题,因为节点自己通常运行了不少驱动 OS 和 Kubernetes 的系统守护进程(system daemons)。除非为这些系统守护进程留出资源,否则它们将与 pod 争夺资源并导致节点资源短缺问题。

kubelet 公开了一个名为 Node Allocatable 的特性,有助于为系统守护进程预留计算资源。Kubernetes 推荐集群管理员按照每个节点上的工作负载密度配置 Node Allocatable。

Node Allocatable

      Node Capacity
---------------------------
|     kube-reserved       |
|-------------------------|
|     system-reserved     |
|-------------------------|
|    eviction-threshold   |
|-------------------------|
|                         |
|      allocatable        |
|   (available for pods)  |
|                         |
|                         |
---------------------------

Kubernetes 节点上的 Allocatable 被定义为 pod 可用计算资源量。调度器不会超额申请 Allocatable。目前支持 CPU, memory 和 storage 这几个参数。

Node Allocatable 暴露为 API 中 v1.Node 对象的一部分,也是 CLI 中 kubectl describe node 的一部分。

在 kubelet 中,可以为两类系统守护进程预留资源。

启用 QoS 和 Pod 级别的 cgroups

为了恰当的在节点范围实施 node allocatable,您必须通过 --cgroups-per-qos 标志启用新的 cgroup 层次结构。这个标志是默认启用的。启用后,kubelet 将在其管理的 cgroup 层次结构中创建所有终端用户的 pod。

配置 cgroup 驱动

kubelet 支持在主机上使用 cgroup 驱动操作 cgroup 层次结构。驱动通过 --cgroup-driver 标志配置。

支持的参数值如下:

  • cgroupfs 是默认的驱动,在主机上直接操作 cgroup 文件系统以对 cgroup 沙箱进行管理。
  • systemd 是可选的驱动,使用 init 系统支持的资源的瞬时切片管理 cgroup 沙箱。

取决于相关容器运行时(container runtime)的配置,操作员可能需要选择一个特定的 cgroup 驱动来保证系统正常运行。例如如果操作员使用 docker 运行时提供的 cgroup 驱动时,必须配置 kubelet 使用 systemd cgroup 驱动。

Kube Reserved

  • Kubelet Flag: --kube-reserved=[cpu=100m][,][memory=100Mi][,][storage=1Gi]
  • Kubelet Flag: --kube-reserved-cgroup=

kube-reserved 是为了给诸如 kubelet、container runtime、node problem detector 等 kubernetes 系统守护进程争取资源预留。这并不代表要给以 pod 形式运行的系统守护进程保留资源。kube-reserved 通常是节点上的一个 pod 密度(pod density) 功能。 这个性能仪表盘 从 pod 密度的多个层面展示了 kubelet 和 docker engine 的 cpu 和 memory使用情况。

要选择性的在系统守护进程上执行 kube-reserved,需要把 kubelet 的 --kube-reserved-cgroup 标志的值设置为 kube 守护进程的父控制组。

推荐将 kubernetes 系统守护进程放置于顶级控制组之下(例如 systemd 机器上的 runtime.slice)。理想情况下每个系统守护进程都应该在其自己的子控制组中运行。请参考这篇文档,获取更过关于推荐控制组层次结构的细节。

请注意,如果 --kube-reserved-cgroup 不存在,Kubelet 将不会创建它。如果指定了一个无效的 cgroup,Kubelet 将会失败。

系统预留值(System Reserved)

  • Kubelet Flag: --system-reserved=[cpu=100mi][,][memory=100Mi][,][storage=1Gi]
  • Kubelet Flag: --system-reserved-cgroup=

system-reserved 用于为诸如 sshd、udev 等系统守护进程争取资源预留。system-reserved 也应该为 kernel 预留 内存,因为目前 kernel 使用的内存并不记在 Kubernetes 的 pod 上。同时还推荐为用户登录会话预留资源(systemd 体系中的 user.slice)。

要想在系统守护进程上可选地执行 system-reserved,请指定 --system-reserved-cgroup kubelet 标志的值为 OS 系统守护进程的父级控制组。

推荐将 OS 系统守护进程放在一个顶级控制组之下(例如 systemd 机器上的system.slice)。

请注意,如果 --system-reserved-cgroup 不存在,Kubelet 不会创建它。如果指定了无效的 cgroup,Kubelet 将会失败。

驱逐阈值(Eviction Thresholds)

  • Kubelet Flag: --eviction-hard=[memory.available<500Mi]

节点级别的内存压力将导致系统内存不足(System OOMs),这将影响到整个节点及其上运行的所有 pod。节点可以暂时离线直到内存已经回收为止。为了防止(或减少可能性)系统内存不足,kubelet 提供了 资源不足(Out of Resource) 管理。驱逐(Eviction)操作只支持 memory 和 storage。通过 --eviction-hard 标志预留一些内存后,当节点上的可用内存降至保留值以下时,kubelet 将尝试 驱逐 pod。假设,如果节点上不存在系统守护进程,pod 将不能使用超过 capacity-eviction-hard 的资源。因此,为驱逐而预留的资源对 pod 是不可用的。

执行节点 Allocatable

  • Kubelet Flag: --enforce-node-allocatable=pods[,][system-reserved][,][kube-reserved]

调度器将 Allocatable 按 pod 的可用 capacity 对待。

kubelet 默认在 pod 中执行 Allocatable。无论何时,如果所有 pod 的总用量超过了 Allocatable,驱逐 pod 的措施将被执行。有关驱逐策略的更多细节可以在 这里 找到。请通过设置 kubelet --enforce-node-allocatable 标志值为 pods 控制这个措施。

可选的,通过在相同标志中同时指定 kube-reserved 和 system-reserved 值能够使 kubelet 执行 kube-reserved 和 system-reserved。请注意,要想执行 kube-reserved 或者 system-reserved时,需要分别指定 --kube-reserved-cgroup 或者 --system-reserved-cgroup。

一般原则

系统守护进程期望被按照类似 Guaranteed pod 一样对待。系统守护进程可以在其范围控制组中爆发式增长,您需要将这个行为作为 kubernetes 部署的一部分进行管理。例如,kubelet 应该有它自己的控制组并和容器运行时(container runtime)共享 Kube-reserved 资源。然而,如果执行了 kube-reserved,则 kubelet 不能突然爆发并耗尽节点的所有可用资源。

在执行 system-reserved 预留操作时请加倍小心,因为它可能导致节点上的关键系统服务 CPU 资源短缺或因为内存不足(OOM)而被终止。

  • 在 pods 上执行 Allocatable 作为开始。
  • 一旦足够用于追踪系统守护进程的监控和告警的机制到位,请尝试基于用量探索(usage heuristics)方式执行 kube-reserved。
  • 随着时间推进,如果绝对必要,可以执行 system-reserved。

随着时间的增长以及越来越多特性的加入,kube 系统守护进程对资源的需求可能也会增加。以后 kubernetes 项目将尝试减少对节点系统守护进程的利用,但目前那并不是优先事项。所以,请期待在将来的发布中将 Allocatable 容量降低。

示例场景

这是一个用于说明节点 Allocatable 计算方式的示例:

  • 节点拥有 32Gi 内存,16 核 CPU 和 100Gi 存储
  • --kube-reserved 设置为 cpu=1,memory=2Gi,storage=1Gi
  • --system-reserved 设置为 cpu=500m,memory=1Gi,storage=1Gi
  • --eviction-hard 设置为 memory.available<500Mi,nodefs.available<10%

在这个场景下,Allocatable 将会是 14.5 CPUs、28.5Gi 内存以及 98Gi 存储。调度器保证这个节点上的所有 pod 请求的内存总量不超过 28.5Gi,存储不超过 88Gi。当 pod 的内存使用总量超过 28.5Gi 或者磁盘使用总量超过 88Gi 时,Kubelet 将会驱逐它们。如果节点上的所有进程都尽可能多的使用 CPU,则 pod 加起来不能使用超过 14.5 CPUs 的资源。

当没有执行 kube-reserved 和/或 system-reserved 且系统守护进程使用量超过其预留时,如果节点内存用量高于 31.5Gi 或存储大于 90Gi,kubelet 将会驱逐 pod。

可用特性

截至 Kubernetes 1.2 版本,已经可以可选的指定 kube-reserved 和 system-reserved 预留。当在相同的发布中都可用时,调度器将转为使用 Allocatable 替代 Capacity。

截至 Kubernetes 1.6 版本,eviction-thresholds 是通过计算 Allocatable 进行考虑。要使用旧版本的行为,请设置 --experimental-allocatable-ignore-eviction kubelet 标志为 true。

截至 Kubernetes 1.6 版本,kubelet 使用控制组在 pod 上执行 Allocatable。要使用旧版本行为,请取消设置 --enforce-node-allocatable kubelet 标志。请注意,除非 --kube-reserved 或者 --system-reserved 或者 --eviction-hard 标志没有默认参数,否则 Allocatable 的实施不会影响已经存在的 deployment。

截至 Kubernetes 1.6 版本,kubelet 在 pod 自己的 cgroup 沙箱中启动它们,这个 cgroup 沙箱在 kubelet 管理的 cgroup 层次结构中的一个独占部分中。在从前一个版本升级 kubelet之前,要求操作员 drain 节点,以保证 pod 及其关联的容器在 cgroup 层次结构中合适的部分中启动。

截至 Kubernetes 1.7 版本,kubelet 支持指定 storage 为 kube-reserved 和 system-reserved 的资源。

译者: xiaosuiba

原文:https://k8smeetup.github.io/docs/tasks/administer-cluster/reserve-compute-resources

K8S中文社区微信公众号

Kubernetes 使用 kubeconfig 共享集群访问

通过复制 kubectl 客户端配置 (kubeconfig),客户端访问运行中的 Kubernetes 集群可以共享。该配置位于 $HOME/.kube/config,由 cluster/kube-up.sh 生成。下面是共享 kubeconfig 的步骤。

1. 创建集群

$ cluster/kube-up.sh

2. 复制 kubeconfig 到新主机

$ scp $HOME/.kube/config user@remotehost:/path/to/.kube/config

3. 在新主机上,让复制的 config 在使用 kubectl 时生效

  • 选择 A:复制到默认的位置
$ mv /path/to/.kube/config $HOME/.kube/config
  • 选择 B:复制到工作目录(运行 kubectl 的当前位置)
$ mv /path/to/.kube/config $PWD
  • 选项 C:通过环境变量 KUBECONFIG 或者命令行标志 kubeconfig 传递给 kubectl
# via environment variable
$ export KUBECONFIG=/path/to/.kube/config

# via commandline flag
$ kubectl ... --kubeconfig=/path/to/.kube/config

手动创建 kubeconfig

kubeconfig 由 kube-up 生成,但是,您也可以使用下面命令生成自己想要的配置(可以使用任何想要的子集)

# create kubeconfig entry
$ kubectl config set-cluster $CLUSTER_NICK \
    --server=https://1.1.1.1 \
    --certificate-authority=/path/to/apiserver/ca_file \
    --embed-certs=true \
    # Or if tls not needed, replace --certificate-authority and --embed-certs with
    --insecure-skip-tls-verify=true \
    --kubeconfig=/path/to/standalone/.kube/config

# create user entry
$ kubectl config set-credentials $USER_NICK \
    # bearer token credentials, generated on kube master
    --token=$token \
    # use either username|password or token, not both
    --username=$username \
    --password=$password \
    --client-certificate=/path/to/crt_file \
    --client-key=/path/to/key_file \
    --embed-certs=true \
    --kubeconfig=/path/to/standalone/.kube/config

# create context entry
$ kubectl config set-context $CONTEXT_NAME \
    --cluster=$CLUSTER_NICK \
    --user=$USER_NICK \
    --kubeconfig=/path/to/standalone/.kube/config

注:

  • 生成独立的 kubeconfig 时,标识 --embed-certs 是必选的,这样才能远程访问主机上的集群。
  • --kubeconfig 既是加载配置的首选文件,也是保存配置的文件。如果您是第一次运行上面命令,那么 --kubeconfig 文件的内容将会被忽略。
$ export KUBECONFIG=/path/to/standalone/.kube/config
  • 上面提到的 ca_file,key_file 和 cert_file 都是集群创建时在 master 上产生的文件,可以在文件夹 /srv/kubernetes 下面找到。持有的 token 或者 基本认证也在 master 上产生。

如果您想了解更多关于 kubeconfig 的详细信息,请查看使用 kubeconfig 配置集群访问认证,或者运行帮助命令 kubectl config -h。

合并 kubeconfig 的示例

kubectl 会按顺序加载和合并来自下面位置的配置

  1. --kubeconfig=/path/to/.kube/config 命令行标志
  2. KUBECONFIG=/path/to/.kube/config 环境变量
  3. $HOME/.kube/config 配置文件

如果在 host1 上创建集群 A 和 B,在 host2 上创建集群 C 和 D,那么,您可以通过运行下面命令,在两个主机上访问所有的四个集群

# on host2, copy host1's default kubeconfig, and merge it from env
$ scp host1:/path/to/home1/.kube/config /path/to/other/.kube/config

$ export KUBECONFIG=/path/to/other/.kube/config

# on host1, copy host2's default kubeconfig and merge it from env
$ scp host2:/path/to/home2/.kube/config /path/to/other/.kube/config

$ export KUBECONFIG=/path/to/other/.kube/config

如果希望查看更加详细的例子以及关于 kubeconfig 加载/合并规则的描述,请您参考 kubeconfig-file

译者:chentao1596

原文:https://k8smeetup.github.io/docs/tasks/administer-cluster/share-configuration/

K8S中文社区微信公众号

Kubernetes Pod 生命周期

该页面将描述 Pod 的生命周期。

Pod phase

Pod 的 status 定义在 PodStatus 对象中,其中有一个 phase 字段。

Pod 的相位(phase)是 Pod 在其生命周期中的简单宏观概述。该阶段并不是对容器或 Pod 的综合汇总,也不是为了做为综合状态机。

Pod 相位的数量和含义是严格指定的。除了本文档中列举的内容外,不应该再假定 Pod 有其他的 phase 值。

下面是 phase 可能的值:

  • 挂起(Pending):Pod 已被 Kubernetes 系统接受,但有一个或者多个容器镜像尚未创建。等待时间包括调度 Pod 的时间和通过网络下载镜像的时间,这可能需要花点时间。
  • 运行中(Running):该 Pod 已经绑定到了一个节点上,Pod 中所有的容器都已被创建。至少有一个容器正在运行,或者正处于启动或重启状态。
  • 成功(Succeeded):Pod 中的所有容器都被成功终止,并且不会再重启。
  • 失败(Failed):Pod 中的所有容器都已终止了,并且至少有一个容器是因为失败终止。也就是说,容器以非0状态退出或者被系统终止。
  • 未知(Unknown):因为某些原因无法取得 Pod 的状态,通常是因为与 Pod 所在主机通信失败。

Pod 状态

Pod 有一个 PodStatus 对象,其中包含一个 PodCondition 数组。 PodCondition 数组的每个元素都有一个 type 字段和一个 status 字段。type 字段是字符串,可能的值有 PodScheduled、Ready、Initialized 和 Unschedulable。status 字段是一个字符串,可能的值有 True、False 和 Unknown。

容器探针

探针 是由 kubelet 对容器执行的定期诊断。要执行诊断,kubelet 调用由容器实现的 Handler。有三种类型的处理程序:

  • ExecAction:在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。
  • TCPSocketAction:对指定端口上的容器的 IP 地址进行 TCP 检查。如果端口打开,则诊断被认为是成功的。
  • HTTPGetAction:对指定的端口和路径上的容器的 IP 地址执行 HTTP Get 请求。如果响应的状态码大于等于200 且小于 400,则诊断被认为是成功的。

每次探测都将获得以下三种结果之一:

  • 成功:容器通过了诊断。
  • 失败:容器未通过诊断。
  • 未知:诊断失败,因此不会采取任何行动。

Kubelet 可以选择是否执行在容器上运行的两种探针执行和做出反应:

  • livenessProbe:指示容器是否正在运行。如果存活探测失败,则 kubelet 会杀死容器,并且容器将受到其 重启策略 的影响。如果容器不提供存活探针,则默认状态为 Success。
  • readinessProbe:指示容器是否准备好服务请求。如果就绪探测失败,端点控制器将从与 Pod 匹配的所有 Service 的端点中删除该 Pod 的 IP 地址。初始延迟之前的就绪状态默认为 Failure。如果容器不提供就绪探针,则默认状态为 Success。

该什么时候使用存活(liveness)和就绪(readiness)探针?

如果容器中的进程能够在遇到问题或不健康的情况下自行崩溃,则不一定需要存活探针; kubelet 将根据 Pod 的restartPolicy 自动执行正确的操作。

如果您希望容器在探测失败时被杀死并重新启动,那么请指定一个存活探针,并指定restartPolicy 为 Always 或 OnFailure。

如果要仅在探测成功时才开始向 Pod 发送流量,请指定就绪探针。在这种情况下,就绪探针可能与存活探针相同,但是 spec 中的就绪探针的存在意味着 Pod 将在没有接收到任何流量的情况下启动,并且只有在探针探测成功后才开始接收流量。

如果您希望容器能够自行维护,您可以指定一个就绪探针,该探针检查与存活探针不同的端点。

请注意,如果您只想在 Pod 被删除时能够排除请求,则不一定需要使用就绪探针;在删除 Pod 时,Pod 会自动将自身置于未完成状态,无论就绪探针是否存在。当等待 Pod 中的容器停止时,Pod 仍处于未完成状态。

Pod 和容器状态

有关 Pod 容器状态的详细信息,请参阅 PodStatus 和 ContainerStatus。请注意,报告的 Pod 状态信息取决于当前的 ContainerState

重启策略

PodSpec 中有一个 restartPolicy 字段,可能的值为 Always、OnFailure 和 Never。默认为 Always。 restartPolicy 适用于 Pod 中的所有容器。restartPolicy 仅指通过同一节点上的 kubelet 重新启动容器。失败的容器由 kubelet 以五分钟为上限的指数退避延迟(10秒,20秒,40秒…)重新启动,并在成功执行十分钟后重置。如 Pod 文档 中所述,一旦绑定到一个节点,Pod 将永远不会重新绑定到另一个节点。

Pod 的生命

一般来说,Pod 不会消失,直到人为销毁他们。这可能是一个人或控制器。这个规则的唯一例外是成功或失败的 phase 超过一段时间(由 master 确定)的Pod将过期并被自动销毁。

有三种可用的控制器:

  • 使用 Job 运行预期会终止的 Pod,例如批量计算。Job 仅适用于重启策略为 OnFailure 或 Never 的 Pod。
  • 对预期不会终止的 Pod 使用 ReplicationControllerReplicaSet 和 Deployment ,例如 Web 服务器。 ReplicationController 仅适用于具有 restartPolicy 为 Always 的 Pod。
  • 提供特定于机器的系统服务,使用 DaemonSet 为每台机器运行一个 Pod 。

所有这三种类型的控制器都包含一个 PodTemplate。建议创建适当的控制器,让它们来创建 Pod,而不是直接自己创建 Pod。这是因为单独的 Pod 在机器故障的情况下没有办法自动复原,而控制器却可以。

如果节点死亡或与集群的其余部分断开连接,则 Kubernetes 将应用一个策略将丢失节点上的所有 Pod 的 phase 设置为 Failed。

示例

高级 liveness 探针示例

存活探针由 kubelet 来执行,因此所有的请求都在 kubelet 的网络命名空间中进行。

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - args:
    - /server
    image: gcr.io/google_containers/liveness
    livenessProbe:
      httpGet:
        # when "host" is not defined, "PodIP" will be used
        # host: my-host
        # when "scheme" is not defined, "HTTP" scheme will be used. Only "HTTP" and "HTTPS" are allowed
        # scheme: HTTPS
        path: /healthz
        port: 8080
        httpHeaders:
          - name: X-Custom-Header
            value: Awesome
      initialDelaySeconds: 15
      timeoutSeconds: 1
    name: liveness

状态示例

  • Pod 中只有一个容器并且正在运行。容器成功退出。
    • 记录完成事件。
    • 如果 restartPolicy 为:
      • Always:重启容器;Pod phase 仍为 Running。
      • OnFailure:Pod phase 变成 Succeeded。
      • Never:Pod phase 变成 Succeeded。
  • Pod 中只有一个容器并且正在运行。容器退出失败。
    • 记录失败事件。
    • 如果 restartPolicy 为:
      • Always:重启容器;Pod phase 仍为 Running。
      • OnFailure:重启容器;Pod phase 仍为 Running。
      • Never:Pod phase 变成 Failed。
  • Pod 中有两个容器并且正在运行。有一个容器退出失败。
    • 记录失败事件。
    • 如果 restartPolicy 为:
      • Always:重启容器;Pod phase 仍为 Running。
      • OnFailure:重启容器;Pod phase 仍为 Running。
      • Never:不重启容器;Pod phase 仍为 Running。
    • 如果有一个容器没有处于运行状态,并且两个容器退出:
      • 记录失败事件。
      • 如果 restartPolicy 为:
        • Always:重启容器;Pod phase 仍为 Running。
        • OnFailure:重启容器;Pod phase 仍为 Running。
        • Never:Pod phase 变成 Failed。
  • Pod 中只有一个容器并处于运行状态。容器运行时内存超出限制:
    • 容器以失败状态终止。
    • 记录 OOM 事件。
    • 如果 restartPolicy 为:
      • Always:重启容器;Pod phase 仍为 Running。
      • OnFailure:重启容器;Pod phase 仍为 Running。
      • Never: 记录失败事件;Pod phase 仍为 Failed。
  • Pod 正在运行,磁盘故障:
    • 杀掉所有容器。
    • 记录适当事件。
    • Pod phase 变成 Failed。
    • 如果使用控制器来运行,Pod 将在别处重建。
  • Pod 正在运行,其节点被分段。
    • 节点控制器等待直到超时。
    • 节点控制器将 Pod phase 设置为 Failed。
    • 如果是用控制器来运行,Pod 将在别处重建。

译者:jimmysong 原文:https://k8smeetup.github.io/docs/concepts/workloads/pods/pod-lifecycle/

K8S中文社区微信公众号