yaml文件

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-pv-1
spec:
  capacity:
    storage: 20Gi
  volumeMode: Filesystem                   # 存储卷的类型为:文件系统
  accessModes:
  - ReadWriteOnce                          # 访问策略:该卷可以被单个节点以读/写模式挂载
  persistentVolumeReclaimPolicy: Delete    #下面有注释
  storageClassName: local-storage
  local:
    path: /home/paas/etcd/etcd             #本地目录
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k8s-node01

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-pv-2
spec:
  capacity:
    storage: 20Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /home/paas/etcd/etcd
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k8s-node02
          
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-pv-3
spec:
  capacity:
    storage: 20Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /home/paas/etcd/etcd
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k8s-node03

---
apiVersion: v1
kind: Service
metadata:
  name: etcd-headless-svc
  namespace: kube-system
  labels:
    app: etcd
spec:
  ports:
  - name: etcd-client
    port: 2379
    protocol: TCP
    targetPort: 2379
  - name: etcd-peer
    port: 2380
    protocol: TCP
    targetPort: 2380
  clusterIP: None
  selector:
    app: etcd
  publishNotReadyAddresses: true   #publishNotReadyAddresses 可以控制是否将未就绪的 Pod 发布到 Service 上,一般不建议为 true

---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: etcd
  name: etcd-nodeport
  namespace: kube-system
spec:
  ports:
  - name: etcd-cluster
    port: 2379
    targetPort: 2379
  selector:
    app: etcd
  sessionAffinity: None                    #sessionAffinity可以设置会话亲和性:默认是None表示轮询,ClientIP表示会话固定。
  type: NodePort

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: etcd
  namespace: kube-system
  labels:
    app: etcd
spec:
  podManagementPolicy: Parallel               # Pod管理策略,默认的“OrderedReady”表示顺序创建并逆序删除,“Parallel”表示并行模式
  replicas: 3
  selector:
    matchLabels:
      app: etcd
  serviceName: etcd-headless-svc
  template:
    metadata:
      labels:
        app: etcd
      name: etcd
    spec:
      nodeSelector:
        app: etcd
      containers:
        - name: etcd
          image: registry.cn-hangzhou.aliyuncs.com/leige24/k8s-etcd:v1  #可以自己制作,下面会写
          imagePullPolicy: Always
          ports:
          - containerPort: 2380
            name: peer
            protocol: TCP
          - containerPort: 2379
            name: client
            protocol: TCP
          env:
          - name: MY_POD_NAME  #当前pod名
            valueFrom:
              fieldRef:
                fieldPath: metadata.name   
          - name: MY_POD_IP  #当前pod ip
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
          - name: CLUSTER_NAMESPACE  #名称空间
            valueFrom: 
              fieldRef:
                fieldPath: metadata.namespace
          - name: HEADLESS_SERVICE_NAME   #内部通信的无头服务名称
            value: "etcd-headless-svc"
          - name: INITIAL_CLUSTER   #initial-cluster的值
            value: "etcd-0=http://etcd-0.etcd-headless-svc.kube-system:2380,etcd-1=http://etcd-1.etcd-headless-svc.kube-system:2380,etcd-2=http://etcd-2.etcd-headless-svc.kube-system:2380"
          resources:
            requests:
              memory: "1Gi"
              cpu: "2000m"
            limits:
              memory: "1Gi"
              cpu: "2000m"
          volumeMounts:
          - mountPath: /var/lib/etcd
            name: data-etcd
  updateStrategy:                    #滚动更新策略
    type: OnDelete                   # 滚动更新类型,可用值有OnDelete(只有手动删除了pod,新的pod才会被创建)和默认滚动升级Rollingupdate
  volumeClaimTemplates:              #下面有注释
  - metadata:
      name: data-etcd
    spec:
      accessModes: [ "ReadWriteMany" ]
      storageClassName: local-storage
      resources:
        requests:
          storage: 2Gi

etcd镜像制作

Dockerfile
FROM eulerx86lib:1.5      #基础镜像随便选
USER root
ADD etcd-v3.5.4-linux-amd/etcd* /usr/bin/
COPY etcd.sh /
RUN chmod 777 /etcd.sh && chmod 777 /usr/bin/etcd
CMD ["/etcd.sh"]
etcd启动脚本
#/bin/bash
etch ${MY_POD_NAME}
echo ${MY_POD_IP}
echo ${HEADLESS_SERVICE_NAME}
echo ${CLUSTER_NAMESPACE}
echo ${INITIAL_CLUSTER}

/usr/bin/etcd --data-dir=/var/lib/etcd --name=${MY_POD_NAME} \
              --listen-peer-urls=http://0.0.0.0:2380 \
              --listen-client-urls http://0.0.0.0:2379 \
              --advertise-client-urls http://${MY_POD_NAME}.${HEADLESS_SERVICE_NAME}.${CLUSTER_NAMESPACE}.svc.cluster.local:2379 \
              --initial-advertise-peer-urls http://${MY_POD_NAME}.${HEADLESS_SERVICE_NAME}.${CLUSTER_NAMESPACE}.svc.cluster.local:2380 \
              --initial-cluster-state='new' \
              --initial-cluster-token='etcd-cluster-token' \
              --initial-cluster=${INITIAL_CLUSTER}

注释:

PV、PVC、StorageClass详解参考:

https://kubernetes.io/zh-cn/docs/concepts/storage/persistent-volumes/

PV的访问模式(spec.accessModes)

三种访问模式

访问模式accessModes一共有三种:

  • ReadWriteOnce: 该卷可以被单个节点以读/写模式挂载
  • ReadOnlyMany: 该卷可以被多个节点以只读模式挂载
  • ReadWriteMany: 该卷可以被多个节点以读/写模式挂载

在命令行cli中,三种访问模式可以简写为:

  • RWO - ReadWriteOnce
  • ROX - ReadOnlyMany
  • RWX - ReadWriteMany

但不是所有的类型的底层存储都支持以上三种,每种底层存储类型支持的都不一样!!

PV的回收策略(spec.persistentVolumeReclaimPolicy)
  • Retain(保留): pv被删除后会保留内存,手动回收
  • Recycle(回收): 删除卷下的所有内容(rm-rf /thevolume/*)
  • Delete(删除): 关联的存储资产(例如AWS EBS、GCE PD、Azure Disk 和OpenStack Cinder卷)将被删除。即直接把卷给删除了

  回收策略注意事项

  1. 当前,只有NFSHostPath支持Recycle回收策略

最新版本中的Recycle已被废弃,截图如下

附:具体官网文档详细说明链接点击此处

PersistentVolume(PV)的状态

PV可以处于以下的某种状态:

  • Available(可用): 块空闲资源还没有被任何声明绑定
  • Bound(已绑定): 卷已经被声明绑定, 注意:但是不一定不能继续被绑定,看accessModes而定
  • Released(已释放): 声明被删除,但是资源还未被集群重新声明
  • Failed(失败): 该卷的自动回收失败
volumeClaimTemplates

volumeClaimTemplates 是 StatefulSet 中的一个字段,它用于定义每个 Pod 所需的持久卷声明(Persistent Volume Claim,PVC)模板。通过使用这个字段,可以自动为 StatefulSet 中的每个 Pod 创建和绑定相应的 PVC。

在 volumeClaimTemplates 字段中,可以定义多个 PVC 模板,每个模板都包含了创建 PVC 所需的属性,如存储类、访问模式、存储资源需求等。StatefulSet 会根据这些模板为每个 Pod 动态创建 PVC,并将其绑定到匹配的持久卷上。

当创建 StatefulSet 的 Pod 时,每个 Pod 将使用与其对应的 PVC,并将其挂载到 Pod 的容器中,从而实现持久化存储的使用


StorageClass的延迟绑定机制

provisioner 字段定义为no-provisioner,这是因为 Local Persistent Volume 目前尚不支持 Dynamic Provisioning动态生成PV,所以我们需要提前手动创建PV。

volumeBindingMode字段定义为WaitForFirstConsumer,它是 Local Persistent Volume 里一个非常重要的特性,即:延迟绑定。延迟绑定就是在我们提交PVC文件时,StorageClass为我们延迟绑定PV与PVC的对应关系。

这样做的原因是:比如我们在当前集群上有两个相同属性的PV,它们分布在不同的节点Node1和Node2上,而我们定义的Pod需要运行在Node1节点上 ,但是StorageClass已经为Pod声明的PVC绑定了在Node2上的PV,这样的话,Pod调度就会失败,所以我们要延迟StorageClass的绑定操作。

也就是延迟到到第一个声明使用该 PVC 的 Pod 出现在调度器之后,调度器再综合考虑所有的调度规则,当然也包括每个 PV 所在的节点位置,来统一决定,这个 Pod 声明的 PVC,到底应该跟哪个 PV 进行绑定。

比如上面的Pod需要运行在node1节点上,StorageClass发现可以绑定的PV后,先不为Pod中的PVC绑定PV,而是等到Pod调度到node1节点后,再为PVC绑定当前节点运行的PV。

所以,通过这个延迟绑定机制,原本实时发生的 PVC 和 PV 的绑定过程,就被延迟到了 Pod 第一次调度的时候在调度器中进行,从而保证了这个绑定结果不会影响 Pod 的正常调度。



其他参考:

https://www.cnblogs.com/baoshu/p/13281876.html#head18