写点什么

Node Driver Registrar 源码分析

用户头像
良凯尔
关注
发布于: 2021 年 06 月 27 日
Node Driver Registrar源码分析

kubernetes ceph-csi 分析 - 目录导航:

https://xie.infoq.cn/article/4b1d3e32f124307a49cd9c1e3


node-driver-registrar 是一个 sidecar 容器,通过 Kubelet 的插件注册机制将 CSI plugin(csi driver,两个名词意义一样)注册到 Kubelet,让 kubelet 做 volume 的 mount/umount 操作时知道怎么调用相应的 csi plugin。


Node Driver Registrar 的内容相对简单,将在本文中对其作用、源码、组件间调用逻辑等进行分析。


repo:https://github.com/kubernetes-csi/node-driver-registrar

关联博客

kubelet之csi driver注册分析

Node Driver Registrar 启动参数

// cmd/csi-node-driver-registrar/main.go// Command line flagsvar (  connectionTimeout       = flag.Duration("connection-timeout", 0, "The --connection-timeout flag is deprecated")  csiAddress              = flag.String("csi-address", "/run/csi/socket", "Path of the CSI driver socket that the node-driver-registrar will connect to.")  kubeletRegistrationPath = flag.String("kubelet-registration-path", "", "Path of the CSI driver socket on the Kubernetes host machine.")  showVersion             = flag.Bool("version", false, "Show version.")  version                 = "unknown"
// List of supported versions supportedVersions = []string{"1.0.0"})
复制代码


下面讲解下最关键的两个启动参数,主要是配置 2 个 socket 地址。


(1)csi-address


csi plugin 组件暴露的 grpc 服务 socket 地址。


(2)kubelet-registration-path


Node Driver Registrar 容器暴露的 grpc 服务 socket 地址,同时 kubelet 也会通过该 socket 地址访问 Node Driver Registrar 容器的接口,主要用于向 kubelet 注册 csi plugin。

Node Driver Registrar 容器权限

(1)RBAC:Node Driver Registrar 容器没有访问 kubernetes API 的需求,所以不用做相关的 RBAC 配置。


(2)需要将前面提到的 2 个 socket 的父目录作为 hostPath 挂载进容器中,并对 socket 拥有创建、删除、访问等权限(通过配置 containers[].securityContext.privileged=true 获得权限)。

Node Driver Registrar 容器部署 yaml

Node-Driver-Registrar 容器与 csi plugin NodeServer 容器一起,使用 daemonset 部署,即每个 node 节点都有。


Node-Driver-Registrar 容器的部署 yaml 如下:


    containers:        - name: driver-registrar-rbd          # This is necessary only for systems with SELinux, where          # non-privileged sidecar containers cannot access unix domain socket          # created by privileged CSI driver container.          securityContext:            privileged: true          image: quay.io/k8scsi/csi/csi-node-driver-registrar:v1.3.0          args:            - "--v=5"            - "--csi-address=/csi/csi.sock"            - "--kubelet-registration-path=/var/lib/kubelet/plugins/rbd.csi.ceph.com/csi.sock"          lifecycle:            preStop:              exec:                command: [                  "/bin/sh", "-c",                  "rm -rf /registration/rbd.csi.ceph.com \                  /registration/rbd.csi.ceph.com-reg.sock"                ]          env:            - name: KUBE_NODE_NAME              valueFrom:                fieldRef:                  fieldPath: spec.nodeName          volumeMounts:            - name: socket-dir              mountPath: /csi            - name: registration-dir              mountPath: /registration          imagePullPolicy: "Always"    volumes:        - name: socket-dir          hostPath:            path: /var/lib/kubelet/plugins/rbd.csi.ceph.com            type: DirectoryOrCreate        - name: registration-dir          hostPath:            path: /var/lib/kubelet/plugins_registry/            type: Directory
复制代码

Node Driver Registrar 注册 csi plugin driver 步骤


第 1 步

Node Driver Registrar 连接 csi plugin 组件暴露的 grpc 服务 socket 地址,调用 GetPluginInfo 接口,获取 csi plugin 的 driver 名称。

第 2 步

kubelet-registration-path目录下启动一个 socket,对外暴露 GetInfo 和 NotifyRegistrationStatus 两个接口。kubelet 通过 Watcher 可以发现该 socket。

第 3 步

kubelet 通过 Watcher 监控/var/lib/kubelet/plugins_registry/目录,发现上述 socket 后,通过该 socket 调用 Node-Driver-Registrar 的 GetInfo 接口,获取 csi plugin 组件暴露的 grpc 服务 socket 地址以及 csi plugin 组件的 driver 名称。

第 4 步

kubelet 通过 csi plugin 组件暴露的 grpc 服务 socket 地址对其 NodeGetInfo 接口进行调用,获取 csi plugin 的 nodeID 等信息。

第 5 步

kubelet 根据上一步获得的信息,去更新 node 节点的 Annotations、Labels、status.allocatable 等信息,同时创建(或更新)一个 CSINode 对象。

第 6 步

kubelet 通过 socket 调用 Node-Driver-Registrar 容器的 NotifyRegistrationStatus 接口,通知注册 csi plugin 成功。


通过以上 6 步就实现了 csi plugin 注册机制。

Node Driver Registrar 源码分析

下面结合 Node Driver Registrar 的源码对注册 csi plugin driver 步骤进行详细分析。主要分析 main()、nodeRegister()、GetInfo()与 NotifyRegistrationStatus()。

1.main()

先从 main()入手。


主要逻辑:


(1)组件启动参数校验;


(2)连接 csi plugin 组件暴露的 grpc 服务 socket 地址,调用 GetPluginInfo 接口,获取 csi plugin 的 driver 名称;


(3)调用 nodeRegister 方法做 csi plugin 注册操作。


// cmd/csi-node-driver-registrar/main.gofunc main() {  klog.InitFlags(nil)  flag.Set("logtostderr", "true")  flag.Parse()
if *kubeletRegistrationPath == "" { klog.Error("kubelet-registration-path is a required parameter") os.Exit(1) }
if *showVersion { fmt.Println(os.Args[0], version) return } klog.Infof("Version: %s", version)
if *connectionTimeout != 0 { klog.Warning("--connection-timeout is deprecated and will have no effect") }
// Once https://github.com/container-storage-interface/spec/issues/159 is // resolved, if plugin does not support PUBLISH_UNPUBLISH_VOLUME, then we // can skip adding mapping to "csi.volume.kubernetes.io/nodeid" annotation.
klog.V(1).Infof("Attempting to open a gRPC connection with: %q", *csiAddress) csiConn, err := connection.Connect(*csiAddress) if err != nil { klog.Errorf("error connecting to CSI driver: %v", err) os.Exit(1) }
klog.V(1).Infof("Calling CSI driver to discover driver name") ctx, cancel := context.WithTimeout(context.Background(), csiTimeout) defer cancel()
csiDriverName, err := csirpc.GetDriverName(ctx, csiConn) if err != nil { klog.Errorf("error retreiving CSI driver name: %v", err) os.Exit(1) }
klog.V(2).Infof("CSI driver name: %q", csiDriverName)
// Run forever nodeRegister(csiDriverName)}
复制代码

2.nodeRegister()

nodeRegister()用于向 kubelet 注册 csi plugin。主要逻辑:


(1)调用 newRegistrationServer()初始化 registrationServer 结构体;


(2)在 kubelet-registration-path目录下启动一个 socket,对外暴露 GetInfo 和 NotifyRegistrationStatus 两个接口(kubelet 通过 Watcher 可以发现该 socket)。


// cmd/csi-node-driver-registrar/node_register.gofunc nodeRegister(  csiDriverName string,) {  // When kubeletRegistrationPath is specified then driver-registrar ONLY acts  // as gRPC server which replies to registration requests initiated by kubelet's  // pluginswatcher infrastructure. Node labeling is done by kubelet's csi code.  registrar := newRegistrationServer(csiDriverName, *kubeletRegistrationPath, supportedVersions)  socketPath := fmt.Sprintf("/registration/%s-reg.sock", csiDriverName)  if err := util.CleanupSocketFile(socketPath); err != nil {    klog.Errorf("%+v", err)    os.Exit(1)  }
var oldmask int if runtime.GOOS == "linux" { // Default to only user accessible socket, caller can open up later if desired oldmask, _ = util.Umask(0077) }
klog.Infof("Starting Registration Server at: %s\n", socketPath) lis, err := net.Listen("unix", socketPath) if err != nil { klog.Errorf("failed to listen on socket: %s with error: %+v", socketPath, err) os.Exit(1) } if runtime.GOOS == "linux" { util.Umask(oldmask) } klog.Infof("Registration Server started at: %s\n", socketPath) grpcServer := grpc.NewServer() // Registers kubelet plugin watcher api. registerapi.RegisterRegistrationServer(grpcServer, registrar)
// Starts service if err := grpcServer.Serve(lis); err != nil { klog.Errorf("Registration Server stopped serving: %v", err) os.Exit(1) } // If gRPC server is gracefully shutdown, exit os.Exit(0)}
复制代码


newRegistrationServer()


// cmd/csi-node-driver-registrar/main.go// NewregistrationServer returns an initialized registrationServer instancefunc newRegistrationServer(driverName string, endpoint string, versions []string) registerapi.RegistrationServer {  return &registrationServer{    driverName: driverName,    endpoint:   endpoint,    version:    versions,  }}
复制代码

3.GetInfo()

GetInfo():kubelet 通过调用 Node-Driver-Registrar 的 GetInfo 接口,获取 csi plugin 组件暴露的 grpc 服务 socket 地址以及 csi plugin 组件的 driver 名称。


// GetInfo is the RPC invoked by plugin watcherfunc (e registrationServer) GetInfo(ctx context.Context, req *registerapi.InfoRequest) (*registerapi.PluginInfo, error) {  klog.Infof("Received GetInfo call: %+v", req)  return &registerapi.PluginInfo{    Type:              registerapi.CSIPlugin,    Name:              e.driverName,    Endpoint:          e.endpoint,    SupportedVersions: e.version,  }, nil}
复制代码

4.NotifyRegistrationStatus()

NotifyRegistrationStatus():kubelet 通过调用 Node-Driver-Registrar 的 NotifyRegistrationStatus 接口,通知注册 csi plugin 成功。


// GetInfo is the RPC invoked by plugin watcherfunc (e registrationServer) NotifyRegistrationStatus(ctx context.Context, status *registerapi.RegistrationStatus) (*registerapi.RegistrationStatusResponse, error) {  klog.Infof("Received NotifyRegistrationStatus call: %+v", status)  if !status.PluginRegistered {    klog.Errorf("Registration process failed with error: %+v, restarting registration container.", status.Error)    os.Exit(1)  }
return &registerapi.RegistrationStatusResponse{}, nil}
复制代码

总结

Node Driver Registrar 作用

node-driver-registrar 是一个 sidecar 容器,通过 Kubelet 的插件注册机制将 CSI plugin(csi driver,两个名词意义一样)注册到 Kubelet,让 kubelet 做 volume 的 mount/umount 操作时知道怎么调用相应的 csi plugin。

Node Driver Registrar 涉及的两个 socket

(1)csi-address


csi plugin 组件暴露的 grpc 服务 socket 地址。


(2)kubelet-registration-path


Node Driver Registrar 容器暴露的 grpc 服务 socket 地址,同时 kubelet 也会通过该 socket 地址访问 Node Driver Registrar 容器的接口,主要用于向 kubelet 注册 csi plugin。

发布于: 2021 年 06 月 27 日阅读数: 9
用户头像

良凯尔

关注

热爱的力量 2020.01.10 加入

kubernetes开发者

评论

发布
暂无评论
Node Driver Registrar源码分析