• 为K8S集群准备Ceph存储


      随着K8S存储接口逐渐成熟并顺势推出CSI接口规范后,原来“in-tree”(树内)模式的很多存储插件也逐步迁移到了“out-of-tree”(树外)模式的CSI插件上,甚至有些原来支持的存储卷类型都被直接移除了(例如在K8S v1.26上直接移除了 glusterfs 卷类型),查阅了一下K8S官方最新版本的存储相关(Storage/Volumes)的说明,综合最新存储支持情况,我们选择Ceph作为K8S集群的存储提供者。

      首先,进入Ceph官网文档查看其安装部署方法,主要看下是否有基于K8S的安装方法,最后在官网“Installing ceph>Recommended methods”(推荐的Ceph安装方法)果然发现了基于K8S的安装方法:

       Ceph官方推荐在K8S集群上使用Rook来部署和管理Ceph集群!

      我们进入Rook官网看看,从官网可以看出Rook是为K8S量身定制的,那就它了:

      Ceph是一个在大规模生产集群中提供文件、块和对象存储的分布式存储系统,而Rook是一个专门支持Ceph与云原生环境集成的开源云原生存储协调器。Rook利用K8S的Operator机制推出了自己的Rook operator,实现自动化的Ceph部署和管理。Rook作为云原生存储平台已经从CNCF顺利毕业!

      以上是对Rook简要说明,接下来借助Rook在K8S集群上部署和管理Ceph。

      Rook支持K8S v1.19+的版本,CPU架构为amd64、x86_64或arm64均可,除此之外部署Ceph存储集群还必须至少满足以下先决条件之一

    • 每个节点至少有一块裸设备(Raw devices,未分区未进行文件系统格式化)
    • 裸分区(Raw partitions,未进行文件系统格式化)
    • LVM逻辑卷(LVM Logical Volumes,未进行文件系统格式化)
    • block模式下存储类(storage class)中可用的持久卷(PV)

       这里我们选择为K8S集群每个工作节点添加一块额外的未格式化磁盘(裸设备),步骤见以下截图:

      将新增的磁盘设置成独立模式(模拟公有云厂商提供的独立磁盘),然后启动K8S集群虚拟机,在工作节点上使用以下命令检查一下磁盘条件是否符合Ceph部署要求:

    复制代码
    [root@node1 ~]# lsblk -f
    NAME        FSTYPE      LABEL           UUID                                   MOUNTPOINT
    sdb                                                                            
    sr0         iso9660     CentOS 7 x86_64 2020-11-04-11-36-43-00                 
    sda                                                                            
    ├─sda2      LVM2_member                 45inUD-qJ4O-Fq9E-L6KD-8eJV-mofD-BuJDq6 
    │ └─centos_node1-root
                xfs                         704f37f0-ae59-4995-80ec-58cba66e023b   /
    └─sda1      xfs                         67243cc8-c3fb-490f-b0da-cc439371d5e1   /boot
    复制代码

      上述命令输出中 sdb 磁盘就是我们为工作节点新添加的裸设备(它的FSTYPE为空),我们可以把它分配给Ceph使用。

      需要在K8S集群中启用Rook准入控制器,用于验证使用自定义资源(CR)正确地配置了Rook。该准入控制器在身份认证和授权之后并在持久化对象之前,拦截发往K8S API Server的请求以进行验证。我们在安装Rook之前,使用以下命令在K8S集群中安装Rook准备入控制器:

    #在master1节点直接应用在线yaml文件
    kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.7.1/cert-manager.yaml
    
    #在master1将cert-manager.yaml下载到本地的方式(推荐)
    kubectl apply -f /etc/kubernetes/rook/cert-manager.yaml
    复制代码
    ......
    service/cert-manager created
    service/cert-manager-webhook created
    deployment.apps/cert-manager-cainjector created
    deployment.apps/cert-manager created
    deployment.apps/cert-manager-webhook created
    mutatingwebhookconfiguration.admissionregistration.k8s.io/cert-manager-webhook created
    validatingwebhookconfiguration.admissionregistration.k8s.io/cert-manager-webhook created
    复制代码

      另外,Ceph OSD在以下情况下需要依赖于LVM(逻辑卷,OSD是Ceph用于在各个存储节点实现存储功能的组件)

    • 在裸设备或裸分区上创建OSD
    • 如果启用了加密(在集群CR中设置了encryptedDevice: "true")
    • 指定了元数据设备(metadata device)

      在以下情况下OSD不需要LVM

    • 在使用 storageClassDeviceSets 的PVC上创建OSD

      目前大多数Linux发生版的LVM由lvm2包提供,在K8S集群中运行Ceph OSD的所有存储节点上都需要有这个包。虽然没有这个包Rook也能够成功创建Ceph OSD,但是当相应的节点(node)重启之后,其上运行的OSD pod将会启动失败。所以需要确保作为存储节点的操作系统上安装了LVM(从上面磁盘条件查验的结果中看到我们是有LVM卷的),CentOS可以使用以下命令安装LVM:

    sudo yum install -y lvm2

      Ceph需要一个带有RBD模块的Linux内核。大多数Linux发行版都有这个模块,但不是所有,你可以在K8S集群的存储节点上运行  lsmod|grep rbd 命令检测一下,如果该命令返回空,那说明当前系统内核没有加载RBD模块,可以使用以下命令尝试加载RBD模块:

    复制代码
    #将RBD模块加载命令放入开机加载项里
    cat > /etc/sysconfig/modules/rbd.modules << EOF
    #!/bin/bash
    modprobe rbd
    EOF
    
    #为上述为脚本添加执行权限
    chmod +x /etc/sysconfig/modules/rbd.modules
    
    #执行上述脚本(如果返回'not found',你可能需要安装一个新的内核、或重新编译一个带有RBD模块的内核、或换一个带有RBD的Linux 发行版)
    /bin/bash /etc/sysconfig/modules/rbd.modules
    
    #查看RBD模块是否加载成功
    lsmod|grep rbd
    复制代码

      Rook默认的RBD配置只指定了分层特性,以便与较旧的内核广泛兼容。如果K8S节点运行在5.4+的系统内核上,则可以启用其他功能特性。例如特别有用的 fast-diff 和 object-map 特性,主要的功能特性如下(在进行块存储的StorageClass定义时指定):

    imageFeatures: layering,fast-diff,object-map,deep-flatten,exclusive-lock

      如果你将来会从Ceph共享文件系统(CephFS)创建卷(volume),那么需要使用4.17+的系统内核,PVC请求的存储配额只在高于该版本的内核上生效。

    ------------------------------- 以上为使用Rook在K8S集群部署Ceph存储的前提条件 ------------------------------- 

       接下来正式使用Rook在K8S集群部署Ceph存储集群!

      首先在K8S所有集群节点上安装Git客户端(用于拉取Rook部署组件清单):

    复制代码
    #安装Git
    yum install -y git
    
    #查看Git版本
    git --version
    
    git version 1.8.3.1
    复制代码

      使用Rook官方提供的示例部署组件清单(manifests)部署一个简单的Ceph集群(测试环境够用了):

    复制代码
    #使用git将部署组件清单示例下载到本地(慢或无法接通的话自己想法办FQ)
    git clone --single-branch --branch v1.10.11 https://github.com/rook/rook.git
    
    #进入到本地部署组件清单示例目录
    cd rook/deploy/examples
    
    #执行以下命令将Rook和Ceph相关CRD资源和通用资源创建到K8S集群(其中psp.yaml是K8S集群受Pod安全策略保护的情况下的可选资原文件)
    kubectl create -f crds.yaml -f common.yaml -f psp.yaml
    复制代码

      接下来部署Rook Operator组件,该组件为Rook与Kubernetes交互的组件,整个集群只需要一个副本,特别注意 Rook Operator 的配置在Ceph集群安装后不能修改,否则Rook会删除Ceph集群并重建,所以部署之前一定要做好规划,修改好operator.yaml的相关配置:

    修改 rook/deploy/examples/operator.yaml文件中的以下内容:

    复制代码
    #修改镜像地址为华中科技大学和阿里云的(可以使用docker pull 验证一下,原来的地址很难下载)
      ROOK_CSI_CEPH_IMAGE: "quay.mirrors.ustc.edu.cn/cephcsi/cephcsi:v3.7.2"
      ROOK_CSI_REGISTRAR_IMAGE: "registry.aliyuncs.com/google_containers/csi-node-driver-registrar:v2.7.0"
      ROOK_CSI_RESIZER_IMAGE: "registry.aliyuncs.com/google_containers/csi-resizer:v1.7.0"
      ROOK_CSI_PROVISIONER_IMAGE: "registry.aliyuncs.com/google_containers/csi-provisioner:v3.4.0"
      ROOK_CSI_SNAPSHOTTER_IMAGE: "registry.aliyuncs.com/google_containers/csi-snapshotter:v6.2.1"
      ROOK_CSI_ATTACHER_IMAGE: "registry.aliyuncs.com/google_containers/csi-attacher:v4.1.0"
    
    #生产环境一般都会将裸设备自动发现开关设为true(方便后面追加设备)
    ROOK_ENABLE_DISCOVERY_DAEMON: "true"
    
    #打开CephCSI 提供者的节点(node)亲和性(去掉前面的注释即可,会同时作用于CephFS和RBD提供者,如果要分开这两者的调度,可以继续打开后面专用的节点亲和性)
    CSI_PROVISIONER_NODE_AFFINITY: "role=storage-node; storage=rook-ceph"
    
    #如果CephFS和RBD提供者的调度亲各性要分开,则在上面的基础上继打开它们专用的开关(去除下面两行前端的#即可)
    # CSI_RBD_PROVISIONER_NODE_AFFINITY: "role=rbd-node"
    # CSI_CEPHFS_PROVISIONER_NODE_AFFINITY: "role=cephfs-node"
    
    #打开CephCSI 插件的节点(node)亲和性(去掉前面的注释即可,会同时作用于CephFS和RBD插件,如果要分开这两者的调度,可以继续打开后面专用的节点亲和性)
    CSI_PLUGIN_NODE_AFFINITY: "role=storage-node; storage=rook-ceph"
    
    #如果CephFS和RBD提供者的调度亲各性要分开,则在上面的基础上继打开它们专用的开关(去除下面两行前端的#即可)
    # CSI_RBD_PLUGIN_NODE_AFFINITY: "role=rbd-node"
    # CSI_CEPHFS_PLUGIN_NODE_AFFINITY: "role=cephfs-node"
    
    #rook-ceph-operator的Deployment中的容器镜像地址rook/ceph:v1.10.11 可以不用换,下载还是很快的!
    
    #生产环境一般还会打开裸设备自动发现守护进程(方便后期增加设备)
    ROOK_ENABLE_DISCOVERY_DAEMON: "true"
    #同时开打发现代理的节点亲和性环境变量
     - name: DISCOVER_AGENT_NODE_AFFINITY
          value: "role=storage-node; storage=rook-ceph"
    复制代码

      确认修改完成后,在master1节点上执行以下命令进行Rook Ceph Operator的部署:

    #执行以下命令在K8S集群中部署Rook Ceph Operator(镜像拉取可能需要一定时间,耐心等待,可用后一条命令监控相关Pod部署情况)
    kubectl create -f operator.yaml
    #使用以下命令监控Rook Ceph Operator相关Pod的部署情况(rook-ceph为默认Rook Ceph Operator部署命名空间)
    watch kubectl get pods -n rook-ceph

      确保rook-ceph-operator相关Pod都运行正常的情况下,修改 rook/deploy/examples/cluster.yaml文件中的以下内容:

    复制代码
    # enable prometheus alerting for cluster(为集群打开prometheus告警)
      monitoring:
    # requires Prometheus to be pre-installed
        enabled: true
    
    #打开节点亲和性调度和污点容忍
    # To control where various services will be scheduled by kubernetes, use the placement configuration sections below.
    # The example under 'all' would have all services scheduled on kubernetes nodes labeled with 'role=storage-node' and
    # tolerate taints with a key of 'storage-node'.
      placement:
        all:
          nodeAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
              - matchExpressions:
                - key: role
                  operator: In
                  values:
                  - storage-node
         # podAffinity:
         # podAntiAffinity:
         # topologySpreadConstraints:
         # tolerations:
         # - key: storage-node
         #   operator: Exists
    
    #将存储设置为我们三个工作节点新加的sdb裸盘
      storage: # cluster level storage configuration and selection
        useAllNodes: false
        useAllDevices: false
        #deviceFilter:
        config:
          # crushRoot: "custom-root" # specify a non-default root label for the CRUSH map
          # metadataDevice: "md0" # specify a non-rotational storage so ceph-volume will use it as block db device of bluestore.
          # databaseSizeMB: "1024" # uncomment if the disks are smaller than 100 GB
          # journalSizeMB: "1024"  # uncomment if the disks are 20 GB or smaller
          # osdsPerDevice: "1" # this value can be overridden at the node or device level
          # encryptedDevice: "true" # the default value for this option is "false"
    # Individual nodes and their config can be specified as well, but 'useAllNodes' above must be set to false. Then, only the named
    # nodes below will be used as storage resources.  Each node's 'name' field should match their 'kubernetes.io/hostname' label.
        nodes:
          - name: "node1"
            devices: # specific devices to use for storage can be specified for each node
              - name: "sdb"
          - name: "node2"
            devices: # specific devices to use for storage can be specified for each node
              - name: "sdb"
          - name: "node3"
            devices: # specific devices to use for storage can be specified for each node
              - name: "sdb"          
        #       - name: "nvme01" # multiple osds can be created on high performance devices
        #         config:
        #           osdsPerDevice: "5"
        #       - name: "/dev/disk/by-id/ata-ST4000DM004-XXXX" # devices can be specified using full udev paths
        #     config: # configuration can be specified at the node level which overrides the cluster level config
        #   - name: "172.17.4.301"
        #     deviceFilter: "^sd."
        # when onlyApplyOSDPlacement is false, will merge both placement.All() and placement.osd
        onlyApplyOSDPlacement: false
    复制代码

      修改完后,根据我们在operator.yaml和cluster.yaml上的节点标签亲和性设置,为三个工作节点打上对应的标签

    kubectl label nodes node1 node2 node3 role=storage-node
    kubectl label nodes node1 node2 node3 storage=rook-ceph
    #确保工作节点打上对应标签,并且cluster文件修改好后,就可以使用cluster.yaml部署Ceph存储集群了(部署需要一定的时间,可用后一条命令监控)
    kubectl create -f cluster.yaml
    #使用以下命令监控Ceph Cluster相关Pod的部署情况(rook-ceph为默认部署命名空间)
    watch kubectl get pods -n rook-ceph

      Rook Ceph集群搭建好并成功运行后,我们就可以开始基于Rook Ceph集群创建可供K8S内的应用消费的块存储(Block Devices)、共享文件系统(Shared Filesystem)和对象存储(Object Storage)了。这些类型的存储资源在Rook Ceph中分别被定义为CephBlockPool、CephFilesystem 和 CephObjectStore。

      首先,来创建Ceph块存储(Block Devices):Ceph能为Pod提供裸块设备存储卷(volumn)。只需要将Rook官方提供的示例部署组件清单项目中 /rook/deploy/examples/csi/rbd 目录下的storageclass.yaml文件或storageclass-ec.yaml文件应用到K8S里即可(这两个文件的主要区别是保障数据高可用的方式不一样,前者是通过多副本的方式,后者是通过擦除算法的方式保障节点故障时数据不丢失,后者不需要三倍的存储空间,但需要更多的CPU资源进行计算),它们都需要三个节点进行部署。我们这里选用storageclass.yaml文件,因为我们的内核版本已升级至5.4+,所以可以开启前面说的特别有用的 fast-diff 和 object-map 特性,打开storageclass.yaml文件修改以下内容(红色为新开启的功能特性):

    复制代码
    ---
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: rook-ceph-block
    # 下面注释将该存储类型设置为默认存储类型 annotations: storageclass.beta.kubernetes.io
    /is-default-class: "true" ...省略... # RBD image features # Available for imageFormat: "2". Older releases of CSI RBD # support only the `layering` feature. The Linux kernel (KRBD) supports the # full complement of features as of 5.4 # `layering` alone corresponds to Ceph's bitfield value of "2" ; # `layering` + `fast-diff` + `object-map` + `deep-flatten` + `exclusive-lock` together # correspond to Ceph's OR'd bitfield value of "63". Here we use # a symbolic, comma-separated format: # For 5.4 or later kernels: #imageFeatures: layering,fast-diff,object-map,deep-flatten,exclusive-lock # For 5.3 or earlier kernels: imageFeatures: layering,fast-diff,object-map,deep-flatten,exclusive-lock ...省略...
    复制代码

      修改好后执行以下命令应用一下即可:

    kubectl create -f rook/deploy/examples/csi/rbd/storageclass.yaml 

      可以查看一下当前集群中的存储资源:

    kubectl get sc
    NAME                        PROVISIONER                     RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
    rook-ceph-block (default)   rook-ceph.rbd.csi.ceph.com      Delete          Immediate           true                   5m10s

      然后,来创建Ceph共享文件系统(Shared Filesystem):Ceph文件系统(CephFS)允许用户将符合posix的共享文件夹“mount”到一个或多个主机中((对于K8S来说就是pod),在K8S中创建CephFS只需要将Rook官方提供的示例部署组件清单项目中 rook/deploy/examples/csi/cephfs 目录下的storageclass.yaml文件或storageclass-ec.yaml文件应用一下即可:

    kubectl create -f /root/rook/deploy/examples/csi/cephfs/storageclass.yaml 

      可以查看一下当前集群中的存储资源:

    kubectl get sc
    NAME                        PROVISIONER                     RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
    rook-ceph-block (default)   rook-ceph.rbd.csi.ceph.com      Delete          Immediate           true                   6m15s
    rook-cephfs                 rook-ceph.cephfs.csi.ceph.com   Delete          Immediate           true                   22s

      最后,来创建Ceph对象存储(Object Storage):Ceph对象存储支持使用HTTP的get、put、post以及delete语义操作的称为对象数据块的存储。在K8S中创建Ceph对象存储只需要将Rook官方提供的示例部署组件清单项目中 rook/deploy/examples 目录下的object.yaml文件或object-ec.yaml文件应用一下即可(如果文件内默认的gateway.port:80端口已经被占用的话,需要更换一个未占用的端口,比如我这边80端口已经分配置Ingress Nginx Controller了,将Ceph对象存储API服务的网关端口换成8088):

    复制代码
      # The gateway service configuration
      gateway:
        # A reference to the secret in the rook namespace where the ssl certificate is stored
        # sslCertificateRef:
        # A reference to the secret in the rook namespace where the ca bundle is stored
        # caBundleRef:
        # The port that RGW pods will listen on (http)
        port: 8088
        # The port that RGW pods will listen on (https). An ssl certificate is required.
        # securePort: 443
        # The number of pods in the rgw deployment
        instances: 1
    复制代码

      修改好后执行以下命令应用一下即可:

    kubectl create -f rook/deploy/examples/object.yaml 

      可以查看一下rook-ceph命名空间中的对象存储服务运行状态:

    kubectl get CephObjectStore -n rook-ceph
    NAME       PHASE
    my-store   Progressing

      最后对于K8S v1.20+后的版本提个醒:官方在 K8S 1.20 中基于对性能和统一API Server调用方式的初衷,移除了对 SelfLink 的支持,而 大多数 provisioner 需要 SelfLink 该项功能,否则会出现PVC申请PV时一直处于 pending 状态,所以我们需要修改 API Server 的配置文件,重新启用 SelfLink 功能

    复制代码
    # vi /etc/kubernetes/manifests/kube-apiserver.yaml
    ...省略...
    spec:
      containers:
      - command:
        - kube-apiserver
    ...省略...
        - --feature-gates=RemoveSelfLink=false # 重新加回的参数
    ...省略...
    复制代码

      然后重新应用一下 kube-apiserver.yaml :

    kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml

      上述问题也可以使用新的不基于 SelfLink 功能的 provisioner 镜像,重新创建 provisioner 容器进行解决,例如下面这个境像:

    registry.cn-beijing.aliyuncs.com/pylixm/nfs-subdir-external-provisioner:v4.0.0

      如果你试了上面的解决法法都不行,这时你要认真检查一下你创建StorageClass资源对象时用的yaml配置文件里的  provisioner 的值是否与创建存储提供者Pod时用的Deployment资源对象文件里env节的 PROVISIONER_NAME 的值相同(不同的话,使用相应StorageClass的PVC也会报相同的错误):

    复制代码
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    ...省略...
          containers:
    ...省略...
              env:
                - name: PROVISIONER_NAME
                  value: local/nfs
    ...省略...
    复制代码
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    ...省略...
    provisioner: local/nfs #要与Deployment里env里的PROVISIONER_NAME的值对应
    allowVolumeExpansion: true

      关于 Rook Ceph就介绍到这里,更多相关知识请到Rook官网查看了。

  • 相关阅读:
    C语言学习:5、C语言程序的选择结构
    python 使用Softmax回归处理IrIs数据集
    CIO如何规划企业BI分析指标体系 —— 从经营出发到绩效管理
    layui中使用JavaScript监听下拉框(select)的变化,根据选中的值来决定是否显示或隐藏input元素
    【Linux 从基础到进阶】自动化部署工具(Jenkins、GitLab CI/CD)
    Python对象复制竟然有这么多种方式,赶紧学起来!
    计算机网络学习实践:模拟静态路由
    文件的一些操作
    七、kubernetes Pod调度
    工控机通过Profinet转Modbus RTU网关连接变频器与电机通讯案例
  • 原文地址:https://www.cnblogs.com/xuruiming/p/17120974.html