基于openshift+华为对象存储的CSI开发

本文涉及的产品
对象存储 OSS,20GB 3个月
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
对象存储 OSS,内容安全 1000次 1年
简介: 基于openshift+华为对象存储的CSI开发

目录

需求来源

项目上目前使用的是openshift 3.11版本,对应kubernetes 1.11,需要在该平台上使用CSI插件。

GitHub地址:https://github.com/woodliu/csi-s3

环境准备

  • 本次使用openshift 3.11,对应kubernetes 1.11,参考github开源代码ctrox/csi-s3进行开发(强烈建议使用的kubernetes不低于1.13)。
  • 部署中涉及如下sidecar容器:csi-attacher,csi-node-driver-registrar,csi-provisioner,csi-s3。最后一个容器是需要开发的CSI插件,其他sidecar版本需要与kubernetes配套才能使用,参照官方开发文档找到合适的版本,例如符合kubernetes 1.11版本的csi-attacher版本为0.42。除csi-s3之外的镜像都可以从官方镜像库中下载。
  • CSI spec规定了protobuf格式的存储相关的数据结构,本次应该采用v0.3.0版本。按照官方说法,kubernetes1.13中废弃了CSI spec 0.2和0.3版本。
Kubernetes CSI Spec Compatibility Status
v1.9 v0.1.0 Alpha
v1.10 v0.2.0 Beta
v1.11 v0.3.0 Beta
v1.13 v0.3.0, v1.0.0 GA

  • 下载华为OBS对象存储Go语言SDK。
  • docker 17.05+(使用multi-stage生成镜像)

代码修改

主要是使用华为OBS的SDK操作替换代码中对bucket的操作等。涉及修改的代码文件为pkg/s3/controllerserver.gonodeserver.gos3-client.go

镜像下载

docker pull quay.io/k8scsi/csi-attacher:v0.4.2
docker pull quay.io/k8scsi/driver-registrar:v0.4.2
docker pull quay.io/k8scsi/csi-provisioner:v0.4.2

镜像生成

/csi-s3目录中执行如下命令可以在/csi-s3/_output/目录中生成可执行文件s3driver

make build

在/csi-s3目录下执行如下命令生成镜像

make container

修改部署文件

  • 修改storageclass
    ctrox/csi-s3目录中的deploy/kubernetes/storageclass.yaml内容如下,mounter使用s3fs,由于仅使用s3fsMounterType类型,因此无需指定StorageClass.mounter字段
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: csi-s3
provisioner: ch.ctrox.csi.s3-driver
parameters:
# specify which mounter to use
# can be set to rclone, s3fs, goofys or s3backer
csiProvisionerSecretName: csi-s3-secret
csiProvisionerSecretNamespace: kube-system
csiControllerPublishSecretName: csi-s3-secret
csiControllerPublishSecretNamespace: kube-system
csiNodeStageSecretName: csi-s3-secret
csiNodeStageSecretNamespace: kube-system
csiNodePublishSecretName: csi-s3-secret
csiNodePublishSecretNamespace: kube-system
  • 其中parameters字段的内容在1.0.1版本进行了修改,因此小于1.0版本的prvisioner采用左边的声明。
Deprecated Replacement
csiProvisionerSecretName csi.storage.k8s.io/provisioner-secret-name
csiProvisionerSecretNamespace csi.storage.k8s.io/provisioner-secret-namespace
csiControllerPublishSecretName csi.storage.k8s.io/controller-publish-secret-name
csiControllerPublishSecretNamespace csi.storage.k8s.io/controller-publish-secret-namespace
csiNodeStageSecretName csi.storage.k8s.io/node-stage-secret-name
csiNodeStageSecretNamespace csi.storage.k8s.io/node-stage-secret-namespace
csiNodePublishSecretName csi.storage.k8s.io/node-publish-secret-name
csiNodePublishSecretNamespace csi.storage.k8s.io/node-publish-secret-namespace
fstype csi.storage.k8s.io/fstype

  • 修改secret.yaml,特别注意endpoint字段不能省略httphttps
apiVersion: v1
kind: Secret
metadata:
name: csi-s3-secret
stringData:
accessKeyID: ${AK}
secretAccessKey: ${SK}
# For AWS set it to "https://s3.<region>.amazonaws.com"
endpoint: http://obs.${mycloud}.com
# If not on S3, set it to ""
region: <S3_REGION>
  • 修改csi-s3.yaml,将容器卷挂载地址修改为/var/lib/origin/openshift.local.volumes/pods/
  • 修改各个配置文件中的镜像仓库和版本

部署CSI插件

部署组件

cd deploy/kubernetes
kubectl create -f provisioner.yaml
kubectl create -f attacher.yaml
kubectl create -f csi-s3.yaml

部署storageclass

kubectl create -f storageclass.yaml

测试,创建一个pvc

kubectl create -f pvc.yaml

查看该pvc是否已经绑定

# oc get pvc
NAME         STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
csi-s3-pvc   Bound     pvc-ea844fa4-6f64-11ea-8eab-fa163e07eb1d   5Gi        RWO            csi-s3         30m

创建一个pod,查看该pod是否运行成功,并在该pod挂载的目录/var/lib/www/html下创建文件,看对象存储那端是否有新的文件生成。

oc create -f pod.yaml

CSI原理

核心原理

CSI的核心原理比较简单,见下图。基本原理就是启动一个CSI容器(自己写的插件),将Node主机上的/var/lib/origin/openshift.local.volumes/pods/目录(该目录包含所有Pod的卷挂载点)挂载到CSI容器内部的/var/lib/origin/openshift.local.volumes/pods/目录下。然后调用s3fs命令将对象存储挂载到/var/lib/origin/openshift.local.volumes/pods/${POD_ID}/volumes/kubernetes.io~csi/${PV_NAME}/mount目录下,而/var/lib/origin/openshift.local.volumes/pods/${POD_ID}/volumes/kubernetes.io~csi/${PV_NAME}/mount就是对应容器挂载PVC对应的node节点上的挂载点。对象存储的BUCKET_NAMEPV_NAME相同。

总之整个过程涉及三次挂载:将应用容器在Node节点上的目录挂载到CSI容器中;将后端存储挂载到CSI容器中;将应用容器在Node节点上的目录挂载到应用容器中。

s3fs ${BUCKET_NAME}:/csi-fs /var/lib/origin/openshift.local.volumes/pods/${POD_ID}/volumes/kubernetes.io~csi/${PV_NAME}/mount -o use_path_request_style -o url=http://obs.${mycloud}.com  -o allow_other -o mp_umask=000

生命周期:

CSI插件的运作流程需要符合卷的生命周期特性,官方给出的生命周期如下:

CreateVolume +------------+ DeleteVolume
+------------->|  CREATED   +--------------+
|              +---+----^---+              |
|       Controller |    | Controller       v
+++         Publish |    | Unpublish       +++
|X|          Volume |    | Volume          | |
+-+             +---v----+---+             +-+
| NODE_READY |
+---+----^---+
               Node |    | Node
            Publish |    | Unpublish
             Volume |    | Volume
+---v----+---+
| PUBLISHED  |
+------------+
Figure 5: The lifecycle of a dynamically provisioned volume, from
creation to destruction.
CreateVolume +------------+ DeleteVolume
+------------->|  CREATED   +--------------+
|              +---+----^---+              |
|       Controller |    | Controller       v
+++         Publish |    | Unpublish       +++
|X|          Volume |    | Volume          | |
+-+             +---v----+---+             +-+
| NODE_READY |
+---+----^---+
               Node |    | Node
              Stage |    | Unstage
             Volume |    | Volume
+---v----+---+
|  VOL_READY |
+---+----^---+
               Node |    | Node
            Publish |    | Unpublish
             Volume |    | Volume
+---v----+---+
| PUBLISHED  |
+------------+
Figure 6: The lifecycle of a dynamically provisioned volume, from
creation to destruction, when the Node Plugin advertises the
STAGE_UNSTAGE_VOLUME capability.
Controller                  Controller
       Publish                  Unpublish
        Volume  +------------+  Volume
 +------------->+ NODE_READY +--------------+
 |              +---+----^---+              |
 |             Node |    | Node             v
+++         Publish |    | Unpublish       +++
|X| <-+      Volume |    | Volume          | |
+++   |         +---v----+---+             +-+
 |    |         | PUBLISHED  |
 |    |         +------------+
 +----+
   Validate
   Volume
   Capabilities
Figure 7: The lifecycle of a pre-provisioned volume that requires
controller to publish to a node (`ControllerPublishVolume`) prior to
publishing on the node (`NodePublishVolume`).
+-+  +-+
       |X|  | |
       +++  +^+
        |    |
   Node |    | Node
Publish |    | Unpublish
 Volume |    | Volume
    +---v----+---+
    | PUBLISHED  |
    +------------+
Figure 8: Plugins MAY forego other lifecycle steps by contraindicating
them via the capabilities API. Interactions with the volumes of such
plugins is reduced to `NodePublishVolume` and `NodeUnpublishVolume`
calls.

可以看到,并不需要实现controller(见下)中的所有功能,只需按照需要实现即可。

组件介绍

官方架构如下,主要分为两部分:DeamonSet PodStatefulset/Deployment Pod ,前者负责各个Node节点上的卷的挂载;后者负责操作后端存储并与API Server交互。

官方提供了两个很好的文档:Container Storage Interface (CSI)CSI Volume Plugins in Kubernetes Design Doc。前者给出了开发涉及的接口,后者给出了内部原理。

CSI需要实现3个RPC服务

在CSI执行前需要调用如下接口注册对应的组件,各个组件需要实现对应的接口(见下文):

func (s *Server) RegisterService(sd *ServiceDesc, ss interface{})
func (s *Server) RegisterService(sd *ServiceDesc, ss interface{})
func (s *Server) RegisterService(sd *ServiceDesc, ss interface{})
service Identity {
  rpc GetPluginInfo(GetPluginInfoRequest)
    returns (GetPluginInfoResponse) {}
  rpc GetPluginCapabilities(GetPluginCapabilitiesRequest)
    returns (GetPluginCapabilitiesResponse) {}
  rpc Probe (ProbeRequest)
    returns (ProbeResponse) {}
}
service Controller {
  rpc CreateVolume (CreateVolumeRequest)
    returns (CreateVolumeResponse) {}
  rpc DeleteVolume (DeleteVolumeRequest)
    returns (DeleteVolumeResponse) {}
  rpc ControllerPublishVolume (ControllerPublishVolumeRequest)
    returns (ControllerPublishVolumeResponse) {}
  rpc ControllerUnpublishVolume (ControllerUnpublishVolumeRequest)
    returns (ControllerUnpublishVolumeResponse) {}
  rpc ValidateVolumeCapabilities (ValidateVolumeCapabilitiesRequest)
    returns (ValidateVolumeCapabilitiesResponse) {}
  rpc ListVolumes (ListVolumesRequest)
    returns (ListVolumesResponse) {}
  rpc GetCapacity (GetCapacityRequest)
    returns (GetCapacityResponse) {}
  rpc ControllerGetCapabilities (ControllerGetCapabilitiesRequest)
    returns (ControllerGetCapabilitiesResponse) {}
  rpc CreateSnapshot (CreateSnapshotRequest)
    returns (CreateSnapshotResponse) {}
  rpc DeleteSnapshot (DeleteSnapshotRequest)
    returns (DeleteSnapshotResponse) {}
  rpc ListSnapshots (ListSnapshotsRequest)
    returns (ListSnapshotsResponse) {}
  rpc ControllerExpandVolume (ControllerExpandVolumeRequest)
    returns (ControllerExpandVolumeResponse) {}
}
service Node {
  rpc NodeStageVolume (NodeStageVolumeRequest)
    returns (NodeStageVolumeResponse) {}
  rpc NodeUnstageVolume (NodeUnstageVolumeRequest)
    returns (NodeUnstageVolumeResponse) {}
  rpc NodePublishVolume (NodePublishVolumeRequest)
    returns (NodePublishVolumeResponse) {}
  rpc NodeUnpublishVolume (NodeUnpublishVolumeRequest)
    returns (NodeUnpublishVolumeResponse) {}
  rpc NodeGetVolumeStats (NodeGetVolumeStatsRequest)
    returns (NodeGetVolumeStatsResponse) {}
  rpc NodeExpandVolume(NodeExpandVolumeRequest)
    returns (NodeExpandVolumeResponse) {}
  rpc NodeGetCapabilities (NodeGetCapabilitiesRequest)
    returns (NodeGetCapabilitiesResponse) {}
  rpc NodeGetInfo (NodeGetInfoRequest)
    returns (NodeGetInfoResponse) {}
}

下看主要看一下controller中涉及的主要功能:

  • CreateVolume/DeleteVolume:由provisioner调用,负责后端存储上卷的创建/删除,如对象存储上bucket的创建/删除。provisioner会监控用户创建/删除的PVC,进而操作PVC指定的storageclass。
  • ControllerPublishVolumeResponse/ControllerUnpublishVolume:由attacher调用,负责将远端卷挂载到node节点上。本次实现中,远端卷直接使用s3fs挂载到了csi容器中,因此没有实现该接口,即没有指定ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME。*注:虽然没有实现该接口,但attacher容器必须要部署。
  • NodeStageVolume/NodeUnstageVolume:由registrar调用,用于处理setup/teardown(初始化设置/清理环境)卷。
  • NodePublishVolume/NodeUnpublishVolume:由registrar调用,用于将卷挂载/卸载到应用容器中。registrar用到了两个UNIX域套接字:
  • Registration socket:
  1. 创建reg.sock文件,用于将驱动注册到kubelet
  2. 通过kubelet插件库路径(通常是/var/lib/kubelet/plugins_registry/<drivername.example.com>-reg.sock暴露服务
  • CSI driver socket:
  1. 创建sock文件,kubelet用该socket与CSI驱动交互
  2. 通过kubelet插件库路径(通常是/var/lib/kubelet/plugins/<drivername.example.com>/csi.sock)暴露服务。

NOTE

  • 本方式其实是将对象存储转化为文件系统的方式,理论上应该是存在性能损失的
  • 更多内容可以参见这篇文章

FAQ

  • pvc创建不成功。这个一般是对后端存储的操作不正确导致的,需要检查controllerserver.go中的代码
  • pv和pvc绑定成功,但容器挂载不成功:transport endpoint is not connected,可以在对应节点的daemonset pod中csi容器中执行如下命令调试,一般是s3fs命令格式不对导致的。
echo ${AK}:${SK} > ${passwd_file}
s3fs ${bucket_name} ${target_mount_path} -o url=http://obs.${mycloud}.com -o passwd_file=${passwd} -o dbglevel=info -f -o curldbg
  • pv和pvc绑定成功,且容器挂载成功,但对容器挂载目录的变更无法同步到后端存储。需要检查对应node节点上的csi容器中的挂载点是否正确。一般是spec.template.spec.containers.csi-s3.volumeMounts.pods-mount-dirspec.template.spec.volumes.pods-mount-dir值不一致导致的

参考:

相关实践学习
借助OSS搭建在线教育视频课程分享网站
本教程介绍如何基于云服务器ECS和对象存储OSS,搭建一个在线教育视频课程分享网站。
目录
相关文章
|
4月前
|
Kubernetes 监控 云计算
Docker与云计算平台集成:AWS、Azure、GCP完全指南
Docker和云计算平台的结合,如AWS(Amazon Web Services)、Azure(Microsoft Azure)和GCP(Google Cloud Platform),为现代应用的构建和部署提供了巨大的便利性。本文将深入研究如何与这些主要云计算平台集成Docker,提供更多示例代码和详细指南,帮助大家更全面地利用这些强大的工具。
|
安全 应用服务中间件 网络安全
开源对象存储Minio部署篇
MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。(摘自minio中文站点https://docs.min.io/cn/ 里面有很详细的介绍)。minio部署简单易用,分布式方式部署可以水平扩容且数据分散存储在所有节点上,只要在线节点N/2数据就非常安全,这类似raid6,不同的是卷级别和对象级别,使用方式有网页端、linux端工具mc、多语言SDK。
1377 0
开源对象存储Minio部署篇
|
4月前
|
存储 Cloud Native 文件存储
云原生之使用Docker部署Nas-Cab个人NAS平台
【5月更文挑战第2天】云原生之使用Docker部署Nas-Cab个人NAS平台
583 4
|
4月前
|
存储 Cloud Native 数据挖掘
MinIO作为一种开源的对象存储系统,具有以下核心特点
MinIO作为一种开源的对象存储系统,具有以下核心特点
307 0
|
9月前
|
存储 Kubernetes 对象存储
Kubernetes版本对接对象存储几种方案
Kubernetes版本对接对象存储几种方案
462 0
|
11月前
|
SQL Cloud Native Go
云服务部署:AWS、Azure和GCP比较
云服务部署:AWS、Azure和GCP比较
416 0
|
机器学习/深度学习 存储 人工智能
「云市场2019」IaaS&PaaS 三强:AWS,Azure,GCP
「云市场2019」IaaS&PaaS 三强:AWS,Azure,GCP
|
存储 Kubernetes Cloud Native
开源云原生存储rook:块存储快速入门实战
开源云原生存储rook:块存储快速入门实战
263 0
EMQ
|
运维 Kubernetes 监控
在 AWS EKS 上部署 EMQX MQTT 集群
本文以EMQX企业版为例,介绍如何使用EMQX Kubernetes Operator在AWS EKS上创建部署MQTT服务集群,并实现自动化管理与监控。
EMQ
282 0
在 AWS EKS 上部署 EMQX MQTT 集群
|
存储 Kubernetes Cloud Native
Longhorn,Kubernetes 云原生分布式块存储
Longhorn,Kubernetes 云原生分布式块存储
780 0