intellij idea汉化 IntelliJ IDEA namespace CPU ssis linux内存管理 linux环境变量生效 js字符串排序 input边框颜色 idea全文搜索快捷键 wordpress本地建站 div字体加粗 kubernetes入门 python中items python开发界面 java学习手册 java中数据类型 java文件重命名 java类方法 java字符比较 python源码下载 微信摇骰子表情包 刺激战场脚本 完美手游模拟器 dota改键工具 野德天赋 go2lan 送货单管理系统 处理器虚拟化技术 dnf95b套 彻底卸载mysql 求字符串长度 抠图教程 目标聚光灯 losecontrol 夜之魇掉落 数据库编程软件 轮播图代码 图片文字提取软件 机械换装
当前位置: 首页 > 学习教程  > 编程语言

kubernetes 存储插件CSI开发指导

2020/9/19 14:57:45 文章标签:

1.K8s 的持久化存储支持

在支持持久化存储方面,K8s 提供了内嵌原生 Driver 的方式连接外部的常见存储系统例如 NFS、iSCSI、CephFS、RBD 等来满足不同业务的需求。但由于存储生态本身也在不断演进,使用 K8s 内嵌的方式来支持不断变化的存储系统在成本和时效上都会对 K8s 项目自身带来巨大的挑战。

所以和其他服务管理系统一样,K8s 逐渐的将存储系统的具体实现从主项目中分离出来,通过定义接口的方式允许第三方厂商自行接入存储服务。在这个道路上也经历了两个阶段:

  1. Flex Volume, 自 1.2 版本引入。
    第三方存储服务提供商直接在 K8s Server 上部署符合 Flex Volume 规范的 Driver,利用 K8s 暴露出的 mount/unmount/attach/detach 等关键 API 实现存储系统的接入。这个模式主要的问题是,在这个形态下第三方厂商的 Driver 有权限接入物理节点的根文件系统,这会带来安全上的隐患。
  2. Container Storage Interface (CSI), 自 1.9 版本引入,目前已经进入 GA 阶段(1.13)。
    CSI 定义了容器场景所需要的存储控制原语和完整的控制流程,并且在 K8s 的 CSI 实现中,所有的第三方 Driver 和 K8s 的其他服务扩展一样,以服务容器的形态的运行,不会直接影响到 K8s 的核心系统稳定性。

2.存储对象

CSI 定义的存储对象为持久化卷,称之为 Volume。包括两种类型:

  • Mounted File Volume,Node 将会把 Volume 以指定的文件格式 Mount 到 Container 上,从 Container 的角度看到的是一个目录;
  • Raw Block Volume, 直接将 Volume 以 Block Device(磁盘)的形态暴露给 Container,对于某些可以直接操作磁盘的服务,这个形态可以省去文件系统的开销获得更好的性能。

Raw Block Volume 目前还处于 Beta 阶段,所以下文的过程描述和 SMTX 的 CSI Driver 目前的实现方式均针对 Mounted File Volume。

3.基本术语

4.Plugin

RPC接口: CO通过RPC与插件交互, 每个SP必须提供两类插件:

  • Controller Plugin,负责存储对象(Volume)的生命周期管理,在集群中仅需要有一个即可;
  • Node Plugin,在必要时与使用 Volume 的容器所在的节点交互,提供诸如节点上的 Volume 挂载/卸载等动作支持,如有需要则在每个服务节点上均部署。

存储服务商可以根据自身需求实现不同的的 Plugin 组合。例如对于以 NFS 形式提供的存储服务,可以仅实现 Controller Plugin 实现资源的创建和访问权限控制,每个节点均可以通过标准的 NFS 方式获得服务,无需通过 Node Plugin 来实现挂载/卸载等操作。而以 iSCSI 形式提供的存储服务,就需要 Node Plugin 在指定节点上,通过挂载 LUN,格式化,挂载文件系统等一系列动作完成 iSCSI LUN 至容器可见的目录形式的转化。

5.Volume 生命周期

一个典型的 CSI Volume 生命周期如下图(来自 CSI SPEC)所示:

    上面这个图是一个较为复杂的卷供应生命周期图,从这个图我们可以看出一个存储卷的供应分别调用了Controller PluginCreateVolumeControllerPublishVolumeNode PluginNodeStageVolumeNodePublishVolume这4个gRPC接口,存储卷的销毁分别调用了Node PluginNodeUnpublishVolumeNodeUnstageVolumeControllerControllerUnpublishVolumeDeleteVolume这4个gRPC接口。

  1. Volume 被创建后进入 CREATED 状态,此时 Volume 仅在存储系统中存在,对于所有的 Node 或者 Container 都是不可感知的;
  2. 对 CREATED 状态的 Volume 进行 Controlller Publish 动作后在指定的 Node 上进入 NODE_READY 的状态,此时 Node 可以感知到 Volume,但是 Container 依然不可见;
  3. 在 Node 对 Volume 进行 Stage 操作,进入 VOL_READY 的状态,此时 Node 已经与 Volume 建立连接。Volume 在 Node 上以某种形式存在;
  4. 在 Node 上对 Volume 进行 Publish 操作,此时 Volume 将转化为 Node 上 Container 可见的形态被 Container 利用,进入正式服务的状态;
  5. 当 Container 的生命周期结束或其他不再需要 Volume 情况发生时,Node 执行 Unpublish Volume 动作,将 Volume 与 Container 之间的连接关系解除,回退至 VOL_READY 状态;
  6. Node Unstage 操作将会把 Volume 与 Node 的连接断开,回退至 NODE_READY 状态;
  7. Controller Unpublish 操作则会取消 Node 对 Volume 的访问权限;
  8. Delete 则从存储系统中销毁 Volume。

CSI 要求状态转化操作是幂等的,并在原则上保证 Volume 的状态转化是有序进行的。

根据存储使用方式和内部实现的不同,状态机可以略有区别,但对应操作必须是成对出现的。例如在不需要额外建立 Node 与 Volume 之间连接的 Stage/Unstage 阶段时,状态机就可以直接通过 Controller Publish/Unpublish 在 NODE_READY 与 PUBISHED 之间转化,而无需经过 VOL_READY 阶段。Plugin 向 CSI 注册时必须声明自身支持哪些语义。

6.RPC

CSI 要求 Plugin 支持的 RPC 包括:

·  身份服务:Node Plugin和Controller Plugin都必须实现这些RPC集。

·  控制器服务:Controller Plugin必须实现这些RPC集。

·  节点服务:Node Plugin必须实现这些RPC集。

(1)Identity Service:认证服务,Controller 和 Node Plugin 均需要支持

  • GetPluginInfo, 获取 Plugin 基本信息
  • GetPluginCapabilities,获取 Plugin 支持的能力
  • Probe,探测 Plugin 的健康状态

(2)Controller Service:控制服务

  • Volume CRUD,包括了扩容和容量探测等 Volume 状态检查与操作接口
  • Controller Publish/Unpublish Volume ,Node 对 Volume 的访问权限管理
  • Snapshot CRD,快照的创建和删除操作,目前 CSI 定义的 Snapshot 仅用于创建 Volume,未提供回滚的语义

(3)Node Service:节点服务

  • Node Stage/Unstage/Publish/Unpublish/GetStats Volume,节点上 Volume 的连接状态管理
  • Node Expand Volume, 节点上的 Volume 扩容操作,在 volume 逻辑大小扩容之后,可能还需要同步的扩容 Volume 之上的文件系统并让使用 Volume 的 Container 感知到,所以在 Node Plugin 上需要有对应的接口
  • Node Get Capabilities/Info, Plugin 的基础属性与 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) {}

  ...
}

service Node {
  ...

  rpc NodePublishVolume (NodePublishVolumeRequest)
    returns (NodePublishVolumeResponse) {}

  rpc NodeUnpublishVolume (NodeUnpublishVolumeRequest)
    returns (NodeUnpublishVolumeResponse) {}

  rpc NodeExpandVolume(NodeExpandVolumeRequest)
    returns (NodeExpandVolumeResponse) {}


  rpc NodeGetCapabilities (NodeGetCapabilitiesRequest)
    returns (NodeGetCapabilitiesResponse) {}

  rpc NodeGetInfo (NodeGetInfoRequest)
    returns (NodeGetInfoResponse) {}
  ...
}

7.部署形态

CSI 使用 Sidecar 的方式实现 CSI Plugin 与 K8s 核心逻辑的解耦。Sidecar 代表监听了 CSI 指定 API 的标准容器,它与 CSI Plugin 共同组成一个 Pod 对外提供服务,它们之间通过 Socket 连接。在这个模式下,Sidecar 成为 CSI Plugin 与 K8s 之间连接的中介和隔离带。理想状态下二者可以在不直接交互和影响的情况下共同工作,解决了安全问题。

CSI 定义了如下几种 Sidecar

  • external-provisioner:监听 Volume CRUD API,完成 Volume 的生命周期管理
  • external-attacher:监听 Controller[Publish|Unpublish]Volume API,实现 Node 和 Volume 的可见性控制
  • external-snapshotter:监听 Snapshot CRD API,完成 Snapshot 的生命周期管理
  • node-driver-register:监听 Node 基本信息查询 API,注册 Node Plugin,每个节点 Node Plugin 均需要通过 driver-register 注册自身才可以与 K8s 之间建立连接获取 Node Volume 相关请求
  • cluster-driver-register:用于向 K8s 注册 Plugin 整体支持的模式,包括是否跳过 Attach 阶段/是否在 Node Publish Volume 阶段时需要 K8s 提供 Pod 信息
  • livenessprobe:心跳检测,用于探测 Plugin 的存活状态

8.适用场景


在容器化发展的早期阶段,Container 多用于承担轻量型的无状态服务,对数据存储的需求大多通过本地的临时共享文件,或者用网络访问的方式将数据置于远端的日志收集或者 DB 等外部存储上。这种模式业务和数据之间从程序管理的角度看是松耦合的,互相独立,没有严格的依赖。

但是另一方面,这个模式下数据本身无法成为服务的一部分,并不能通过 K8s 统一管理。并且需要为每个应用打开通往远端存储服务的网络通道,这在安全性上有时并不是一个好的选择。

而基于持久化卷,将数据服务提供方也放入 K8s Pod 中(例如挂载持久化卷作为磁盘,上面部署容器运行 DB)作为完整应用的一部分,数据即可以做到与应用无缝的统一管理,所有应用内部 Pod 间的业务数据请求均可以在 K8s 提供的虚拟网络中进行。而基于 K8s 本身的高可用特性和 CSI Driver 的灵活配置能力也可以获得不逊色于外部存储的可靠性与性能。

9.Kubernetes csi driver开发示例

type driver struct {
    csiDriver   *csicommon.CSIDriver
    endpoint    string

    ids *csicommon.DefaultIdentityServer
    cs  *controllerServer
    ns  *nodeServer
}

首先我们需要定义一个driver结构体,基本包含了plugin启动的所需信息(除了以上信息还可以添加其他参数):

(1)csicommon.CSIDriver

k8s自定义代表插件的结构体, 初始化的时候需要指定插件的RPC功能和支持的读写模式.

func NewCSIDriver(nodeID string) *csicommon.CSIDriver {
    csiDriver := csicommon.NewCSIDriver(driverName, version, nodeID)
    csiDriver.AddControllerServiceCapabilities(
        []csi.ControllerServiceCapability_RPC_Type{
            csi.ControllerServiceCapability_RPC_LIST_VOLUMES,
            csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME,
            csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME,
        })
    csiDriver.AddVolumeCapabilityAccessModes([]csi.VolumeCapability_AccessMode_Mode{csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER})
    return csiDriver
}

(2)endpoint:

插件的监听地址,一般的,我们测试的时候可以用tcp方式进行,比如tcp://127.0.0.1:10000,最后在k8s中部署的时候一般使用unix方式:/csi/csi.sock

(3)csicommon.DefaultIdentityServer :

认证服务一般不需要特别实现,使用k8s公共部分的即可.

(4)controllerServer:

实现CSI中的controller服务的RPC功能,继承后可以选择性覆盖部分方法.

type controllerServer struct {
    *csicommon.DefaultControllerServer
}

(5)nodeServer:

实现CSI中的node服务的RPC功能,继承后可以选择性覆盖部分方法.

type nodeServer struct {
    *csicommon.DefaultNodeServer
}

 

(6)driver的Run方法:

该方法中调用csicommon的公共方法启动socket监听,RunControllerandNodePublishServer方法会同时启动controller和node.还可以单独启动controller和node,需要写两个入口main函数.

func (d *driver) Run(nodeID, endpoint string) {
    d.endpoint = endpoint
    d.cloudconfig = cloudConfig

    csiDriver := NewCSIDriver(nodeID)
    d.csiDriver = csiDriver
    // Create GRPC servers
    ns, err := NewNodeServer(d, nodeID, containerized)
    if err != nil {
        glog.Fatalln("failed to create node server, err:", err.Error())
    }

    glog.V(3).Infof("Running endpoint [%s]", d.endpoint)

    csicommon.RunControllerandNodePublishServer(d.endpoint, d.csiDriver, NewControllerServer(d), ns)
}

其他主要函数功能实现如下(以下以我们iscsi产品实现描述):

1.Create Volume:Controller Plugin 收到创建请求之后会创建一个 iSCSI LUN ,如有必要则会自动再创建需要的 Target, iSCSI LUN 以及所属的 Target 均为逻辑对象,不与物理磁盘绑定。

2.Controller Publish Volume:目前 Kubernetes 使用 Open iSCSI 作为节点上的数据接入服务,Open iSCSI 在挂载 Target 时,会将所有 Target 内的 LUN 均挂载到主机上作为一个 Block Device(例如 /dev/sdx 这样的磁盘) 。

3.Node Stage Volume: Node Plugin 会将 LUN 通过 Open iSCSI 命令挂载至主机,呈现为一个磁盘;

4.Node Publish Volume: Node Plugin 对磁盘进行格式化(如果磁盘之前尚未被格式化,如已经格式化则为跳过对应步骤),将磁盘 Mount 到主机上提供给 Container 使用;

5.Node Unpublish Volume: Node Plugin 将磁盘上的文件系统 Unmount;

6.Node Stage Volume: Node Plugin 在主机上将断开磁盘的 iSCSI 链接;

7.Controller Unpublish Volume: Controller Plugin 向后端注销指定 Node 在 LUN 上的访问权限;

8.Delete Volume: Controller Plugin 请求删除对应的 LUN ,LUN 所占用的数据空间将会在存储系统中被回收。


本文链接: http://www.dtmao.cc/news_show_200210.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?