Kubernetes 存储

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

  • 1)PersistentVolume(PV)
    是对具体存储资源的描述,比如NFS、Ceph、GlusterFS等,通过PV可以访问到具体的存储资源;
  • 2)PersistentVolumeClaim(PVC)
    Pod想要使用具体的存储资源需要对接到PVC,PVC里会定义好Pod希望使用存储的属性,通过PVC再去申请合适的存储资源(PV),匹配到合适的资源后PVC和PV会进行绑定,它们两者是一一对应的;
  • 3)StorageClass(SC)
    PV可以手动创建,也可以自动创建,当PV需求量非常大时,如果靠手动创建PV就非常麻烦了,SC可以实现自动创建PV,并且会将PVC和PV绑定。

SC会定义两部分内容:

  • ① pv的属性,比如存储类型、大小;
  • ② 创建该PV需要用到的存储插件(provisioner),这个provisioner是实现自动创建PV的关键。

1、API资源对象PV和PVC

1.1 PV

PV YAML示例:
vi 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 定义该存储访问路径,这里指的是本地的磁盘。

1.2 PVC

PVC YAML示例
vi 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

查看状态

$ kubectl get pv,pvc

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

$ kubectl get pv,pvc
NAME                                  CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM       STORAGECLASS   REASON   AGE
persistentvolume/testpv               500Mi      RWO            Retain           Available               test-storage            22s

NAME                                  STATUS    VOLUME          CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/testpvc         Pending                                             test-storage   10s

1.3 PV和PVC匹配规则

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

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

2、本地存储

2.1 本地存储介绍

在10.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

2.2 本地存储示例

首先,确保在每个要使用本地存储的节点上创建一个本地目录。例如,在节点上创建/mnt/local-storage目录:

$ mkdir -p /mnt/local-storage

然后,创建一个PV资源配置文件,例如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: /mnt/local-storage
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname  #这是内置的节点标签,表示节点的主机名
          operator: In
          values:
          - aminglinux02  #只有aminglinux02这个主机节点才满足要求

应用PV资源配置文件:

$ kubectl apply -f local-pv.yaml

再创建一个PVC资源配置文件,例如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

使用本地存储时,storageClass可以不存在
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.21.0
    volumeMounts:
    - name: local-storage
      mountPath: /data
  volumes:
  - name: local-storage
    persistentVolumeClaim:
      claimName: local-pvc

应用Pod资源配置文件:

$ kubectl apply -f local-pod.yaml

现在,local-pod中的local-container已经挂载了本地存储。所有写入/data目录的数据都将持久化在本地存储中。

3、NFS存储

注意:在做本章节示例时,需要拿单独一台机器来部署NFS,具体步骤略。
NFS作为常用的网络文件系统,在多机之间共享文件的场景下用途广泛,毕竟NFS配置方便,而且稳定可靠。
NFS同样也有一些缺点:

  • ① 存在单点故障的风险;
  • ② 不方便扩容;
  • ③ 性能一般。

NFS比较适合一些简单的、对存储要求不高的场景,比如测试环境、开发环境。

完整示例:
首先部署好NFS服务,并且保证所有Kubernetes节点可以顺利挂载(showmount -e 192.168.222.128 )
安装NFS客户端

$ yum install -y nfs-utils

定义基于NFS的PV

$ vi nfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs-storage
  nfs:
    path: /data/nfs2
    server: 192.168.222.128

应用

$ kubectl apply -f nfs-pv.yaml

定义PVC

$ vi nfs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
spec:
  storageClassName: nfs-storage
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi

应用

$ kubectl apply -f nfs-pvc.yaml

定义Pod

$ vi  nfs-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nfs-pod
spec:
  containers:
    - name: nfs-container
      image: nginx:1.21.0
      volumeMounts:
        - name: nfs-storage
          mountPath: /data
  volumes:
    - name: nfs-storage
      persistentVolumeClaim:
        claimName: nfs-pvc

应用

$ kubectl apply -f nfs-pod.yaml

4、API资源对象StorageClass

SC的主要作用在于,自动创建PV,从而实现PVC按需自动绑定PV。
下面我们通过创建一个基于NFS的SC来演示SC的作用。
要想使用NFS的SC,还需要安装一个NFS provisioner,provisioner里会定义NFS相关的信息(服务器IP、共享目录等)
github地址: https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

将源码下载下来:

git clone https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
$ cd nfs-subdir-external-provisioner/deploy
$ sed -i 's/namespace: default/namespace: kube-system/' rbac.yaml  ##修改命名空间为kube-system
$ kubectl apply -f rbac.yaml  ##创建rbac授权

修改deployment.yaml

sed -i 's/namespace: default/namespace: kube-system/' deployment.yaml ##修改命名空间为kube-system
  ##你需要修改注释的部分 
   spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: registry.cn-hangzhou.aliyuncs.com/kavin1028/nfs-subdir-external-provisioner:v4.0.2  ##改为dockerhub地址
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: k8s-sigs.io/nfs-subdir-external-provisioner
            - name: NFS_SERVER
              value: 192.168.222.99  ##nfs服务器地址
            - name: NFS_PATH
              value: /data/nfs  ##nfs共享目录
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.222.99  ##nfs服务器地址
            path: /data/nfs  ##nfs共享目录

应用yaml

$ kubectl apply -f deployment.yaml
$ kubectl apply -f class.yaml ##创建storageclass

SC YAML示例

$ cat class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-client
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # or choose another name, must match deployment's env PROVISIONER_NAME'
parameters:
  archiveOnDelete: "false"  ##自动回收存储空间

有了SC,还需要一个PVC

$ vi nfsPvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfspvc

spec:
  storageClassName: nfs-client
  accessModes:
    - ReadWriteMany

  resources:
    requests:
      storage: 500Mi

下面创建一个Pod,来使用PVC

$ vi  nfsPod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nfspod
spec:
  containers:
  - name: nfspod
    image: nginx:1.23.2
    volumeMounts:
    - name: nfspv
      mountPath: "/usr/share/nginx/html"
  volumes:
  - name: nfspv
    persistentVolumeClaim:
      claimName: nfspvc

总结一下:

pod想使用共享存储 --> PVC (定义具体需求属性) -->SC (定义Provisioner) --> Provisioner(定义具体的访问存储方法) --> NFS-server
            |                                   |
            |___________________________________|-->自动创建PV

5、Ceph存储

说明:Kubernetes使用Ceph作为存储,有两种方式,一种是将Ceph部署在Kubernetes里,需要借助一个工具rook;另外一种就是使用外部的Ceph集群,也就是说需要单独部署Ceph集群。本文主要介绍第二种。

5.1 对比两种方式

Rook Ceph 集成:

  • 使用 Rook 可以在 Kubernetes 集群内部部署和管理 Ceph 存储集群。Rook 是一个开源的项目,它允许你通过 Kubernetes Operator 部署和管理 Ceph。这种方式将 Ceph 部署为 Kubernetes 集群的一部分,使得存储资源更加集成和易于管理。这适用于需要在 Kubernetes 内部快速部署和管理 Ceph 的场景。

外部独立 Ceph 集群

  • 在这种方式下,你需要单独部署和维护一个独立的 Ceph 存储集群,而不将其嵌入到 Kubernetes 集群中。Kubernetes 集群将通过 Ceph 的网络接口与外部 Ceph 集群进行通信。这适用于已经存在或希望独立维护的 Ceph 集群,并且 Kubernetes 集群只需要连接到这个外部 Ceph 集群来使用存储资源。

选择哪种方式取决于你的需求和环境。如果你需要更好的集成和自动化管理,并且不希望单独管理 Ceph 集群,那么使用 Rook Ceph 集成可能更合适。如果你已经有一个独立的 Ceph 集群或者希望将存储资源与 Kubernetes 集群分离开,那么使用外部独立 Ceph 集群可能更合适。

5.2 搭建Ceph集群

见文档:http://10.0.1.2:10086/modify_doc/26/

5.3 k8s使用ceph

1、获取ceph集群信息和admin用户的key(ceph那边)

#获取集群信息
$ ceph mon dump
epoch 3
fsid 69e2f148-7005-11ee-8438-00505684afef
last_changed 2023-10-21T11:50:13.661918+0000
created 2023-10-21T11:33:10.492205+0000
min_mon_release 16 (pacific)
election_strategy: 1
0: [v2:10.0.1.211:3300/0,v1:10.0.1.211:6789/0] mon.ceph01
1: [v2:10.0.1.212:3300/0,v1:10.0.1.212:6789/0] mon.ceph02
2: [v2:10.0.1.213:3300/0,v1:10.0.1.213:6789/0] mon.ceph03
dumped monmap epoch 3

#获取admin用户key
$ ceph auth get-key client.admin ; echo
AQD2tjNlGwqpABAArWONbjkHLYkvu9KnpvHbig==

2、下载并导入镜像(K8S集群操作)
将用到的镜像先下载下来,避免启动容器时,镜像下载太慢或者无法下载
可以下载到其中某一个节点上,然后将镜像拷贝到其它节点

#下载镜像(其中一个节点)
$ wget -P /tmp/ https://d.frps.cn/file/tools/ceph-csi/k8s_1.24_ceph-csi.tar

#拷贝(所有k8s节点)
$ scp /tmp/k8s_1.24_ceph-csi.tar  aminglinux02:/tmp/
$ scp /tmp/k8s_1.24_ceph-csi.tar  aminglinux03:/tmp/

#导入镜像(所有k8s节点)
ctr -n k8s.io i import k8s_1.24_ceph-csi.tar

3、创建ceph的 provisioner
创建ceph目录,后续将所有yaml文件放到该目录下

$ mkdir ceph
$ cd ceph

创建secret.yaml

cat > secret.yaml <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: csi-rbd-secret
  namespace: default
stringData:
  userID: admin
  userKey: AQD2tjNlGwqpABAArWONbjkHLYkvu9KnpvHbig==  #这串上面已经获取 
EOF

创建config-map.yaml

cat >  csi-config-map.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: "ceph-csi-config"
data:
  config.json: |-
    [
      {
        "clusterID": "69e2f148-7005-11ee-8438-00505684afef",  # ceph mon dump命令回显中的fsid
        "monitors": [
          "10.0.1.211:6789",
          "10.0.1.212:6789",
          "10.0.1.213:6789"
        ]
      }
    ]
EOF
  • clusterID后的注释一定要删除

创建ceph-conf.yaml

cat > ceph-conf.yaml <<EOF
apiVersion: v1
kind: ConfigMap
data:
  ceph.conf: |

[global]

auth_cluster_required = cephx auth_service_required = cephx auth_client_required = cephx # keyring is a required key and its value should be empty keyring: | metadata: name: ceph-config EOF

创建csi-kms-config-map.yaml(该config内容为空)

cat > csi-kms-config-map.yaml <<EOF
---
apiVersion: v1
kind: ConfigMap
data:
  config.json: |-
    {}
metadata:
  name: ceph-csi-encryption-kms-config
EOF

下载其余rbac以及provisioner相关yaml

$ wget  https://d.frps.cn/file/tools/ceph-csi/csi-provisioner-rbac.yaml
$ wget  https://d.frps.cn/file/tools/ceph-csi/csi-nodeplugin-rbac.yaml

$ wget https://d.frps.cn/file/tools/ceph-csi/csi-rbdplugin.yaml
$ wget https://d.frps.cn/file/tools/ceph-csi/csi-rbdplugin-provisioner.yaml

应用所有yaml(注意,当前目录是在ceph目录下)

$ for f in `ls *.yaml`; do echo $f; kubectl apply -f $f; done

检查provisioner的pod,状态为running才对

$ kubectl get po

NAME                                         READY   STATUS    RESTARTS      AGE
csi-rbdplugin-29q68                          3/3     Running   0             14s
csi-rbdplugin-h44dh                          3/3     Running   0             14s
csi-rbdplugin-ktchl                          3/3     Running   0             14s
csi-rbdplugin-lpbmj                          3/3     Running   0             14s
csi-rbdplugin-provisioner-5c7d54dd8d-2msht   7/7     Running   0             14s
csi-rbdplugin-provisioner-5c7d54dd8d-btvr6   7/7     Running   0             14s
csi-rbdplugin-provisioner-5c7d54dd8d-sxdtj   7/7     Running   0             14s
csi-rbdplugin-sn66h                          3/3     Running   0             14s
csi-rbdplugin-wmgpn                          3/3     Running   0             14s

4、创建storageclass
在k8s上创建ceph-sc.yaml

cat > ceph-sc.yaml <<EOF
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
   name: csi-rbd-sc     #storageclass名称
provisioner: rbd.csi.ceph.com   #驱动器
parameters:
   clusterID: 69e2f148-7005-11ee-8438-00505684afef    #ceph集群id
   pool: pool01      #pool空间
   imageFeatures: layering   #rbd特性
   csi.storage.k8s.io/provisioner-secret-name: csi-rbd-secret
   csi.storage.k8s.io/provisioner-secret-namespace: default
   csi.storage.k8s.io/controller-expand-secret-name: csi-rbd-secret
   csi.storage.k8s.io/controller-expand-secret-namespace: default
   csi.storage.k8s.io/node-stage-secret-name: csi-rbd-secret
   csi.storage.k8s.io/node-stage-secret-namespace: default
reclaimPolicy: Delete   #pvc回收机制
allowVolumeExpansion: true   #对扩展卷进行扩展
mountOptions:           #StorageClass 动态创建的 PersistentVolume 将使用类中 mountOptions 字段指定的挂载选项
   - discard
EOF

##应用yaml
kubectl apply -f ceph-sc.yaml 

5、创建pvc
在k8s上创建ceph-pvc.yaml

cat > ceph-pvc.yaml <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ceph-pvc     #pvc名称
spec:
  accessModes:
    - ReadWriteOnce     #访问模式
  resources:
    requests:
      storage: 1Gi      #存储空间
  storageClassName: csi-rbd-sc
EOF

#应用yaml
kubectl apply -f ceph-pvc.yaml

查看pvc状态,STATUS必须为Bound

$ kubectl get pvc

NAME                    STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
ceph-pvc                Bound    pvc-b7e8db6e-043c-4ee9-9fde-6158906d93a7   1Gi        RWO            csi-rbd-sc     72s

6、创建pod使用ceph存储

cat > ceph-pod.yaml <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: ceph-pod
spec:
  containers:
  - name: ceph-ng
    image: nginx:1.23.2
    volumeMounts:
    - name: ceph-mnt
      mountPath: /mnt
      readOnly: false
  volumes:
  - name: ceph-mnt
    persistentVolumeClaim:
      claimName: ceph-pvc
EOF

$ kubectl apply -f ceph-pod.yaml

查看pv

$ kubectl get pv

NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                           STORAGECLASS   REASON   AGE
pvc-b7e8db6e-043c-4ee9-9fde-6158906d93a7   1Gi        RWO            Delete           Bound    default/ceph-pvc                csi-rbd-sc              2m57s

查看pod

$ kubectl get po

NAME                                         READY   STATUS    RESTARTS      AGE
ceph-pod                                     1/1     Running   0             51s

在ceph这边查看rbd

[ceph: root@ceph1 /]# rbd ls pool01

csi-vol-16a0841b-7010-11ee-81a4-b6725ee39efe

在pod里查看挂载情况

$ kubectl exec -it ceph-pod -- df

Filesystem          1K-blocks     Used Available Use% Mounted on
overlay              64161168 14775416  49385752  24% /
tmpfs                   65536        0     65536   0% /dev
tmpfs                 8200248        0   8200248   0% /sys/fs/cgroup
/dev/rbd0              996780       24    980372   1% /mnt
/dev/mapper/rl-root  64161168 14775416  49385752  24% /etc/hosts
shm                     65536        0     65536   0% /dev/shm
tmpfs                16298100       12  16298088   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs                 8200248        0   8200248   0% /proc/acpi
tmpfs                 8200248        0   8200248   0% /proc/scsi
tmpfs                 8200248        0   8200248   0% /sys/firmware

10.6 GlusterFS存储

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

给TA打赏
共{{data.count}}人
人已打赏
Kubernetes云原生

Kubernetes高级调度

2025-4-9 8:37:48

Kubernetes云原生

Kubernetes集群维护

2025-4-9 8:39:16

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索
Index