您的位置 首页 > 腾讯云社区

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

需求来源

项目上目前使用的是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.go,nodeserver.go,s3-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字段不能省略http或https 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.yamlCSI原理核心原理

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_NAME与PV_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 Pod和Statefulset/Deployment Pod ,前者负责各个Node节点上的卷的挂载;后者负责操作后端存储并与API Server交互。

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

CSI需要实现3个RPC服务

Identity Service: 允许容器编排系统像插件获取capabilities, health(probe), 和其他元数据Controller Service: 见下文Controller接口的介绍Node Service:见下文Node接口的介绍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: 创建reg.sock文件,用于将驱动注册到kubelet通过kubelet插件库路径(通常是/var/lib/kubelet/plugins_registry/<drivername.example.com>-reg.sock暴露服务CSI driver socket: 创建sock文件,kubelet用该socket与CSI驱动交互通过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-dir和spec.template.spec.volumes.pods-mount-dir值不一致导致的 参考:openshift Persistent Storage Using Container Storage Interface (CSI)Device Plugins基于 CSI Kubernetes 存储插件的开发实践kubernetes(k8s) csi 插件attach-detach流程此外还可以参考更多官方例子 ---来自腾讯云社区的---charlieroro

关于作者: 瞎采新闻

这里可以显示个人介绍!这里可以显示个人介绍!

热门文章

留言与评论(共有 0 条评论)
   
验证码: