k8s存储系统

一. k8s服务种类

1. 无状态

  • 没有存储, pod挂掉会被拉起来, 来保证pod实例数量
  • K8S的Service后面可以挂多个Pod,实现服务的高可用, 比如常用的nginx就为无状态服务

2. 普通有状态

  • k8s提供VolumePersistent Volume为基础的存储系统,可以实现服务的状态保存
  • 常用的k8s中部署的Jenkins就是普通有状态服务

3. 有状态集群

  • 在普通有状态的基础上, 通过Headless Service(无头服务), 来将实现有状态服务的集群或高可用, 例如redis集群, mysql主从等等

二. k8s 存储

官网

1. Volumes

  • 不做过多介绍, 参考官网

ceph

2. PersistentVolume

  • PersistentVolume(持久卷, PV), 是集群中的一块存储, PV是集群资源,作用于所有namespace
  • PV 卷的供应有两种方式: 静态供应或动态供应(StorageClass)

3. PersistentVolumeClaim

  • PersistentVolumeClaim(持久卷声明, PVC), 是用户对存储的请求, 概念上与 Pod 类似, Pod 会耗用节点资源,而 PVC 声明会耗用 PV 资源

  • PVC 声明请求以特定的大小和访问模式, 可以要求 PV 卷能够以 ReadWriteOnceReadOnlyManyReadWriteMany 模式之一来挂载

    ReadWriteOnce
    

    简称RWO, 卷可以被一个节点以读写方式挂载。 ReadWriteOnce 访问模式也允许运行在同一节点上的多个 Pod 访问卷

    ReadOnlyMany
    

    简称ROX, 卷可以被多个节点以只读方式挂载

    ReadWriteMany
    

    简称RWX, 卷可以被多个节点以读写方式挂载

    ReadWriteOncePod
    

    简称RWOP, 卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC, 请使用ReadWriteOncePod 访问模式。这只支持 CSI 卷以及需要 Kubernetes 1.22 以上版本

4. 存储回收

  • 当不再使用其存储卷时,可以从 API 中将 PVC 对象删除,从而允许 该资源被回收再利用。PersistentVolume 对象的回收策略告诉集群,目前数据卷可以被 Retained(保留)、Recycled(回收)或 Deleted(删除)
    • 回收策略 Recycle 已被废弃, 取而代之的建议方案是使用动态供应

三. Kubernetes StorageClass

k8s Lifecycle

官网

  • StorageClass是k8s存储类, 每个StorageClass 都包含 provisionerparametersreclaimPolicy 字段, 这些字段会在 StorageClass 需要动态分配 PersistentVolume 时会使用到
  • 每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备 PV。 该字段必须指定
  • 由 StorageClass 动态创建的 PersistentVolume 会在类的 reclaimPolicy 字段中指定回收策略,可以是 Delete 或者 Retain。如果 StorageClass 对象被创建时没有指定 reclaimPolicy,它将默认为 Delete
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2
reclaimPolicy: Retain		# 线上是用的默认的
allowVolumeExpansion: true
mountOptions:
  - debug
volumeBindingMode: Immediate

process

1. 创建StorageClass

vim provisioner.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-es-provisioner
  labels:
    app: nfs-es-provisioner
  namespace: logging
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nfs-es-provisioner
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-es-provisioner
  template:
    metadata:
      labels:
        app: nfs-es-provisioner
    spec:
      serviceAccountName: nfs-es-provisioner
      containers:
        - name: nfs-es-provisioner
          image: quay.io/external_storage/nfs-client-provisioner:latest
          imagePullPolicy: IfNotPresent
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: nfs-es                 # 和storageclass的provisioner字段必须一致才对接的上.
            - name: NFS_SERVER
              value: 192.168.188.225    	# nfs服务端地址
            - name: NFS_PATH
              value: /data	            	# 挂载的路径
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.188.225
            path: /data

vim rbac.yaml

kind: ServiceAccount
apiVersion: v1
metadata:
  name: nfs-es-provisioner
  namespace: logging
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-es-provisioner-runner
  namespace: logging
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-es-provisioner
  namespace: logging
subjects:
  - kind: ServiceAccount
    name: nfs-es-provisioner
    namespace: logging
roleRef:
  kind: ClusterRole
  name: nfs-es-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-es-provisioner
  namespace: logging
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-es-provisioner
  namespace: logging
subjects:
  - kind: ServiceAccount
    name: nfs-es-provisioner
    namespace: logging
roleRef:
  kind: Role
  name: leader-locking-nfs-es-provisioner
  apiGroup: rbac.authorization.k8s.io

vim storageclass.yaml

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-es
provisioner: nfs-es

2. 使用StorageClass

vim redis-sts.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-app
  namespace: logging
  labels:
    k8s-app: redis
spec:
  serviceName: redis-service
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
  replicas: 3
  template:
    metadata:
      labels:
        app: redis
        appCluster: redis-cluster
    spec:
      terminationGracePeriodSeconds: 20
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - redis
              topologyKey: kubernetes.io/hostname
      containers:
      - name: redis
        image: redis
        imagePullPolicy: IfNotPresent
        command:
          - "redis-server"
        args:
          - "/etc/redis/redis.conf"
          - "--protected-mode"
          - "no"
        resources:
          requests:
            cpu: "100m"
            memory: "100Mi"
        ports:
            - name: redis
              containerPort: 6379
              protocol: "TCP"
            - name: cluster
              containerPort: 16379
              protocol: "TCP"
        volumeMounts:
          - name: "redis-conf"
            mountPath: "/etc/redis"
          - name: "redis-data"
            mountPath: "/var/lib/redis"
      volumes:
      - name: "redis-conf"
        configMap:
          name: "redis-conf"
          items:
            - key: "redis.conf"
              path: "redis.conf"
  volumeClaimTemplates:
  - metadata:
      name: redis-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: nfs-es
      resources:
        requests:
          storage: 300M

四. Volume Snapshots

官网

1. VolumeSnapshotContent和VolumeSnapshot

  • PersistentVolumePersistentVolumeClaim 两个 API 资源用于提供卷类似,VolumeSnapshotContentVolumeSnapshot 两个 API 资源用于给用户和管理员创建卷快照
    • VolumeSnapshotContent 类似于PersistentVolume 属于集群资源, 是一种快照
    • VolumeSnapshot类似于PersistentVolumeClaim, 作用于namespace, 是对于卷的快照的请求

2. VolumeSnapshotClass

参考

  • 就像 StorageClass 为提供了一种在配置卷时描述存储“类”的方法,VolumeSnapshotClass 提供了一种在配置卷快照时描述存储“类”的方法。

  • 每个 VolumeSnapshotClass 都包含 driverdeletionPolicyparameters 字段,当需要动态配置属于该类的 VolumeSnapshot 时使用

  • VolumeSnapshotClass对象的名称很重要,是用户可以请求特定类的方式。 管理员在首次创建 VolumeSnapshotClass 对象时设置类的名称和其他参数,对象一旦创建就无法更新

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
  name: csi-hostpath-snapclass
driver: hostpath.csi.k8s.io
deletionPolicy: Delete
parameters:
  • 卷快照类具有 deletionPolicy 属性。用户可以配置当所绑定的 VolumeSnapshot 对象将被删除时,如何处理 VolumeSnapshotContent 对象。 卷快照类的这个策略可以是 Retain 或者 Delete。这个策略字段必须指定

  • 如果删除策略是 Delete,那么底层的存储快照会和 VolumeSnapshotContent 对象 一起删除。如果删除策略是 Retain,那么底层快照和 VolumeSnapshotContent 对象都会被保留


版权声明:本文为moon_naxx原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。