【kubernetes/k8s源码分析】 kubernetes csi external-attacher 源码分析

本文深入探讨了Kubernetes CSI外部挂载器的工作原理,详细介绍了其如何通过监听VolumeAttachment和PersistentVolume资源,同步并执行插件的Attach/Detach操作,实现动态存储卷的挂载与卸载。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Status and Releases

Latest stable releaseBranchMin CSI VersionMax CSI VersionContainer ImageMin K8s VersionMax K8s VersionRecommended K8s Version
external-attacher v1.2.1release-1.2v1.0.0-quay.io/k8scsi/csi-attacher:v1.2.1v1.13-v1.15
external-attacher v1.1.1release-1.1v1.0.0-quay.io/k8scsi/csi-attacher:v1.1.1v1.13-v1.14
external-attacher v1.0.1release-1.0v1.0.0-quay.io/k8scsi/csi-attacher:v1.0.1v1.13-v1.13
external-attacher v0.4.2release-0.4v0.3.0v0.3.0quay.io/k8scsi/csi-attacher:v0.4.2v1.10-v1.10

 

The following table reflects the head of this branch.

FeatureStatusDefaultDescription
CSINode*BetaOnexternal-attacher uses the CSINode object to get the driver's node name instead of the Node annotation.
CSIMigration*AlphaOnMigrating in-tree volume plugins to CSI.

 

Description

    external-attacher 对 kubernetes api-server watch VolumeAttachment 资源对象,对相应的后端插件 Controller[Publish|Unpublish]Volume 操作,前提是为 controller 设置  PUBLISH_UNPUBLISH_VOLUME 能力

 

     分为三阶段就是 Provision Attach Mount

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  |
                +------------+

The lifecycle of a dynamically provisioned volume, from
creation to destruction, when the Node Plugin advertises the
STAGE_UNSTAGE_VOLUME capability.

 

     监听 VolumeAttachments 和 PersistentVolumes 两个对象,会调用 SyncNewOrUpdatedVolumeAttachment 来同步,调用 csi dirver 的 Attach 函数

     attach-controller 会创建 VolumeAttatchment.Spec.Attacher 指向的是 external-attacher

 

  ① external attacher  组件启动与 CSI-plugin 插件建立 GRPC 连接,发送 GetPluginInfo 获取 driver 名字,调用 GetPluginCapabilities 获取插件具有的能力,调用 ControllerGetCapabilies 获取 contoller 的能力
  ② kube-controller-manager 中的 AD controller 关注 PVC PV Node Pod等 资源

  ③ AD controller 根据 volume 的 spec 获插件,创建 volumeattachment 资源

  ④ external attacher watch 到 volumeattachment 进行 syncPV syncVA 

  ⑤ 最终调用 CSI plugin 的 Attach Detach 处理

 

     attacher 与 plugin,Attach 则 GRPC ControllerPublishVolumeRequest 请求到插件,这个插件是与 provisioner attacher 为同一个pod

apiVersion: storage.k8s.io/v1
kind: VolumeAttachment
metadata:
  creationTimestamp: "2020-05-14T02:28:32Z"
  name: csi-fbcecaed2a122919ca11ef1fa40d9d51f545db042b9a0acf386c0726c9100a5d
  resourceVersion: "995957"
  selfLink: /apis/storage.k8s.io/v1/volumeattachments/csi-fbcecaed2a122919ca11ef1fa40d9d51f545db042b9a0acf386c0726c9100a5d
  uid: a2ab45ec-a90c-4614-8361-76c0144e70e0
spec:
  attacher: hostpath.csi.k8s.io
  nodeName: master-node
  source:
    persistentVolumeName: pvc-149bb57c-4c8f-48bf-8439-09dd505e6aa2
status:
  attached: true

 

启动命令

    csi-attacher --v=5 --csi-address=/var/lib/csi/sockets/pluginproxy/mock.socket --leader-election --leader-election-type=leases

 

1. main函数

     1.1 创建CSIAttachController对象

       watch API server的volumeAttachment对象

       csiHandler实现了handler,定义在pkg/controller/csi_handler.go

ctrl := controller.NewCSIAttachController(
	clientset,
	csiAttacher,
	handler,
	factory.Storage().V1beta1().VolumeAttachments(),
	factory.Core().V1().PersistentVolumes(),
	workqueue.NewItemExponentialFailureRateLimiter(*retryIntervalStart, *retryIntervalMax),
	workqueue.NewItemExponentialFailureRateLimiter(*retryIntervalStart, *retryIntervalMax),
)

  

2. Run函数

    路径: pkg/controller/controller.go,同样的套路

    同步volumeAttachement与pv

// Run starts CSI attacher and listens on channel events
func (ctrl *CSIAttachController) Run(workers int, stopCh <-chan struct{}) {
	defer ctrl.vaQueue.ShutDown()
	defer ctrl.pvQueue.ShutDown()

	klog.Infof("Starting CSI attacher")
	defer klog.Infof("Shutting CSI attacher")

	if !cache.WaitForCacheSync(stopCh, ctrl.vaListerSynced, ctrl.pvListerSynced) {
		klog.Errorf("Cannot sync caches")
		return
	}
	for i := 0; i < workers; i++ {
		go wait.Until(ctrl.syncVA, 0, stopCh)
		go wait.Until(ctrl.syncPV, 0, stopCh)
	}

	<-stopCh
}

 

3 SyncNewOrUpdatedPersistentVolume函数

    路径 pkg/controller/csi_handler.go

    3.1 如果pv已经被删除,则删除队列无需处理

// Sync and remove finalizer on given PV
if pv.DeletionTimestamp == nil {
	// Don't process anything that has no deletion timestamp.
	klog.V(4).Infof("CSIHandler: processing PV %q: no deletion timestamp, ignoring", pv.Name)
	h.pvQueue.Forget(pv.Name)
	return
}

     3.2 更新persistentVolume

clone := pv.DeepCopy()
newFinalizers := []string{}
for _, f := range pv.Finalizers {
	if f == finalizer {
		continue
	}
	newFinalizers = append(newFinalizers, f)
}
if len(newFinalizers) == 0 {
	// Canonize empty finalizers for unit test (so we don't need to
	// distinguish nil and [] there)
	newFinalizers = nil
}
clone.Finalizers = newFinalizers
_, err = h.client.CoreV1().PersistentVolumes().Update(clone)
if err != nil {
	klog.Errorf("Failed to remove finalizer from PV %q: %s", pv.Name, err.Error())
	h.pvQueue.AddRateLimited(pv.Name)
	return
}

     

4. SyncNewOrUpdatedVolumeAttachment

  • 如果标记为删除状态则调用syncDetach函数detach操作
  •  如果未标记删除状态则调用syncAttach函数attach操作
func (h *csiHandler) SyncNewOrUpdatedVolumeAttachment(va *storage.VolumeAttachment) {
	klog.V(4).Infof("CSIHandler: processing VA %q", va.Name)

	var err error
	if va.DeletionTimestamp == nil {
		err = h.syncAttach(va)
	} else {
		err = h.syncDetach(va)
	}
	if err != nil {
		// Re-queue with exponential backoff
		klog.V(2).Infof("Error processing %q: %s", va.Name, err)
		h.vaQueue.AddRateLimited(va.Name)
		return
	}
	// The operation has finished successfully, reset exponential backoff
	h.vaQueue.Forget(va.Name)
	klog.V(4).Infof("CSIHandler: finished processing %q", va.Name)
}

    4.1 syncAttach函数

     前面巴拉巴拉一大堆,最终函数调用attacher实现了Attacher接口

// We're not interested in `detached` return value, the controller will
// issue Detach to be sure the volume is really detached.
publishInfo, _, err := h.attacher.Attach(ctx, volumeHandle, readOnly, nodeID, volumeCapabilities, attributes, secrets)
if err != nil {
	return va, nil, err
}

    4.1.1 Attach函数GRPC请求

func (a *attacher) Attach(ctx context.Context, volumeID string, readOnly bool, nodeID string, caps *csi.VolumeCapability, context, secrets map[string]string) (metadata map[string]string, detached bool, err error) {
	client := csi.NewControllerClient(a.conn)

	req := csi.ControllerPublishVolumeRequest{
		VolumeId:         volumeID,
		NodeId:           nodeID,
		VolumeCapability: caps,
		Readonly:         readOnly,
		VolumeContext:    context,
		Secrets:          secrets,
	}

	rsp, err := client.ControllerPublishVolume(ctx, &req)
	if err != nil {
		return nil, isFinalError(err), err
	}
	return rsp.PublishContext, false, nil
}

    4.1.2 更新 volumeattachment 状态

     AD controller 会waitForAttach,检查为 true 则成功完成 attach 操作

func markAsAttached(client kubernetes.Interface, va *storage.VolumeAttachment, metadata map[string]string) (*storage.VolumeAttachment, error) {
	klog.V(4).Infof("Marking as attached %q", va.Name)
	clone := va.DeepCopy()
	clone.Status.Attached = true
	clone.Status.AttachmentMetadata = metadata
	clone.Status.AttachError = nil
	patch, err := createMergePatch(va, clone)
	if err != nil {
		return va, err
	}
	newVA, err := client.StorageV1beta1().VolumeAttachments().Patch(va.Name, types.MergePatchType, patch)
	if err != nil {
		return va, err
	}
	klog.V(4).Infof("Marked as attached %q", va.Name)
	return newVA, nil
}

 

总结:

     watch volumeAttachment pv 资源进行同步操作

     最终向插件发送 GRPC ControllerPublishVolumeRequest 请求

 

     部署 deployment.yaml 配置

kind: Deployment
apiVersion: apps/v1
metadata:
  name: csi-attacher
spec:
  replicas: 3
  selector:
    matchLabels:
      external-attacher: mock-driver
  template:
    metadata:
      labels:
        external-attacher: mock-driver
    spec:
      serviceAccount: csi-attacher
      containers:
        - name: csi-attacher
          image: quay.io/k8scsi/csi-attacher:canary
          args:
            - "--v=5"
            - "--csi-address=$(ADDRESS)"
            - "--leader-election"
          env:
            - name: MY_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: ADDRESS
              value: /var/lib/csi/sockets/pluginproxy/mock.socket
          imagePullPolicy: "IfNotPresent"
          volumeMounts:
            - name: socket-dir
              mountPath: /var/lib/csi/sockets/pluginproxy/

        - name: mock-driver
          image: quay.io/k8scsi/mock-driver:canary
          imagePullPolicy: "IfNotPresent"
          env:
            - name: CSI_ENDPOINT
              value: /var/lib/csi/sockets/pluginproxy/mock.socket
          volumeMounts:
            - name: socket-dir
              mountPath: /var/lib/csi/sockets/pluginproxy/

      volumes:
        - name: socket-dir
          emptyDir:

 

参考:

    https://github.com/kubernetes-csi/external-attacher

    https://kubernetes-csi.github.io/docs/external-attacher.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值