504-602 API资源对象PV和PVC (Pod亲和性 反亲和性 污点与容忍度 API资源对象PV和PVC)5.4-6.2

发布时间 2023-10-23 20:29:55作者: zhan0

一、Pod亲和性

1.1  针对对象为Pod,目的是实现,新建Pod和目标Pod调度到一起,在同一个Node。

podAffinity 示例

apiVersion: v1
kind: Pod
metadata:
  name: testpod01
  namespace: prod
  labels:
    app: myapp01
    env: test1
spec:
  containers:
  - name: testpod01
    image: nginx:1.25.2

---
apiVersion: v1
kind: Pod
metadata:
  name: testpod02
  namespace: prod
  labels:
    app: myapp02
    env: test2
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:  ##必须满足下面匹配规则
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In  
            values:
            - myapp01  ## app=myapp01, 上面的Pod是符合要求的
        topologyKey: "kubernetes.io/hostname"
  containers:
  - name: testpod02
    image: redis:6.2

应用YAML文件

# kubectl  apply -f podAffinity.yaml 
pod/testpod01 created
pod/testpod02 created

验证:

# kubectl  get pod -n prod -o wide

二、Pod反亲和性

2.1 针对对象为Pod,目的是实现,新建Pod和目标Pod不能调度到一起,不在同一个Node。

podAntiAffinity示例

apiVersion: v1
kind: Pod
metadata:
  name: testpod01
  namespace: prod
  labels:
    app: myapp01
    env: test1
spec:
  containers:
  - name: testpod01
    image: nginx:1.25.2

---
apiVersion: v1
kind: Pod
metadata:
  name: testpod02
  namespace: prod
  labels:
    app: myapp02
    env: test2
spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:  ##必须满足下面匹配规则
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In  
            values:
            - myapp01  ## app=myapp01, 上面的Pod是符合要求的
        topologyKey: "kubernetes.io/hostname"
  containers:
  - name: testpod02
    image: redis:6.2

应用YAML:

# kubectl  apply -f podAntiAffinity.yaml 
pod/testpod01 created
pod/testpod02 created

 验证:

# kubectl  get pod -n prod -o wide

三、污点与容忍度

污点(Taint)针对节点来说,和节点亲和性正好相对,节点清河性使Pod被吸引到一类特定的节点,而污点则使节点能够排斥一类特定的Pod。

容忍度(Toleration) 应用于Pod上,它用来允许调度器调度带有对应污点的节点。容忍度允许调度但是不保证调度:作为其功能的一部分,调度器也会评估其他参数。

污点和容忍度相互配合,可以避免Pod被分配到不合适的节点上。每个节点上都可以应用一个或多个污点,这表示对于那些不能容忍这些污点的Pod,是不会被该节点接受。

3.1 设置污点命令格式:

kubectl taint node [node] key-value:[effect]

说明:

其中[effect]可取值:[NoSchedule|PreferNoSchedule|NoExecute]

  • NoSchedule:一定不能被调度,已经在运行中的Pod不受影响。
  • PreferNoSchedule:尽量不要调度,实在没有节点可调度再调度到此节点。
  • NoExecute:不仅不会调度,还会驱逐Node上已有的Pod。

清除污点格式:

kubectl taint node [node] key:[effect]-

示例:

kubectl taint node node-1-231 name=stage:NoSchedule

查看污点:

kubectl describe node [node] |grep -i taint -A 10

3.2 设置容忍度的几种规则:

1)完全匹配

tolerations:
- key: "taintKey"      #和污点的key名字保持一致
  operator: "Equal"    #匹配类型,Equal表示匹配污点的所有值
  value: "taintValue"  #和污点key的值保持一致
  effect: "NoSchedule" #污点类型

说明:

Pod的Toleration 声明中的key和effect需要与Taint的设置保持一直。

Operator如果设置为Equal,则key和value,要和Taint的设置保持一致。

2) 不完全匹配

tolerations:
- key: "taintKey"      #和污点的key名字保持一致
  operator: "Exists"   #匹配类型,只要符合污点设置的key即可
  effect: "NoSchedule" #污点的类型

说明:Operator 如果设置为Exists,则不需要指定value,只看key名字。

3)大范围匹配

tolerations:
- key: "taintKey"      #和污点的key名字保持一致
  operator: "Exists"   

说明:如果省略effect,则需要只看key名字即可,不管Taint里的effect设置为什么都不会匹配到。

4)匹配所有

tolerations:
- operator: "Exists"

说明:如果省略key和effect,则匹配所有Taint, 在k8s中的daemonsets资源默认情况下是容忍所有污点的。

 设置驱逐延缓时间设置

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

说明:如果Pod正在运行,那么Pod还将继续在节点运行3600秒,然后被驱逐。如果在此之前上述污点被删除,则Pod不会被驱逐。

Pod YAML示例:

apiVersion: v1
kind: Pod
metadata:
  name: ng
  namespace: prod
  labels:
    env: dev
spec:
  containers:
  - name: ng
    image: nginx:1.25.2
  tolerations:
  - key: name
    operator: Exists
    effect: NoSchedule

应用YAML

# kubectl describe node node-1-23 |grep -i taint
Taints:             <none>
Taints:             <none>
Taints:             <none>
# kubectl  apply -f tolerations.yaml 
pod/ng created
# kubectl  get pod -n prod -o wide
NAME        READY   STATUS    RESTARTS   AGE   IP               NODE         NOMINATED NODE   READINESS GATES
ng          1/1     Running   0          9s    10.244.154.15    node-1-233   <none>           <none>
testpod01   1/1     Running   0          66m   10.244.154.12    node-1-233   <none>           <none>
testpod02   1/1     Running   0          66m   10.244.167.144   node-1-231   <none>           <none>

get3个node打污点

# kubectl taint node node-1-231 name1=prod:NoSchedule
node/node-1-231 tainted
# kubectl taint node node-1-232 name1=prod:NoSchedule
node/node-1-232 tainted
# kubectl taint node node-1-233 name1=prod:NoSchedule
node/node-1-233 tainted
# kubectl describe node node-1-23 |grep -i taint
Taints:             name1=prod:NoSchedule
Taints:             name1=prod:NoSchedule
Taints:             name1=prod:NoSchedule

验证:

# kubectl  apply -f tolerations.yaml 
pod/ng created
# kubectl  get pod -o wide -n prod
NAME        READY   STATUS    RESTARTS   AGE   IP               NODE         NOMINATED NODE   READINESS GATES
ng          0/1     Pending   0          28s   <none>           <none>       <none>           <none>
testpod01   1/1     Running   0          69m   10.244.154.12    node-1-233   <none>           <none>
testpod02   1/1     Running   0          69m   10.244.167.144   node-1-231   <none>           <none>
kubectl describe pod ng -n prod
 # kubectl  describe pod ng -n prod
Name:             ng
Namespace:        prod
Priority:         0
Service Account:  default
Node:             <none>
Labels:           env=dev
Annotations:      <none>
Status:           Pending
IP:               
IPs:              <none>
Containers:
  ng:
    Image:        nginx:1.25.2
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-fjpwr (ro)
Conditions:
  Type           Status
  PodScheduled   False 
Volumes:
  kube-api-access-fjpwr:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 name:NoSchedule op=Exists
                             node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  56s   default-scheduler  0/4 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }, 3 node(s) had untolerated taint {name1: prod}. preemption: 0/4 nodes are available: 4 Preemption is not helpful for scheduling..

给node-1-231 加上name名称污点

# kubectl taint node node-1-231 name1=prod:NoSchedule-
node/node-1-231 untainted
# kubectl taint node node-1-231 name=prod:NoSchedule
node/node-1-231 tainted
# kubectl  get pod -o wide -n prod
NAME        READY   STATUS    RESTARTS   AGE    IP               NODE         NOMINATED NODE   READINESS GATES
ng          1/1     Running   0          4m3s   10.244.167.146   node-1-231   <none>           <none>
testpod01   1/1     Running   0          73m    10.244.154.12    node-1-233   <none>           <none>
testpod02   1/1     Running   0          73m    10.244.167.144   node-1-231   <none>           <none>

四、API资源对象PV和PVC

存储持久化相关的三个概念:

1)PersistentVolume(PV)

是对具体存储资源的描述,比如NFS、Ceph、GlusterFS等,通过PV可以访问到具体的存储资源

2)PersistentVolumeChaim(PVC)

Pod想要使用具体的存储资源需要对接到PVC,PVC会定义好Pod希望使用存储的属性,通过PVC再去申请合适的存储资源PV,匹配到合适的资源后PVC和PV会进行绑定,他们两者是一一对应的

3)StorageClass(SC)

PV可以手动创建,也可以自动创建,当PV需求量非常大时,如果靠手动创建PV非常麻烦,SC可以实现自动创建PV,并且将PVC和PV绑定

SC会定义两部分内容:

1)PV的属性,比如存储类型、大小

2)创建PV需要用到存储插件(provisioner),provisioner是实现自动创建PV的关键。

4.1 PV YAML示例

vim  testpv.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: testpv
spec:
  storageClassName: test-storage
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 500Mi  ##提供500Mi空间
  hostPath:
    path: /tmp/testpv/

说明:

storageClassName:定义存储类名称,PV和PVC中都会有改字段,目的是为了方便两者匹配绑定在一起

accessModes定义改pv的访问权限模式:

  • ReadWriteOnce:存储卷可读可写,但是只能被一个节点上的Pod挂载
  • ReadOnlyMany:存储卷只读不可写,可以被任意节点上的Pod多次挂载
  • ReadWriteMany:存储卷可读可写,可以被任意节点上的Pod多次挂载

capacity:定义该存储大小

hostPath:定义该存储访问路径,这里指本地磁盘

4.2 PVC YAML示例

vim  testpvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: testpvc

spec:
  storageClassName: test-storage
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 100Mi  ##期望申请100Mi空间

应用pv和pvc的YAML

# kubectl  apply -f testpv.yaml  -f testpvc.yaml 
persistentvolume/testpv created
persistentvolumeclaim/testpvc created

查看状态

# kubectl get pv,pvc

实验:

将testpvc的期望100Mi改为1000Mi,查看PV的STATUS

4.3 PV 和PVC匹配规则

PV创建好后,会等待PVC与其进行绑定,PVC一旦找到合适的PV就会绑定。如果有多个PV时,PVC又是如何匹配的?它有如下一些规则:

  1. 访问模式和存储类匹配:Kubernetes会筛选出访问模式(accessModes)和存储类(storageClassName)与PVC相匹配的PV。如果没有匹配的PV,PVC将保持未绑定状态。
  2. 资源大小:在满足访问模式和存储类匹配的PV中,Kubernetes会选择资源大小大于或等于PVC请求大小的PV
  3. 最佳匹配:在满足访问模式、存储类和资源大小的PV中,Kubernetes会选择资源大小最接近PVC请求大小的PV。如果有多个PV具有相同的资源小小,Kubernetes会选择其中一个进行绑定。
  4. 避免重复绑定:一个PV在任何时候只能被一个PVC绑定。一旦PV被绑定到一个PVC,它将不再可用于其他PVC。

五、本地存储

在6.1章节中的PV YAML示例,就是本地存储。本地存储类型的PV是Kubernetes中一种比较特殊的持久化存储,它允许将节点上的本地磁盘或目录用作PV。与其他PV类型(例如NFS、Ceph或云存储)不同,本地存储类型的PV直接使用节点上的存储资源,因此具有更低的延迟和更高的性能。

使用本地存储类型的PV时,需注意以下几个关键点:

  • 节点特性:本地存储类型的PV与特定的节点绑定,因为它直接使用节点上的存储资源。这意味着当创建PV时,需要指定与之关联的节点。可以在PV的spec部分设置nodeAffinity来实现的。
  nodeAffinity:  ##定义节点亲和性
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - node-name
  • 数据持久性:由于本地存储类型的PV与特定节点关联,当该节点发生故障时,存储在PV中的数据可能无法访问。因此,在使用本地存储类型的PV时,请确保采取适当的数据备份策略,以防止节点故障导致的数据丢失。
  • 调度限制:Pod使用本地存储类型的Persistent Volume Claim(PVC)时,Kubernetes会尝试将Pod调度到关联PV的节点上。如果节点上的资源不足以运行Pod,Pod将无法启动。因此,在使用本地存储类型的PV时,请确保关联的节点有足够的资源来运行Pod。
  • 回收策略:当PVC被删除时,PV的回收策略将决定如何处理关联的本地存储。对于本地存储类型的PV,建议使用Retain或Delete回收策略。Retain策略表示保留存储和数据,以便手动清理和管理;Delete策略表示删除存储和数据。需要注意的是,Recycle策略并不适用于本地存储类型的PV。
  persistentVolumeReclaimPolicy: Retain

完整示例:

首先,确保在每个要使用本地存储的节点(node-1-231)上创建一个本地目录。例如在节点上创建/data/local-storage

mkdir -p /data/local-storage

然后创建一个PV资源配置文件

vim local-pv.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv
  labels:
    type: local
spec:
  storageClassName: local-storage
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  local:
    path: /data/local-storage
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname  #这是内置的节点标签,表示节点的主机名
          operator: In
          values:
          - node-1-231  #只有node-1-231这个主机节点才满足要求

应用PV资源配置文件

# kubectl  apply -f local-pv.yaml 
persistentvolume/local-pv created

再创建一个PVC资源配置文件

vim local-pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: local-pvc
spec:
  storageClassName: local-storage
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

 应用PVC资源配置文件

kubectl apply -f local-pvc.yaml

 查看状态:

kubectl  get pv
kubectl  get pvc

 

Kubernetes会自动将PVC与PV绑定。创建好的PVC可以在Pod中使用,将本地存储挂载到容器中。
最后,创建一个Pod资源配置文件,例如local-pod.yaml:

 

apiVersion: v1
kind: Pod
metadata:
  name: local-pod
spec:
  containers:
  - name: local-container
    image: nginx:1.25.2
    volumeMounts:
    - name: local-storage
      mountPath: /data
  volumes:
  - name: local-storage
    persistentVolumeClaim:
      claimName: local-pvc

 应用Pod YAML文件

# kubectl apply -f local-pd.yaml 
pod/local-pod created

 测试:

进入pod写入测试文件1.txt

# kubectl  exec -it local-pod  -- bash
root@local-pod:/# cd /data/
root@local-pod:/data# echo "Hello Kubernetes!" >1.txt 
root@local-pod:/data# cat 1.txt 
Hello Kubernetes!
root@local-pod:/data# 

 在node-1-231节点验证1.txt文件内容