k8s:pod-1

k8s:pod-1

pod生命周期

pod创建过程:

运行初始化容器(init container) > 运行主容器(main container) >容器启动后钩子(post start) 容器终止前钩子(pre stop)

容器的存活性探测(liveness probe)、就绪性探测(readiness probe)

在生命周期中会出现5种状态:
挂起(Pending):apiserver已经创建了pod资源对象,但它尚未被调度完成或者仍处于下载镜像的过程中
运行中(Running):pod已经被调度至某节点,并且所有容器都已经被kubelet创建完成
成功(Succeeded):pod中的所有容器都已经成功终止并且不会被重启
失败(Failed):所有容器都已经终止,但至少有一个容器终止失败,即容器返回了非0值的退出状态
未知(Unknown):apiserver无法正常获取到pod对象的状态信息,通常由网络通信失败所导致
pod的创建过程

步骤 1: 用户提交请求

  • 动作 : 用户通过 kubectl、客户端库或 REST API 向 Kubernetes API Server 提交 Pod 的定义文件(通常是 YAML 或 JSON 格式)。
  • 角色: API Server 是集群的网关和管理前端。

步骤 2: API Server 持久化存储

  • 动作 : API Server 对请求进行验证和准入控制,然后根据用户提交的信息,在 etcd 中创建一个新的 Pod 对象,并将其状态设置为 Pending
  • 角色: etcd 是 Kubernetes 的"唯一可信数据源",所有集群状态都存储于此。

步骤 3: 事件驱动与监听机制

  • 动作 : API Server 中 Pod 资源的创建和更新事件会被诸如 Scheduler 和 Kubelet 这样的组件监听到。这些组件通过 Watch 机制与 API Server 建立长连接,实时获取与其相关的资源变动。
  • 关键: 这是 Kubernetes 声明式 API 和控制器模式的核心,各组件通过"观察-调整-反馈"循环来驱动系统达到期望状态。

步骤 4: Scheduler 调度决策

  • 动作
    1. Scheduler 监测到有一个新的 Pod 被创建,且其 nodeName 为空,于是开始调度流程。
    2. 它通过一系列预选和优选算法,从集群中选择一个最适合的节点。
    3. 调度器将决策结果(即目标节点名)通过 nodeName 字段绑定到 Pod 对象上,并将此更新提交给 API Server。
  • 角色: Scheduler 是负责资源分配的"调度中心"。

步骤 5: Kubelet 创建容器

  • 动作
    1. 目标节点上的 Kubelet 监测到有一个 Pod 被调度到了自己所在的节点。
    2. Kubelet 根据 Pod 规格描述,通过 Container Runtime(如 Docker、containerd 等)来拉取镜像、创建并启动容器。
    3. 它还会根据配置设置网络、挂载存储卷等。
  • 角色: Kubelet 是节点上的"代理",负责管理本节点 Pod 的生命周期。

步骤 6: 状态上报与完成

  • 动作
    1. Kubelet 持续监控容器的状态。一旦容器启动成功,Kubelet 会将 Pod 的状态更新为 Running 并上报给 API Server。如果启动失败,则上报 Failed
    2. API Server 接收到状态更新后,再次将 Pod 的最新状态写入 etcd。
  • 最终状态: 此时,Pod 的完整信息(包括其运行状态)已在 etcd 中持久化,整个创建流程结束。

流程核心特点总结

  • 中心化存储:所有状态都以 Pod 对象的形式存储在 etcd 中,API Server 是唯一可对其进行操作的中心。
  • 事件驱动:Scheduler 和 Kubelet 等组件不主动轮询,而是通过 Watch 机制监听 API Server 的事件,实现高效、实时的协同。
  • 异步操作:用户的创建请求的返回,仅表示 API Server 已接收并存储了 Pod 的定义,并不代表 Pod 已成功运行。后续的调度和创建过程是异步进行的。
  • 自我修复 :如果 Pod 在运行过程中意外终止,Kubelet 会监测到这一状态变化,并根据 Pod 的 restartPolicy 决定是否重启容器,并再次将最新状态上报给 API Server,进入一个新的状态更新循环。

pod的终止过程

步骤 1: 用户发起删除命令

  • 动作 : 用户通过 kubectl delete pod 或其他客户端向 API Server 发送删除 Pod 的请求。

步骤 2 , 3: API Server 标记 Pod 为终止状态

  • 动作
    1. API Server 接收到删除请求后,不会立即删除 Pod 对象,而是会修改 Pod 的元数据。
    2. 它会在 Pod 上设置一个删除时间戳deletionGracePeriodSeconds(宽限期,默认30秒)
    3. Pod 的状态随之变为 Terminating。此时,Pod 虽然仍在运行,但已被视为"正在死亡"的状态。

步骤 4: Kubelet 监控到终止状态并启动关闭流程

  • 动作 : 目标节点上的 Kubelet 通过 Watch 机制监测到其管理的 Pod 被标记为 Terminating,随即开始触发本节点的 Pod 关闭过程。

步骤 5: 从 Service 端点列表中移除

  • 动作Endpoints Controller (端点控制器)同样监控到 Pod 进入 Terminating 状态。它会立即将这个 Pod 的 IP 地址从所有关联的 Service 的 Endpoints(或 EndpointSlice)列表中移除
  • 关键作用 : 这一步切断了流向该 Pod 的新流量,是确保优雅终止、避免请求丢失的关键。此后,Service 的负载均衡器不会再转发新的连接到此 Pod。

步骤 6: 执行 preStop Hook

  • 动作 : 如果 Pod 中定义了 preStop 钩子处理器,Kubelet 会同步地在 Pod 的容器内执行它。
  • 目的 : 这是应用在收到终止信号前,执行自定义清理逻辑(如通知注册中心、完成剩余任务、优雅关闭连接)的最后机会。preStop 钩子的执行时间包含在宽限期之内。

步骤 7: 发送 SIGTERM 信号

  • 动作preStop 钩子执行完毕后(或如果没有定义钩子),Kubelet 会向 Pod 内每个容器的主进程发送 SIGTERM 信号。
  • 目的: 这是一个"优雅终止"信号,通知应用程序自行关闭。设计良好的应用会捕获此信号并开始清理工作,然后退出。

步骤 8: 宽限期结束与强制终止

  • 动作
    • 情况A(成功) : 如果所有容器在主进程收到 SIGTERM 后,在宽限期结束前自行退出,则关闭流程顺利完成。
    • 情况B(超时) : 如果宽限期(例如30秒)结束后,Pod 中仍有容器在运行,Kubelet 会向这些容器发送 SIGKILL 信号,立即强制终止进程

最终清理

  • 动作: 无论进程是自行退出还是被强制杀死,一旦 Pod 内的所有容器都停止运行,Kubelet 就会通知 API Server,API Server 随后会彻底清理并删除 etcd 中的 Pod 记录。
  • 结果: 至此,Pod 对象对于用户而言已完全消失,其生命周期正式结束。

流程核心特点总结

  • 优雅终止:核心设计思想是给予应用充分的时间进行清理,而非直接暴力杀死。
  • 流量切断:通过先从 Service 移除端点来防止新的请求到达正在关闭的 Pod。
  • 双重信号 :先 SIGTERM(友好协商),再 SIGKILL(强制措施)。
  • 可配置性terminationGracePeriodSecondspreStop 钩子让用户可以根据应用特性定制关闭行为

初始化容器

初始化容器是在 Pod 的主应用程序容器启动之前运行的一个或多个特殊容器,它们为 Pod 的部署提供了强大的前置准备和校验能力。

核心特征
  1. 强制性成功完成
    • 每个初始化容器都必须运行直至结束
    • 如果某个初始化容器运行失败 (即退出码非零),Kubernetes 会根据 Pod 的 restartPolicy 策略(通常为 AlwaysOnFailure重启它,直到它成功运行完毕
    • 这是一个"全部或没有"的操作:所有初始化容器都必须成功,Pod 才能启动主容器。
  2. 顺序执行
    • 初始化容器按照它们在 Pod 清单中定义的顺序逐个执行
    • 当前一个初始化容器成功退出后,下一个初始化容器才能开始启动。
    • 这与主容器的并行启动模式形成鲜明对比。
主要应用场景

初始化容器与主容器共享镜像空间,这使得它们非常适合完成以下前置工作:

  • 提供主容器不具备的工具或代码
    • 场景 :主容器镜像是为了最小化和安全而构建的,可能不包含 curlnslookupdig 或特定脚本等调试或设置工具。
    • 做法:可以使用一个包含必要工具的初始化容器镜像,来执行这些操作,而无需污染主容器镜像。
  • 延迟应用启动,直到依赖就绪
    • 场景:主应用容器需要依赖外部服务(如数据库、API 后端、缓存服务)先正常运行。
    • 做法 :在初始化容器中使用一个脚本,通过循环检测(如 curlnc)来等待依赖服务变为可连接状态。只有当依赖服务就绪后,初始化容器才会成功退出,从而允许主容器启动。
  • 生成配置文件或下载数据
    • 场景:主容器运行所需的配置文件需要根据环境变量或 API 查询结果动态生成。
    • 做法:让初始化容器从 ConfigMap、Secret 或外部服务获取数据,然后渲染生成最终的配置文件,并存入一个共享的 Volume 中供主容器使用。
  • 权限分离与安全控制
    • 场景:主容器需要以非 root 用户运行以保证安全,但某些初始化步骤(如修改文件系统权限、挂载敏感目录)需要更高的权限。
    • 做法:可以让初始化容器以高权限运行来完成这些设置,然后退出。随后主容器以低权限运行,从而减小了攻击面。

假设要以主容器来运行nginx,但是要求在运行nginx之前先要能够连接上mysql所在服务器

创建pod-initcontainer.yaml
复制代码
[root@master ~]# vim pod-initcontainer.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-initcontainer
  namespace: dev
spec:
  containers:
  - name: main-container
    image: nginx:1.17.1
    ports: 
    - name: nginx-port
      containerPort: 80
  initContainers:
  - name: test-mysql
    image: busybox:1.30
    command: ['sh', '-c', 'until ping 192.168.100.10 -c 2 ; do echo waiting for mysql...; sleep 2; done;']
创建pod
复制代码
[root@master ~]# kubectl create ns dev
namespace/dev created

[root@master ~]# kubectl create -f pod-initcontainer.yaml 
pod/pod-initcontainer created
查看pod
复制代码
 [root@master ~]# kubectl describe pod pod-initcontainer -n dev 
  
  Normal  Scheduled  7m59s  default-scheduler  Successfully assigned dev/pod-initcontainer to
  Normal  Pulling    8m     kubelet            Pulling image "busybox:1.30"
  Normal  Pulled     7m58s  kubelet            Successfully pulled image "busybox:1.30" in 1.
  Normal  Created    7m58s  kubelet            Created container test-mysql
  Normal  Started    7m58s  kubelet            Started container test-mysql
  Normal  Pulling    7m56s  kubelet            Pulling image "nginx:1.17.1"
  Normal  Pulled     7m47s  kubelet            Successfully pulled image "nginx:1.17.1" in 8.
  Normal  Created    7m47s  kubelet            Created container main-container
  Normal  Started    7m47s  kubelet            Started container main-container
  #可以看到这里是先运行第一个初始化容器,然后再运行主容器,pod也是正常运行
  #现在关闭192.168.100.10模拟故障
  #删除pod
  [root@master ~]# kubectl delete -f pod-initcontainer.yaml
  #192.167.100.10关机,再次创建
  [root@master ~]# kubectl apply -f pod-initcontainer.yaml 
pod/pod-initcontainer created
  [root@master ~]# kubectl get pods -n dev
NAME                READY   STATUS    RESTARTS   AGE
pod-initcontainer   0/1     Pending   0          11s
#查看会发现出问题了,原因是192.168.100.10关机,初始化过程失败导致主容器一直不能运行,实验成功

钩子函数

钩子函数能够感知自身生命周期中的事件,并在相应的时刻到来时运行用户指定的程序代码。

kubernetes在主容器的启动之后和停止之前提供了两个钩子函数:

post start:容器创建之后执行,如果失败了会重启容器
pre stop :容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作
钩子处理器支持3种定义动作
  • Exec命令:在容器内执行一次命令

    复制代码
    lifecycle:
    
      postStart: 
    
        exec:
    
          command:
    
          - cat
    
          - /tmp/healthy
  • TCPSocket:在当前容器尝试访问指定的socket

    复制代码
    lifecycle:
    
      postStart:
    
        tcpSocket:
    
          port: 8080
  • HTTPGet:在当前容器中向某url发起http请求

    复制代码
    lifecycle:
    
     postStart:
    
       httpGet:
    
         path: / #URI地址
    
         port: 80 #端口号
    
         host: 192.168.5.3 #主机地址
    
         scheme: HTTP #支持的协议,http或者https
以exec方式为例,创建pod-hook-exec.yaml
复制代码
[root@master ~]# vim pod-hook-exec.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-hook-exec
  namespace: dev
spec:
  containers:
  - name: main-container
    image: nginx:1.17.1
    ports:
    - name: nginx-port
      containerPort: 80
    lifecycle:
      postStart: 
        exec: 
          command: ["/bin/sh", "-c", "echo test111... > /usr/share/nginx/html/index.html"]
      preStop:
        exec: 
          command: ["/usr/sbin/nginx","-s","quit"]
创建pod
复制代码
[root@master ~]# kubectl apply -f pod-hook-exec.yaml 
pod/pod-hook-exec created
查看pod
复制代码
[root@master ~]# kubectl get pods  pod-hook-exec -n dev -o wide
NAME            READY   STATUS    RESTARTS   AGE   IP              NODE    NOMINATED NODE   READINESS GATES
pod-hook-exec   1/1     Running   0          26s   172.16.104.28   node2   <none>           <none>
访问测试
复制代码
[root@master ~]# curl 172.16.104.28
test111...

容器探测

容器探测是 Kubernetes 用于检测容器应用实例是否正常工作的健康检查机制。通过定期执行诊断,确保业务的可用性。

两种核心探针类型

1. Liveness Probe(存活性探针)
  • 作用 :检测应用实例当前是否处于正常运行状态
  • 失败后果 :如果探测失败,Kubernetes 会重启容器
  • 使用场景 :用于恢复"进程存在但应用已死"的状态
    • 应用死锁
    • 内部错误导致无法响应
    • 内存泄漏导致应用卡死
2. Readiness Probe(就绪性探针)
  • 作用 :检测应用实例当前是否可以接收请求
  • 失败后果 :如果探测失败,Kubernetes 会将该实例从 Service 的负载均衡中摘除不转发流量
  • 使用场景 :用于处理"应用正在启动或临时不可用"的情况
    • 应用启动较慢,需要时间初始化
    • 依赖服务暂时不可用
    • 临时高负载无法处理新请求
两种探针都支持三种探测方式:
Exec命令:在容器内执行一次命令,如果命令执行的退出码为0,则认为程序正常,否则不正常
TCPSocket:将会尝试访问一个用户容器的端口,如果能够建立这条连接,则认为程序正常,否则不正常
HTTPGet:调用容器内Web应用的URL,如果返回的状态码在200和399之间,则认为程序正常,否则不正常
创建pod-liveness-exec.yaml(exec)
复制代码
apiVersion: v1
kind: Pod
metadata:
  name: pod-liveness-exec
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports: 
    - name: nginx-port
      containerPort: 80
    livenessProbe:
      exec:
        command: ["/bin/cat","/tmp/hello.txt"] 
[root@master ~]# kubectl apply -f pod-liveness-exec.yaml 
pod/pod-liveness-exec created

查看

复制代码
  Normal   Scheduled  20s               default-scheduler  Successfully assigned dev/pod-liveness-exec to node1
  Normal   Pulled     20s               kubelet            Container image "nginx:1.17.1" already present on machine
  Normal   Created    20s               kubelet            Created container nginx
  Normal   Started    20s               kubelet            Started container nginx
  Warning  Unhealthy  0s (x2 over 10s)  kubelet            Liveness probe failed: /bin/cat: /tmp/hello.txt: No such file or directory
#这里由于没有这个文件,导致运行失败,存活性探针检测到运行失败然后就会重启容器
[root@master ~]# kubectl get pods -n dev
NAME                READY   STATUS    RESTARTS     AGE
pod-hook-exec       1/1     Running   0            13m
pod-initcontainer   1/1     Running   0            17m
pod-liveness-exec   1/1     Running   4 (3s ago)   2m3s
#这里发现重启了4次
如果想让它恢复正常,可以修改为存在的文件,这样运行就会成功,就不会一直重启
创建pod-liveness-tcpsocket.yaml(TCPSocket)
复制代码
apiVersion: v1
kind: Pod
metadata:
  name: pod-liveness-tcpsocket
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports: 
    - name: nginx-port
      containerPort: 80
    livenessProbe:
      tcpSocket:
        port: 8080
创建pod
复制代码
[root@master ~]# kubectl apply -f pod-liveness-tcpsocket.yaml 
pod/pod-liveness-tcpsocket created
查看
复制代码
[root@master ~]# kubectl describe pods pod-liveness-tcpsocket -n dev


  Normal   Scheduled  19s   default-scheduler  Successfully assigned dev/pod-liveness-tcpsocket to node1
  Normal   Pulled     19s   kubelet            Container image "nginx:1.17.1" already present on machine
  Normal   Created    19s   kubelet            Created container nginx
  Normal   Started    19s   kubelet            Started container nginx
  Warning  Unhealthy  9s    kubelet            Liveness probe failed: dial tcp 172.16.166.152:8080: connect: connection refused
  #这里是因为无法连接到8080端口而失败
[root@master ~]# kubectl get pods pod-liveness-tcpsocket  -n dev
NAME                     READY   STATUS    RESTARTS     AGE
pod-liveness-tcpsocket   1/1     Running   4 (5s ago)   2m6s
#这里重启了4次,如果想恢复正常,则把8080端口换成80端口
创建pod-liveness-httpget.yaml(HTTPGet)
复制代码
[root@master ~]# vim pod-liveness-httpget.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-liveness-httpget
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports:
    - name: nginx-port
      containerPort: 80
    livenessProbe:
      httpGet:    
        scheme: HTTP 
        port: 80 
        path: /hello 
[root@master ~]# kubectl apply -f pod-liveness-httpget.yaml 
pod/pod-liveness-httpget created

查看

复制代码
[root@master ~]# kubectl describe pod pod-liveness-httpget -n dev

  Normal   Scheduled  20s               default-scheduler  Successfully assigned dev/pod-liveness-httpget to node2
  Normal   Pulled     20s               kubelet            Container image "nginx:1.17.1" already present on machine
  Normal   Created    20s               kubelet            Created container nginx
  Normal   Started    20s               kubelet            Started container nginx
  Warning  Unhealthy  1s (x2 over 11s)  kubelet            Liveness probe failed: HTTP probe failed with statuscode: 404
  #这里返回404原因是因为没有这个网页
  [root@master ~]# kubectl get pod pod-liveness-httpget -n dev
NAME                   READY   STATUS    RESTARTS      AGE
pod-liveness-httpget   1/1     Running   1 (28s ago)   58s
#这里重启了一次,如果想恢复正常,则需要换成能够访问的路径
livenessProbe其他的配置
复制代码
[root@master ~]# kubectl explain pod.spec.containers.livenessProbe

FIELDS:
   exec <Object>  
   tcpSocket    <Object>
   httpGet      <Object>
   initialDelaySeconds  <integer>  # 容器启动后等待多少秒执行第一次探测
   timeoutSeconds       <integer>  # 探测超时时间。默认1秒,最小1秒
   periodSeconds        <integer>  # 执行探测的频率。默认是10秒,最小1秒
   failureThreshold     <integer>  # 连续探测失败多少次才被认定为失败。默认是3。最小值是1
   successThreshold     <integer>  # 连续探测成功多少次才被认定为成功。默认是1
重启策略
三种重启策略

Pod 的重启策略在 .spec.restartPolicy 字段中定义,共有三种:

  1. Always(默认值)
    • 行为:只要容器终止运行,无论其退出代码是什么,Kubernetes 都会自动重启该容器。
    • 适用场景:适用于需要长期持续运行的应用,如 Web 服务器、API 服务等。
  2. OnFailure
    • 行为:只有当容器异常终止(即退出码不为 0)时,才会自动重启。如果容器正常退出(退出码为 0),则不会重启。
    • 适用场景:适用于执行一次性任务或批处理作业,但任务可能因临时故障而失败需要重试的场景。
  3. Never
    • 行为:无论容器因何种原因终止,都不会对其进行重启。
    • 适用场景:适用于确保证任务只运行一次且不允许自动重试的场景。
创建pod-restartpolicy.yaml
复制代码
apiVersion: v1
kind: Pod
metadata:
  name: pod-restartpolicy
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports:
    - name: nginx-port
      containerPort: 80
    livenessProbe:
      httpGet:
        scheme: HTTP
        port: 80
        path: /hello
  restartPolicy: Never       #运行失败不会重启

测试

复制代码
[root@master ~]# kubectl apply -f pod-restartpolicy.yaml 
pod/pod-restartpolicy created
#查看
[root@master ~]# kubectl describe pods pod-restartpolicy -n dev
  Normal   Scheduled  25s               default-scheduler  Successfully assigned dev/pod-restartpolicy to node2
  Normal   Pulled     24s               kubelet            Container image "nginx:1.17.1" already present on machine
  Normal   Created    24s               kubelet            Created container nginx
  Normal   Started    24s               kubelet            Started container nginx
  Warning  Unhealthy  4s (x2 over 14s)  kubelet            Liveness probe failed: HTTP probe failed with statuscode: 404
  #状态码返回404是因为没有这个网页
  [root@master ~]# kubectl get pods -n dev
NAME                READY   STATUS      RESTARTS   AGE
pod-restartpolicy   0/1     Completed   0          76s

Pod调度

pod调度有4种方式:

自动调度:运行在哪个节点上完全由Scheduler经过一系列的算法计算得出
定向调度:NodeName、NodeSelector
亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity
污点(容忍)调度:Taints、Toleration

定向调度

核心概念
  • 作用:直接将 Pod 调度到指定的 Node 节点上。
  • 级别 :这是一种强制性的调度约束。
  • 绕过调度器 :当设置了 nodeName 时,Kubernetes 的默认调度器(kube-scheduler)会被绕过。它不会进行任何资源充足性检查(如 CPU、内存)、节点选择策略检查(如节点亲和性)或污点容忍度检查。
  • 风险 :如果指定的节点不存在,或者节点资源不足,Pod 将会运行失败 ,并处于 Pending 状态。
创建一个pod-nodename.yaml文件
复制代码
[root@master ~]# vim pod-nodename.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodename
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  nodeName: node1    #调度到node1节点上

查看

复制代码
[root@master ~]# kubectl apply -f pod-nodename.yaml 
pod/pod-nodename created

[root@master ~]# kubectl get pods -n dev -o wide
NAME           READY   STATUS    RESTARTS   AGE   IP               NODE    NOMINATED NODE   READINESS GATES
pod-nodename   1/1     Running   0          18s   172.16.166.153   node1   <none>           <none>

接下来,删除pod,修改nodeName的值为node3(无节点)

复制代码
apiVersion: v1
kind: Pod
metadata:
  name: pod-nodename
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  nodeName: node3

查看

复制代码
[root@master ~]# kubectl apply -f pod-nodename.yaml 
pod/pod-nodename created

[root@master ~]# kubectl get pods -n dev -o wide
NAME           READY   STATUS    RESTARTS   AGE   IP       NODE    NOMINATED NODE   READINESS GATES
pod-nodename   0/1     Pending   0          35s   <none>   node3   <none>           <none>
#确实调度到了node3,但是由于没有这个节点导致pod运行失败

NodeSelector

NodeSelector 是 Pod 规范中的一个字段,它通过为 Pod 指定一个或多个节点标签选择器 ,将 Pod 强制调度到拥有对应标签的节点上。它提供了一种比 nodeName 更灵活、更具声明性的调度方式。

核心概念
  • 作用:根据节点的标签来选择合适的节点。
  • 机制 :它是一个简单的键值对匹配。Pod 的 nodeSelector 中指定的键值必须与目标节点上设置的标签完全匹配,Pod 才能被调度到该节点。
  • 级别 :这是一种强制性的 调度约束。如果没有任何节点拥有匹配的标签,Pod 将无法被调度,并处于 Pending 状态。
  • 经过调度器 :与 nodeName 不同,nodeSelector 会经过 kube-scheduler。调度器会检查节点的资源是否充足,并确保节点满足 Pod 的其他要求
首先为节点node1,node2添加标签
复制代码
[root@master ~]# kubectl label nodes node1 tag=test1
node/node1 labeled
[root@master ~]# kubectl label nodes node2 tag=test2
node/node2 labeled
创建一个pod-nodeselector.yaml文件
复制代码
apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeselector
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  nodeSelector: 
    tag: test1
[root@master ~]# kubectl apply -f pod-nodeselector.yaml
pod/pod-nodeselector created

查看

复制代码
[root@master ~]# kubectl get pods -n dev -o wide 
NAME               READY   STATUS    RESTARTS   AGE   IP               NODE    NOMINATED NODE   READINESS GATES
pod-nodeselector   1/1     Running   0          46s   172.16.166.154   node1   <none>           <none>

可以发现是在node1上运行,修改文件为,让pod在node2上运行

复制代码
apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeselector
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  nodeSelector:
    tag: test2

再次运行查看

复制代码
[root@master ~]# kubectl get pods -n dev -o wide 
NAME               READY   STATUS    RESTARTS   AGE   IP              NODE    NOMINATED NODE   READINESS GATES
pod-nodeselector   1/1     Running   0          5s    172.16.104.31   node2   <none>           <none>
#可以发现这次运行在node2上
#如果不存在这个标签,则会运行失败

修改标签为tag=test3

复制代码
[root@master ~]# kubectl get pods -n dev -o wide 
NAME               READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
pod-nodeselector   0/1     Pending   0          9s    <none>   <none>   <none>           <none>
#如果没有指定的标签则会运行失败

亲和性调度

Affinity,它在NodeSelector的基础之上的进行了扩展,可以通过配置的形式,实现优先选择满足条件的Node进行调度,如果没有,也可以调度到不满足条件的节点上,使调度更加灵活。

Affinity主要分为三类:

  • nodeAffinity(node亲和性): 以node为目标,解决pod可以调度到哪些node的问题

  • podAffinity(pod亲和性) : 以pod为目标,解决pod可以和哪些已存在的pod部署在同一个拓扑域中的问题

  • podAntiAffinity(pod反亲和性) : 以pod为目标,解决pod不能和哪些已存在pod部署在同一个拓扑域中的问题

关于亲和性(反亲和性)使用场景的说明:

亲和性:如果两个应用频繁交互,那就有必要利用亲和性让两个应用的尽可能的靠近,这样可以减少因网络通信而带来的性能损耗。

反亲和性:当应用的采用多副本部署时,有必要采用反亲和性让各个应用实例打散分布在各个node上,这样可以提高服务的高可用性

NodeAffinity

可配置选项

复制代码
pod.spec.affinity.nodeAffinity
  requiredDuringSchedulingIgnoredDuringExecution  Node节点必须满足指定的所有规则才可以,相当于硬限制
    nodeSelectorTerms  节点选择列表
      matchFields   按节点字段列出的节点选择器要求列表
      matchExpressions   按节点标签列出的节点选择器要求列表(推荐)
        key    键
        values 值
        operator 关系符 支持Exists, DoesNotExist, In, NotIn, Gt, Lt
  preferredDuringSchedulingIgnoredDuringExecution 优先调度到满足指定的规则的Node,相当于软限制 (倾向)
    preference   一个节点选择器项,与相应的权重相关联
      matchFields   按节点字段列出的节点选择器要求列表
      matchExpressions   按节点标签列出的节点选择器要求列表(推荐)
        key    键
        values 值
        operator 关系符 支持In, NotIn, Exists, DoesNotExist, Gt, Lt
    weight 倾向权重,在范围1-100。

关系符的使用说明:
- matchExpressions:
  - key: nodeenv              # 匹配存在标签的key为nodeenv的节点
    operator: Exists
  - key: nodeenv              # 匹配标签的key为nodeenv,且value是"xxx"或"yyy"的节点
    operator: In
    values: ["xxx","yyy"]
  - key: nodeenv              # 匹配标签的key为nodeenv,且value大于"xxx"的节点
    operator: Gt
    values: "xxx"
创建pod-nodeaffinity-required.yaml
复制代码
apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeaffinity-required
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  affinity:  
    nodeAffinity: 
      requiredDuringSchedulingIgnoredDuringExecution: 
        nodeSelectorTerms:
        - matchExpressions: 
          - key: tag
            operator: In
            values: ["xxx","yyy"]

查看

复制代码
[root@master ~]# kubectl apply -f pod-nodeaffinity-required.yaml
[root@master ~]# kubectl get pods -n dev
NAME                        READY   STATUS    RESTARTS   AGE
pod-nodeaffinity-required   0/1     Pending   0          13s
#这里失败是因为没有values: ["xxx","yyy"],删除pod然后换成有的
[root@master ~]# kubectl delete -f pod-nodeaffinity-required.yaml
pod "pod-nodeaffinity-required" deleted

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeaffinity-required
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: tag
            operator: In
            values: ["test1","yyy"]

再次运行查看

复制代码
[root@master ~]# kubectl get pods -n dev -o wide 
NAME                        READY   STATUS    RESTARTS   AGE   IP               NODE    NOMINATED NODE   READINESS GATES
pod-nodeaffinity-required   1/1     Running   0          8s    172.16.166.155   node1   <none>

运行在了node1节点上

查看node1的标签

复制代码
[root@master ~]# kubectl get nodes node1 --show-labels 
NAME    STATUS   ROLES    AGE    VERSION    LABELS
node1   Ready    <none>   7d4h   v1.28.15   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux,tag=test1
PodAffinity

PodAffinity主要实现以运行的Pod为参照,实现让新创建的Pod跟参照pod在一个区域的功能

PodAffinity可配置选项

复制代码
pod.spec.affinity.podAffinity
  requiredDuringSchedulingIgnoredDuringExecution  硬限制
    namespaces       指定参照pod的namespace
    topologyKey      指定调度作用域
    labelSelector    标签选择器
      matchExpressions  按节点标签列出的节点选择器要求列表(推荐)
        key    键
        values 值
        operator 关系符 支持In, NotIn, Exists, DoesNotExist.
      matchLabels    指多个matchExpressions映射的内容
  preferredDuringSchedulingIgnoredDuringExecution 软限制
    podAffinityTerm  选项
      namespaces      
      topologyKey
      labelSelector
        matchExpressions  
          key    键
          values 值
          operator
        matchLabels 
    weight 倾向权重,在范围1-100
topologyKey用于指定调度时作用域,例如:
    如果指定为kubernetes.io/hostname,那就是以Node节点为区分范围
    如果指定为beta.kubernetes.io/os,则以Node节点的操作系统类型来区分
创建pod-podaffinity-target.yaml
复制代码
apiVersion: v1
kind: Pod
metadata:
  name: pod-podaffinity-target
  namespace: dev
  labels:
    tag: test
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  nodeName: node1 
[root@master ~]# kubectl apply -f pod-podaffinity-target.yaml 
pod/pod-podaffinity-target created
#查看pod
[root@master ~]# kubectl get pods -n dev
NAME                     READY   STATUS    RESTARTS   AGE
pod-podaffinity-target   1/1     Running   0          24s
创建pod-podaffinity-required.yaml
复制代码
apiVersion: v1
kind: Pod
metadata:
  name: pod-podaffinity-required
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  affinity:
    podAffinity: 
      requiredDuringSchedulingIgnoredDuringExecution: 
      - labelSelector:
          matchExpressions:
          - key: tag
            operator: In
            values: ["xxx","yyy"]		#新pod的标签要匹配之前创建的pod标签
        topologyKey: kubernetes.io/hostname
[root@master ~]# kubectl apply -f pod-podaffinity-required.yaml 
pod/pod-podaffinity-required created
#查看
[root@master ~]# kubectl get pods -n dev
NAME                       READY   STATUS    RESTARTS   AGE
pod-podaffinity-required   0/1     Pending   0          17s
pod-podaffinity-target     1/1     Running   0          2m1s
#由于没有匹配到上一个pod的标签,因此运行失败,删除这个pod修改标签
[root@master ~]# kubectl delete -f pod-podaffinity-required.yaml 
pod "pod-podaffinity-required" deleted
[root@master ~]# vim pod-podaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-podaffinity-required
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: tag
            operator: In
            values: ["test","yyy"]    #修改为test
        topologyKey: kubernetes.io/hostname
 [root@master ~]# kubectl apply -f pod-podaffinity-required.yaml 
pod/pod-podaffinity-required created
#再次查看pod会发现运行成功,查看部署节点
[root@master ~]# kubectl get pods -n dev
NAME                       READY   STATUS    RESTARTS   AGE
pod-podaffinity-required   1/1     Running   0          18s
pod-podaffinity-target     1/1     Running   0          3m55s

[root@master ~]# kubectl get pods -n dev -o wide 
NAME                       READY   STATUS    RESTARTS   AGE     IP               NODE    NOMINATED NODE   READINESS GATES
pod-podaffinity-required   1/1     Running   0          54s     172.16.166.158   node1   <none>           <none>
pod-podaffinity-target     1/1     Running   0          4m31s   172.16.166.157   node1   <none>           <none>
#可以看到都是部署到node1节点
PodAntiAffinity

PodAntiAffinity主要实现以运行的Pod为参照,让新创建的Pod跟参照pod不在一个区域中的功能

创建pod-podantiaffinity-required.yaml
复制代码
apiVersion: v1
kind: Pod
metadata:
  name: pod-podantiaffinity-required
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  affinity:  
    podAntiAffinity: 
      requiredDuringSchedulingIgnoredDuringExecution: 
      - labelSelector:
          matchExpressions: 
          - key: tag
            operator: In
            values: ["test"]
        topologyKey: kubernetes.io/hostname
[root@master ~]# kubectl apply -f pod-podantiaffinity-required.yaml 
pod/pod-podantiaffinity-required created 
#查看
[root@master ~]# kubectl get pods -n dev -o wide 
NAME                           READY   STATUS    RESTARTS   AGE     IP               NODE    NOMINATED NODE   READINESS GATES
pod-podaffinity-required       1/1     Running   0          9m19s   172.16.166.158   node1   <none>           <none>
pod-podaffinity-target         1/1     Running   0          12m     172.16.166.157   node1   <none>           <none>
pod-podantiaffinity-required   1/1     Running   0          17s     172.16.104.32    node2   <none>           <none>

污点和容忍

组成部分
  • key: 污点的键(标签名)
  • value: 污点的值(标签值)
  • effect: 污点的作用效果
污点效果(effect)类型
1. PreferNoSchedule
  • 作用: Kubernetes 会尽量避免将 Pod 调度到具有该污点的 Node 上
  • 行为: 除非没有其他节点可调度,否则不会选择该节点
  • 影响: 不影响已存在的 Pod
2. NoSchedule
  • 作用: Kubernetes 不会将 Pod 调度到具有该污点的 Node 上
  • 行为: 硬性限制,新 Pod 不会被调度到该节点
  • 影响: 不影响当前 Node 上已存在的 Pod
3. NoExecute
  • 作用: Kubernetes 不会将 Pod 调度到具有该污点的 Node 上,同时驱逐已存在的 Pod
  • 行为 :
    • 新 Pod 不会被调度到该节点
    • 节点上已存在的 Pod 会被驱逐(除非设置了容忍)
  • 影响: 同时影响新 Pod 调度和已有 Pod 运行

命令:

复制代码
# 设置污点
kubectl taint nodes node1 key=value:effect
# 去除污点
kubectl taint nodes node1 key:effect-
# 去除所有污点
kubectl taint nodes node1 key-

关闭node2节点进行测试,给node1节点设置污点

复制代码
[root@master ~]# kubectl taint nodes node1 tag=test:PreferNoSchedule
node/node1 tainted
#创建pod
[root@master ~]# kubectl run taint1 --image=nginx:1.17.1 -n dev
pod/taint1 created

[root@master ~]# kubectl get pods -n dev -o wide
NAME     READY   STATUS    RESTARTS   AGE   IP               NODE    NOMINATED NODE   READINESS GATES
taint1   1/1     Running   0          39s   172.16.166.159   node1   <none>           <none>
#这里由于没有其他节点可用,只能在node1运行,将node1的污点修改为NoSchedule
[root@master ~]# kubectl taint nodes node1 tag:PreferNoSchedule-
node/node1 untainted
[root@master ~]# kubectl taint nodes node1 tag=chenyu:NoSchedule
node/node1 tainted
#再次创建pod
[root@master ~]# kubectl run taint2 --image=nginx:1.17.1 -n dev
pod/taint2 created
[root@master ~]# kubectl get pods -n dev -o wide
NAME     READY   STATUS    RESTARTS   AGE     IP               NODE     NOMINATED NODE   READINESS GATES
taint1   1/1     Running   0          2m25s   172.16.166.159   node1    <none>           <none>
taint2   0/1     Pending   0          13s     <none>           <none>   <none>           <none>
#由于pod设置了NoSchedule的,pod没有节点能够运行,于是运行失败
再次修改node1的节点,设置为NoExecute
[root@master ~]# kubectl taint nodes node1 tag:NoSchedule-
node/node1 untainted
[root@master ~]# kubectl taint nodes node1 tag=test:NoExecute
node/node1 tainted
#再次创建pod
[root@master ~]# kubectl get pods -n dev -o wide
NAME     READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
taint3   0/1     Pending   0          5s    <none>   <none>   <none>           <none>
#不仅运行失败,之前两个pod也被踢出节点
 使用kubeadm搭建的集群,默认就会给master节点添加一个污点标记,所以pod就不会调度到master节点上.
容忍(Toleration)

添加容忍后pod就会忽略污点,不管是什么污点都能调度

创建pod-toleration.yaml
复制代码
apiVersion: v1
kind: Pod
metadata:
  name: pod-toleration
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  tolerations:      # 添加容忍
  - key: "tag"        # 要容忍的污点的key
    operator: "Equal" # 操作符
    value: "test"    # 容忍的污点的value
    effect: "NoExecute"   # 添加容忍的规则,这里必须和标记的污点规则相同
[root@master ~]# kubectl apply -f pod-toleration.yaml 
pod/pod-toleration created
[root@master ~]# kubectl get pods -n dev -o wide
NAME             READY   STATUS    RESTARTS   AGE   IP               NODE    NOMINATED NODE   READINESS GATES
pod-toleration   1/1     Running   0          22s   172.16.166.164   node1   <none>           <none>

容忍的详细配置

复制代码
FIELDS:
   key       # 对应着要容忍的污点的键,空意味着匹配所有的键
   value     # 对应着要容忍的污点的值
   operator  # key-value的运算符,支持Equal和Exists(默认)
   effect    # 对应污点的effect,空意味着匹配所有影响
   tolerationSeconds   # 容忍时间, 当effect为NoExecute时生效,表示pod在Node上的停留时间
相关推荐
正经教主9 小时前
【docker基础】第六课:Web应用与数据库容器部署
网络·docker·容器
Shacoray9 小时前
K8s 中 Ingress 的 HTTPS 证书 如何生成?
容器·https·kubernetes
开发者联盟league9 小时前
使用Jenkins整合Sonarqube/Gitlab/Harbor/Kubernetes的Demo工程
kubernetes·gitlab·jenkins
Patrick_Wilson9 小时前
Node.js SSR 内存治理:为什么 --max-old-space-size 不等于进程内存
kubernetes·node.js·v8
开发者联盟league9 小时前
使用k8s安装Jenkins
容器·kubernetes·jenkins
正经教主10 小时前
【docker基础】 第七课:Docker Compose 多容器实战
运维·docker·容器
正经教主10 小时前
【docker基础】Redis的docker部署
redis·docker·容器
DolphinScheduler社区10 小时前
Apache DolphinScheduler 3.4.2 正式发布!新增 Amazon EMR Serverless 插件,增强监控与补数据能力
大数据·云原生·serverless·apache·海豚调度·版本发版
成为你的宁宁10 小时前
【基于 Prometheus Operator 实现 K8s 环境下 Redis Cluster 集群监控部署】
redis·kubernetes·prometheus
是一个Bug11 小时前
Docker 与 Kubernetes:从“集装箱”到“远洋舰队”
docker·容器·kubernetes