需求来源

项目上目前使用的是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值不一致导致的

参考:

基于openshift+华为对象存储的CSI开发的更多相关文章

  1. 基于七牛云对象存储,搭建一个自己专属的极简Web图床应用(手摸手的注释讲解核心部分的实现原理)

    一个极简的Web图床应用,支持复制粘贴与拖拽上传图片 1.开发缘由 日常使用Vs Code编写markdown笔记与博客文章时,在文章中插入图片时发现非常不便 使用本地文件编写相对路径---没法直接复 ...

  2. Java基于文件的对象存储

    工作中经常需要处理对象的处理,有的时候还需要将对象保存到文件中做持久化. 特别是当不能使用数据库的时候就特别需要一个简单的对象集合的增删改查操作, 于是就有了下面这个文件DB的工具类 package ...

  3. swift对象存储

    swift对象存储 简介 OpenStack Object Storage(Swift)是OpenStack开源云计算项目的子项目之一,被称为对象存储,提供了强大的扩展性.冗余和持久性.对象存储,用于 ...

  4. CEPH-4:ceph RadowGW对象存储功能详解

    ceph RadosGW对象存储使用详解 一个完整的ceph集群,可以提供块存储.文件系统和对象存储. 本节主要介绍对象存储RadosGw功能如何灵活的使用,集群背景: $ ceph -s clust ...

  5. 【巨杉数据库Sequoiadb】巨杉⼯具系列之一 | ⼤对象存储⼯具sdblobtool

    近期,巨杉数据库正式推出了完整的SequoiaDB 工具包,作为辅助工具,更好地帮助大家使用和运维管理分布式数据库.为此,巨杉技术社区还将持续推出工具系列文章,帮助大家了解巨杉数据库丰富的工具矩阵. ...

  6. 基于华为物联网IOT的应用开发 --- 基于.net 的SDK封装

    最近,物联网的概念比较热门,一大批厂商抢着占领物联网的高低,包括有华为物联网.阿里云物联网.腾讯物联网.AWS物联网等等,无法一一列举,一般物联网包含设备侧开发.平台侧开发.应用侧开发,三个部分构成了 ...

  7. 腾讯云存储专家深度解读基于Ceph对象存储的混合云机制

    背景 毫无疑问,乘着云计算发展的东风,Ceph已经是当今最火热的软件定义存储开源项目.如下图所示,它在同一底层平台之上可以对外提供三种存储接口,分别是文件存储.对象存储以及块存储,本文主要关注的是对象 ...

  8. 基于华为物联网IOT的应用开发 ---界面管理开发

    在前面随笔<基于华为物联网IOT的应用开发 --- 基于.net 的SDK封装>介绍过IOT中应用侧SDK的封装,主要就是基于华为IOT的应用侧封装,以便在应用系统中进行调用.应用侧SDK ...

  9. 基于LAMP php7.1搭建owncloud云盘与ceph对象存储S3借口整合案例

    ownCloud简介 是一个来自 KDE 社区开发的免费软件,提供私人的 Web 服务.当前主要功能包括文件管理(内建文件分享).音乐.日历.联系人等等,可在PC和服务器上运行. 简单来说就是一个基于 ...

随机推荐

  1. 1122 Hamiltonian Cycle (25 分)

    1122 Hamiltonian Cycle (25 分) The "Hamilton cycle problem" is to find a simple cycle that ...

  2. 产品需求说明书 PRD模版

    XXX产品需求说明书 [版本号:V+数字]                 编  制: 日  期: 评  审:   日  期: 批  准: 日  期:       修订记录 版本 修订章节 修订内容 ...

  3. CS229 Lesson 5 生成学习算法

    课程视频地址:http://open.163.com/special/opencourse/machinelearning.html 课程主页:http://cs229.stanford.edu/ 更 ...

  4. Oracle最大进程连接数问题

    问题描述 分析报告保存功能,在本地测试使用时可以正常保存:但是部署在客户现场的系统该功能无法保存成功(全部保存): ---->代码功能没有问题,问题应该在服务器配置或者数据库配置等方面出现问题: ...

  5. 由uploadfive看servlet

    一.uploadfive的使用 上传工具是程序设计中最常用的功能,其中,uploadfive插件使用比较多,此处该插件进行文件的上传操作.该插件是基于HTML5的,因此PC端和移动端都可以使用. 使用 ...

  6. Vue数据绑定(一)

    Contents Vue作为当下炙手可热的前端三大框架之一,一直都想深入研究一下其内部的实现原理,去学习MVVM模式的精髓.如果说MVVM是当下最流行的图形用户界面开发模式,那么数据绑定则是这一模式的 ...

  7. IIS+PHP+Mysql 返回500,服务器内部资源问题

    这个错误困扰了我好久.... 尝试了好多方法都不管用,最后突然发现我的代码是: <?php $link=mysql_connect("localhost","xxx ...

  8. bootstrap简介与入门

    bootstrap前端框架 1.概念:一个前端开发的框架,Bootstrap,来自 Twitter,是目前很受欢迎的前端框架.Bootstrap 是基于 HTML.CSS.JavaScript 的,它 ...

  9. pip安装psycopg2失败解决

    pip install psycopg2==2.8.4报错ERROR: Command "python setup.py egg_info" failed with error c ...

  10. 【WPF学习】第五十六章 基于帧的动画

    除基于属性的动画系统外,WPF提供了一种创建基于帧的动画的方法,这种方法只使用代码.需要做的全部工作是响应静态的CompositionTarge.Rendering事件,触发该事件是为了给每帧获取内容 ...