3. K8S Pod 资源管理

温馨提醒
总结摘要
本文全面解析了 Kubernetes 中 Pod 的核心概念、生命周期、资源配置与管理机制。内容涵盖 Pod 的基本定义与亲密性应用场景、Pod 模板与控制器的关系、Init 容器、静态 Pod、标签与节点选择器的使用方法,并深入探讨了容器镜像策略、环境变量、特权模式、生命周期钩子(postStart/preStop)、健康探针(Liveness/Readiness/Startup)以及资源限制(requests/limits)等关键配置项,为理解和高效管理 Kubernetes 最小调度单元提供系统性指导。

Kubernetes Pod资源管理

Pod基本概念

  • 最小部署单元
  • 一组容器的集合
  • 一个Pod中的容器共享网络命名空间
  • Pod是短暂的

image-20211119135510009

Pod是一个或多个容器;同一个Pod中的容器共享存储、网络、以及怎样运行这些容器的生命。Pod中的内容总是并置在一起的,接受统一调度,在共享的上下文(包括一组命名空间,控制组等)中运行。

  • Pod对象:存在的意义
    • Pod为亲密性应用而存在。
  • 亲密性应用场景:
    • 两个应用之间发生文件交互
    • 两个文件需要通过127.0.0.1或者socket(套接字-一种程序之间通信的方式)通信例如:nginx+php
    • 两个应用需要发生频繁的调用

Pod的使用

通常不会直接创建Pod或单实例Pod。通常使用Deployment或Job这类==工作负载资源==来创建Pod。如果Pod需要跟踪状态,可以考虑StatefuSet资源。

Kubernetes集群中的Pod主要有两种方法:

  • **运行单个容器的Pod:**可以将Pod看作单个容器的包装箱,并且Kubernetes直接管理Pod,而不是容器
  • **运行多个协同工作的容器:**Pod可能封装多个联系紧密且需要共享资源的共处容器,

当你为Pod对象创建清单时,请确保所指定的Pod名称是有效的DNS子域名。

Pod和控制器

可以使用工作负载资源来创建和管理多个Pod。资源的控制器能够处理副本的管理、上线,并在Pod失效时提供自愈能力。

用来管理一个或多个Pod的工作资源:

  • Deployment
  • StatefulSet
  • DeamonSet

Pod模板

负载资源的控制器通常使用Pod模板来创建Pod并管理。

Pod模板是包含在工作负载对象中的规范,用来创建Pod。这类负载资源包括Deployment、Job和DaemonSets等

工作负载的均衡器会使用负载对象中的PodTemplate来生成实际的Pod。PodTemplate是你用来运行应用是指定的负载资源的目标状态的一部分。

以下示例是一个简单的Job的清单,其中的template指示启动一个容器。该Pod中的容器会打印一条消息后暂停。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
apiVersion: batch/v1
kind: Job
metadata:
  name: hello
spec:
  template:
    # 这里是 Pod 模版
    spec:
      containers:
      - name: hello
        image: busybox
        command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
      restartPolicy: OnFailure
    # 以上为 Pod 模版

修改 Pod 模版或者切换到新的 Pod 模版都不会对已经存在的 Pod 起作用。 Pod 不会直接收到模版的更新。相反, 新的 Pod 会被创建出来,与更改后的 Pod 模版匹配。

例如,Deployment 控制器针对每个 Deployment 对象确保运行中的 Pod 与当前的 Pod 模版匹配。如果模版被更新,则 Deployment 必须删除现有的 Pod,基于更新后的模版 创建新的 Pod。每个工作负载资源都实现了自己的规则,用来处理对 Pod 模版的更新。

在节点上, kubelet 并不直接监测 或管理与 Pod 模版相关的细节或模版的更新,这些细节都被抽象出来。 这种抽象和关注点分离简化了整个系统的语义,并且使得用户可以在不改变现有代码的 前提下就能扩展集群的行为。

Pod 更新与替换

当某工作负载的Pod模板被更改时,控制器会基于更新的模板创建新的Pod对象,而不是对现有的Pod执行更新或者修补操作。

Kubernetes 并不禁止你直接管理 Pod。对运行中的 Pod 的某些字段执行就地更新操作 还是可能的。不过,类似 patch 和 replace 这类更新操作有一些限制:

  • Pod 的绝大多数元数据都是不可变的。例如,你不可以改变其 namespacenameuid 或者 creationTimestamp 字段;generation 字段是比较特别的,如果更新 该字段,只能增加字段取值而不能减少。
  • 如果 metadata.deletionTimestamp 已经被设置,则不可以向 metadata.finalizers 列表中添加新的条目。
  • Pod 更新不可以改变除 spec.containers[*].imagespec.initContainers[*].imagespec.activeDeadlineSecondsspec.tolerations 之外的字段。 对于 spec.tolerations,你只被允许添加新的条目到其中。
  • 在更新spec.activeDeadlineSeconds 字段时,以下两种更新操作是被允许的:
    1. 如果该字段尚未设置,可以将其设置为一个正数;
    2. 如果该字段已经设置为一个正数,可以将其设置为一个更小的、非负的整数。

Pod 中的存储

一个 Pod 可以设置一组共享的存储卷。 Pod 中的所有容器都可以访问该共享卷,从而允许这些容器共享数据。 卷还允许 Pod 中的持久数据保留下来,即使其中的容器需要重新启动。 有关 Kubernetes 如何在 Pod 中实现共享存储并将其提供给 Pod 的更多信息, 请参考卷。

容器的特权模式

在 Linux 中,Pod 中的任何容器都可以使用容器规约中的安全性上下文中的 privileged(Linux)参数启用特权模式。 这对于想要使用操作系统管理权能(Capabilities,如操纵网络堆栈和访问设备) 的容器很有用。

如果你的集群启用了 WindowsHostProcessContainers 特性,你可以使用 Pod 规约中安全上下文的 windowsOptions.hostProcess 参数来创建 Windows HostProcess Pod。 这些 Pod 中的所有容器都必须以 Windows HostProcess 容器方式运行。 HostProcess Pod 可以直接运行在主机上,它也能像 Linux 特权容器一样,用于执行管理任务。

说明: 你的容器运行时必须支持 特权容器的概念才能使用这一配置。

管理POD资源对象

镜像及其获取策略

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
    - name: nginx-pod
      image: nginx:latest
      imagePullPolicy: Always
      command: [ "echo", "SUCCESS" ]

支持三种ImagePullPolicy

  • Always:不管镜像是否已经存在,都会进行一次拉取
  • Never:不管镜像是否存在都不会进行拉取
  • IfNotPresent:只有镜像不存在时,才会进行拉取

注意:

  • 默认为IfNotPresent,但:latest标签的镜像默认为Always。
  • 拉取镜像时docker会进行校验,如果镜像中的MD5码没有变,则不会拉取镜像数据。
  • 生产环境中应尽量不免使用:label 标签,而开发环境中可以使用 :label 标签自动拉取最新的镜像。

自定义运行的容器化应用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - name: nginx-pod
    image: nginx:1.18
    command: [ "/bin/sh" ]
    args: [ "-c","while true; do sleep30; done"]

环境变量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - name: nginx-pod
    image: nginx:1.18
    env:
    - name: HOST
      value: www.abc.com
    - name: LOG
      value: info

共享宿主机网络栈

1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - name: nginx-pod
    image: nginx:1.18
  hostNetwork: true

初始化Pod

init容器是一种特殊容器,在Pod内的容器启动之前运行。init容器可以包括一些应用镜像中不存在的实用工具和脚本。

每个Pod中可以包含多个容器,应用运行在这些容器里面,同时Pod也可以有一个或多个先于应用容器启动的Init容器。

Init容器与普通的容器非常像,除了以下两点:

  • 它们总是==运行到完成==
  • 每个都必须在下一个启动之前==成功完成==

如果Pod的Init容器失败,kubectl不断地重启该init容器直到该容器成功为止。然而,如果Pod对应的restartPolicy值为”Never“,kubernetes不会重新启动Pod。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup myservice.$(cat/var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
  - name: init-mydb
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup mydb.$(cat/var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]

静态的Pod

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
cat /var/lib/kubelet/config.yaml

apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
  anonymous:
    enabled: false
  webhook:
    cacheTTL: 0s
    enabled: true
  x509:
    clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
  mode: Webhook
  webhook:
    cacheAuthorizedTTL: 0s
    cacheUnauthorizedTTL: 0s
cgroupDriver: cgroupfs
clusterDNS:
- 10.1.0.10
clusterDomain: cluster.local
cpuManagerReconcilePeriod: 0s
evictionPressureTransitionPeriod: 0s
fileCheckFrequency: 0s
healthzBindAddress: 127.0.0.1
healthzPort: 10248
httpCheckFrequency: 0s
imageMinimumGCAge: 0s
kind: KubeletConfiguration
logging: {}
nodeStatusReportFrequency: 0s
nodeStatusUpdateFrequency: 0s
rotateCertificates: true
runtimeRequestTimeout: 0s
staticPodPath: /etc/kubernetes/manifests
streamingConnectionIdleTimeout: 0s
syncFrequency: 0s
volumeStatsAggPeriod: 0s

systemctl restart kubelet

systemctl enabled kubelet

标签与标签选择器

标签其实就是一对key/value,被关联到对象上,比如Pod标签的使用我们倾向于能够标示对象的特殊点,并且对用户而言是有意义的(就是一眼就看出了这个Pod是数据库),但是标签对内核系统是没有直接意义的。

标签可以用来划分特定组的对象(比如所有女生),标签可以在创建一个对象的时候直接给与,也可以在后期随时修改,每一个对象可以拥有多个标签,但是,key值必须是唯一的。

==key最多能使用63个字符,可使用字母、数字、连接号(-)、下划线(_)、点号(.)等字段,并且只 能以字母或者数字开头。==

Kubernetes(k8s)中文文档 名词解释 Labels_Kubernetes中文社区

使用lables关键字添加标签

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
apiVersion: v1
kind: Pod
metadata: 
  name: nginx-pod
  labels:
    app1: nginx
    app2: v1.18
spec:
  containers:
  - name: nginx-pod
    image: nginx:1.18

查看Pod的标签

1
2
# kubectl get pod --show-labels
````

==当标签过多时,可以在 kubectl get pod 上使用 -L ,选择显示的标签==

1
2
3
kubectl get pods -L app1

kubectl get pods -L app1,app2

kubectl label 命令可以直接管理活动对象的标签

1
2
kubectl label pod/nginx-test app=80 ##添加标签
kubectl label pod/nginx-test app=123 --overwrite ##强制修改标签

标签选择器

1
2
3
4
5
kubectl get pods -l "app1=nginx" -L app1,app2

kubectl get pods -l "app2=nginx" -L app1,app2

kubectl get pods -l "app1=nginx,app2=v1.18" -L app1,app2

基于集合关系的标签选择器有:in、notin、exists

KEY in (VALUE1,VALUE2,…)满足给定列表的集合

KEY notin (VALUE1,VALUE2,…)满足不存在的给定列表的集合

KEY 满足键名标签的资源

!KEY 满足不存在此键名标签的资源

在k8S中,诸多资源对象必须以标签选择器的方式关联到Pod资源对象,例如:Service、Deployment等 类型的资源,它们在spec字段中嵌套使用嵌套的“selector”字段,通过“matchLabels”来指定标签选择器 Pod节点选择器(nodeSelector)

Pod节点选择器

1
2
kubectl label node node2 disk=ssd --overwrite    ##给node2添加disk=ssd标签
kubectl get nodes -l disk=ssd -L disk		##查询带disk=ssd标签的选择器
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
##编写yaml文件
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd
  

Pod 对象生命周期

Pod 的生命周期 | Kubernetes

Pod 阶段

Pod 的 status 字段是一个 PodStatus 对象,其中包含一个 phase 字段。 相位

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

Pod 阶段的数量和含义是严格定义的。

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

容器状态

  • waiting(等待)

    如果容器并不处在Running或者Terminated状态,他就处在Waiting状态。处在Waiting状态的容器仍在运行它完成启动所需要的操作,例如:从某个容器镜像仓库拉取容器镜像,或者向容器应用Secret数据等。当使用kubectl来查询包含Waiting状态的容器的Pod时,也会看到一个Reason字段,其中给出了容器处于等待状态的原因。

  • Running(运行中)

    Running状态表明容器正在执行状态并且没有问题发生。如果配置了postStart回调,那么该回调已经执行并且已经完成。如果使用kubectl来查询包含Running状态的容器的Pod时,也会看到关于容器进入Running状态的信息。

  • Terminated(已终止)

    处于Terminated状态的容器已经开始执行并且正常结束或者应为某些原因失败。如果使用kubectl来查询包含Terminated状态的容器的Pod时,你会看到容器进入此状态的原因、退出代码以及容器执行期间的起止时间。

如果容器配置了preStop回调,则该回调会在容器进入Terminated状态之前执行。

==Pod创建过程==

image-20211122163353261

==Pod终止过程==

image-20211122163417317

容器生命周期钩子

容器生命周期钩子(Container Lifecycle Hooks)监听容器生命周期的特定事件,并在事件发生时执行已注册的回调函数。

支持两种钩子:

  • postStart:容器启动后执行,注意由于是异步执行,他无法保证一定在ENTRYPOINT之后运行。如果失败,容器会被杀死,并根据RestartPolicy决定是否重启。
  • preStop:容器停止前执行,常用于资源清理。如果失败,容器同样也会被杀死

而钩子的回调函数支持两种方式:

  • exec:在容器内执行。
  • httpGet:向指定URL发起GET请求。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
    preStop:
      exec:
        command: ["/usr/sbin/nginx","-s","quit"]

Pod健康状态检查

==Pod存活性探针==

为了确保容器在部署后确实处在正常运行状态,Kubernetes提供了两种探针(Probe,支持exec、tcp和httpGet方式)来探测容器的状态:

LivenessProbe(存活检查):如果检查失败,将杀死容器,根据Pod的restartPolicy来操作。

ReadinessProbe(就绪检查):如果检查失败,Kubernetes会把Pod从service endpoints中剔除。

验证:检测/tmp/nginx文件是否存在,如果不存在将报错,并重启容器。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox
    args: ["/bin/sh", "-c", "touch /tmp/nginx; sleep 60; rm -rf /tmp/nginx; sleep 60"]
    livenessProbe:
      exec:
        command: ["test", "-e", "/tmp/nginx" ]

验证:检测/nihao路径是否能够访问成功,如果访问失败将重启容器。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: nginx:1.18
    ports:
    - name: http
      containerPort: 80
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo nihao > /usr/share/nginx/html/nihao"]
#preStop:
#exec:
#command: ["/usr/sbin/nginx","-s","quit"]
    livenessProbe:
      httpGet:
        path: /nihao
        port: http
        scheme: HTTP

验证:检查http端口是否能访问

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: nginx:1.18
    ports:
    - name: http
      containerPort: 80
    livenessProbe:
      tcpSocket:
        port: http

何时该使用启动探针?

FEATURE STATE: Kubernetes v1.18 [beta]

对于所包含的容器需要较长时间才能启动就绪的 Pod 而言,启动探针是有用的。你不再需要配置一个较长的存活态探测时间间隔,只需要设置另一个独立的配置选定, 对启动期间的容器执行探测,从而允许 使用远远超出存活态时间间隔所允许的时长。

如果你的容器启动时间通常超出 initialDelaySeconds + failureThreshold × periodSeconds 总值,你应 该设置一个启动探测,对存活态探针所使用的同一端点执行检查。 periodSeconds 的默认值是 10 秒。 你应该将其 failureThreshold 设置得足够高, 以便容器有充足的时间完成启动,并且避免更改存活态探 针所使用的默认值。 这一设置有助于减少死锁状况的发生。

容器重启策略

==RestartPolicy==

支持三种RestartPolicy

  • Always:只要退出就重启
  • OnFailure:只要失败退出(exit code不等于0)时重启
  • Never:只要退出就不再重启

注意:这里的重启是指==在Pod所在的node上面本地重启,并不会被调度到其他Node上去。==

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  restartPolicy: Always
  containers:
  - name: myapp-container
    image: busybox
    args: ["/bin/sh", "-c", "touch /tmp/nginx; sleep 60; rm -rf /tmp/nginx; sleep 60"]
    livenessProbe:
      exec:
        command: ["test", "-e", "/tmp/nginx" ]

Pod资源限制

kubernetes通过cgroups限制容器的CPU和内存等计算资源,包括requests(请求,调度器保证调度到资源充足的Node上)和limits(上限)等;

  • spec.containers[].resources.limits.cpu:CPU上限,可以短暂超过,容器也不会被停止
  • spec.containers[].resources.limits.memory:内存上限,不可超过;如果超过,容器可能会被停止或调度到其他资源充足的机器上
  • spec.containers[].resources.requests.cpu:CPU请求,可以超过
  • spec.containers[].resources.requests.memory:内存请求,可以超过;但如果超过,容器可能会在node内存不足时清理

比如:nginx容器请求30%的CPU和56MB的内存,但是限制最多只能使用50%的CPU和128MB的内存;

注意:CPU的单位时milicpu,500mcpu=0.5cpu;而内存的单位则包括E,P,T,M,K,Ei,Pi,Ti,Gi,Mi,Ki等。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  containers:
    - image: nginx
      name: nginx
      resources:
        requests:
          cpu: "300m"
          memory: "56Mi"
        limits:
          cpu: "500m"
          memory: "128Mi"