需求来源

项目上目前使用的是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. springboot ——oracle.jdbc.driver.OracleDriver

    网上很多案例讲是oracle的驱动包没有导入进去,我之前尝试下图示方式导入解决该问题: 但是在后期调试的时候,发现会影响后续oracle数据源连接驱动的问题,导致不能查询,因此想,另辟途径,解决这个问 ...

  2. JavaScript中的document.fullscreenEnabled

    本文主要讲述了: 什么是document.fullscreenEnabled 作用 兼容性 正文 什么是document.fullscreenEnabled document.fullscreenEn ...

  3. 当学术邂逅浪漫 – 记MobiCom 2015大会

    作者:微软亚洲研究院主管研究员 刘云新 今年的MobiCom大会在著名的浪漫之都巴黎举行.通常于欧洲举办的会议的参会人数会相对少一些,但今年的MobiCom大会吸引了近400人参加,绝不少于往年.浪漫 ...

  4. ubuntu 18.04用apt安装mysql-server

    mysql5.7安装完成后普通用户不能进mysql 原因:root的plugin被修改成了auth_socket,用密码登陆的plugin应该是mysql_native_password,直接用roo ...

  5. MySQL增、删、改、查基础操作(C++)

    系统平台:Centos7 MySQL版本:5.7.19 连接MySQL数据库 MySQL::MySQL(string host, string user, string passwd, string ...

  6. java23种设计模式 (转)

    文章在:http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html 随着自己的开发经验增加以及自己做了很多的 大专栏  jav ...

  7. Mysql锁和死锁分析

    在MySQL中,行级锁并不是直接锁记录,而是锁索引.索引分为主键索引和非主键索引两种,如果一条sql语句操作了主键索引,MySQL就会锁定这条主键索引;如果一条语句操作了非主键索引,MySQL会先锁定 ...

  8. swoole(3)网络服务模型(单进程阻塞、预派生子进程、单进程阻塞复用模型)

    一:单进程阻塞 设计流程: 创建一个socket,绑定端口bind,监听端口listen 进入while循环,阻塞在accept操作上,等待客户端连接进入,进入睡眠状态,直到有新的客户发起connet ...

  9. 一个基于Bootstrap实现的HMTL可视化编辑工具

    疫情禁足在家,用原生的JS实现了一个HTML可视化编辑工具,页面布局基于Bootstrap.大约一个月时间,打通主要技术关卡,实现了第一版:   可以拖放编辑,实现了几乎所有的bootstrap预定义 ...

  10. JMeter-WebService接口的测试

    前言 JMeter3.2版本之后就没有SOAP/XML-RPC Request插件了,那么该如何进行webservice接口的测试呢? 今天我们来一起学习一下怎么在3.2以后版本的JMeter进行we ...