k8s入门到实战(十二)—— pod的深入理解

pod 深入理解

pod 容器生命周期

pod 的几种状态

可以使用命令kubectl get pod -w实时监控查看 pod 的状态

  • running:正常运行状态
  • Pending:资源分配不对的时候会挂起,出现此状态
  • Terminating:某个节点突然关机,上面的 pod 就会是这种状态
  • ContainerCreating:容器创建的时候
  • OOMKilled:当要求的内存超过限制的时候,k8s 会把这个容器 kill 后重启
  • ErrImagePull:宿主机上不了网,镜像拉取失败

生命周期

在这里插入图片描述

每个 Pod 里运行着一个特殊的被称之为 Pause 的容器,其他容器则为业务容器,这些业务容器共享 Pause 容器的网络栈和 Volume 挂载卷,因此他们之间通信和数据交换更为高效。在设计时可以充分利用这一特性,将一组密切相关的服务进程放入同一个 Pod 中;同一个 Pod 里的容器之间仅需通过localhost 就能互相通信。

Pause 容器,又叫 Infra 容器

比如说现在有一个 Pod,其中包含了一个容器 A 和一个容器 B Pod ip,它们两个就要共享 Network Namespace。在 k8s 里的解法是这样的:它会在每个 Pod 里,额外起一个 Infra container 小容器来共享整个 Pod 的 Network Namespace。

Infra container 是一个非常小的镜像,大概 700KB 左右,是一个 C 语言写的、永远处于 “暂停” 状态的容器。由于有了这样一个 Infra container 之后,其他所有容器都会通过 Join Namespace 的方式加入到 Infra container 的 Network Namespace 中。

所以说一个 Pod 里面的所有容器,它们看到的网络视图是完全一样的。即:它们看到的网络设备、IP 地址、Mac 地址等等,跟网络相关的信息,其实全是一份,这一份都来自于 Pod 第一次创建的这个 Infra container。这就是 Pod 解决网络共享的一个解法。

由于需要有一个相当于说中间的容器存在,所以整个 Pod 里面,必然是 Infra container 第一个启动。并且整个 Pod 的生命周期是等同于 Infra container 的生命周期的,与容器 A 和 B 是无关的。这也是为什么在 k8s 里面,它是允许去单独更新 Pod 里的某一个镜像的,即:做这个操作,整个 Pod 不会重建,也不会重启,这是非常重要的一个设计。

Pod hook(钩子)是由 k8s 管理的 kubelet 发起的,当容器中的进程启动前或者容器中的进程终止之前运行,这是包含在容器的生命周期之中。可以同时为 Pod 中的所有容器都配置 hook。

init 容器

官网:https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/init-containers/

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

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

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

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

如果 Pod 的 Init 容器失败,kubelet 会不断地重启该 Init 容器直到该容器成功为止。 然而,如果 Pod 对应的 restartPolicy 值为 “Never”,并且 Pod 的 Init 容器失败, 则 k8s 会将整个 Pod 状态设置为失败。

为 Pod 设置 Init 容器需要在 Pod 规约 中添加 initContainers 字段, 该字段以 Container 类型对象数组的形式组织,和应用的 containers 数组同级相邻。

Init 容器的状态在 status.initContainerStatuses 字段中以容器状态数组的格式返回 (类似 status.containerStatuses 字段)。

下面我们编写一个案例测试 init 容器

  1. 编写 yaml 文件my-init.yaml
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"]
  1. 根据文件创建 pod
[root@k8s-master k8s]# vim my-init.yaml
[root@k8s-master k8s]# kubectl apply -f my-init.yaml 
pod/myapp-pod created
  1. 查看 pod
[root@k8s-master k8s]# kubectl get pod
NAME        READY   STATUS     RESTARTS   AGE
myapp-pod   0/1     Init:0/2   0          86s

可以看到我们的这个 pod 没有运行成功,这是因为 init 容器没有启动成功

Init 容器会等待至发现名称为 mydbmyservice 的服务。

  1. 接下来我们创建这两个服务,编写 yaml 文件init-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
  name: mydb
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9377
  1. 启动两个服务,查看 pod 状态
[root@k8s-master k8s]# vim init-svc.yaml
[root@k8s-master k8s]# kubectl apply -f init-svc.yaml 
service/myservice created
service/mydb created
[root@k8s-master k8s]# kubectl get pod
NAME        READY   STATUS    RESTARTS   AGE
myapp-pod   1/1     Running   0          5m13s

可以看到 pod 已经顺利启动了

总结

Init 容器与普通的容器非常像,区别是每个都必须在下一个容器启动之前成功完成。

如果 Pod 的 Init 容器失败,kubelet 会不断地重启该 Init 容器直到该容器成功为止。 然而,如果 Pod 对应的 restartPolicy 值为 “Never”,k8s 不会重新启动 Pod。容器重启策略默认是Always

为 Pod 设置 Init 容器需要在 Pod 的 spec 中添加 initContainers 字段, 该字段以 Container类型对象数组的形式组织,和应用的 containers 数组同级相邻。

Init 容器支持应用容器的全部字段和特性,包括资源限制、数据卷和安全设置。

如果为一个 Pod 指定了多个 Init 容器,这些容器会按顺序逐个运行。 每个 Init 容器必须运行成功,下一个才能够运行。当所有的 Init 容器运行完成时, k8s 才会为 Pod 初始化应用容器并像平常一样运行。

容器探针

官网:https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/

当你使用 k8s 的时候,有没有遇到过 Pod 在启动后一会就挂掉然后又重新启动这样的恶性循环?你有没有想过 k8s 是如何检测 pod 是否还存活?虽然容器已经启动,但是 k8s 如何知道容器的进程是否准备好对外提供服务了呢?

k8s 探针(Probe)是用于检查容器运行状况的一种机制。探针可以检查容器是否正在运行,容器是否能够正常响应请求,以及容器内部的应用程序是否正常运行等。在 k8s 中,探针可以用于确定容器的健康状态,如果容器的健康状态异常,k8s 将会采取相应的措施,例如重启容器或将其从服务中删除。

为什么需要容器探针?

容器探针可以确保您的容器在任何时候都处于可预测的状态。

如果没有容器探针,那么容器对于 k8s 平台而言,就处于一个黑盒状态。下面是没有使用容器探针可能出现的一些 case:

  • 容器未启动,负载均衡就把流量转发给容器,导致请求大量异常
  • 容器内服务不可用/发生异常,负载均衡把流量转发给容器,导致请求大量异常
  • 容器已经不正常工作(如容器死锁导致的应用程序停止响应),k8s 平台本身无法感知,不能即时低重启容器。

探测类型

针对运行中的容器,kubelet 可以选择是否执行以下三种探针,以及如何针对探测结果作出反应:

分别是Liveness Probe(存活探针)、Readiness Probe(就绪探针)、Startup Probe(启动探针)

# livenessProbe
用于检查容器是否正在运行,如果Liveness Probe检查失败,则Kubernetes将重启容器

# readinessProbe
用于检查容器是否能够正常响应请求,如果Readiness Probe检查失败,则Kubernetes将停止将流量发送到该容器

# startupProbe
用于检查容器内部的应用程序是否已经启动并且已经准备好接受流量,如果Startup Probe检查失败,则Kubernetes将重启容器

探针资源 yaml 中的常用的配置,如下。可根据具体的需求去设置,心跳机制

spec:
    containers:
        # 就绪探针
        readinessProbe:
            # 检测方式
            httpGet: 
            # 超时时间
            timeoutSeconds: 
            # 延迟时间
            initialDelaySeconds:
            # 失败次数限制
            failureThreshold:
            # 每多少秒检测一次
            periodSeconds:
        # 存活探针
        livenessProbe:
            # 检测方式
            httpGet: 
            # 超时时间
            timeoutSeconds: 
            # 延迟时间
            initialDelaySeconds:
            # 失败次数限制
            failureThreshold:
            # 每多少秒检测一次
            periodSeconds:

探针机制

使用探针来检查容器有四种不同的方法。 每个探针都必须准确定义为这四种机制中的一种:请求-响应。

#exec
在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。

#httpGet
对容器的 IP 地址上指定端口和路径执行 HTTP GET 请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。

#tcpSocket
对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的。如果远程系统 (容器)在打开连接后立即将其关闭,这算作是健康的。

#grpc
使用 gRPC 执行一个远程过程调用。 目标应该实现 gRPC健康检查。 如果响应的状态是"SERVING",则认为诊断成功。 gRPC 探针是一个 alpha 特性,只有在你启用了"GRPCContainerProbe"特性门控时才能使用。

探测结果

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

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

测试

  1. 编辑 yaml 文件my-liveness.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600
    livenessProbe:
      exec:	# exec方式检测
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5	# 容器启动成功后5s,开始执行探针检测
      periodSeconds: 5			# 每隔5s检测一次

从 yaml 文件中可以看出:首先创建目录/tmp/healthy,每隔5秒进行探针检测,检测目录是否存在,pod 创建完成后30s,删除目录,这时候探针就检测不到了,重复检测,在600秒后,pod 结束

  1. 启动 pod
[root@k8s-master k8s]# vim my-liveness.yaml
[root@k8s-master k8s]# kubectl apply -f my-liveness.yaml 
pod/liveness-exec configured
  1. 查看 pod 描述信息
kubectl describe pod liveness-exec
......
Events:
  Type     Reason     Age                  From               Message
  ----     ------     ----                 ----               -------
  Normal   Scheduled  2m26s                default-scheduler  Successfully assigned default/liveness-exec to k8s-node2
  Normal   Pulling    61s                  kubelet            Pulling image "busybox"
  Normal   Pulled     45s                  kubelet            Successfully pulled image "busybox" in 15.275s (15.275s including waiting)
  Normal   Created    45s                  kubelet            Created container liveness
  Normal   Started    45s                  kubelet            Started container liveness
  Warning  Unhealthy  1s (x3 over 11s)     kubelet            Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
  Normal   Killing    1s                   kubelet            Container liveness failed liveness probe, will be restarted

可以看到,容器在创建30秒后Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory无法打开这个目录了,这时候容器探针检测失败,重新启动

我们这个案例是使用exec机制(在容器内执行指定命令)来检测探针是否成功,官网还有 httpGet 请求和 tcpSocket,自行测试即可

节点亲和性

问题:每次pod调度都是随机事件,不知道pod会被调度到哪一个节点

Scheduler 是 k8s 的调度器,主要的任务是把定义的 pod 分配到集群的节点上。听起来非常简单,但有很多要考虑的问题:

  • 公平:如何保证每个节点都能被分配资源
  • 资源高效利用:集群所有资源最大化被使用
  • 效率:调度的性能要好,能够尽快地对大批量的 pod 完成调度工作
  • 灵活:允许用户根据自己的需求控制调度的逻辑

Scheduler 是作为单独的程序运行的,启动之后会一直监听 API Server ,获取 Pod.Spec.NodeName 为空的 pod,对每个 pod 都会创建一个 binding,表明该 pod 应该放到哪个节点上。

调度的几个部分

  • 首先是过滤掉不满足条件的节点,这个过程称为 predicate(预选);
  • 然后对通过的节点按照优先级排序,这个是 priority(优选);
  • 最后从中选择优先级最高的节点。

如果中间任何一步骤有错误,就直接返回错误(先预选,后优选)

Predicate(预选)有一系列的算法可以使用:

  • PodFitsResources:节点上剩余的资源是否大于 pod 请求的资源
  • PodFitsHost:如果 pod 指定了 NodeName,检查节点名称是否和 NodeName 匹配
  • PodFitsHostPorts:节点上已经使用的 port 是否和 pod 申请的 port 冲突
  • PodSelectorMatches:过滤掉和 pod 指定的 label 不匹配的节点
  • NoDiskConflict:已经 mount 的 volume 和 pod 指定的 volume 不冲突,除非它们都是只读

如果在 predicate 过程中没有合适的节点,pod 会一直在 pending 状态(pending:等待),不断重试调度,直到有节点满足条件。经过这个步骤,如果有多个节点满足条件,就继续 priorities 过程:按照优先级大小对节点排序。

优先级由一系列键值对组成,键是该优先级项的名称,值是它的权重(该项的重要性)。这些优先级选项包括:

  • LeastRequestedPriority:通过计算 CPU 和 Memory 的使用率来决定权重,使用率越低权重越高。换句话说,这个优先级指标倾向于资源使用比例更低的节点

  • BalancedResourceAllocation:节点上 CPU 和 Memory 使用率越接近,权重越高。这个应该和上面的一起使用,不应该单独使用

  • ImageLocalityPriority:倾向于已经有要使用镜像的节点,镜像总大小值越大,权重越高

通过算法对所有的优先级项目和权重进行计算,得出最终的结果

pod 与 node 的亲和性

pod.spec.nodeAffinity

  • preferredDuringSchedulingIgnoredDuringExecution(优先执行计划):软策略

  • requiredDuringSchedulingIgnoredDuringExecution(要求执行计划):硬策略
    (preferred:首选,较喜欢 required:需要,必须)

键值运算关系

  • In:label 的值在某个列表中
  • NotIn:label 的值不在某个列表中
  • Gt:label 的值大于某个值
  • Lt:label 的值小于某个值
  • Exists:某个 label 存在
  • DoesNotExist:某个 label 不存在

在这里插入图片描述

在这里插入图片描述

#软硬策略(先满足硬策略再满足软策略)
apiVersion: v1
kind: Pod
metadata:
  name: affinity2
  labels:
    app: node-affinity-pod
spec:
  containers:
  - name: with-node-affinity
    image: nginx
  affinity:
    # 可以编写多个亲和性策略
    nodeAffinity:        #node亲和性
      # 硬亲和性限制
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname # 标签键名
            operator: NotIn      #键值运算关系 ,NotIn:label的值不在某个列表中。 表示不是node02节点就可运行
            values:
            - k8s-node02 # 标签键值
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1   #权重,权重越大越亲和(多个软策略的情况)
        preference: 
          matchExpressions:
          - key: kubernetes.io/hostname
            operator: In
            values:
            - k8s-node03  # 期望是node03

pod 与 pod 的亲和性

pod.spec.affinity.podAffinity 亲和性 podAntiAffinity 反亲和性

  • preferredDuringSchedulingIgnoredDuringExecution:软策略
  • requiredDuringSchedulingIgnoredDuringExecution:硬策略

要有选择中的 Pod —— 才可以说是 Pod 间 —— 也就是要有 labelSelector(若没有设置此字段,表示没有选中的 Pod)

topology 就是拓扑的意思,这里指的是一个拓扑域,是指一个范围的概念,比如一个 Node、一个机柜、一个机房或者是一个地区(如杭州、上海)等,实际上对应的还是 Node 上的标签。

topologyKey 可以理解为 Node 的 label,比如默认所有节点都会有 kubernetes.io/hostname 这 label,相应的值为节点名称,如 master01 节点的 label 为 kubernetes.io/hostname=master01,这种情况每个节点对应的值都不同。

pod亲和性调度需要各个相关的 pod 对象运行于"同一位置", 而反亲和性调度则要求他们不能运行于"同一位置",这里指定“同一位置” 是通过 topologyKey 来定义的。

在这里插入图片描述

在我这个集群中,找到我期望的pod启动节点:pod亲和性策略:标签: app=node-affinity-pod

反亲和性 podAntiAffinity

在这里插入图片描述

Taint 和 Toleration(污点和容忍)

所谓污点就是故意给某个节点服务器上设置个污点参数,那么你就能让生成 pod 的时候使用相应的参数去避开有污点参数的 node 服务器。而容忍呢,就是当资源不够用的时候,即使这个 node 服务器上有污点,那么只要 pod 的 yaml 配置文件中写了容忍参数,最终 pod 还是会容忍的生成在该污点服务器上。默认 master 节点是 NoSchedule(不会调度任何新的 Pod)。

污点(Taint)是应用在节点机器 node 上之上的,从这个名字就可以看出来,是为了排斥 pod 所存在的。

容忍度(Toleration)是应用于 Pod 上的,允许(但并不要求)Pod 调度到带有与之匹配的污点的节点上。

Taint(污点)和 Toleration(容忍)可以作用于 node 和 pod 上,其目的是优化 pod 在集群间的调度,这跟节点亲和性类似,只不过它们作用的方式相反,具有 taint 的 node 和 pod 是互斥关系,而具有节点亲和性关系的 node 和 pod 是相吸的。另外还有可以给 node 节点设置 label,通过给 pod 设置 nodeSelector 将 pod 调度到具有匹配标签的节点上。

Taint 和 toleration 相互配合,可以用来避免 pod 被分配到不合适的节点上。每个节点上都可以应用一个或多个 taint ,这表示对于那些不能容忍这些 taint 的 pod,是不会被该节点接受的。如果将 toleration 应用于 pod 上,则表示这些 pod 可以(但不要求)被调度到具有相应 taint 的节点上。

基本操作

查看某个节点的 Taint 信息

kubectl describe node nodename
[root@k8s-master01 ~]# kubectl describe node k8s-node01 
......
Taints:             <none>   # 关注这个地方即可 ---没有设置过污点的节点属性中的参数是这样的Taints:     <none>
......

为 node 设置 taint

使用 kubectl taint 命令可以给某个 Node 节点设置污点,Node 被设置上污点之后就和 Pod 之间存在了一种相斥的关系,可以让 Node 拒绝 Pod 的调度执行,甚至将 Node 已经存在的 Pod 驱逐出去。

每个污点有一个 key 和 value 作为污点的标签,其中 value 可以为空,effect 描述污点的作用。当前 taint effect 支持如下三个选项:

# NoSchedule:表示k8s将不会将Pod调度到具有该污点的Node上

# PreferNoSchedule:表示k8s将尽量避免将Pod调度到具有该污点的Node上

# NoExecute:表示k8s将不会将Pod调度到具有该污点的Node上,同时会将Node上已经存在的Pod驱逐出去,赶到其它节点上

这也是为什么生成的pod不会分配到master的原因(因为天生就打了这个污点NoSchedule)

接下来我们给节点node1增加一个污点,它的键名是key1,键值是value1,效果是NoSchedule。 这表示只有拥有和这个污点相匹配的容忍度的 Pod 才能够被分配到node1这个节点。

为 node1 设置 taint:

kubectl taint nodes k8s-node1 key1=value1:NoSchedule

删除上面的 taint:

# 去除污点,最后一个"-"代表删除
kubectl taint nodes k8s-node1 key1:NoSchedule-

为 pod 设置 toleration

只要在 pod 的 spec 中设置 tolerations 字段即可,可以存在多个 key,如下所示:

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
- key: "node.alpha.kubernetes.io/unreachable"
  operator: "Exists"
  effect: "NoExecute"
  tolerationSeconds: 6000
  • value 的值可以为 NoSchedulePreferNoScheduleNoExecute
  • tolerationSeconds 是当 pod 需要被驱逐时,可以继续在 node 上运行的时间。

多个污点匹配原则

可以给一个节点添加多个污点,也可以给一个 Pod 添加多个容忍度设置。

k8s 处理多个污点和容忍度的过程就像一个过滤器:从一个节点的所有污点开始遍历, 过滤掉那些 Pod 中存在与之相匹配的容忍度的污点。余下未被过滤的污点的 effect 值决定了 Pod 是否会被分配到该节点。

例如,假设您给一个节点添加了如下污点:

kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule

假定有一个 Pod,它有两个容忍度:

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"

上述 Pod 不会被分配到 node1 节点,因为其没有容忍度和第三个污点相匹配。

但是如果在给节点添加上述污点之前,该 Pod 已经在上述节点运行, 那么它还可以继续运行在该节点上,因为第三个污点是三个污点中唯一不能被这个 Pod 容忍的。

总结:

通过污点和容忍度,可以灵活地让 Pod 避开某些节点或者将 Pod 从某些节点驱逐。

如果您想将某些节点专门分配给特定的一组用户使用,您可以给这些节点添加一个污点(即, kubectl taint nodes nodename dedicated=groupName:NoSchedule), 然后给这组用户的 Pod 添加一个相对应的 toleration。

拥有上述容忍度的 Pod 就能够被分配到上述专用节点,同时也能够被分配到集群中的其它节点。

在部分节点配备了特殊硬件(比如 GPU)的集群中, 我们希望不需要这类硬件的 Pod 不要被分配到这些特殊节点,以便为后继需要这类硬件的 Pod 保留资源。

要达到这个目的,可以先给配备了特殊硬件的节点添加 taint (例如 kubectl taint nodes nodename special=true:NoSchedulekubectl taint nodes nodename special=true:PreferNoSchedule), 然后给使用了这类特殊硬件的 Pod 添加一个相匹配的 toleration。

指定调度节点

亲和性和污点,容忍都比较含蓄。

指定调度节点是绝对指定目标,我就要这个 node

Pod.spec.nodeName 将 Pod 直接调度到指定的 Node 节点上,会跳过 Scheduler 的调度策略,该匹配规则是强制匹配

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: myweb
spec:
  replicas: 7
  template:
    metadata:
      labels:
        app: nginx
    spec:
      nodeName: k8s-node01 #指定全在node01
      containers:
      - name: myweb
        image: nginx
        ports:
        - containerPort: 80

Pod.spec.nodeSelector:通过 k8s 的 label-selector 机制选择节点,由调度器调度策略匹配 label,而后调度 Pod 到目标节点,该匹配规则属于强制约束

首先在 master 节点给需要调度的目标 node 打上 label,并在创建 pod 的 yaml 文件中指定 label 来使得 pod 创建的时候调度到指定标签的 node 节点。

# 首先给指定的 node 节点加上标签
kubectl label nodes  kube-node01 disk=ssd
kubectl get nodes kube-node01 --show-labels |grep disk
 
# 编写 yaml 文件引入标签
[root@kube-master scheduler]# vim nodeLabels-test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-busybox
spec:
  replicas: 1
  selector:
    matchLabels:
      app: busybox
  template:
    metadata:
      labels:
        app: busybox
    spec:
      containers:
      - name: busybox
        image: busybox
        imagePullPolicy: IfNotPresent
      nodeSelector:                  # 定义 nodeSelector 参数
        disk: ssd                    # 引入标签

指定在某台节点启动的命令: nodename 、nodeSelector(需要再node上指定labels)

Pod 相关配置完整图

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/493167.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uniapp微信小程序消息订阅详解

一、微信公众平台申请订阅模板 注意&#xff1a;订阅信息 这个事件 是 当用户 点击的时候触发 或者 是 支付成功后触发&#xff0c; 用户勾选 “总是保持以上选择&#xff0c;不再询问” 之后或长期订阅&#xff0c;下次订阅调用 wx.requestSubscribeMessage 不会弹窗&#xf…

浅析扩散模型与图像生成【应用篇】(十三)——PITI

13. Pretraining is All You Need for Image-to-Image Translation 该文提出一种基于预训练扩散模型的图像转换方法&#xff0c;称为PITI。其思想并不复杂&#xff0c;就是借鉴现有视觉和NLP领域中常见的预训练方法&#xff0c;考虑预先在一个大规模的任务无关数据集上对扩散模…

基于java+springboot+vue实现的校园二手交易系统(文末源码+Lw+ppt)23-336

摘 要 自从新冠疫情爆发以来&#xff0c;各个线下实体越来越难做&#xff0c;线下购物的人也越来越少&#xff0c;随之带来的是一些不必要的浪费&#xff0c;尤其是即将毕业的大学生&#xff0c;各种用品不方便携带走导致被遗弃&#xff0c;造成大量的浪费。本系统目的就是让…

浏览器如何渲染页面

浏览器渲染页面的过程是一个多步骤的复杂流程&#xff0c;下面我将通过一个简单的例子来逐步说明这一过程&#xff1a; 以下是浏览器渲染该页面的主要步骤&#xff1a; 请求HTML文档&#xff1a; 用户在浏览器中输入URL并回车&#xff0c;浏览器向服务器发送HTTP请求。 服务器…

【Java多线程】1——多线程知识回顾

1 多线程知识回顾 ⭐⭐⭐⭐⭐⭐ Github主页&#x1f449;https://github.com/A-BigTree 笔记仓库&#x1f449;https://github.com/A-BigTree/tree-learning-notes 个人主页&#x1f449;https://www.abigtree.top ⭐⭐⭐⭐⭐⭐ 如果可以&#xff0c;麻烦各位看官顺手点个star…

测试开发工程师(QA)职业到底需要干些什么?part6:数据测试工程师QA

常见任务和工作内容 作为数据测试工程师QA&#xff08;Quality Assurance&#xff09;&#xff0c;您的主要职责是确保数据的质量、准确性和一致性。以下是数据测试工程师QA的一些常见任务和工作内容&#xff1a; 数据验证和准确性测试&#xff1a;您将负责验证数据的准确性和…

数论问题代码模板

文章目录 一、质数1.1、质数筛&#xff08;筛1~n中的所有质数&#xff09;1.2、判断一个数是否为质数1.3、对一个数进行质因数分解 二、快速幂2.1、费马小定理——乘法逆元2.2、快速幂 三、约数3.1、N个数的正约数集合3.2、一个数的正约数集合 四、欧拉函数&#xff08;互质数数…

洗地机好用吗?哪款型号值得推荐?看完本文你就知道

在如今社会生活节奏不断加快的情况下&#xff0c;洗地机已经成为众多家庭的必备的清洁设备&#xff0c;面对市面上种类繁多的洗地机&#xff0c;我们常常会发出感叹“洗地机好用吗&#xff1f;洗地机哪个型好用&#xff1f;”等的疑问&#xff0c;今天&#xff0c;为了帮助大家…

一文搞定用python实现终身免费的听书工具

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

C语言程序练习——汉诺塔递归

1. 题目 在终端输入汉诺塔层数n&#xff0c;实现将n层汉诺塔通过三座塔座A、B、C进行排列 2. 代码 #include <stdio.h>int hannuota(int len, int str, int tmp, int dst) {if (1 len){printf("%c -> %c\n", str, dst);}else{hannuota(len-1, str, dst, …

好展位、抢先订!2024第二十二届上海国际涂料展|上海涂料展

致/To: 展会负责人、 市场部、 企划部、 销售部负责人 2024中国国际涂料博览会暨第二十二届中国国际涂料展览会 时间&#xff1a;2024年8月7-9日 地点&#xff1a;上海新国际博览中心 主办方&#xff1a; 中国涂料工业协会 承办方&#xff1a; 北京涂博国际展览有限公司 …

javaSwing坦克大战游戏

在游戏开发领域&#xff0c;坦克大战是一款经典的游戏&#xff0c;其简单而又耐玩的玩法吸引了无数玩家。而今&#xff0c;在Java编程技术的支持下&#xff0c;我们可以用Java Swing技术轻松实现这款经典游戏。本文将介绍如何使用Java Swing技术编写坦克大战游戏&#xff0c;并…

某对象存储元数据集群改造流水账

软件产品&#xff1a;某厂商提供的不便具名的对象存储产品&#xff0c;核心底层技术源自HDFS和Amazon S3&#xff0c;元数据集群采用了基于MongoDB的NOSQL数据库产品和MySQL数据库产品相结合。 该产品的元数据逻辑示意图如下&#xff1a; 业务集群现状&#xff1a;当前第3期建…

Qt 窗口MainWindow(上)

Qt 窗口是通过 QMainWindow 类来实现的。 QMainWindow 是一个为用户提供主窗口程序的类&#xff0c;继承自 QWidget 类&#xff0c;并且提供了⼀个预定义的布局。QMainWindow 包含一个菜单栏&#xff08;menubar&#xff09;、多个工具栏(toolbars)、多个浮动窗口&#xff08;…

JVM第八讲:GC - Java 垃圾回收基础知识

GC - Java 垃圾回收基础知识 本文是JVM第八讲&#xff0c; Java 垃圾回收基础知识。垃圾收集主要是针对堆和方法区进行&#xff1b;程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的&#xff0c;只存在于线程的生命周期内&#xff0c;线程结束之后也会消失&#xff0…

Vue3尚硅谷张天禹笔记

1. Vue3简介 2020年9月18日&#xff0c;Vue.js发布版3.0版本&#xff0c;代号&#xff1a;One Piece&#xff08;n 经历了&#xff1a;4800次提交、40个RFC、600次PR、300贡献者 官方发版地址&#xff1a;Release v3.0.0 One Piece vuejs/core 截止2023年10月&#xff0c;最…

Java零基础入门到精通_Day 3

37 switch default&#xff1a; 后面的break;可以省略 38 春夏秋冬 注意事项:在switch语句中&#xff0c;如果case控制的语句体后面不写break&#xff0c;将出现穿透现象&#xff0c;在不判断下一个case值的情况下&#xff0c;向下运行 直到遇到break&#xff0c;或者整体swi…

在Python中进行封装

在Python中&#xff0c;封装是一种面向对象编程&#xff08;OOP&#xff09;的特性&#xff0c;它允许我们将数据&#xff08;属性&#xff09;和操作这些数据的方法&#xff08;函数&#xff09;捆绑在一起&#xff0c;形成一个独立的对象。封装的主要目的是隐藏对象的内部状态…

如何保证缓存与数据库的双写一致性?

如何保证缓存与数据库的双写一致性&#xff1f; 概述同步策略更新缓存还是删除缓存&#xff1a;先操作数据库还是缓存&#xff1a;案例一、先删除缓存&#xff0c;在更新数据库案例二 先操作数据库&#xff0c;再删除缓存 延时双删策略&#xff08;不推荐&#xff09;使用分布式…

Java拆装箱及128陷阱

有以下一段代码&#xff1a; Integer a 123; Integer b 123; int c 123; int d 123; System.out.println(c d); System.out.println(a b); System.out.println(a c); 这段代码运行的结果是什么呢&#xff1f; c d 一定为True。 由于Java中存在自动拆装箱&#xff0…