背景:

不知道有没有小伙伴跟我一样在集群创建应用的时候没有详细计算过自己的资源配比。然后我是看到kubectl top node 一看每个节点还有很多的资源,就直接创建了几个资源配比较高的应用,而且这几个应用是高负载运行的….然后的结果就是集群中好多应用开始崩溃了……

为什么要限制资源?

1. 对pod进行资源限制,可以防止由于某一个pod应用过多占用资源,造成其他应用异常。

2. 资源的有效隔离。

3. pod调度的优先级。

4. 资源的高效合理利用。

Kubernets中的资源分类

Kubernetes根据资源能否伸缩进行分类,划分为可压缩资源和不可以压缩资源2种:

1. 可压缩资源(compressible resources )当可压缩资源不足时,Pod 只会“饥饿”,但不会退出。例如 CPU(GPU也是吧?只不过没有搞gpu应用忽略)

2. 不可压缩资源(incompressible resources)当不可压缩资源不足时,Pod 就会因为 OOM(Out-Of-Memory)被内核杀掉。例如:Memory(内存) Disk(硬盘)。

注:参见极客时间磊神的专栏:https://time.geekbang.org/column/article/69678

资源配额管理-LimitRange ResourceQuota

ResourceQuota

ResourceQuota 用来限制 namespace 中所有的 Pod 占用的总的资源 request 和 limit
可以参照:https://kubernetes.io/zh/docs/concepts/policy/resource-quotas/

1. 计算资源配额(Compute Resource Quata)

可以参照:https://kubernetes.io/zh/docs/tasks/administer-cluster/manage-resources/quota-memory-cpu-namespace/
image.png
但是根据磊神:https://time.geekbang.org/column/article/69678中建议的还是将配置文件写成一下内部通用格式了:
image.png

  1. cat > ResourceQuota.yaml << EOF
  2. apiVersion: v1
  3. kind: ResourceQuota
  4. metadata:
  5. name: cpu-mem
  6. namespace: quota
  7. spec:
  8. hard:
  9. requests.cpu: 1000m
  10. requests.memory: 1000Mi
  11. limits.cpu: 2000m
  12. limits.memory: 2000Mi
  13. EOF
  1. [root@sh-master-01 quota]# kubectl create ns quota
  2. namespace/quota created
  3. [root@sh-master-01 quota]# kubectl apply -f ResourceQuota.yaml
  4. resourcequota/cpu-mem created
  1. kubectl get resourcequota cpu-mem -n quota
  2. kubectl get resourcequota cpu-mem--namespace=quota --output=yaml

image.png
用官方文档实例创建一个pod测试一下:

  1. cat > quota-mem-cpu-pod.yaml << EOF
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: quota-mem-cpu-demo
  6. spec:
  7. containers:
  8. - name: quota-mem-cpu-demo-ctr
  9. image: nginx
  10. resources:
  11. limits:
  12. memory: "800Mi"
  13. cpu: "800m"
  14. requests:
  15. memory: "600Mi"
  16. cpu: "400m"
  17. EOF

在quota 命名空间中创建pod:

  1. kubectl apply -f quota-mem-cpu-pod.yaml --namespace=quota

检查下 Pod 中的容器在运行:

  1. kubectl get pod quota-mem-cpu-demo --namespace=quota

再查看 ResourceQuota 的详情:

  1. kubectl get resourcequota cpu-mem --namespace=quota --output=yaml

输出结果显示了配额以及有多少配额已经被使用。你可以看到 Pod 的内存和 CPU 请求值及限制值没有超过配额
image.png
再创建一个pod:

  1. cat > quota-mem-cpu-pod-2.yaml << EOF
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: quota-mem-cpu-demo-2
  6. spec:
  7. containers:
  8. - name: quota-mem-cpu-demo-2-ctr
  9. image: nginx
  10. resources:
  11. limits:
  12. memory: "1000Mi"
  13. cpu: "800m"
  14. requests:
  15. memory: "700Mi"
  16. cpu: "400m"
  17. EOF
  1. kubectl apply -f quota-mem-cpu-pod-2.yaml --namespace=quota

得到如下报错,第二个 Pod 不能被创建成功。输出结果显示创建第二个 Pod 会导致内存请求总量超过内存请求配额。
image.png

2. 存储资源配额(Volume Count Quata)

存储资源配额也演示一下吧。我这里就只限制存储总量了。其他的可设置参数可以参考官方文档:

  1. cat > storage.yaml << EOF
  2. apiVersion: v1
  3. kind: ResourceQuota
  4. metadata:
  5. name: storage
  6. namespace: quota
  7. spec:
  8. hard:
  9. requests.storage: 10Gi
  10. EOF
  1. cat > pvc.yaml << EOF
  2. apiVersion: v1
  3. kind: PersistentVolumeClaim
  4. metadata:
  5. name: quota-mem-cpu-demo-pvc
  6. spec:
  7. accessModes:
  8. - ReadWriteOnce
  9. resources:
  10. requests:
  11. storage: 20Gi
  12. storageClassName: cbs-csi
  13. EOF

在quota namespace中创建pvc:

  1. kubectl apply -f pvc.yaml -n quota

得到如下报错。最大限制为10G。
image.png

3. 对象数量配额(Object Count Quata)

  1. cat > objects.yaml << EOF
  2. apiVersion: v1
  3. kind: ResourceQuota
  4. metadata:
  5. name: objects
  6. namespace: quota
  7. spec:
  8. hard:
  9. pods: 10
  10. replicationcontrollers: 5
  11. secrets: 10
  12. configmaps: 10
  13. persistentvolumeclaims: 4
  14. services: 5
  15. services.loadbalancers: 1
  16. services.nodeports: 2
  17. cbs.storageclass.storage.k8s.io/persistentvolumeclaims: 2
  18. EOF

数量配额就不演示。根据官方文档设置可以设置的限制数量的参数,并设置数量。只要数量大于设置数量失败就可以验证!

4. 配额的作用域(Quota Scopes)

image.png
image.png
这个鬼东西功能没有怎么用过,就抄了一下官方的。有时间研究一下,一下实例摘自kubernetes权威指南第五版750页:

1. 创建ResourceQuota scope

创建一个quota-scopes的命名空间,并创建一个名为best-effort的 ResourceQuota,一个名为not-best-effort的 ResourceQuota:

  1. kubectl create ns quota-scopes
  2. kubectl create quota best-effort --hard=pods=10 --scopes=BestEffort -n quota-scopes
  3. kubectl create quota not-best-effort --hard=pods=4,requests.cpu=1000m,requests.memory=1024Mi,limits.cpu=2000m,limits.memory=2048Mi --scopes=NotBestEffort -n quota-scopes

Kubernetes中资源的管理与调度 - 图10
注:官方的方式是yaml的方式。正巧看到了http://docs.kubernetes.org.cn/541.html#kubectl_create_quota
kubectl create quota就用了一下.

2. 创建两个deployment

  1. kubectl run best-effort-nginx --image=nginx --replicas=8 --namespace=quota-scope
  2. kubectl run not-best-effort-nginx --image=nginx= --replicas=2 --requests=cpu=100m,memory=256Mi --limits=cpu=200m,memory=512Mi --namespace=quota-scopes

Kubernetes中资源的管理与调度 - 图11
Kubernetes中资源的管理与调度 - 图12

嗯这里就坑了。kubectl run 在1.16.16测试了下可以带replicas。但是我的1.21.1不能这样执行了。看了下文档使用下面的kubectl create deployment 命令

  1. kubectl create deployment best-effort-nginx --image=nginx --replicas=8 --namespace=quota-scopes

但是创建not-best-effort-nginx deployment时候,命令中貌似不能使用 —requests 和—limits了?用最笨的方法吧,创建deployment然后edit deployment增加resources限制:

  1. kubectl create deployment not-best-effort-nginx --image=nginx --replicas=2 --namespace=quota-scopes
  2. kubectl edit deployment not-best-effort-nginx -n quota-scopes

image.png

3. 验证:

  1. kubectl get pods -n quota-scopes
  2. kubectl describe quota -n quota-scopes

image.png

5. 基于优先级类(PriorityClass)来设置资源配额

注:这个鬼东西没有用过官方文档看来的……
FEATURE STATE: Kubernetes v1.17 [stable]
Pod 可以创建为特定的优先级。 通过使用配额规约中的 scopeSelector 字段,用户可以根据 Pod 的优先级控制其系统资源消耗。
仅当配额规范中的 scopeSelector 字段选择到某 Pod 时,配额机制才会匹配和计量 Pod 的资源消耗。
如果配额对象通过 scopeSelector 字段设置其作用域为优先级类,则配额对象只能 跟踪以下资源:

  • pods
  • cpu
  • memory
  • ephemeral-storage
  • limits.cpu
  • limits.memory
  • limits.ephemeral-storage
  • requests.cpu
  • requests.memory
  • requests.ephemeral-storage

本示例创建一个配额对象,并将其与具有特定优先级的 Pod 进行匹配。 该示例的工作方式如下:

  • 集群中的 Pod 可取三个优先级类之一,即 “low”、”medium”、”high”。
  • 为每个优先级创建一个配额对象。

将以下 YAML 保存到文件 quota.yml 中。

  1. apiVersion: v1
  2. kind: List
  3. items:
  4. - apiVersion: v1
  5. kind: ResourceQuota
  6. metadata:
  7. name: pods-high
  8. spec:
  9. hard:
  10. cpu: "1000"
  11. memory: 200Gi
  12. pods: "10"
  13. scopeSelector:
  14. matchExpressions:
  15. - operator : In
  16. scopeName: PriorityClass
  17. values: ["high"]
  18. - apiVersion: v1
  19. kind: ResourceQuota
  20. metadata:
  21. name: pods-medium
  22. spec:
  23. hard:
  24. cpu: "10"
  25. memory: 20Gi
  26. pods: "10"
  27. scopeSelector:
  28. matchExpressions:
  29. - operator : In
  30. scopeName: PriorityClass
  31. values: ["medium"]
  32. - apiVersion: v1
  33. kind: ResourceQuota
  34. metadata:
  35. name: pods-low
  36. spec:
  37. hard:
  38. cpu: "5"
  39. memory: 10Gi
  40. pods: "10"
  41. scopeSelector:
  42. matchExpressions:
  43. - operator : In
  44. scopeName: PriorityClass
  45. values: ["low"]

使用 kubectl create 命令运行以下操作。

  1. kubectl create -f quota.yml
  2. kubectl describe quota

image.png

LimitRange

LimitRange 用来限制 namespace 中 单个Pod 默认资源 request 和 limit

1. 一般的例子 单个container的pod

嗯 一般是通过requests limits去限制pod cpu与内存的资源的。如下:

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: php-test
  5. spec:
  6. replicas: 2
  7. strategy:
  8. rollingUpdate:
  9. maxSurge: 1
  10. maxUnavailable: 0
  11. selector:
  12. matchLabels:
  13. app: php-test
  14. template:
  15. metadata:
  16. labels:
  17. app: php-test
  18. spec:
  19. containers:
  20. - name: php-test
  21. image: ccr.ccs.tencentyun.com/master/php-test:0.1
  22. env:
  23. - name: PHP_MEM_LIMIT
  24. value: "256M"
  25. envFrom:
  26. - configMapRef:
  27. name: deploy
  28. ports:
  29. - containerPort: 80
  30. resources:
  31. requests:
  32. memory: "256M"
  33. cpu: "250m"
  34. limits:
  35. memory: "2048M"
  36. cpu: "4000m"
  37. livenessProbe:
  38. httpGet:
  39. scheme: HTTP
  40. path: /test.html
  41. port: 80
  42. initialDelaySeconds: 30
  43. periodSeconds: 30
  44. successThreshold: 1
  45. failureThreshold: 3
  46. readinessProbe:
  47. httpGet:
  48. scheme: HTTP
  49. path: /test.html
  50. port: 80
  51. initialDelaySeconds: 30
  52. periodSeconds: 30
  53. imagePullSecrets:
  54. - name: tencent
  55. ---
  56. apiVersion: v1
  57. kind: Service
  58. metadata:
  59. name: php-test
  60. labels:
  61. app: php-test
  62. spec:
  63. ports:
  64. - port: 80
  65. protocol: TCP
  66. targetPort: 80
  67. selector:
  68. app: php-test

2. 多个container的pod

当然了 一个pod可以有多个container:

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: kucc1
  5. spec:
  6. containers:
  7. - image: nginx
  8. name: nginx
  9. resources:
  10. requests:
  11. memory: "256M"
  12. cpu: "250m"
  13. limits:
  14. memory: "2048M"
  15. cpu: "4000m"
  16. - image: redis
  17. name: redis
  18. resources:
  19. requests:
  20. memory: "256M"
  21. cpu: "250m"
  22. limits:
  23. memory: "2048M"
  24. cpu: "4000m"

这个pod的request 内存最低是512M ,cpu是0.5核。limits 内存是4096M,cpu是8核。

嗯 pod 与container的关系,一个pod可以包括多个container

关于资源的调度与pod的Qos模型:

资源管理-Compute Resource
qos-服务质量管理

kubernetes在对pod进行调度的时候,kube-scheduler 只会按照 requests 的值进行计算。而在真正设置 Cgroups 限制的时候,kubelet 则会按照 limits 的值来进行设置。
Kubernetes 将 pod 划分为 3 种 QoS 等级 :

  • BestEffort (优先级最低)
  • Burstable
  • Guaranteed (优先级最高)

最低优先级的 QoS 等级是 BestEffort 。 会分配给那些没有(为任何容器) 设置任何 requests 和 limits 的 pod。在这个等级运行的容器没有任何资源保证。 在最坏情况下,它们分不到任何 CPU 资源,同时在需要为其他 pod 释放内存时, 这些容器会第一批被杀死。 不过因为 BestEffort pod 没有配置内存 limits, 当有充足的可用内存时,这些容器可以使用任意多的内存。

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: php-test
  5. namespace: develop
  6. spec:
  7. containers:
  8. - name: php-test
  9. image: nginx

Guaranteed 级别的 pod,有以下几个条件:

  • CPU 和内存都要设置 requests 和 limits
  • 每个容器都需要设置资源量
  • 它们必须相等(每个容器的每种资源的 requests 和 limits 必须相等) ```

apiVersion: v1 kind: Pod metadata: name: php-test namespace: qa spec: containers:

  • name: php-test image: nginx resources: limits:
    1. memory: "200Mi"
    2. cpu: "700m"
    requests:
    1. memory: "200Mi"
    2. cpu: "700m"
    ```

Burstable QoS 等级介于 BestEffort 和 Guaranteed 之间。其他所有 的 pod 都属于这个等级。 包括容器的requests 和 limits 不相同的单容器 pod,至少有 一个容器只定义了 requests 但没有定义 limits 的 pod,以及一个容器的 requests,limits 相等,但是另一个容器不指定 requests 或 limits 的 pod。

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: php-test
  5. namespace: master
  6. spec:
  7. containers:
  8. - name: php-test
  9. image: nginx
  10. resources:
  11. limits
  12. memory: "200Mi"
  13. requests:
  14. memory: "100Mi"

注:
Guaranteed >Burstable > BestEffort 故 在上面的实例中我采用了三个不同的namespace进行了优先性的一个区分吧算是。因为我的master namespace是最重要优先的线上服务,qa算是测试环境,develop是开发环境。避免资源被抢占的最好方式就是将resource中limits与requests值设置为相同的值,Qos优先级为Guaranteed。

如何保证pod的资源优先性与调度的优先性?

kubernetes pod应用分布到哪个节点上默认是通过master的scheduler经过一系列算法得出的,用户是无干预过程和结果的。设置Qos优先级为Guaranteed只能保证资源的优先。还有什么其他的方式去保证资源的优先与调度呢?

1. NodeSelector 定向调度

将pod 调度到一组含有相同标签的work节点

  1. [root@sh-master-01 ~]# kubectl get nodes --show-labels
  2. NAME STATUS ROLES AGE VERSION LABELS
  3. sh-master-01 Ready control-plane,master 92d v1.21.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=sh-master-01,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
  4. sh-master-02 Ready control-plane,master 92d v1.21.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=sh-master-02,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
  5. sh-master-03 Ready control-plane,master 92d v1.21.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=sh-master-03,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
  6. sh-work-01 Ready <none> 92d v1.21.1 IngressProxy=true,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=sh-work-01,kubernetes.io/os=linux,topology.com.tencent.cloud.csi.cbs/zone=ap-shanghai-2
  7. sh-work-02 Ready <none> 92d v1.21.1 IngressProxy=true,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=sh-work-02,kubernetes.io/os=linux,topology.com.tencent.cloud.csi.cbs/zone=ap-shanghai-2
  8. sh-work-03 Ready <none> 92d v1.21.1 IngressProxy=true,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=sh-work-03,kubernetes.io/os=linux,topology.com.tencent.cloud.csi.cbs/zone=ap-shanghai-2

给节点sh-work-01打一个zone=shanghai标签,

  1. [root@sh-master-01 ~]# kubectl label node sh-work-01 zone=shanghai
  2. node/sh-work-01 labeled
  3. [root@sh-master-01 ~]# kubectl get nodes --show-labels
  4. NAME STATUS ROLES AGE VERSION LABELS
  5. sh-master-01 Ready control-plane,master 92d v1.21.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=sh-master-01,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
  6. sh-master-02 Ready control-plane,master 92d v1.21.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=sh-master-02,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
  7. sh-master-03 Ready control-plane,master 92d v1.21.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=sh-master-03,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
  8. sh-work-01 Ready <none> 92d v1.21.1 IngressProxy=true,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=sh-work-01,kubernetes.io/os=linux,topology.com.tencent.cloud.csi.cbs/zone=ap-shanghai-2,zone=shanghai
  9. sh-work-02 Ready <none> 92d v1.21.1 IngressProxy=true,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=sh-work-02,kubernetes.io/os=linux,topology.com.tencent.cloud.csi.cbs/zone=ap-shanghai-2
  10. sh-work-03 Ready <none> 92d v1.21.1 IngressProxy=true,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=sh-work-03,kubernetes.io/os=linux,topology.com.tencent.cloud.csi.cbs/zone=ap-shanghai-2

新建一个pod调度到zone=shanghai节点
就用 nginx镜像去做测试了:

  1. kubectl run nginx --image=nginx:latest --port=80 --dry-run -o yaml > nodeselector.yaml

vim nodeselector.yaml,增加nodeSelector 配置

  1. cat > nodeselector.yaml << EOF
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. creationTimestamp: null
  6. labels:
  7. run: nginx
  8. name: nginx
  9. spec:
  10. containers:
  11. - image: nginx:latest
  12. name: nginx
  13. ports:
  14. - containerPort: 80
  15. resources: {}
  16. nodeSelector:
  17. zone: shanghai
  18. dnsPolicy: ClusterFirst
  19. restartPolicy: Always
  20. status: {}
  21. EOF

部署并验证pod是否调度到sh-work-01节点:

  1. kubectl apply -f nodeselector.yaml
  2. kubectl get pods -o wide

image.png

注:nodeSelector属于强制性的,如果我们的目标节点没有可用的资源,Pod 就会一直处于 Pending 状态。

2. NodeAffinity: Node亲和性

1. 亲和性调度可以分成软策略和硬策略两种方式:

1. 软策略-preferredDuringSchedulingIgnoredDuringExecution

  1. 只是强调优先,但是不强求。就是如果你没有满足调度要求的节点的话,pod 就会忽略这条规则,继续完成调度过程,说白了就是**满足条件最好了,没有的话也无所谓了**的策略。当然还可以有多个优先规则,可以设置wight权重值,定义先后顺序。

2. 硬策略-requiredDuringSchedulingIgnoredDuringExecution

必须满足指定的规则才可以调度pod到node上,比较强硬了,如果没有满足条件的节点的话,就不断重试直到满足条件为止,简单说就是你必须满足我的要求,不然我就不干的策略。

2. 关于IgnoredDuringExecution

详细见官方:
image.png
IgnoredDuringExecution类似于 nodeSelector 的工作原理, 如果节点的标签在运行时发生变更,从而不再满足 Pod 上的亲和性规则,那么 Pod 将仍然继续在该节点上运行。

3. 常见的语法操作符

提供的操作符有下面的几种:

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

如果你同时指定了 nodeSelector 和 nodeAffinity,两者必须都要满足, 才能将 Pod 调度到候选节点上。
如果你指定了多个与 nodeAffinity 类型关联的 nodeSelectorTerms,则 如果其中一个 nodeSelectorTerms 满足的话,pod将可以调度到节点上。
如果你指定了多个与 nodeSelectorTerms 关联的 matchExpressions,则 只有当所有 matchExpressions 满足的话,Pod 才会可以调度到节点上。
如果你修改或删除了 pod 所调度到的节点的标签,Pod 不会被删除。 换句话说,亲和性选择只在 Pod 调度期间有效。
preferredDuringSchedulingIgnoredDuringExecution 中的 weight 字段值的 范围是 1-100。 对于每个符合所有调度要求(资源请求、RequiredDuringScheduling 亲和性表达式等) 的节点,调度器将遍历该字段的元素来计算总和,并且如果节点匹配对应的 MatchExpressions,则添加“权重”到总和。 然后将这个评分与该节点的其他优先级函数的评分进行组合。 总分最高的节点是最优选的。

4. 举个例子

1. 关于硬策略的例子

搞一个没有的的标签试一下硬限制:

  1. cat > pod-nodeAffinity.yaml << EOF
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: pod-nodeaffinity
  6. namespace: default
  7. labels:
  8. app: myapp
  9. type: pod
  10. spec:
  11. containers:
  12. - name: myapp
  13. image: ikubernetes/myapp:v1
  14. ports:
  15. - name: http
  16. containerPort: 80
  17. affinity:
  18. nodeAffinity:
  19. requiredDuringSchedulingIgnoredDuringExecution:
  20. nodeSelectorTerms:
  21. - matchExpressions:
  22. - key: kubernetes.io/hostname
  23. operator: In
  24. values:
  25. - node01
  26. EOF
  1. kubectl apply -f pod-nodeAffinity.yaml
  2. kubectl get pods

image.png
嗯 我是没有kubernetes.io/hostname:node01标签的节点的所以pod一直处于pending状态
修改一下kubernetes.io/hostname:sh-work-01吧
image.png
嗯 如下
image.png
删除重新部署一下试试:

  1. kubectl delete -f pod-nodeAffinity.yaml
  2. kubectl create -f pod-nodeAffinity.yaml

image.png
然后我现在修改下values试试? 将sh-work-01修改为sh-work-02
image.png
image.png
依然如此! 进一步验证了IgnoredDuringExecution的特性!

2. 关于软策略的例子

依然是整一个没有的label标签的节点去调度:

  1. cat > pod-prefer.yaml << EOF
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: pod-prefer
  6. namespace: default
  7. labels:
  8. app: myapp
  9. type: pod
  10. spec:
  11. containers:
  12. - name: myapp
  13. image: ikubernetes/myapp:v1
  14. ports:
  15. - name: http
  16. containerPort: 80
  17. affinity:
  18. nodeAffinity:
  19. preferredDuringSchedulingIgnoredDuringExecution:
  20. - weight: 10
  21. preference:
  22. matchExpressions:
  23. - key: kubernetes.io/hostname
  24. operator: In
  25. values:
  26. - node01
  27. EOF
  1. [root@sh-master-01 priority]# kubectl apply -f pod-prefer.yaml
  2. pod/pod-prefer created
  3. [root@sh-master-01 priority]# kubectl get pods
  4. NAME READY STATUS RESTARTS AGE
  5. csi-app 1/1 Running 10 93d
  6. nginx 1/1 Running 0 62m
  7. php-apache-5b95f8f674-clzn5 1/1 Running 2 86d
  8. pod-nodeaffinity 1/1 Running 0 19m
  9. pod-prefer 0/1 ContainerCreating 0 3s
  10. [root@sh-master-01 priority]# kubectl get pods -o wide
  11. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  12. csi-app 1/1 Running 10 93d 10.0.4.204 sh-work-01 <none> <none>
  13. nginx 1/1 Running 0 62m 10.0.4.60 sh-work-01 <none> <none>
  14. php-apache-5b95f8f674-clzn5 1/1 Running 2 86d 10.0.3.64 sh-work-03 <none> <none>
  15. pod-nodeaffinity 1/1 Running 0 19m 10.0.4.118 sh-work-01 <none> <none>
  16. pod-prefer 0/1 ContainerCreating 0 7s <none> sh-work-02 <none> <none>

image.png
嗯 我是没有label kubernetes.io/hostname:node01节点的。结果他调度到了sh-work-02节点。这算是体现了不强制了吧?
然后再测试一下权重?

  1. cat > pod-prefer1.yaml << EOF
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: pod-prefer1
  6. namespace: default
  7. labels:
  8. app: myapp1
  9. type: pod
  10. spec:
  11. containers:
  12. - name: myapp1
  13. image: ikubernetes/myapp:v1
  14. ports:
  15. - name: http
  16. containerPort: 80
  17. affinity:
  18. nodeAffinity:
  19. preferredDuringSchedulingIgnoredDuringExecution:
  20. - weight: 10
  21. preference:
  22. matchExpressions:
  23. - key: kubernetes.io/hostname
  24. operator: In
  25. values:
  26. - sh-work-01
  27. preferredDuringSchedulingIgnoredDuringExecution:
  28. - weight: 20
  29. preference:
  30. matchExpressions:
  31. - key: kubernetes.io/hostname
  32. operator: In
  33. values:
  34. - sh-work-03
  35. EOF
  1. [root@sh-master-01 priority]# kubectl apply -f pod-prefer1.yaml
  2. pod/pod-prefer1 created
  3. [root@sh-master-01 priority]# kubectl get pods -o wide
  4. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  5. csi-app 1/1 Running 10 93d 10.0.4.204 sh-work-01 <none> <none>
  6. nginx 1/1 Running 0 67m 10.0.4.60 sh-work-01 <none> <none>
  7. php-apache-5b95f8f674-clzn5 1/1 Running 2 86d 10.0.3.64 sh-work-03 <none> <none>
  8. pod-nodeaffinity 1/1 Running 0 23m 10.0.4.118 sh-work-01 <none> <none>
  9. pod-prefer 1/1 Running 0 4m57s 10.0.5.181 sh-work-02 <none> <none>
  10. pod-prefer1 0/1 ContainerCreating 0 6s <none> sh-work-03 <none> <none>

嗯基于权重调度到了sh-work-03的节点上了
image.png

3. 至于软硬策略同事存在呢?

做一个例子同事存在软策略与硬策略

  1. cat > pod-prefer2.yaml << EOF
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: with-node-affinity
  6. spec:
  7. affinity:
  8. nodeAffinity:
  9. requiredDuringSchedulingIgnoredDuringExecution:
  10. nodeSelectorTerms:
  11. - matchExpressions:
  12. - key: kubernetes.io/hostname
  13. operator: In
  14. values:
  15. - sh-work-01
  16. - sh-work-02
  17. preferredDuringSchedulingIgnoredDuringExecution:
  18. - weight: 10
  19. preference:
  20. matchExpressions:
  21. - key: zone
  22. operator: In
  23. values:
  24. - shanghai
  25. containers:
  26. - name: with-node-affinity
  27. image: k8s.gcr.io/pause:2.0
  28. EOF
 kubectl apply -f pod-prefer2.yaml
 kubectl get pods -o wide

image.png

当然了不管preferredDuringSchedulingIgnoredDuringExecution中的值是什么,前提是要先满足requiredDuringSchedulingIgnoredDuringExecution中的values。


3. PodAfinity: Pod亲和性和互斥调度策略

Pod 间亲和性通过 PodSpec 中 affinity 字段下的 podAffinity 字段进行指定。 而 Pod 间反亲和性通过 PodSpec 中 affinity 字段下的 podAntiAffinity 字段进行指定。
主要还是用来让一些频繁调用互相依赖的pod尽可能部署在一台node或者相同区域,还有占用资源的pod互斥,分布在不同的节点或者区域。
举例子:

1.先部署一个带有标签security=S1 app=busybox的Pod

cat > pod-flag.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
  name: pod-flag
  namespace: default
  labels:
    app: busybox
    security: S1
spec:
  containers:
  - name: busybox
    image: busybox:1.28.4
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
EOF
kubectl apply -f pod-flag.yaml
kubectl get pods --show-labels

image.png

2. Pod 使用 pod 亲和性 的示例:

1. 软策略亲和性

cat > with-pod-affinity.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: kubernetes.io/hostname
  containers:
  - name: with-pod-affinity
    image: k8s.gcr.io/pause:2.0
EOF
kubectl apply -f with-pod-affinity.yaml
kubectl get pods -o wide

image.png

2. 关于topologyKey

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

3. 硬策略亲和性

强制要求

cat > with-pod-affinity1.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity1
spec:
  affinity:
    podAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: kubernetes.io/hostname
  containers:
  - name: with-pod-affinity1
    image: k8s.gcr.io/pause:2.0
EOF

image.png
ErrImagePull忽略,用的官方例子没有改镜像image。正常的用nginx image就好了。

3. Pod的互斥性调度

Pod与security=S1处于同一个zone,不与app=busybox的 pod在同一个Node:

cat > with-pod-antiaffinity.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
  name: with-pod-antiaffinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: topology.com.tencent.cloud.csi.cbs/zone
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - busybox
        topologyKey: kubernetes.io/hostname
  containers:
  - name: with-pod-antiaffinity
    image: nginx
EOF
 kubectl apply -f with-pod-antiaffinity.yaml
 kubectl get pods -o wide --show-labels

image.png
嗯 pod调度到了 sh-work-02之外的节点,这个地方还是有点绕,有时间再好好研究一下

4. Taints and Tolerations污点和容忍

1. 集群信息

[root@sh-master-01 priority]# kubectl get node
NAME           STATUS                     ROLES                  AGE   VERSION
sh-master-01   Ready                      control-plane,master   93d   v1.21.1
sh-master-02   Ready                      control-plane,master   93d   v1.21.1
sh-master-03   Ready                      control-plane,master   93d   v1.21.1
sh-work-01     Ready                      <none>                 93d   v1.21.1
sh-work-02     Ready                      <none>                 93d   v1.21.1
sh-work-03     Ready                      <none>                 93d   v1.21.1
test-01        Ready,SchedulingDisabled   <none>                 86d   v1.21.0

2.给sh-work-01节点打上污点(key=work、value=ops、effect=NoSchedule)

kubectl taint nodes sh-work-01 work=ops:NoSchedule

3. 整一个daemonset做测试吧 比较直观

cat > nginx-daemonset.yaml << EOF
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: nginx-1
  namespace: default
  labels:
    web: nginx-1
spec:
  selector:
    matchLabels:
      web: nginx-1
  template:
    metadata:
      labels:
        web: nginx-1
    spec:
      containers:
      - name: nginx-1
        image: nginx:1.17
        ports:
        - containerPort: 80
EOF
kubectl apply -f nginx-daemonset.yaml
kubectl get pods -o wide --show-labels

image.png
注: 我将test-01节点加上污点是因为我这个node是SchedulingDisabled的其实。但是daemonset也是会部署到IE点上去为了方便区分。
移除sh-work-01的污点看一下会发生什么:

[root@sh-master-01 priority]#  kubectl taint nodes sh-work-01 work:NoSchedule-
node/sh-work-01 untainted
[root@sh-master-01 priority]#  kubectl get pods -o wide --show-labels

image.png
嗯 去掉sh-work-01节点的误点标签。节点就恢复调度了。

4. 增加容忍度参数,进行测试

cat > nginx-daemonset-2.yaml << EOF
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: nginx-2
  namespace: default
  labels:
    web: nginx-2
spec:
  selector:
    matchLabels:
      web: nginx-2
  template:
    metadata:
      labels:
        web: nginx-2
    spec:
      containers:
      - name: nginx-2
        image: nginx:1.17
        ports:
        - containerPort: 80
      tolerations:
      - key: "work"
        operator: "Equal"
        value: "ops"
        effect: "NoSchedule"        
EOF
kubectl apply -f nginx-daemonset-2.yaml
kubectl get pods -o wide --show-labels

image.png
嗯误点节点都被调度了,包括test-01节点。这里在给test-01节点加个其他误点?反正就是不想让他被调度:

kubectl taint nodes test-01 work1=dev:NoSchedule

test-01节点中的pod仍未被驱逐
image.png
修改下work=ops的标签要不然?

kubectl taint nodes test-01 work=dev:NoSchedule --overwrite=true

image.png
还是一样。
该怎么搞呢?

5. 修改test-01节点的污点,将effect修改为”NoExecute”,测试Pod是否会被驱逐

kubectl taint nodes test-01 work=dev:NoExecute --overwrite=true

image.png
驱逐成功了……
关于Taints and Tolerations可以参照:https://www.jianjiacc.cn/archives/6fdbe877.html

总结一下:

  1. 创建集群应该进行合理的资源规划,对容量进行规划
  2. 可以根据pod的 qos进行资源的优先调度以及资源分配(当然了还是会有pod OOM)
  3. 可以通过节点打标签 亲和性反亲和性对资源进行合理调度,以免造成集群资源雪崩
  4. 不同kubernetes版本直接还是有些许的区别的,尽量还官方看文档吧!