{"id":234,"date":"2021-03-11T15:19:35","date_gmt":"2021-03-11T07:19:35","guid":{"rendered":"\/?p=234"},"modified":"2021-03-11T15:19:36","modified_gmt":"2021-03-11T07:19:36","slug":"28-%e5%b0%9d%e8%af%95%e7%bc%96%e5%86%99csi%e6%8f%92%e4%bb%b6%e6%8c%87%e5%8d%97","status":"publish","type":"post","link":"http:\/\/xinblog.ltd\/?p=234","title":{"rendered":"28.\u5c1d\u8bd5\u7f16\u5199CSI\u63d2\u4ef6\u6307\u5357"},"content":{"rendered":"<p>\u6211\u4eec\u8fdb\u884c\u5b9e\u8df5\u4e00\u4e2aCSI\u63d2\u4ef6\u7684\u7f16\u5199\u8fc7\u7a0b<\/p>\n<p>\u4e3a\u4e86\u7b80\u5355\u7684\u7f16\u5199,\u6211\u4eec\u4f7f\u7528\u4e86DigitalOcean\u7684\u5757\u5b58\u50a8 Block Storage\u670d\u52a1,\u4f5c\u4e3a\u5b9e\u8df5\u7684\u5bf9\u8c61<\/p>\n<p>DigitalOcean\u63d0\u4f9b\u7684\u529f\u80fd\u8f83\u5c11,\u4f46\u662f\u65b9\u4fbf\u5b9e\u8df5<\/p>\n<p>\u8fd9\u6b21\u7f16\u5199\u7684CSI\u63d2\u4ef6\u7684\u529f\u80fd,\u5c31\u662f\u53ef\u4ee5\u8fd0\u884c\u5728DigitalOcean\u4e0a\u7684Kubernetes\u96c6\u7fa4\u80fd\u591f\u4f7f\u7528\u5bf9\u5e94\u7684\u5757\u5b58\u50a8\u670d\u52a1,\u4f5c\u4e3a\u5bb9\u5668\u7684\u6301\u4e45\u5316\u5b58\u50a8<\/p>\n<p>\u5728DigitalOcean\u4e0a\u90e8\u7f72\u4e00\u4e2aKubernetes\u96c6\u7fa4\u4e0d\u96be,\u4e5f\u53ef\u4ee5\u73b0\u5728DigitalOcean\u4e0a\u521b\u5efa\u51e0\u4e2a\u865a\u62df\u673a<\/p>\n<p>\u5047\u8bbe\u5bf9\u5e94\u7684CSI\u63d2\u4ef6\u5df2\u7ecf\u5b8c\u6210\u4e86,\u90a3\u4e48\u6211\u4eec\u4f7f\u7528\u8fd9\u4e2a\u6301\u4e45\u5316\u5b58\u50a8\u7684\u65b9\u5f0f\u5c31\u5f88\u7b80\u5355\u4e86,\u53ea\u9700\u8981\u521b\u5efa\u4e00\u4e2a\u5982\u4e0b\u6240\u793a\u7684StorageClass\u5bf9\u8c61<\/p>\n<table>\n<tr>\n<td>\n  kind: StorageClass<\/p>\n<p>apiVersion: storage.k8s.io\/v1<\/p>\n<p>metadata:<\/p>\n<p>name: do-block-storage<\/p>\n<p>namespace: kube-system<\/p>\n<p>annotations:<\/p>\n<p>storageclass.kubernetes.io\/is-default-class: &#8220;true&#8221;<\/p>\n<p>#\u4e0a\u9762\u7684default-class\u4e3atrue\u7684\u610f\u601d,\u662f\u4f7f\u7528StorageClass\u4f5c\u4e3a\u9ed8\u8ba4\u7684\u6301\u4e45\u5316\u5b58\u50a8\u7684\u63d0\u4f9b\u8005<\/p>\n<p>provisioner: com.digitalocean.csi.dobs<\/td>\n<\/tr>\n<\/table>\n<p>\u6709\u4e86\u8fd9\u4e2aStorageClass External Provisioner\u5c31\u4f1a\u4e3a\u96c6\u7fa4\u4e2d\u65b0\u51fa\u73b0\u7684PVC\u81ea\u52a8\u521b\u5efa\u51faPV,\u7136\u540e\u8c03\u7528CSI\u63d2\u4ef6\u521b\u5efa\u51fa\u8fd9\u4e2aPV\u5bf9\u5e94\u7684Volume,\u8fd9\u5c31\u662fCSI\u4f53\u7cfb\u4e2dDynamic Provisioning\u7684\u5b9e\u73b0\u65b9\u5f0f<\/p>\n<p>\u8fd9\u4e2aStorageClass\u4e2d\u552f\u4e00\u5f15\u4eba\u6ce8\u610f\u7684,\u662fprovisioner=com,digitalocean.csi.dobs\u8fd9\u4e2a\u5b57\u6bb5,\u8fd9\u4e2a\u5b57\u6bb5\u53ef\u4ee5\u544a\u8bc9Kubernetes,\u8bf7\u4f7f\u7528\u540d\u53ebcom.digitalocean.csi.dobs\u7684CSI\u63d2\u4ef6\u6765\u4e3a\u6211\u5904\u7406\u8fd9\u4e2aStorageClass\u76f8\u5173\u7684\u6240\u6709\u64cd\u4f5c<\/p>\n<p>\u90a3\u4e48Kubernetes\u5982\u4f55\u77e5\u9053\u4e00\u4e2aCSI\u63d2\u4ef6\u7684\u540d\u5b57\u7684\u5462?<\/p>\n<p>\u8fd9\u5c31\u9700\u8981\u4eceCSI\u63d2\u4ef6\u7684\u7b2c\u4e00\u4e2a\u670d\u52a1CSI Identity\u8bf4\u4e86<\/p>\n<p>\u5148\u770b\u770b\u4e00\u4e2aCSI\u63d2\u4ef6\u7684\u4ee3\u7801\u7ed3\u6784,\u5982\u4e0b\u6240\u793a<\/p>\n<table>\n<tr>\n<td>\n  tree $GOPATH\/src\/github.com\/digitalocean\/csi-digitalocean\/driver<\/p>\n<p>$GOPATH\/src\/github.com\/digitalocean\/csi-digitalocean\/driver<\/p>\n<p>\u251c\u2500\u2500 controller.go<\/p>\n<p>\u251c\u2500\u2500 driver.go<\/p>\n<p>\u251c\u2500\u2500 identity.go<\/p>\n<p>\u251c\u2500\u2500 mounter.go<\/p>\n<p>\u2514\u2500\u2500 node.go<\/td>\n<\/tr>\n<\/table>\n<p>CSI Identity\u670d\u52a1\u5b9e\u73b0,\u5c31\u5b9a\u4e49\u5728\u4e86identity.go\u6587\u4ef6\u4e2d<\/p>\n<p>\u4e3a\u4e86\u80fd\u591f\u8ba9Kubernetes\u8bbf\u95ee\u5230CSI Identity\u670d\u52a1,\u73b0\u5728driver.go\u6587\u4ef6\u4e2d,\u5b9a\u4e49\u4e00\u4e2a\u6807\u51c6\u7684gRPC Server<\/p>\n<table>\n<tr>\n<td>\n  \/\/ Run starts the CSI plugin by communication over the given endpoint<\/p>\n<p>func (d *Driver) Run() error {<\/p>\n<p>&#8230;<\/p>\n<p>listener, err := net.Listen(u.Scheme, addr)<\/p>\n<p>&#8230;<\/p>\n<p>d.srv = grpc.NewServer(grpc.UnaryInterceptor(errHandler))<\/p>\n<p>csi.RegisterIdentityServer(d.srv, d)<\/p>\n<p>csi.RegisterControllerServer(d.srv, d)<\/p>\n<p>csi.RegisterNodeServer(d.srv, d)<\/p>\n<p>d.ready = true \/\/ we&#8217;re now ready to go!<\/p>\n<p>&#8230;<\/p>\n<p>return d.srv.Serve(listener)<\/p>\n<p>}<\/td>\n<\/tr>\n<\/table>\n<p>\u8fd9\u4e2agRPC Server\u6ce8\u518c\u7ed9\u4e86CSI,\u5c31\u53ef\u4ee5\u54cd\u5e94\u6765\u81eaExternal Components\u7684CSI\u8bf7\u6c42\u4e86<\/p>\n<p>CSI Identity\u670d\u52a1\u4e2d,\u6700\u91cd\u8981\u7684\u63a5\u53e3\u662fGetPluginInfo,\u8fd4\u56de\u5c31\u662f\u8fd9\u4e2a\u63d2\u4ef6\u7684\u540d\u5b57\u548c\u7248\u672c\u53f7<\/p>\n<table>\n<tr>\n<td>\n  func (d *Driver) GetPluginInfo(ctx context.Context, req *csi.GetPluginInfoRequest) (*csi.GetPluginInfoResponse, error) {<\/p>\n<p>resp := &amp;csi.GetPluginInfoResponse{<\/p>\n<p>Name:\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 driverName,<\/p>\n<p>VendorVersion: version,<\/p>\n<p>}<\/p>\n<p>&#8230;<\/p>\n<p>}<\/td>\n<\/tr>\n<\/table>\n<p>\u5728\u672c\u4f8b\u4e2d driverName\u7684\u503c,\u5c31\u662fcom.digitalocean.csi.dobs Kubernetes\u6b63\u662f\u901a\u8fc7GetPluginInfo\u7684\u8fd4\u56de\u503c,\u6765\u627eStorageClass\u4e2d\u58f0\u660e\u8981\u4f7f\u7528\u7684CSI\u7684<\/p>\n<p>\u5728\u4e0a\u7bc7\u4e2d,\u8fd8\u8bf4\u4e86,\u8fd9\u4e2a\u6587\u4ef6,\u9664\u4e86GetPluginInfo\u4e4b\u5916,\u8fd8\u5305\u542bGetPluginCapabilities\u63a5\u53e3,\u4e5f\u5f88\u91cd\u8981,\u8fd9\u4e2a\u63a5\u53e3\u8fd4\u56de\u7684\u662fCSI\u63d2\u4ef6\u7684\u80fd\u529b<\/p>\n<p>\u5982\u679c,\u7f16\u5199\u7684CSI\u63d2\u4ef6\u4e0d\u51c6\u5907\u5b9e\u73b0Provision\u9636\u6bb5\u548cAttach\u9636\u6bb5\u65f6\u5019,\u5c31\u53ef\u4ee5\u901a\u8fc7\u5076\u8fd9\u4e2a\u8fd9\u4e2a\u63a5\u53e3\u8fd4\u56de\u4e0d\u652f\u6301\u5bf9\u5e94\u7684Controller\u670d\u52a1,\u6ca1\u6709csi.PluginCapability_Service_CONTROLLER_SERVICE\u8fd9\u4e2a\u80fd\u529b,\u8fd9\u6837\u5c31\u80fd\u77e5\u9053\u4e86<\/p>\n<p>\u6700\u540eCSI Identity\u63d2\u4ef6\u8fd8\u63d0\u4f9b\u4e86\u4e00\u4e2aProbe\u63a5\u53e3,\u65b9\u4fbfKuberenetes\u901a\u8fc7\u8fd9\u4e2a\u63a5\u53e3\u68c0\u67e5CSI\u63d2\u4ef6\u662f\u5426\u6b63\u5e38\u5de5\u4f5c<\/p>\n<p>\u7136\u540e\u6211\u4eec,\u53ef\u4ee5\u7f16\u5199\u7b2c\u4e8c\u4e2a\u670d\u52a1\u7c7b,CSI Controller\u670d\u52a1\u4e86,\u5b9e\u73b0\u7684\u4ee3\u7801,\u5728controller,go\u6587\u4ef6\u4e2d<\/p>\n<p>\u8fd9\u4e2a\u670d\u52a1\u4e3b\u8981\u5b9e\u73b0\u7684\u5c31\u662fVolume\u7ba1\u7406\u6d41\u7a0b\u4e2d\u7684Provision\u9636\u6bb5\u548cAttach\u9636\u6bb5<\/p>\n<p>Provision\u9636\u6bb5\u5bf9\u5e94\u7684\u53e3\u662fCreateVolume\u548cDeleteVolueme,\u8c03\u7528\u8005\u662fExternal Provisoner,\u4ee5CreateVolume\u4e3a\u4f8b,\u4e3b\u8981\u7684\u903b\u8f91\u5982\u4e0b<\/p>\n<table>\n<tr>\n<td>\n  func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {<\/p>\n<p>&#8230;<\/p>\n<p>volumeReq := &amp;godo.VolumeCreateRequest{<\/p>\n<p>Region:\u00a0 \u00a0 \u00a0 \u00a0 d.region,<\/p>\n<p>Name:\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 volumeName,<\/p>\n<p>Description:\u00a0 \u00a0createdByDO,<\/p>\n<p>SizeGigaBytes: size \/ GB,<\/p>\n<p>}<\/p>\n<p>&#8230;<\/p>\n<p>vol, _, err := d.doClient.Storage.CreateVolume(ctx, volumeReq)<\/p>\n<p>&#8230;<\/p>\n<p>resp := &amp;csi.CreateVolumeResponse{<\/p>\n<p>Volume: &amp;csi.Volume{<\/p>\n<p>Id:\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 vol.ID,<\/p>\n<p>CapacityBytes: size,<\/p>\n<p>AccessibleTopology: []*csi.Topology{<\/p>\n<p>{<\/p>\n<p>Segments: map[string]string{<\/p>\n<p>&#8220;region&#8221;: d.region,<\/p>\n<p>},<\/p>\n<p>},<\/p>\n<p>},<\/p>\n<p>},<\/p>\n<p>}<\/p>\n<p>return resp, nil<\/p>\n<p>}<\/td>\n<\/tr>\n<\/table>\n<p>createVolume\u9700\u8981\u7684\u64cd\u4f5c,\u5c31\u662f\u8c03\u7528DigtalOcean\u5757\u5b58\u50a8\u670d\u52a1\u7684API,\u521b\u5efa\u51fa\u4e00\u4e2a\u5b58\u50a8\u5377,\u5982\u679c\u662f\u5176\u4ed6\u7c7b\u578b\u7684\u5757\u5b58\u50a8,\u4e5f\u53ef\u4ee5\u7c7b\u4f3c\u7684\u65b9\u5f0f\u8c03\u7528\u521b\u5efaAPI<\/p>\n<p>Attach\u9636\u6bb5\u5bf9\u5e94\u7684\u63a5\u53e3\u662fControllerPublishVolume\u548cControllerUnpublishVolume,\u8c03\u7528\u8005\u662fExternal Attacher,\u4ee5ControllerPublishVolume\u4e3a\u4f8b,\u903b\u8f91\u5982\u4e0b<\/p>\n<table>\n<tr>\n<td>\n  func (d *Driver) ControllerPublishVolume(ctx context.Context, req *csi.ControllerPublishVolumeRequest) (*csi.ControllerPublishVolumeResponse, error) {<\/p>\n<p>&#8230;<\/p>\n<p>dropletID, err := strconv.Atoi(req.NodeId)<\/p>\n<p>\/\/ check if volume exist before trying to attach it<\/p>\n<p>_, resp, err := d.doClient.Storage.GetVolume(ctx, req.VolumeId)<\/p>\n<p>&#8230;<\/p>\n<p>\/\/ check if droplet exist before trying to attach the volume to the droplet<\/p>\n<p>_, resp, err = d.doClient.Droplets.Get(ctx, dropletID)<\/p>\n<p>&#8230;<\/p>\n<p>action, resp, err := d.doClient.StorageActions.Attach(ctx, req.VolumeId, dropletID)<\/p>\n<p>&#8230;<\/p>\n<p>if action != nil {<\/p>\n<p>ll.Info(&#8220;waiting until volume is attached&#8221;)<\/p>\n<p>if err := d.waitAction(ctx, req.VolumeId, action.ID); err != nil {<\/p>\n<p>return nil, err<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>ll.Info(&#8220;volume is attached&#8221;)<\/p>\n<p>return &amp;csi.ControllerPublishVolumeResponse{}, nil<\/p>\n<p>}<\/td>\n<\/tr>\n<\/table>\n<p>\u5bf9\u4e8eDigtalOcean\u6765\u8bf4,ControllerPublishVolume\u5728Attach\u9636\u6bb5,\u5c31\u662f\u8c03\u7528DigitalOcean\u7684API,\u5c06\u524d\u9762\u521b\u5efa\u7684\u6570\u636e\u5377,\u6302\u8f7d\u5230\u6307\u5b9a\u7684\u865a\u62df\u673a\u4e0a d.doClient.StorageActions.Attach<\/p>\n<p>\u5176\u4e2d\u5b58\u50a8\u5377\u7531\u8bf7\u6c42\u7684VolumeId\u6765\u6307\u5b9a,\u800c\u865a\u62df\u673a,\u5c31\u662f\u8981\u8fd0\u884cPod\u7684\u5bbf\u4e3b\u673a,\u5219\u7531\u8bf7\u6c42\u4e2d\u7684NodeId\u6765\u6307\u5b9a,\u8fd9\u4e9b\u53c2\u6570,\u5c31\u662fExternal Attacher\u53d1\u8d77\u8bf7\u6c42\u65f6\u5019\u7ed9\u51fa\u7684<\/p>\n<p>\u7136\u540eExternal Attacher\u7684\u539f\u7406,\u5c31\u662f\u76d1\u542cWatch\u4e00\u4e2a\u540d\u4e3aVolumeAttachment\u7684API\u5bf9\u8c61,\u8fd9\u4e2a\u5bf9\u8c61\u7684\u4e3b\u8981\u5b57\u6bb5\u5982\u4e0b<\/p>\n<table>\n<tr>\n<td>\n  \/\/ VolumeAttachmentSpec is the specification of a VolumeAttachment request.<\/p>\n<p>type VolumeAttachmentSpec struct {<\/p>\n<p>\/\/ Attacher indicates the name of the volume driver that MUST handle this<\/p>\n<p>\/\/ request. This is the name returned by GetPluginName().<\/p>\n<p>Attacher string<\/p>\n<p>\/\/ Source represents the volume that should be attached.<\/p>\n<p>Source VolumeAttachmentSource<\/p>\n<p>\/\/ The node that the volume should be attached to.<\/p>\n<p>NodeName string<\/p>\n<p>}<\/td>\n<\/tr>\n<\/table>\n<p>\u8fd9\u4e2a\u5bf9\u8c61\u7684\u751f\u547d\u5468\u671f,\u5c31\u662f\u7531AttachDetachController\u8d1f\u8d23\u7ba1\u7406\u4e2d\u7684\u76f8\u5173\u5185\u5bb9,\u5728\u8fd9\u4e2akube Controller manger\u8d1f\u8d23\u7ef4\u62a4\u7684\u5faa\u73af\u4e2d,\u5c31\u662f\u4e0d\u65ad\u7684\u68c0\u67e5Pod\u5bf9\u5e94\u7684PV,\u5e76\u51b3\u5b9a\u662f\u5426\u9700\u8981\u5bf9\u8fd9\u4e2aPV\u8fdb\u884cattach\u6216\u8005deattch,\u7136\u540e\u521b\u5efa\u51fa\u4e86\u4e00\u4e2aVolumeAttachment\u5bf9\u8c61,\u8fd9\u4e2a\u5bf9\u8c61\u5305\u542b\u4e86Attach\u6240\u9700\u7684PV\u540d\u5b57,\u5bbf\u4e3b\u673a\u7684\u540d\u5b57,\u5b58\u50a8\u63d2\u4ef6\u7684\u540d\u5b57,\u5f53\u8fd9\u4e2a\u5bf9\u8c61\u51fa\u73b0\u7684\u65f6\u5019,\u5c31\u53ef\u4ee5\u4f7f\u7528\u8fd9\u4e2a\u5bf9\u8c61\u4e2d\u7684\u5b57\u6bb5,\u5c01\u88c5\u4e3a\u4e00\u4e2agRPC\u8bf7\u6c42\u8c03\u7528CSI Controller\u7684ControllerPublishVolume\u65b9\u6cd5<\/p>\n<p>\u6700\u540e\u5c31\u662fCSI\u7684Node\u670d\u52a1<\/p>\n<p>CSI Node\u670d\u52a1\u5bf9\u5e94\u7684,\u662fVolume\u7ba1\u7406\u6d41\u7a0b\u7684Mount\u9636\u6bb5,\u4ee3\u7801\u5b9e\u73b0,\u5728node.go\u6587\u4ef6\u4e2d<\/p>\n<p>\u8fd9\u4e2a\u670d\u52a1,\u4e3b\u8981\u5c31\u662f\u5229\u7528\u63a7\u5236\u5faa\u73af,\u5faa\u73af\u7684\u8c03\u7528CSI Node\u670d\u52a1\u5b8c\u6210Volume\u7684Mount\u9636\u6bb5<\/p>\n<p>\u8fd9\u4e2aMount\u9636\u6bb5,\u88ab\u7ec6\u5206\u4e3a\u4e86NodeStageVolume\u548cNodePublishVolume\u8fd9\u4e24\u4e2a\u63a5\u53e3,\u540c\u6837,\u5728\u4e0a\u5c42\u8c03\u7528\u8005VolumeMangerReconciler\u63a7\u5236\u5faa\u73af\u4e2d,\u8fd9\u4e24\u90e8\u64cd\u4f5c\u8fd8\u6709\u522b\u540d,\u5206\u522b\u4e3aMountDevice\u548cSetUp,\u5176\u4e2dMountDevice\u64cd\u4f5c,\u5c31\u662f\u76f4\u63a5\u8c03\u7528\u4e86CSI Node\u670d\u52a1\u4e2d\u7684NodeStageVolume\u63a5\u53e3,\u8fd9\u4e2a\u63a5\u53e3,\u5c31\u662f\u683c\u5f0f\u5316\u4e86Volume\u5728\u5bbf\u4e3b\u673a\u4e0a\u5bf9\u5e94\u7684\u5b58\u50a8\u8bbe\u5907,\u7136\u540e\u8fdb\u884c\u4e86\u6302\u8f7d\u5230\u4e00\u4e2a\u4e34\u65f6\u76ee\u5f55\u4e2d,<\/p>\n<p>\u5bf9\u4e8eDigitalOcean\u6765\u8bf4,NodeStageVolume\u63a5\u53e3\u7684\u5b9e\u73b0\u5982\u4e0b<\/p>\n<table>\n<tr>\n<td>\n  func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) {<\/p>\n<p>&#8230;<\/p>\n<p>vol, resp, err := d.doClient.Storage.GetVolume(ctx, req.VolumeId)<\/p>\n<p>&#8230;<\/p>\n<p>source := getDiskSource(vol.Name)<\/p>\n<p>target := req.StagingTargetPath<\/p>\n<p>&#8230;<\/p>\n<p>if !formatted {<\/p>\n<p>ll.Info(&#8220;formatting the volume for staging&#8221;)<\/p>\n<p>if err := d.mounter.Format(source, fsType); err != nil {<\/p>\n<p>return nil, status.Error(codes.Internal, err.Error())<\/p>\n<p>}<\/p>\n<p>} else {<\/p>\n<p>ll.Info(&#8220;source device is already formatted&#8221;)<\/p>\n<p>}<\/p>\n<p>&#8230;<\/p>\n<p>if !mounted {<\/p>\n<p>if err := d.mounter.Mount(source, target, fsType, options&#8230;); err != nil {<\/p>\n<p>return nil, status.Error(codes.Internal, err.Error())<\/p>\n<p>}<\/p>\n<p>} else {<\/p>\n<p>ll.Info(&#8220;source device is already mounted to the target path&#8221;)<\/p>\n<p>}<\/p>\n<p>&#8230;<\/p>\n<p>return &amp;csi.NodeStageVolumeResponse{}, nil<\/p>\n<p>}<\/td>\n<\/tr>\n<\/table>\n<p>\u5728NodeStageVolume\u5b9e\u73b0\u4e2d,\u6211\u4eec\u901a\u8fc7DigitalOcean\u7684API\u83b7\u53d6\u5230\u8fd9Volume\u5bf9\u5e94\u7684\u8bbe\u5907\u8def\u5f84getDiskSource,\u7136\u540e\u5c06\u8fd9\u4e2a\u8bbe\u5907\u683c\u5f0f\u5316\u6210\u6307\u5b9a\u7684\u683c\u5f0f d.mounter.Format,\u6700\u540e,\u5c06\u683c\u5f0f\u5316\u540e\u7684\u8bbe\u5907\u6302\u8f7d\u5230\u4e00\u4e2a\u4e34\u65f6\u7684Staging\u76ee\u5f55\u4e0b stagingTargetPath\u4e0b<\/p>\n<p>\u800cSetUp\u64cd\u4f5c\u4f1a\u8c03\u7528CSI Node\u670d\u52a1\u7684NodePublishVolume\u63a5\u53e3,\u5bf9\u4e0a\u9762\u8bbe\u5907\u8fdb\u884c\u63a5\u4e0b\u6765\u7684\u5904\u7406,\u5c31\u662f\u7ed1\u5b9a\u64cd\u4f5c<\/p>\n<table>\n<tr>\n<td>\n  func (d *Driver) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {<\/p>\n<p>&#8230;<\/p>\n<p>source := req.StagingTargetPath<\/p>\n<p>target := req.TargetPath<\/p>\n<p>mnt := req.VolumeCapability.GetMount()<\/p>\n<p>options := mnt.MountFlag<\/p>\n<p>&#8230;<\/p>\n<p>if !mounted {<\/p>\n<p>ll.Info(&#8220;mounting the volume&#8221;)<\/p>\n<p>if err := d.mounter.Mount(source, target, fsType, options&#8230;); err != nil {<\/p>\n<p>return nil, status.Error(codes.Internal, err.Error())<\/p>\n<p>}<\/p>\n<p>} else {<\/p>\n<p>ll.Info(&#8220;volume is already mounted&#8221;)<\/p>\n<p>}<\/p>\n<p>return &amp;csi.NodePublishVolumeResponse{}, nil<\/p>\n<p>}<\/td>\n<\/tr>\n<\/table>\n<p>\u5728\u4e0a\u9762,\u5c06Staging\u76ee\u5f55,\u7ed1\u5b9a\u6302\u8f7d\u5230\u4e86Volume\u5bf9\u5e94\u7684\u5bbf\u4e3b\u673a\u76ee\u5f55\u4e0a<\/p>\n<p>Staging\u76ee\u5f55,\u6b63\u662fVolume\u5bf9\u5e94\u7684\u8bbe\u5907\u88ab\u683c\u5f0f\u5316\u540e\u5728\u5bbf\u4e3b\u673a\u4e0a\u7684\u4f4d\u7f6e,\u6240\u4ee5\u5f53\u5176\u548cVolume\u6302\u8f7d\u4e4b\u540e,\u8fd9\u4e2aVolume\u5bbf\u4e3b\u673a\u76ee\u5f55\u7684\u6301\u4e45\u5316\u5904\u7406\u5c31\u5b8c\u6210\u4e86<\/p>\n<p>\u5f53\u7136,\u5bf9\u4e8e\u6587\u4ef6\u7cfb\u7edf\u7c7b\u578b\u7684\u5b58\u50a8\u670d\u52a1\u6765\u8bf4,\u6bd4\u5982NFS\u6216\u8005GlusterFS,\u5e76\u4e0d\u9700\u8981\u8fdb\u884c\u683c\u5f0f\u5316\u540e\u6302\u8f7d,\u800c\u53ef\u4ee5\u76f4\u63a5\u8fdb\u884c\u76f8\u5173\u7684\u6302\u8f7d,\u6240\u4ee5\u53ef\u4ee5\u8df3\u8fc7MountDevice\u64cd\u4f5c,\u76f4\u63a5\u6267\u884cSetUp\u64cd\u4f5c,\u4e5f\u4e0d\u9700\u8981\u5b9e\u73b0NodeStageVolume\u63a5\u53e3\u4e86<\/p>\n<p>\u7f16\u5199\u5b8c\u4e86\u5bf9\u5e94\u7684CSI\u63d2\u4ef6,\u76f4\u63a5\u8fdb\u884c\u90e8\u7f72\u5427<\/p>\n<p>\u6211\u4eec\u9700\u8981\u5148\u521b\u5efa\u4e00\u4e2aDigitalOcean client\u6388\u6743\u4f7f\u7528\u7684Secret\u5bf9\u8c61<\/p>\n<table>\n<tr>\n<td>\n  apiVersion: v1<\/p>\n<p>kind: Secret<\/p>\n<p>metadata:<\/p>\n<p>name: digitalocean<\/p>\n<p>namespace: kube-system<\/p>\n<p>stringData:<\/p>\n<p>access-token: &#8220;a05dd2f26b9b9ac2asdas__REPLACE_ME____123cb5d1ec17513e06da&#8221;<\/td>\n<\/tr>\n<\/table>\n<p>\u7136\u540e\u90e8\u7f72CSI\u63d2\u4ef6<\/p>\n<p>$ kubectl apply -f <a href=\"https:\/\/raw.githubusercontent.com\/digitalocean\/csi-digitalocean\/master\/deploy\/kubernetes\/releases\/csi-digitalocean-v0.2.0.yaml\">https:\/\/raw.githubusercontent.com\/digitalocean\/csi-digitalocean\/master\/deploy\/kubernetes\/releases\/csi-digitalocean-v0.2.0.yaml<\/a><\/p>\n<p>\u63d2\u4ef6\u7684YAML\u5982\u4e0b<\/p>\n<table>\n<tr>\n<td>\n  kind: DaemonSet<\/p>\n<p>apiVersion: apps\/v1beta2<\/p>\n<p>metadata:<\/p>\n<p>name: csi-do-node<\/p>\n<p>namespace: kube-system<\/p>\n<p>spec:<\/p>\n<p>selector:<\/p>\n<p>matchLabels:<\/p>\n<p>app: csi-do-node<\/p>\n<p>template:<\/p>\n<p>metadata:<\/p>\n<p>labels:<\/p>\n<p>app: csi-do-node<\/p>\n<p>role: csi-do<\/p>\n<p>spec:<\/p>\n<p>serviceAccount: csi-do-node-sa<\/p>\n<p>hostNetwork: true<\/p>\n<p>containers:<\/p>\n<p>&#8211; name: driver-registrar<\/p>\n<p>image: quay.io\/k8scsi\/driver-registrar:v0.3.0<\/p>\n<p>&#8230;<\/p>\n<p>&#8211; name: csi-do-plugin<\/p>\n<p>image: digitalocean\/do-csi-plugin:v0.2.0<\/p>\n<p>args :<\/p>\n<p>&#8211; &#8220;&#8211;endpoint=$(CSI_ENDPOINT)&#8221;<\/p>\n<p>&#8211; &#8220;&#8211;token=$(DIGITALOCEAN_ACCESS_TOKEN)&#8221;<\/p>\n<p>&#8211; &#8220;&#8211;url=$(DIGITALOCEAN_API_URL)&#8221;<\/p>\n<p>env:<\/p>\n<p>&#8211; name: CSI_ENDPOINT<\/p>\n<p>value: unix:\/\/\/csi\/csi.sock<\/p>\n<p>&#8211; name: DIGITALOCEAN_API_URL<\/p>\n<p>value: <a href=\"https:\/\/api.digitalocean.com\/\">https:\/\/api.digitalocean.com\/<\/a><\/p>\n<p>&#8211; name: DIGITALOCEAN_ACCESS_TOKEN<\/p>\n<p>valueFrom:<\/p>\n<p>secretKeyRef:<\/p>\n<p>name: digitalocean<\/p>\n<p>key: access-token<\/p>\n<p>imagePullPolicy: &#8220;Always&#8221;<\/p>\n<p>securityContext:<\/p>\n<p>privileged: true<\/p>\n<p>capabilities:<\/p>\n<p>add: [&#8220;SYS_ADMIN&#8221;]<\/p>\n<p>allowPrivilegeEscalation: true<\/p>\n<p>volumeMounts:<\/p>\n<p>&#8211; name: plugin-dir<\/p>\n<p>mountPath: \/csi<\/p>\n<p>&#8211; name: pods-mount-dir<\/p>\n<p>mountPath: \/var\/lib\/kubelet<\/p>\n<p>mountPropagation: &#8220;Bidirectional&#8221;<\/p>\n<p>&#8211; name: device-dir<\/p>\n<p>mountPath: \/dev<\/p>\n<p>volumes:<\/p>\n<p>&#8211; name: plugin-dir<\/p>\n<p>hostPath:<\/p>\n<p>path: \/var\/lib\/kubelet\/plugins\/com.digitalocean.csi.dobs<\/p>\n<p>type: DirectoryOrCreate<\/p>\n<p>&#8211; name: pods-mount-dir<\/p>\n<p>hostPath:<\/p>\n<p>path: \/var\/lib\/kubelet<\/p>\n<p>type: Directory<\/p>\n<p>&#8211; name: device-dir<\/p>\n<p>hostPath:<\/p>\n<p>path: \/dev<\/p>\n<p>&#8212;<\/p>\n<p>kind: StatefulSet<\/p>\n<p>apiVersion: apps\/v1beta1<\/p>\n<p>metadata:<\/p>\n<p>name: csi-do-controller<\/p>\n<p>namespace: kube-system<\/p>\n<p>spec:<\/p>\n<p>serviceName: &#8220;csi-do&#8221;<\/p>\n<p>replicas: 1<\/p>\n<p>template:<\/p>\n<p>metadata:<\/p>\n<p>labels:<\/p>\n<p>app: csi-do-controller<\/p>\n<p>role: csi-do<\/p>\n<p>spec:<\/p>\n<p>serviceAccount: csi-do-controller-sa<\/p>\n<p>containers:<\/p>\n<p>&#8211; name: csi-provisioner<\/p>\n<p>image: quay.io\/k8scsi\/csi-provisioner:v0.3.0<\/p>\n<p>&#8230;<\/p>\n<p>&#8211; name: csi-attacher<\/p>\n<p>image: quay.io\/k8scsi\/csi-attacher:v0.3.0<\/p>\n<p>&#8230;<\/p>\n<p>&#8211; name: csi-do-plugin<\/p>\n<p>image: digitalocean\/do-csi-plugin:v0.2.0<\/p>\n<p>args :<\/p>\n<p>&#8211; &#8220;&#8211;endpoint=$(CSI_ENDPOINT)&#8221;<\/p>\n<p>&#8211; &#8220;&#8211;token=$(DIGITALOCEAN_ACCESS_TOKEN)&#8221;<\/p>\n<p>&#8211; &#8220;&#8211;url=$(DIGITALOCEAN_API_URL)&#8221;<\/p>\n<p>env:<\/p>\n<p>&#8211; name: CSI_ENDPOINT<\/p>\n<p>value: unix:\/\/\/var\/lib\/csi\/sockets\/pluginproxy\/csi.sock<\/p>\n<p>&#8211; name: DIGITALOCEAN_API_URL<\/p>\n<p>value: <a href=\"https:\/\/api.digitalocean.com\/\">https:\/\/api.digitalocean.com\/<\/a><\/p>\n<p>&#8211; name: DIGITALOCEAN_ACCESS_TOKEN<\/p>\n<p>valueFrom:<\/p>\n<p>secretKeyRef:<\/p>\n<p>name: digitalocean<\/p>\n<p>key: access-token<\/p>\n<p>imagePullPolicy: &#8220;Always&#8221;<\/p>\n<p>volumeMounts:<\/p>\n<p>&#8211; name: socket-dir<\/p>\n<p>mountPath: \/var\/lib\/csi\/sockets\/pluginproxy\/<\/p>\n<p>volumes:<\/p>\n<p>&#8211; name: socket-dir<\/p>\n<p>emptyDir: {}<\/td>\n<\/tr>\n<\/table>\n<p>\u6211\u4eec\u901a\u8fc7DaemonSet\u5728\u6bcf\u4e00\u4e2a\u8282\u70b9\u4e0a\u90fd\u542f\u52a8\u4e00\u4e2aCSI\u63d2\u4ef6,\u4e3akubelet\u63d0\u4f9bCSI Node\u670d\u52a1,\u6bcf\u4e2a\u8282\u70b9\u4e0a\u90fd\u9700\u8981\u88abkubelet\u63d0\u4f9bCSI Node,\u8fd9\u6837CSI Node\u5c31\u53ef\u4ee5\u76f4\u63a5\u8c03\u7528,\u548ckubelet\u4e00\u5bf9\u4e00\u7684\u90e8\u7f72\u8d77\u6765<\/p>\n<p>\u800c\u4e14\u8fd8\u8fd0\u884c\u4e86\u4e00\u4e2adriver-registrara\u7ec4\u4ef6,\u4ee5sidecar\u7684\u65b9\u5f0f\u8fd0\u884c\u8fd9\u4e2a\u7ec4\u4ef6,\u53ef\u4ee5\u5411kubelet\u6ce8\u518c\u8fd9\u4e2aCSI\u63d2\u4ef6,\u901a\u8fc7\u8bbf\u95ee\u540c\u4e00\u4e2aPod\u91cc\u7684CSI\u63d2\u4ef6\u5bb9\u5668\u7684Identity\u670d\u52a1\u83b7\u53d6\u5230<\/p>\n<p>\u7136\u540e\u56e0\u4e3aCSI\u63d2\u4ef6\u8fd0\u884c\u5728\u4e00\u4e2a\u5bb9\u5668\u4e2d,\u90a3\u4e48CSI Node\u670d\u52a1\u5728MoUnt\u9636\u6bb5\u7684\u6302\u8f7d\u64cd\u4f5c,\u4e00\u822c\u662f\u6302\u8f7d\u4e86\u5bb9\u5668\u5185\u90e8\u7684Mount Namespace\u91cc\u4e86,\u4e3a\u4e86\u53ef\u4ee5\u5b9e\u9645\u6302\u8f7d\u5230\u5bbf\u4e3b\u673a\u4e0a,\u6240\u4ee5\u6211\u4eec\u9700\u8981\u5c06\u5bbf\u4e3b\u673a\u4e0a\u7684\/var\/lib\/kubelet\u4ee5Volume\u7684\u65b9\u5f0f\u6302\u8f7d\u5230CSI\u63d2\u4ef6\u5bb9\u5668\u7684\u540c\u540d\u76ee\u5f55\u4e0b\u4e86,\u7136\u540e\u8bbe\u7f6e\u8fd9\u4e2aVolume\u7684mountPropageation=Bidirectional,\u5f00\u542f\u53cc\u5411\u6302\u8f7d\u4f20\u64ad,\u5c06\u8fd9\u4e2a\u5bb9\u5668\u5728\u8fd9\u4e2a\u76ee\u5f55\u4e0b\u8fdb\u884c\u7684\u6302\u8f7d\u64cd\u4f5c,\u4f20\u64ad\u7ed9\u5bbf\u4e3b\u673a<\/p>\n<p>\u7136\u540e\u901a\u8fc7StatefulSet\u5728\u4efb\u610f\u8282\u70b9\u4e0a\u542f\u52a8\u4e00\u4e2aCSI\u63d2\u4ef6,\u4e3aExternal Components\u63d0\u4f9bCSI Controller\u670d\u52a1,\u4f5c\u4e3aCSI Controller\u670d\u52a1\u7684\u8c03\u7528\u8005,External Provisioner\u548cExternal Attacher\u4e24\u4e2a\u5916\u90e8\u7ec4\u4ef6,\u5c31\u9700\u8981\u4ee5sidecar\u7684\u65b9\u5f0f\u548c\u8fd9\u6b21\u7684CSI\u63d2\u4ef6\u5b9a\u4e49\u5728\u4e00\u4e2aPod\u4e2d<\/p>\n<p>\u6211\u4eec\u4f7f\u7528StatefulSet\u662f\u56e0\u4e3a\u53ef\u4ee5\u4e25\u683c\u786e\u4fdd\u5e94\u7528\u62d3\u8865\u7684\u7a33\u5b9a\u6027,\u5bf9Pod\u66f4\u65b0\u662f\u786e\u4fdd\u4e0a\u4e00\u4e2a\u5df2\u7ecfPod\u5e76\u5220\u9664\u540e,\u624d\u4f1a\u63d2\u4ef6\u5e76\u542f\u52a8\u4e0b\u4e00\u4e2aPod,\u8fd9\u6837\u7684\u987a\u5e8f\u786e\u4fdd\u662f\u975e\u5e38\u91cd\u8981\u7684<\/p>\n<p>\u7136\u540e,\u53ea\u9700\u8981\u5b9a\u4e49\u4e00\u4e2a\u4f7f\u7528\u8fd9\u4e2a\u63d2\u4ef6\u7684StorageClass\u5c31\u53ef\u4ee5\u4e86<\/p>\n<table>\n<tr>\n<td>\n  kind: StorageClass<\/p>\n<p>apiVersion: storage.k8s.io\/v1<\/p>\n<p>metadata:<\/p>\n<p>name: do-block-storage<\/p>\n<p>namespace: kube-system<\/p>\n<p>annotations:<\/p>\n<p>storageclass.kubernetes.io\/is-default-class: &#8220;true&#8221;<\/p>\n<p>provisioner: com.digitalocean.csi.dobs<\/td>\n<\/tr>\n<\/table>\n<p>\u5982\u4e00\u5f00\u5934\u7684StorageClass\u4e00\u6837<\/p>\n<p>\u90a3\u4e48\u5728\u672c\u7ae0\u4e2d,\u6211\u4eec\u8bf4\u4e86\u5982\u4f55\u7f16\u5199\u4e00\u4e2aCSI\u63d2\u4ef6\u6211\u4eec\u53ef\u4ee5\u501f\u52a9\u6b64\u6765\u4e86\u89e3Kubernetes\u6301\u4e45\u5316\u7684\u5b58\u50a8\u4f53\u7cfb<\/p>\n<p>\u5f53\u7528\u6237\u521b\u5efa\u4e86\u4e00\u4e2aPVC\u4e4b\u540e,\u4e4b\u524d\u90e8\u7f72\u7684StatefulSet\u7684External Provisioner\u5bb9\u5668,\u5c31\u4f1a\u76d1\u542c\u5230\u8fd9\u4e2aPVC\u7684\u521b\u5efa,\u5e76\u4e14\u5229\u7528CSI Controller\u7684CreateVolume,\u521b\u5efa\u51fa\u5bf9\u5e94\u7684PV<\/p>\n<p>\u8fd0\u884c\u5728Kubernetes Master\u8282\u70b9\u4e0aVolume Controller,\u5c31\u4f1a\u901a\u8fc7PersistentVolumeController,\u53d1\u73b0\u8fd9\u5bf9\u65b0\u521b\u5efa\u7684PV\u548cPVC\u4f7f\u7528\u7684\u662f\u76f8\u540c\u7684StorageClass,\u5c31\u4f1a\u5c06\u4e00\u5bf9PV\u548cPVC\u8fdb\u884c\u7ed1\u5b9a,\u4f7f\u5f97PVC\u8fdb\u5165Bound\u72b6\u6001<\/p>\n<p>\u5168\u5c40\u7684Volume Controller\u901a\u8fc7AttachDetachController\u63a7\u5236\u5faa\u73af\u521b\u5efa\u4e00\u4e2aVolumeAttachment\u5bf9\u8c61,\u8fd9\u4e2a\u5bf9\u8c61\u643a\u5e26\u4e86\u5bbf\u4e3b\u673aA\u548c\u5f85\u5904\u7406Volume\u540d\u5b57<\/p>\n<p>\u7136\u540e\u518d\u4ea4\u7ed9\u6211\u4eec\u8fdb\u884cAttach\u548cMount\u8fc7\u7a0b,\u6211\u4eec\u7684CSI Controller\u4f1a\u76d1\u542c\u5230\u8fd9\u4e2aVolumeAttachment\u5bf9\u8c61,\u7136\u540e\u4f7f\u7528\u8fd9\u4e2a\u5bf9\u8c61\u7684\u5bbf\u4e3b\u673a\u548cVolume\u540d\u5b57,\u8c03\u7528\u540c\u4e00\u4e2aPod\u91cc\u76f8\u540c\u7684CSI\u63d2\u4ef6\u7684CSI Controller\u670d\u52a1\u7684ControllerPublishVolume\u65b9\u6cd5,\u5b8c\u6210\u8fd9\u4e2aAttach\u9636\u6bb5<\/p>\n<p>\u4e0a\u8ff0\u8fc7\u7a0b\u5b8c\u6210\u540e,\u8fd0\u884c\u5728\u8282\u70b9\u4e0a\u7684kubelet,\u5c31\u4f1a\u901a\u8fc7VolumeMangerReconciler\u63a7\u5236\u5faa\u73af,\u53d1\u73b0\u5f53\u524d\u5bbf\u4e3b\u673a\u4e0a\u6709\u4e00\u4e2aVolume\u5bf9\u5e94\u7684\u8bbe\u5907\u78c1\u76d8,\u5df2\u7ecf\u88abAttach\u5230\u67d0\u4e2a\u8bbe\u5907\u76ee\u5f55\u4e0b,\u4e8e\u662fkubelet\u5c31\u4f1a\u8c03\u7528\u540c\u4e00\u4e2a\u5bbf\u4e3b\u4e0b\u7684CSI Node\u670d\u52a1\u7684NodeStageVolume\u548cNodePublishVolume,\u5b8c\u6210\u8fd9\u4e2aVolume\u7684Mount\u9636\u6bb5<\/p>\n<p>\u8fd9\u6837,\u4fbf\u662f\u4e00\u4e2a\u5b8c\u6574\u7684\u6301\u4e45\u5316Volume\u7684\u521b\u5efa\u548c\u6302\u8f7d\u6d41\u7a0b<\/p>\n<p>\u90a3\u4e48\u6211\u4eec\u9700\u8981\u601d\u8003\u4e00\u4e2a\u95ee\u9898,\u4ec0\u4e48\u65f6\u5019\u4f7f\u7528FlexVolume,\u4ec0\u4e48\u65f6\u5019\u4f7f\u7528CSI<\/p>\n<p>\u7b80\u5355\u6765\u8bf4,\u662f\u4f7f\u7528\u573a\u666f\u51b3\u5b9a\u7684,\u5982\u679c\u4f7f\u7528\u7b80\u5355,\u800c\u4e14CSI\u63d2\u4ef6\u5305\u542b\u4e86\u4e00\u90e8\u5206\u539f\u6765Kuberentes\u4e2d\u5b58\u50a8\u7ba1\u7406\u7684\u529f\u80fd,\u5b9e\u73b0,\u90e8\u7f72\u6bd4\u8f83\u590d\u6742,\u573a\u666f\u6bd4\u8f83\u7b80\u5355,\u4e0d\u9700\u8981Dynamic Provisioning,\u53ef\u4ee5\u4f7f\u7528flexVolume,\u573a\u666f\u590d\u6742,\u652f\u6301Dynamci<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u6211\u4eec\u8fdb\u884c\u5b9e\u8df5\u4e00\u4e2aCSI\u63d2\u4ef6\u7684\u7f16 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"_links":{"self":[{"href":"http:\/\/xinblog.ltd\/index.php?rest_route=\/wp\/v2\/posts\/234"}],"collection":[{"href":"http:\/\/xinblog.ltd\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/xinblog.ltd\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/xinblog.ltd\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/xinblog.ltd\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=234"}],"version-history":[{"count":0,"href":"http:\/\/xinblog.ltd\/index.php?rest_route=\/wp\/v2\/posts\/234\/revisions"}],"wp:attachment":[{"href":"http:\/\/xinblog.ltd\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=234"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/xinblog.ltd\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=234"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/xinblog.ltd\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=234"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}