目 录CONTENT

文章目录

高级调度

简中仙
2022-05-10 / 0 评论 / 0 点赞 / 78 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
本文最后更新于2024-01-14,若内容或图片失效,请留言反馈。 本文如有错误或者侵权的地方,欢迎您批评指正!

这篇学习笔记是基于杜宽老师在51CTO上发布的视频课程制作的。在此,感谢杜宽老师的分享和教学。如有侵权,请及时联系我。版权归原作者所有,未经允许不得转载或使用。

一、计划任务CronJob&Job

1、CronJob介绍

在k8s 里面运行周期性的计划任务,crontab

* * * * *  分 时 日 月 周

可以利用 CronJobs 执行基于时间调度的任务。这些自动化任务和 Linux 或者 Unix 系统的 Cron 任务类似。

CronJobs在创建周期性以及重复性的任务时很有帮助,例如执行备份操作或者发送邮件。CronJobs 也可以在特定时间调度单个任务,例如你想调度低活跃周期的任务。

2、Job配置参数详解

apiVersion: batch/v1
kind: Job
metadata:
  labels:
    job-name: echo
  name: echo
  namespace: default
spec:
  #suspend: true # 1.21+版本支持
  #ttlSecondsAfterFinished: 100		# 需要开启TTLAfterFinished特性
  backoffLimit: 4	
  completions: 5
  parallelism: 3
  template:
    spec:
      containers:
      - command:
        - echo
        - Hello, Job
        image: registry.cn-beijing.aliyuncs.com/dotbalo/busybox
        imagePullPolicy: IfNotPresent
        name: echo
        resources: {}
      restartPolicy: Never
# 创建job
# kubectl create -f job.yaml 
参数说明
backoffLimit如果任务执行失败,失败多少次后不再执行
completions有多少个Pod执行成功,认为任务是成功的。为空默认和parallelism数值一样
parallelism并行执行任务的数量。如果parallelism数值大于未完成任务数,只会创建未完成的数量;比如completions是4,并发是3,第一次会创建3个Pod执行任务,第二次只会创建一个Pod执行任务
ttlSecondsAfterFinishedJob在执行结束之后(状态为completed或Failed)自动清理。设置为0表示执行结束立即删除,不设置则不会清除,需要开启TTLAfterFinished特性

3、查看job

# kubectl get jobs
NAME               COMPLETIONS   DURATION   AGE
echo-ds26p   		0/1           7m44s      7m44s
echo-f95mz   		0/1           6m42s      6m42s
echo-hdzbv   		0/1           5m41s      5m41s
echo-pwr7z   		0/1           4m50s      4m50s
echo-tln7h   		0/1           3m49s      3m49s
# 删除
# kubectl delete cronjob echo
cronjob.batch "echo" deleted

4、CronJob配置参数详解

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  labels:
    run: hello
  name: hello
  namespace: default
spec:
  concurrencyPolicy: Allow
  failedJobsHistoryLimit: 1
  jobTemplate:
    metadata:
    spec:
      template:
        metadata:
          labels:
            run: hello
        spec:
          containers:
          - args:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
            image: registry.cn-beijing.aliyuncs.com/dotbalo/busybox
            imagePullPolicy: Always
            name: hello
            resources: {}
          restartPolicy: OnFailure
          securityContext: {}
  schedule: '*/1 * * * *'
  successfulJobsHistoryLimit: 3
  suspend: false
参数说明
apiVersion: batch/v1beta11.21+ 版本为batch/v1
schedule调度周期,和Linux一致,分别是分时日月周
restartPolicy重启策略,和Pod一致
concurrencyPolicy并发调度策略。可选参数如下:Allow、Forbid、Replace
Allow允许同时运行多个任务
Forbid不允许并发运行,如果之前的任务尚未完成,新的任务不会被创建
Replace如果之前的任务尚未完成,新的任务会替换的之前的任务
suspend如果设置为true,则暂停后续的任务,默认为false
successfulJobsHistoryLimit保留多少已完成的任务,按需配置
failedJobsHistoryLimit保留多少失败的任务

查看CronJob

# kubectl get cj
NAME     schedule       SUSPEND		ACTIVE		LAST SCHEDULE	AGE	
hello   */1 * * * *		False		 0			9s				21s
# kubectl get jobs
NAME               COMPLETIONS   DURATION   AGE
hello-26987582     1/1           1s      40s

二、初始化容器InitContainer

Init Container就是用来做初始化工作的容器,可以是一个或者多个,如果有多个的话,这些容器会按定义的顺序依次执行,只有所有的Init Container执行完后,主容器才会被启动。我们知道一个Pod里面的所有容器是共享数据卷和网络命名空间的,所以Init Container里面产生的数据可以被主容器使用到的。

Init Container与应用容器本质上是一样的,但他们是仅运行一次就结束的任务,并且必须在成功执行完后,系统才能继续执行下一个容器

1、用途

  • Init 容器可以包含一些安装过程中应用容器中不存在的实用工具或个性化代码;
  • Init 容器可以安全地运行这些工具,避免这些工具导致应用镜像的安全性降低;
  • Init容器可以以root身份运行,执行一些高权限命令;
  • Init容器相关操作执行完成以后即退出,不会给业务容器带来安全隐患

2、InitContainer应用场景

  • 等待其他量关联组件正确运行(例如solr启动先依赖zookeeper)
  • 基于环境变量或配置模板生成配置文件
  • 从远程数据库获取本地所需配置,或者将吱声注册到某个中央数据库中
  • 下载相关依赖包,或者对系统进行一些预配置操作(可以用python或者bash对系统做初始化操作)

3、初始化容器和PostStart区别

  • PostStart:依赖主应用的环境,而且并不一定先于Command运行
  • InitContainer:不依赖主应用的环境,可以有更高的权限和更多的工具,一定会在主应用启动之前完成

4、初始化容器和普通容器的区别

  • Init 容器与普通的容器非常像,除了如下几点:
  • 它们总是运行到完成;
  • 上一个运行完成才会运行下一个;
  • 如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止,但是Pod 对应的 restartPolicy 值为 Never,Kubernetes 不会重新启动 Pod。
  • Init 容器不支持 lifecycle、livenessProbe、readinessProbe 和 startupProbe

5、初始化容器配置解析

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: es-cluster
spec:
  serviceName: elasticsearch
  replicas: 3
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      labels:
        app: elasticsearch
    spec:
      volumes:
      - name: data
        emptyDir: {}
      initContainers:
      - name: fix-permissions
        image: busybox
        command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
        securityContext:
          privileged: true
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
      - name: increase-vm-max-map
        image: busybox
        command: ["sysctl", "-w", "vm.max_map_count=262144"]
        securityContext:
          privileged: true
      - name: increase-fd-ulimit
        image: busybox
        command: ["sh", "-c", "ulimit -n 65536"]
        securityContext:
          privileged: true
      containers:
      - name: elasticsearch
        #image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.3
        image: dotbalo/es:2.4.6-cluster
        imagePullPolicy: Always
        resources:
            limits:
              cpu: 1000m
            requests:
              cpu: 100m
        ports:
        - containerPort: 9200
          name: rest
          protocol: TCP
        - containerPort: 9300
          name: inter-node
          protocol: TCP
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
        env:
          - name: "cluster.name"
            value: "pscm-cluster"
          - name: "CLUSTER_NAME"
            value: "pscm-cluster"
          - name: "discovery.zen.minimum_master_nodes"
            value: "2"
          - name: "MINIMUM_MASTER_NODES"
            value: "2"
          - name: "node.name"
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: "NODE_NAME"
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: "discovery.zen.ping.unicast.hosts"
            value: "es-cluster-0.elasticsearch, es-cluster-1.elasticsearch, es-cluster-2.elasticsearch"
          #- name: ES_JAVA_OPTS
          #  value: "-Xms512m -Xmx512m"

6、初始容器使用

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: test-init
  name: test-init
  namespace: kube-public
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test-init
  template:
    metadata:
      labels:
        app: test-init
    spec:
      volumes:
      - name: data
        emptyDir: {}
      initContainers:
      - command:
        - sh
        - -c
        - touch /mnt/test-init.txt
        image: nginx
        imagePullPolicy: IfNotPresent
        name: init-touch
        volumeMounts:
        - name: data
          mountPath: /mnt
      containers:
      - image: nginx
        imagePullPolicy: IfNotPresent
        name: test-init
        volumeMounts:
        - name: data
          mountPath: /mnt

三、临时容器EphemeralContainer

1、什么是临时容器

临时容器:一种特殊的容器,该容器在现有 Pod 中临时运行,以便完成用户发起的操作,例如故障排查。 你会使用临时容器来检查服务,而不是用它来构建应用程序。

临时容器与其他容器的不同之处在于,它们缺少对资源或执行的保证,并且永远不会自动重启, 因此不适用于构建应用程序。 临时容器使用与常规容器相同的 ContainerSpec 节来描述,但许多字段是不兼容和不允许的。

  • 临时容器没有端口配置,因此像 portslivenessProbereadinessProbe 这样的字段是不允许的。
  • Pod 资源分配是不可变的,因此 resources 配置是不允许的。
  • 有关允许字段的完整列表,请参见 EphemeralContainer 参考文档

临时容器是使用 API 中的一种特殊的 ephemeralcontainers 处理器进行创建的, 而不是直接添加到 pod.spec 段,因此无法使用 kubectl edit 来添加一个临时容器。

与常规容器一样,将临时容器添加到 Pod 后,将不能更改或删除临时容器。

2、开启临时容器

1.25+版本默认开启

1、二进制

# vim /usr/lib/systemd/system/kube-apiserver.service
--feature-gates=EphemeralContainers=true
# vim /etc/kubernetes/kubelet-conf.yml
featureGates:
  EphemeralContainers: true
#重启所有服务kube-apiserver kubelet

2、kubeadm

#vim /etc/kubernetes/manifests/kube-apiserver.yaml
--feature-gates=EphemeralContainers=true
#vim /var/lib/kubelet/config.yaml
featureGates:
  EphemeralContainers: true
#重启kubelet

3、临时容器使用

K8s 1.20+

kubectl debug redis-new-5b577b46c7-2jv4j -ti --image=registry.cn-beijing.aliyuncs.com/dotbalo/debug-tools
kubectl debug node/k8s-node01 -it --image=registry.cn-beijing.aliyuncs.com/dotbalo/debug-tools

临时容器是使用 Pod 的 ephemeralcontainers 子资源创建的,可以使用 kubectl --raw 命令进行显示。 首先描述临时容器被添加为一个 EphemeralContainers 列表:

{
    "apiVersion": "v1",
    "kind": "EphemeralContainers",
    "metadata": {
        "name": "example-pod"
    },
    "ephemeralContainers": [{
        "command": [
            "sh"
        ],
        "image": "busybox",
        "imagePullPolicy": "IfNotPresent",
        "name": "debugger",
        "stdin": true,
        "tty": true,
        "terminationMessagePolicy": "File"
    }]
}

使用如下命令更新已运行的临时容器 example-pod

kubectl replace --raw /api/v1/namespaces/default/pods/example-pod/ephemeralcontainers  -f ec.json

这将返回临时容器的新列表:

{
   "kind":"EphemeralContainers",
   "apiVersion":"v1",
   "metadata":{
      "name":"example-pod",
      "namespace":"default",
      "selfLink":"/api/v1/namespaces/default/pods/example-pod/ephemeralcontainers",
      "uid":"a14a6d9b-62f2-4119-9d8e-e2ed6bc3a47c",
      "resourceVersion":"15886",
      "creationTimestamp":"2019-08-29T06:41:42Z"
   },
   "ephemeralContainers":[
      {
         "name":"debugger",
         "image":"busybox",
         "command":[
            "sh"
         ],
         "resources":{

         },
         "terminationMessagePolicy":"File",
         "imagePullPolicy":"IfNotPresent",
         "stdin":true,
         "tty":true
      }
   ]
}

可以使用以下命令查看新创建的临时容器的状态:

kubectl describe pod example-pod

输出为:

...
Ephemeral Containers:
  debugger:
    Container ID:  docker://cf81908f149e7e9213d3c3644eda55c72efaff67652a2685c1146f0ce151e80f
    Image:         busybox
    Image ID:      docker-pullable://busybox@sha256:9f1003c480699be56815db0f8146ad2e22efea85129b5b5983d0e0fb52d9ab70
    Port:          <none>
    Host Port:     <none>
    Command:
      sh
    State:          Running
      Started:      Thu, 29 Aug 2019 06:42:21 +0000
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:         <none>
...

可以使用以下命令连接到新的临时容器:

kubectl attach -it example-pod -c debugger

四、污点和容忍Taint&Toleration

1、什么是Taint和Toleration

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

设计理念:Taint在一类服务器上打上污点,让不能容忍这个污点的Pod不能部署在打了污点的服务器上。Toleration是让Pod容忍节点上配置的污点,可以让一些需要特殊配置的Pod能够调用到具有污点和特殊配置的节点上。

官方文档:https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/taint-and-toleration/

2、Taint(污点)

1、污点(Taint)的组成

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

创建一个污点

kubectl taint nodes NODE_NAME TAINT_KEY=TAINT_VALUE:EFFECT

kubectl taint nodes k8s-node01 ssd=true:PreferNoSchedule
Taint配置解析
NoSchedule禁止调度到该节点,已经在该节点上的Pod不受影响
NoExecute禁止调度到该节点,如果不符合这个污点,会立马被驱逐(或在一段时间后)
PreferNoSchedule尽量避免将Pod调度到指定的节点上,如果没有更合适的节点,可以部署到该节点

2、查看某个节点的Taint配置情况

查看某个节点的Taint信息

# kubectl  get node k8s-node01 -o go-template --template {{.spec.taints}}
# kubectl describe node k8s-node01 | grep Taints -A 10
Name:               k8s-node01
Roles:              <none>
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    disktype=ssd
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=k8s-node01
                    kubernetes.io/os=linux
                    node.kubernetes.io/node=
Annotations:        node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Mon, 21 Dec 2020 05:13:32 +0800
Taints:             <none>   # 关注这个地方即可 ---没有设置过污点的节点属性中的参数是这样的Taints:     <none>
Unschedulable:      false

3、给某个节点服务器打上污点标签

给节点k8s-node01服务器打上污点标签NoExecute(TAINT_KEY=TAINT_VALUE:EFFECT)

kubectl taint nodes k8s-node01 check=xtaint:NoExecute

4、删除某个节点上的设置的污点

基于Key删除

kubectl taint nodes k8s-node01 ssd-

基于Key+Effect删除

kubectl taint nodes k8s-node02 test=xtaint:NoExecute-

5、修改污点

Key和Effect相同

kubectl  taint nodes k8s-node01 ssd=true:PreferNoSchedule --overwrite

3、Toleration(容忍)

1、先在k8s-node02节点上打上一个NoSchedule

# 打上NoExecute,k8s-node02、k8s-master01、k8s-master02节点上的pod都会自动被删除
# kubectl taint nodes k8s-node02 test=xtaint:NoExecute
node/k8s-node02 tainted

2、创建一个包含有容忍toleration的配置文件

test-taint-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.5.2
  tolerations:				# 容忍
  - key: "check"				# 容忍的键
    operator: "Equal"				# 操作符"等于"
    value: "xtaint"				# 容忍的键对应的键值
    effect: "NoExecute"				# 容忍的键对应的影响效果
    tolerationSeconds: 3600				# 容忍3600秒。本pod配置文件中有这个参数了,然后再给本服务器设置污点NoExecute,那么这个pod也不会像普通pod那样立即被驱逐,而是再等上3600秒才被删除。
# kubectl create -f test-taint-pod.yaml 
pod/nginx created

3、toleration配置解析

方式一:完全匹配:
tolerations:
- key: "taintKey"
  operator: "Equal"
  value: "taintValue"
  effect: "NoSchedule"

方式二:不完全匹配:
tolerations:
- key: "taintKey"
  operator: "Exists"
  effect: "NoSchedule"

方式三:大范围匹配(不推荐key为内置Taint):
- key: "taintKey"
  operator: "Exists"

方式四匹配所有(不推荐):
tolerations:
- operator: "Exists"

停留时间配置:
tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
  tolerationSeconds: 3600 

4、内置污点、

配置说明
node.kubernetes.io/not-ready节点未准备好,相当于节点状态Ready的值为False
node.kubernetes.io/unreachableNode Controller访问不到节点,相当于节点状态Ready的值为Unknown
node.kubernetes.io/out-of-disk节点磁盘耗尽
node.kubernetes.io/memory-pressure节点存在内存压力
node.kubernetes.io/disk-pressure节点存在磁盘压力
node.kubernetes.io/network-unavailable节点网络不可达
node.kubernetes.io/unschedulable节点不可调度
node.cloudprovider.kubernetes.io/uninitialized如果Kubelet启动时指定了一个外部的cloudprovider,它将给当前节点添加一个Taint将其标记为不可用。在cloud-controller-manager的一个controller初始化这个节点后,Kubelet将删除这个Taint。

Kubernetes会自动给Pod添加一个key为node.kubernetes.io/not-ready的Toleration并配置tolerationSeconds=300,同样也会给Pod添加一个key为node.kubernetes.io/unreachable的Toleration并配置tolerationSeconds=300,除非用户自定义了上述key,否则会采用这个默认设置。

一个使用了很多本地状态的应用程序在网络断开时,仍然希望停留在当前节点上运行一段时间,愿意等待网络恢复以避免被驱逐。在这种情况下,Pod的Toleration可以这样配置:节点不健康,6000秒后再驱逐(默认是300秒)

tolerations:
- key: "node.kubernetes.io/unreachable"
  operator: "Exists"
  effect: "NoExecute"
  tolerationSeconds: 6000

5、节点宕机快速恢复业务应用

快速迁移

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: tolerations-second
  name: tolerations-second
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tolerations-second
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: tolerations-second
    spec:
      containers:
      - image: registry.cn-beijing.aliyuncs.com/dotbalo/nginx
        name: nginx
        resources: 
          requests:
            cpu: 10m
      nodeSelector:
        ssd: "true"
      tolerations:
      - key: ssd
        operator: Equal
        value: "true"
      - effect: NoExecute
        key: node.kubernetes.io/unreachable
        operator: Exists
        tolerationSeconds: 10
      - effect: NoExecute
        key: node.kubernetes.io/not-ready
        operator: Exists
        tolerationSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: tolerations-second
  name: tolerations-second
spec:
  ports:
  - name: web
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: tolerations-second

四、亲和力Affinity

1、Affinity分类

Kubernetes中的调度策略可以大致分为两种

一种是全局的调度策略,要在启动调度器时配置,包括kubernetes调度器自带的各种predicates和priorities算法

另一种是运行时调度策略,包括nodeAffinity(主机亲和性),podAffinity(POD亲和性)以及podAntiAffinity(POD反亲和性)。

nodeAffinity 主要解决POD要部署在哪些主机,以及POD不能部署在哪些主机上的问题,处理的是POD和主机之间的关系。

podAffinity 主要解决POD可以和哪些POD部署在同一个拓扑域中的问题(拓扑域用主机标签实现,可以是单个主机,也可以是多个主机组成的cluster、zone等。)

podAntiAffinity主要解决POD不能和哪些POD部署在同一个拓扑域中的问题。它们处理的是Kubernetes集群内部POD和POD之间的关系。

三种亲和性和反亲和性策略的比较如下表所示:

策略名称匹配目标支持的操作符支持拓扑域设计目标
nodeAffinity主机标签In,NotIn,Exists,DoesNotExist,Gt,Lt不支持决定Pod可以部署在哪些主机上
podAffinityPod标签In,NotIn,Exists,DoesNotExist支持决定Pod可以和哪些Pod部署在同一拓扑域
PodAntiAffinityPod标签In,NotIn,Exists,DoesNotExist支持决定Pod不可以和哪些Pod部署在同一拓扑域

2、节点亲和力配置详解

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:		# 硬亲和力配置
        nodeSelectorTerms:		# 节点选择器配置,可以配置多个matchExpressions(满足其一),每个matchExpressions下可以配置多个key、value类型的选择器(都需要满足),其中values可以配置多个(满足其一)
        - matchExpressions:
          - key: kubernetes.io/e2e-az-name
            operator: In
            values:
            - e2e-az1
            - az-2
      preferredDuringSchedulingIgnoredDuringExecution:		# 软亲和力配置
      - weight: 		# 1软亲和力的权重,权重越高优先级越大,范围1-100
        preference:		# 软亲和力配置项,和weight同级,可以配置多个,matchExpressions和硬亲和力一致
          matchExpressions:
          - key: another-node-label-key
            operator: In		# 标签匹配的方式
            values:
            - another-node-label-value
  containers:
  - name: with-node-affinity
    image: nginx
operator标签参数说明
In相当于key = value的形式
NotIn相当于key != value的形式
Exists节点存在label的key为指定的值即可,不能配置values字段
DoesNotExist节点不存在label的key为指定的值即可,不能配置values字段
Gt大于value指定的值
Lt小于value指定的值

3、Pod亲和力和反亲和力详解

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:				# Pod选择器配置,可以配置多个
          matchExpressions:				# 和节点亲和力配置一致	
          - key: security
            operator: In				# 配置和节点亲和力一致,但是没有Gt和Lt
            values:
            - S1
        topologyKey: failure-domain.beta.kubernetes.io/zone				# 匹配的拓扑域的key,也就是节点上label的key,key和value相同的为同一个域,可以用于标注不同的机房和地区
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          namespaces:				# 和哪个命名空间的Pod进行匹配,为空为当前命名空间
          - default
          topologyKey: failure-domain.beta.kubernetes.io/zone
  containers:
  - name: with-pod-affinity
    image: nginx

1、同一个应用部署在不同的宿主机

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: must-be-diff-nodes
  name: must-be-diff-nodes
  namespace: kube-public
spec:
  replicas: 3
  selector:
    matchLabels:
      app: must-be-diff-nodes
  template:
    metadata:
      labels:
        app: must-be-diff-nodes
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:				# Pod选择器配置,可以配置多个
              matchExpressions:				# 和节点亲和力配置一致	
              - key: app
                operator: In				# 配置和节点亲和力一致,但是没有Gt和Lt
                values:
                - must-be-diff-nodes
            topologyKey: kubernetes.io/hostname				# 匹配的拓扑域的key,也就是节点上label的key,key和value相同的为同一个域,可以用于标注不同的机房和地区
      containers:
      - image: nginx
        imagePullPolicy: IfNotPresent
        name: must-be-diff-nodes

2、同一个应用不同副本固定节点

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-cache
spec:
  selector:
    matchLabels:
      app: store
  replicas: 3
  template:
    metadata:
      labels:
        app: store
    spec:
      nodeSelector:
          app: store
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: redis-server
        image: redis:3.2-alpine

3、应用和缓存尽量部署在同一个域内

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
spec:
  selector:
    matchLabels:
      app: web-store
  replicas: 3
  template:
    metadata:
      labels:
        app: web-store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web-store
            topologyKey: "kubernetes.io/hostname"
        podAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - store
              topologyKey: "kubernetes.io/hostname"
      containers:
      - name: web-app
        image: nginx:1.16-alpine

4、尽量调度到高配置服务器

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: prefer-ssd
  name: prefer-ssd
  namespace: kube-public
spec:
  replicas: 3
  selector:
    matchLabels:
      app: prefer-ssd
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: prefer-ssd
    spec:
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - preference:
              matchExpressions:
              - key: ssd
                operator: In
                values:
                - "true"
              - key: master
                operator: NotIn
                values:
                - "true"
            weight: 100
          - preference:
              matchExpressions:
              - key: type
                operator: In
                values:
                - physical
            weight: 10
      containers:
      - env:
        - name: TZ
          value: Asia/Shanghai
        - name: LANG
          value: C.UTF-8
        image: nginx
        imagePullPolicy: IfNotPresent
        name: prefer-ssd

五、Topology拓扑域

1、什么是topologyKey

顾名思义,topology 就是 拓扑 的意思,这里指的是一个 拓扑域,是指一个范围的概念,比如一个 Node、一个机柜、一个机房或者是一个地区(如杭州、上海)等,实际上对应的还是 Node 上的标签。这里的 topologyKey 对应的是 Node 上的标签的 Key(没有Value),可以看出,其实 topologyKey 就是用于筛选 Node 的。通过这种方式,我们就可以将各个 Pod 进行跨集群、跨机房、跨地区的调度了。

2、如何使用topologyKey

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: failure-domain.beta.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          topologyKey: kubernetes.io/hostname
  containers:
  - name: with-pod-affinity
    image: k8s.gcr.io/pause:2.0

这里 Pod 的亲和性规则是:这个 Pod 要调度到的 Node 必须有一个标签为 security: S1 的 Pod,且该 Node 必须有一个 Key 为 failure-domain.beta.kubernetes.io/zone 的 标签,即 Node 必须属于 failure-domain.beta.kubernetes.io/zone 拓扑域。

Pod 的反亲和性规则是:这个 Pod 尽量不要调度到这样的 Node,其包含一个 Key 为 kubernetes.io/hostname 的标签,且该 Node 上有标签为 security: S2 的 Pod。

3、topologyKey详解

既然 topologyKey 是拓扑域,那 Pod 之间怎样才是属于同一个拓扑域?

如果使用 k8s.io/hostname,则表示拓扑域为 Node 范围,那么 k8s.io/hostname 对应的值不一样就是不同的拓扑域。比如 Pod1 在 k8s.io/hostname=node1 的 Node 上,Pod2 在 k8s.io/hostname=node2 的 Node 上,Pod3 在 k8s.io/hostname=node1 的 Node 上,则 Pod2 和 Pod1、Pod3 不在同一个拓扑域,而Pod1 和 Pod3在同一个拓扑域。

如果使用 failure-domain.k8s.io/zone ,则表示拓扑域为一个区域。同样,Node 的标签 failure-domain.k8s.io/zone 对应的值不一样也不是同一个拓扑域,比如 Pod1 在 failure-domain.k8s.io/zone=beijing 的 Node 上,Pod2 在 failure-domain.k8s.io/zone=hangzhou 的 Node 上,则 Pod1 和 Pod2 不属于同一个拓扑域。

当然,topologyKey 也可以使用自定义标签。比如可以给一组 Node 打上标签 custom_topology,那么拓扑域就是针对这个标签了,则该标签相同的 Node 上的 Pod 属于同一个拓扑域。

4、注意事项

原则上,topologyKey 可以是任何合法的标签 Key。但是出于性能和安全原因,对 topologyKey 有一些限制:

  • 对于亲和性和 requiredDuringSchedulingIgnoredDuringExecution 的 Pod 反亲和性,topologyKey 不能为空。
  • 对于 requiredDuringSchedulingIgnoredDuringExecution 的 Pod 反亲和性,引入 LimitPodHardAntiAffinityTopology 准入控制器来限制 topologyKey 只能是 kubernetes.io/hostname。如果要使用自定义拓扑域,则可以修改准入控制器,或者直接禁用它。
  • 对于 preferredDuringSchedulingIgnoredDuringExecution 的 Pod 反亲和性,空的 topologyKey 表示所有拓扑域。截止 v1.12 版本,所有拓扑域还只能是 kubernetes.io/hostname、failure-domain.beta.kubernetes.io/zone 和 failure-domain.beta.kubernetes.io/region 的组合。
  • 除上述情况外,topologyKey 可以是任何合法的标签 key。

5、同一个应用多区域部署

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: must-be-diff-zone
  name: must-be-diff-zone
  namespace: kube-public
spec:
  replicas: 3
  selector:
    matchLabels:
      app: must-be-diff-zone
  template:
    metadata:
      labels:
        app: must-be-diff-zone
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - must-be-diff-zone
            topologyKey: region
      containers:
      - image: nginx
        imagePullPolicy: IfNotPresent
        name: must-be-diff-zone

六、资源配额ResourceQuota

中文官网文档:https://kubernetes.io/zh/docs/concepts/policy/limit-range/

1、ResourceQuota配置

apiVersion: v1
kind: ResourceQuota
metadata:
  name: resource-test
  labels:
    app: resourcequota
spec:
  hard:
    pods: 50
    requests.cpu: 0.5
    requests.memory: 512Mi
    limits.cpu: 5
    limits.memory: 16Gi
    configmaps: 20
    requests.storage: 40Gi
    persistentvolumeclaims: 20
    replicationcontrollers: 20
    secrets: 20
    services: 50
    services.loadbalancers: "2"
    services.nodeports: "10"
参数说明
pods限制最多启动Pod的个数
requests.cpu限制最高CPU请求数
requests.memory限制最高内存的请求数
limits.cpu限制最高CPU的limit上限
limits.memory限制最高内存的limit上限

2、配置命名空间下 Pod 配额

1、创建一个命名空间

kubectl create namespace quota-pod-example

2、创建 ResourceQuota,指定改ns只可以创建2个pod

apiVersion: v1
kind: ResourceQuota
metadata:
  name: pod-demo
spec:
  hard:
    pods: "2"
# kubectl apply -f quota-pod.yaml --namespace=quota-pod-example

3、查看资源配额的详细信息

kubectl get resourcequota pod-demo --namespace=quota-pod-example --output=yaml

4、创建Deployment,且replicas是3

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-quota-demo
spec:
  selector:
    matchLabels:
      purpose: quota-demo
  replicas: 3
  template:
    metadata:
      labels:
        purpose: quota-demo
    spec:
      containers:
      - name: pod-quota-demo
        image: nginx

七、资源限制LimitRange

1、LimitRange 作用

一个 LimitRange(限制范围)对象提供的限制能够做到

  • 在一个命名空间中实施对每个 Pod 或 Container 最小和最大的资源使用量的限制。
  • 在一个命名空间中实施对每个 PersistentVolumeClaim 能申请的最小和最大的存储空间大小的限制。
  • 在一个命名空间中实施对一种资源的申请值和限制值的比值的控制。
  • 设置一个命名空间中对计算资源的默认申请/限制值,并且自动的在运行时注入到多个 Container 中。

2、启用 LimitRang

  • 对 LimitRange 的支持自 Kubernetes 1.10 版本默认启用。
  • LimitRange 支持在很多 Kubernetes 发行版本中也是默认启用的。
  • LimitRange 的名称必须是合法的 DNS 子域名

3、限制范围总览

  • 管理员在一个命名空间内创建一个 LimitRange 对象。
  • 用户在命名空间内创建 Pod ,Container 和 PersistentVolumeClaim 等资源。
  • LimitRanger 准入控制器对所有没有设置计算资源需求的 Pod 和 Container 设置默认值与限制值, 并跟踪其使用量以保证没有超出命名空间中存在的任意 LimitRange 对象中的最小、最大资源使用量以及使用量比值。
  • 若创建或更新资源(Pod、 Container、PersistentVolumeClaim)违反了 LimitRange 的约束, 向 API 服务器的请求会失败,并返回 HTTP 状态码 403 FORBIDDEN 与描述哪一项约束被违反的消息。
  • 若命名空间中的 LimitRange 启用了对 cpumemory 的限制, 用户必须指定这些值的需求使用量与限制使用量。否则,系统将会拒绝创建 Pod。
  • LimitRange 的验证仅在 Pod 准入阶段进行,不对正在运行的 Pod 进行验证。

能够使用限制范围创建的策略示例有:

  • 在一个有两个节点,8 GiB 内存与16个核的集群中,限制一个命名空间的 Pod 申请 100m 单位,最大 500m 单位的 CPU,以及申请 200Mi,最大 600Mi 的内存。
  • 为 spec 中没有 cpu 和内存需求值的 Container 定义默认 CPU 限制值与需求值 150m,内存默认需求值 300Mi。

在命名空间的总限制值小于 Pod 或 Container 的限制值的总和的情况下,可能会产生资源竞争。 在这种情况下,将不会创建 Container 或 Pod。

竞争和对 LimitRange 的改变都不会影响任何已经创建了的资源

4、配置LimitRange

1、配置 CPU 最小和最大约束

创建一个命名空间

# kubectl create namespace constraints-cpu-example
namespace/constraints-cpu-example created

LimitRange 的配置文件

# cat cpu-constraints.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-min-max-demo-lr
spec:
  limits:
  - max:
      cpu: "800m"
    min:
      cpu: "200m"
    type: Container

创建 LimitRange

# kubectl apply -f cpu-constraints.yaml -n constraints-cpu-example
limitrange/cpu-min-max-demo-lr created

查看结果

# kubectl get limitrange cpu-min-max-demo-lr --output=yaml --namespace=constraints-cpu-example
spec:
  limits:
  - default:				# 默认limits配置
      cpu: 800m
    defaultRequest:			# 默认requests配置
      cpu: 800m
    max:					# 内存CPU的最大配置
      cpu: 800m
    min:					# 内存CPU的最小配置
      cpu: 200m
    type: Container

输出结果显示 CPU 的最小和最大限制符合预期。但需要注意的是,尽管你在 LimitRange 的配置文件中你没有声明默认值,默认值也会被自动创建

现在不管什么时候在 constraints-cpu-example 命名空间中创建容器,Kubernetes 都会执行下面这些步骤:

  • 如果容器没有声明自己的 CPU 请求和限制,将为容器指定默认 CPU 请求和限制。
  • 核查容器声明的 CPU 请求确保其大于或者等于 200 millicpu。
  • 核查容器声明的 CPU 限制确保其小于或者等于 800 millicpu。

流程就是说,我们创建了一个namespace=constraints-cpu-example的名称空间,然后在这个名称空间创建了一个LimitRange。然后现在不管什么时候在 constraints-cpu-example 命名空间中创建容器,Kubernetes 都会执行上面那些步骤!我们创建的LimitRange对这个namespace=constraints-cpu-example的名称空间里面起低调Pod都起了限制的作用

2、配置命名空间的最小和最大内存约束

LimitRange 的配置文件

apiVersion: v1
kind: LimitRange
metadata:
  name: mem-min-max-demo-lr
spec:
  limits:
  - max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container
# 输出显示预期的最小和最大内存约束。 但请注意,即使你没有在 LimitRange 的配置文件中指定默认值,也会自动创建它们。
  limits:
  - default:
      memory: 1Gi
    defaultRequest:
      memory: 1Gi
    max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container

现在,只要在 constraints-mem-example 命名空间中创建容器,Kubernetes 就会执行下面的步骤:

  • 如果 Container 未指定自己的内存请求和限制,将为它指定默认的内存请求和限制。
  • 验证 Container 的内存请求是否大于或等于500 MiB。
  • 验证 Container 的内存限制是否小于或等于1 GiB。

3、配置 CPU 最小和最大约束

LimitRange 的配置文件:

apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-min-max-demo-lr
spec:
  limits:
  - max:
      cpu: "800m"
    min:
      cpu: "200m"
    type: Container
#  输出结果显示 CPU 的最小和最大限制符合预期。但需要注意的是,尽管你在 LimitRange 的配置文件中你没有声明默认值,默认值也会被自动创建。
limits:
- default:
    cpu: 800m
  defaultRequest:
    cpu: 800m
  max:
    cpu: 800m
  min:
    cpu: 200m
  type: Container

现在不管什么时候在 constraints-cpu-example 命名空间中创建容器,Kubernetes 都会执行下面这些步骤:

  • 如果容器没有声明自己的 CPU 请求和限制,将为容器指定默认 CPU 请求和限制。
  • 核查容器声明的 CPU 请求确保其大于或者等于 200 millicpu。
  • 核查容器声明的 CPU 限制确保其小于或者等于 800 millicpu。

说明: 当创建 LimitRange 对象时,你也可以声明大页面和 GPU 的限制。 当这些资源同时声明了 'default' 和 'defaultRequest' 参数时,两个参数值必须相同。

4、限制申请存储空间的大小

apiVersion: v1
kind: LimitRange
metadata:
  name: storagelimits
spec:
  limits:
  - type: PersistentVolumeClaim
    max:									# 最大PVC的空间
      storage: 2Gi
    min:									# 最小PVC的空间
      storage: 1Gi

八、服务质量QoS

1、什么是Qos

QoS类是Kubernetes用来决定Pod的调度和驱逐的策略

2、QoS级别

  • Guaranteed:最高服务质量,当宿主机内存不够时,会先kill掉QoS为BestEffort和Burstable的Pod,如果内存还是不够,才会kill掉QoS为Guaranteed,该级别Pod的资源占用量一般比较明确,即requests的cpu和memory和limits的cpu和memory配置的一致
  • Burstable:服务质量低于Guaranteed,当宿主机内存不够时,会先kill掉QoS为BestEffort的Pod,如果内存还是不够之后就会kill掉QoS级别为Burstable的Pod,用来保证QoS质量为Guaranteed的Pod,该级别Pod一般知道最小资源使用量,但是当机器资源充足时,还是想尽可能的使用更多的资源,即limits字段的cpu和memory大于requests的cpu和memory的配置
  • BestEffort:尽力而为,当宿主机内存不够时,首先kill的就是该QoS的Pod,用以保证Burstable和Guaranteed级别的Pod正常运行

删除策略:先删除服务质量为BestEffort,然后在删除Burstable,Guaranteed最后被删除

3、举例说明

1、创建命名空间

创建一个命名空间

[root@k8s-master01 ~]# kubectl create namespace qos-example
namespace/qos-example created

2、创建一个 QoS 类为 Guaranteed 的 Pod

对于 QoS 类为 Guaranteed 的 Pod:

  • Pod 中的每个容器,包含初始化容器,必须指定内存请求和内存限制,并且两者要相等。
  • Pod 中的每个容器,包含初始化容器,必须指定 CPU 请求和 CPU 限制,并且两者要相等

创建qos-pod.yaml

cat > qos-pod.yaml << EFO
apiVersion: v1
kind: Pod
metadata:
  name: qos-demo
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-ctr
    image: nginx:1.15.2
    resources:
      limits:
        memory: "200Mi"
        cpu: "700m"
      requests:
        memory: "200Mi"
        cpu: "700m"
EFO

创建 Pod

# kubectl create -f qos-pod.yaml -n qos-example
pod/qos-demo created

查看 Pod 详情

# kubectl get pod qos-demo --namespace=qos-example --output=yaml
spec:
  containers:
  - image: nginx:1.15.2
    imagePullPolicy: IfNotPresent
    name: qos-demo-ctr
    resources:
      limits:
        cpu: 700m
        memory: 200Mi
      requests:
        cpu: 700m
        memory: 200Mi
  ............
status:
  qosClass: Guaranteed 

结果表明 Kubernetes 为 Pod 配置的 QoS 类为 Guaranteed。 结果也确认了 Pod 容器设置了与内存限制匹配的内存请求,设置了与 CPU 限制匹配的 CPU 请求

删除Pod

# kubectl delete pod qos-demo --namespace=qos-example
pod "qos-demo" deleted

说明: 如果容器指定了自己的内存限制,但没有指定内存请求,Kubernetes 会自动为它指定与内存限制匹配的内存请求。 同样,如果容器指定了自己的 CPU 限制,但没有指定 CPU 请求,Kubernetes 会自动为它指定与 CPU 限制匹配的 CPU 请求。

3、创建一个 QoS 类为 Burstable 的 Pod

如果满足下面条件,将会指定 Pod 的 QoS 类为 Burstable:

  • Pod 不符合 Guaranteed QoS 类的标准。
  • Pod 中至少一个容器具有内存或 CPU 请求。

下面是包含一个容器的 Pod 配置文件。 容器设置了内存限制 200 MiB 和内存请求 100 MiB。

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-2
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"

# 结果表明 Kubernetes 为 Pod 配置的 QoS 类为 Guaranteed。 结果也确认了 Pod 容器设置了与内存限制匹配的内存请求,设置了与 CPU 限制匹配的 CPU 请求。
spec:
  containers:
    ...
    resources:
      limits:
        cpu: 700m
        memory: 200Mi
      requests:
        cpu: 700m
        memory: 200Mi
  ......
status:
  qosClass: Guaranteed

说明: 如果容器指定了自己的内存限制,但没有指定内存请求,Kubernetes 会自动为它指定与内存限制匹配的内存请求。 同样,如果容器指定了自己的 CPU 限制,但没有指定 CPU 请求,Kubernetes 会自动为它指定与 CPU 限制匹配的 CPU 请求。

4、创建一个 QoS 类为 BestEffort 的 Pod

  • 对于 QoS 类为 BestEffort 的 Pod,Pod 中的容器必须没有设置内存和 CPU 限制或请求

下面是包含一个容器的 Pod 配置文件。 容器没有设置内存和 CPU 限制或请求。

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-3
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-3-ctr
    image: nginx
    
# 结果表明 Kubernetes 为 Pod 配置的 QoS 类为 BestEffort。
spec:
  containers:
    ...
    resources: {}
  ...
status:
  qosClass: BestEffort
0

评论区