https://github.com/kubernetes-csi/csi-driver-nfs
nfs csi 的实现,其实这里不支持动态storageclass,只支持 mount 操作,看看怎么实现的
- endpoint:csi socket
- nodeid: node id 也很好理解
Usage:
NFS [flags]Flags:
--alsologtostderr log to standard error as well as files
--endpoint string CSI endpoint
-h, --help help for NFS
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0)
--log_dir string If non-empty, write log files in this directory
--logtostderr log to standard error instead of files (default true)
--nodeid string node id
--stderrthreshold severity logs at or above this threshold go to stderr (default 2)
-v, --v Level log level for V logs
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
1. NewDriver 函数
实例化 driver,包括设置 AddVolume权限,AddController权限(这里未实现,可以修改这些实现动态Provision功能)
func NewDriver(nodeID, endpoint string) *driver {
glog.Infof("Driver: %v version: %v", driverName, version)
d := &driver{}
d.endpoint = endpoint
csiDriver := csicommon.NewCSIDriver(driverName, version, nodeID)
csiDriver.AddVolumeCapabilityAccessModes([]csi.VolumeCapability_AccessMode_Mode{csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER})
// NFS plugin does not support ControllerServiceCapability now.
// If support is added, it should set to appropriate
// ControllerServiceCapability RPC types.
csiDriver.AddControllerServiceCapabilities([]csi.ControllerServiceCapability_RPC_Type{csi.ControllerServiceCapability_RPC_UNKNOWN})
d.csiDriver = csiDriver
return d
}
2. Run 函数
初始化 Identity Controller Node这三个RPC实现
func (d *driver) Run() {
s := csicommon.NewNonBlockingGRPCServer()
s.Start(d.endpoint,
csicommon.NewDefaultIdentityServer(d.csiDriver),
// NFS plugin has not implemented ControllerServer
// using default controllerserver.
getControllerServer(d.csiDriver),
NewNodeServer(d))
s.Wait()
}
3. 启动GRPC 服务
func (s *nonBlockingGRPCServer) Start(endpoint string, ids csi.IdentityServer, cs csi.ControllerServer, ns csi.NodeServer) {
s.wg.Add(1)
go s.serve(endpoint, ids, cs, ns)
return
}
4. DefaultIdentityServer
实现了这三个方法,执行操作会查看插件有没有设置权限
- func (ids *DefaultIdentityServer) GetPluginInfo
- func (ids *DefaultIdentityServer) Probe
- func (ids *DefaultIdentityServer) GetPluginCapabilities
5. ControllerServer
这里未实现,这里可以做一些文章,实现动态provision功能,可以这样这样修改,创建目录,然后让pod去挂载吧
type ControllerServer struct {
*csicommon.DefaultControllerServer
}
func (cs ControllerServer) ControllerExpandVolume(ctx context.Context, req *csi.ControllerExpandVolumeRequest) (*csi.ControllerExpandVolumeResponse, error) {
return nil, status.Error(codes.Unimplemented, "")
}
func getControllerServer(csiDriver *csicommon.CSIDriver) ControllerServer {
return ControllerServer{
csicommon.NewDefaultControllerServer(csiDriver),
}
}
6. nodeServer
这里实现也是如此简单,只是进行了mount操作
type nodeServer struct { *csicommon.DefaultNodeServer }
7. NodePublishVolume
这个是 attacher 那个 csi 插件发送 GRPC 请求
这个是查看挂载目录是否存在,存在就不处理了
targetPath := req.GetTargetPath()
notMnt, err := mount.New("").IsLikelyNotMountPoint(targetPath)
if err != nil {
if os.IsNotExist(err) {
if err := os.MkdirAll(targetPath, 0750); err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
notMnt = true
} else {
return nil, status.Error(codes.Internal, err.Error())
}
}
if !notMnt {
return &csi.NodePublishVolumeResponse{}, nil
}
拿到挂载权限,rw / ro,server 与 share等
mo := req.GetVolumeCapability().GetMount().GetMountFlags()
if req.GetReadonly() {
mo = append(mo, "ro")
}
s := req.GetVolumeContext()["server"]
ep := req.GetVolumeContext()["share"]
source := fmt.Sprintf("%s:%s", s, ep)
啥也不说了,直接进行 mount操作,注意容器需要有权限去挂载
- name: nfs securityContext: privileged: true capabilities: add: ["SYS_ADMIN"] allowPrivilegeEscalation: true
mounter := mount.New("")
err = mounter.Mount(source, targetPath, "nfs", mo)
if err != nil {
if os.IsPermission(err) {
return nil, status.Error(codes.PermissionDenied, err.Error())
}
if strings.Contains(err.Error(), "invalid argument") {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
return nil, status.Error(codes.Internal, err.Error())
}
8. NodeUnpublishVolume
这个操作更简单,如果已经mount上的,那就 umount呗
func (ns *nodeServer) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpublishVolumeRequest) (*csi.NodeUnpublishVolumeResponse, error) {
targetPath := req.GetTargetPath()
notMnt, err := mount.New("").IsLikelyNotMountPoint(targetPath)
if err != nil {
if os.IsNotExist(err) {
return nil, status.Error(codes.NotFound, "Targetpath not found")
} else {
return nil, status.Error(codes.Internal, err.Error())
}
}
if notMnt {
return nil, status.Error(codes.NotFound, "Volume not mounted")
}
err = mount.CleanupMountPoint(req.GetTargetPath(), mount.New(""), false)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
return &csi.NodeUnpublishVolumeResponse{}, nil
}
总结
实现了csi nfs 插件,原型比较简单,只实现了 mount 与 umount 操作
可以修改 controller那块,实现动态 provision 功能