kubernetes ceph-csi分析目录导航

基于tag v2.1.1

https://github.com/kubernetes-csi/external-attacher/releases/tag/v2.1.1

external-attacher

external-attacher属于external plugin中的一个。下面我们先来回顾一下external plugin以及csi系统结构。

external plugin

external plugin包括了external-provisioner、external-attacher、external-resizer、external-snapshotter等,external plugin辅助csi plugin组件,共同完成了存储相关操作。external plugin负责watch pvc、volumeAttachment等对象,然后调用volume plugin来完成存储的相关操作。如external-provisioner watch pvc对象,然后调用csi plugin来创建存储,最后创建pv对象;external-attacher watch volumeAttachment对象,然后调用csi plugin来做attach/dettach操作,并修改volumeAttachment对象与pv对象;external-resizer watch pvc对象,然后调用csi plugin来做存储的扩容操作等。

csi系统结构

external-attacher作用分析

根据CSI plugin是否支持ControllerPublish/ControllerUnpublish操作,external-attacher的作用分为如下两种:

(1)当CSI plugin不支持ControllerPublish/ControllerUnpublish操作时,AD controller(或kubelet的volume manager)创建VolumeAttachment对象后,external-attacher仅参与VolumeAttachment对象的修改,将attached属性值patch为true;而external-attacher对pv对象无任何同步处理操作。

(2)当CSI plugin支持ControllerPublish/ControllerUnpublish操作时,external-attacher调用csi plugin(ControllerPublishVolume)进行存储的attach操作,然后更改VolumeAttachment对象,将attached属性值patch为true,并patch pv对象,增加该external-attacher相关的finalizer;对于pv对象,external-attacher负责处理pv对象的finalizer,patch pv对象,去除该external-attacher相关的finalizer(该external-attacher执行attach操作时添加的finalizer)。

源码分析

external-attacher的源码分析将分为两部分:

(1)main方法以及启动参数分析;

(2)核心处理逻辑分析。

前面一篇博客已经对external-attacher的main方法以及启动参数做了分析,这篇博客将对external-attacher的核心处理逻辑进行源码分析。

Run

核心逻辑为跑goroutine不停的调用ctrl.syncVA与ctrl.syncPV对VolumeAttachment对象以及PV对象进行同步处理。

//external-attcher/pkg/controller/controller.go

// 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)
} if ctrl.shouldReconcileVolumeAttachment {
go wait.Until(func() {
err := ctrl.handler.ReconcileVA()
if err != nil {
klog.Errorf("Failed to reconcile volume attachments: %v", err)
}
}, ctrl.reconcileSync, stopCh)
} <-stopCh
}

1.syncVA

syncVA负责VolumeAttachment对象的处理,核心逻辑为:

(1)判断VolumeAttachment对象的.Spec.Attacher属性值,判断是否由本attacher组件来负责VolumeAttachment对象的同步处理;

(2)调用ctrl.handler.SyncNewOrUpdatedVolumeAttachment(va)。

//external-attcher/pkg/controller/controller.go

func (ctrl *CSIAttachController) syncVA() {
key, quit := ctrl.vaQueue.Get()
if quit {
return
}
defer ctrl.vaQueue.Done(key) vaName := key.(string)
klog.V(4).Infof("Started VA processing %q", vaName) // get VolumeAttachment to process
va, err := ctrl.vaLister.Get(vaName)
if err != nil {
if apierrs.IsNotFound(err) {
// VolumeAttachment was deleted in the meantime, ignore.
klog.V(3).Infof("VA %q deleted, ignoring", vaName)
return
}
klog.Errorf("Error getting VolumeAttachment %q: %v", vaName, err)
ctrl.vaQueue.AddRateLimited(vaName)
return
}
if va.Spec.Attacher != ctrl.attacherName {
klog.V(4).Infof("Skipping VolumeAttachment %s for attacher %s", va.Name, va.Spec.Attacher)
return
}
ctrl.handler.SyncNewOrUpdatedVolumeAttachment(va)
}

1.1 SyncNewOrUpdatedVolumeAttachment

ctrl.handler.SyncNewOrUpdatedVolumeAttachment()包含两个实现,将根据CSI plugin是否支持ControllerPublish/ControllerUnpublish操作来调用不同的实现。(调用不同实现的判断逻辑在external-attacher的main方法里,前面分析external-attacher的main方法时已经分析过了,忘记的可以回去看下)

(1)trivialHandler.SyncNewOrUpdatedVolumeAttachment:当CSI plugin不支持ControllerPublish/ControllerUnpublish操作时,AD controller(或kubelet的volume manager)创建VolumeAttachment对象后,external-attacher仅参与VolumeAttachment对象的修改,将attached属性值patch为true。

(2)csiHandler.SyncNewOrUpdatedVolumeAttachment:当CSI plugin支持ControllerPublish/ControllerUnpublish操作时,external-attacher调用csi plugin(ControllerPublishVolume)进行存储的attach操作,然后更改VolumeAttachment对象,将attached属性值patch为true。

1.1.1 trivialHandler.SyncNewOrUpdatedVolumeAttachment

先来看trivialHandler的实现。

当VolumeAttachment的attached属性值为false时,调用markAsAttached做进一步处理。

//external-attcher/pkg/controller/trivial_handler.go

func (h *trivialHandler) SyncNewOrUpdatedVolumeAttachment(va *storage.VolumeAttachment) {
klog.V(4).Infof("Trivial sync[%s] started", va.Name)
if !va.Status.Attached {
// mark as attached
if _, err := markAsAttached(h.client, va, nil); err != nil {
klog.Warningf("Error saving VolumeAttachment %s as attached: %s", va.Name, err)
h.vaQueue.AddRateLimited(va.Name)
return
}
klog.V(2).Infof("Marked VolumeAttachment %s as attached", va.Name)
}
h.vaQueue.Forget(va.Name)
}
markAsAttached

markAsAttached主要是将VolumeAttachment的attached属性值patch为true。

//external-attcher/pkg/controller/util.go

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
}

1.1.2 csiHandler.SyncNewOrUpdatedVolumeAttachment

先来看csiHandler的实现。主要逻辑如下:

(1)当VolumeAttachment对象的.DeletionTimestamp字段为空,调用h.syncAttach(调用了csi plugin的ControllerPublishVolume方法来做存储的attach操作,并调用markAsAttached将VolumeAttachment的attached属性值patch为true);

(2)当VolumeAttachment对象的.DeletionTimestamp字段不为空,调用h.syncDetach(调用了csi plugin的ControllerUnpublishVolume方法来做存储的dettach操作,并调用markAsDetached将VolumeAttachment的attached属性值patch为false)。

// pkg/controller/csi_handler.go
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)
}
syncAttach

syncAttach不展开分析了,感兴趣的可以自己深入分析,从h.csiAttach调用入手。

主要逻辑为:

(1)调用h.csiAttach做attach操作(patch pv对象,增加该external-attacher相关的Finalizer,并最终调用了csi plugin的ControllerPublishVolume方法来做存储的attach操作);

(2)调用markAsAttached将VolumeAttachment的attached属性值patch为true。

// pkg/controller/csi_handler.go
func (h *csiHandler) syncAttach(va *storage.VolumeAttachment) error {
if !h.consumeForceSync(va.Name) && va.Status.Attached {
// Volume is attached and no force sync, there is nothing to be done.
klog.V(4).Infof("%q is already attached", va.Name)
return nil
} // Attach and report any error
klog.V(2).Infof("Attaching %q", va.Name)
va, metadata, err := h.csiAttach(va)
if err != nil {
var saveErr error
va, saveErr = h.saveAttachError(va, err)
if saveErr != nil {
// Just log it, propagate the attach error.
klog.V(2).Infof("Failed to save attach error to %q: %s", va.Name, saveErr.Error())
}
// Add context to the error for logging
err := fmt.Errorf("failed to attach: %s", err)
return err
}
klog.V(2).Infof("Attached %q", va.Name) // Mark as attached
if _, err := markAsAttached(h.client, va, metadata); err != nil {
return fmt.Errorf("failed to mark as attached: %s", err)
}
klog.V(4).Infof("Fully attached %q", va.Name)
return nil
}
syncDetach

同样的,syncDetach不展开分析,这里直接给出结果,最终调用了csi plugin的ControllerUnpublishVolume方法来做存储的dettach操作,并调用markAsDetached将VolumeAttachment的attached属性值patch为false,感兴趣的可以自己深入分析,从h.csiDetach调用入手。

// pkg/controller/csi_handler.go
func (h *csiHandler) syncDetach(va *storage.VolumeAttachment) error {
klog.V(4).Infof("Starting detach operation for %q", va.Name)
if !h.consumeForceSync(va.Name) && !h.hasVAFinalizer(va) {
klog.V(4).Infof("%q is already detached", va.Name)
return nil
} // Detach and report any error
klog.V(2).Infof("Detaching %q", va.Name)
va, err := h.csiDetach(va)
if err != nil {
var saveErr error
va, saveErr = h.saveDetachError(va, err)
if saveErr != nil {
// Just log it, propagate the detach error.
klog.V(2).Infof("Failed to save detach error to %q: %s", va.Name, saveErr.Error())
}
// Add context to the error for logging
err := fmt.Errorf("failed to detach: %s", err)
return err
}
klog.V(4).Infof("Fully detached %q", va.Name)
return nil
}

2.syncPV

syncPV负责PV对象的处理,核心逻辑为调用ctrl.handler.SyncNewOrUpdatedPersistentVolume(pv)来对pv对象做进一步处理。

//external-attcher/pkg/controller/controller.go

// syncPV deals with one key off the queue.  It returns false when it's time to quit.
func (ctrl *CSIAttachController) syncPV() {
key, quit := ctrl.pvQueue.Get()
if quit {
return
}
defer ctrl.pvQueue.Done(key) pvName := key.(string)
klog.V(4).Infof("Started PV processing %q", pvName) // get PV to process
pv, err := ctrl.pvLister.Get(pvName)
if err != nil {
if apierrs.IsNotFound(err) {
// PV was deleted in the meantime, ignore.
klog.V(3).Infof("PV %q deleted, ignoring", pvName)
return
}
klog.Errorf("Error getting PersistentVolume %q: %v", pvName, err)
ctrl.pvQueue.AddRateLimited(pvName)
return
}
ctrl.handler.SyncNewOrUpdatedPersistentVolume(pv)
}

2.1 SyncNewOrUpdatedPersistentVolume

跟上面分析的syncVA中的SyncNewOrUpdatedVolumeAttachment一样,ctrl.handler.SyncNewOrUpdatedPersistentVolume()也包含两个实现,将根据CSI plugin是否支持ControllerPublish/ControllerUnpublish操作来调用不同的实现。(调用不同实现的判断逻辑在external-attacher的main方法里,前面分析external-attacher的main方法时已经分析过了,忘记的可以回去看下)

(1)trivialHandler.SyncNewOrUpdatedPersistentVolume:当CSI plugin不支持ControllerPublish/ControllerUnpublish操作时,external-attacher不对pv对象做任何同步处理操做。

(2)csiHandler.SyncNewOrUpdatedPersistentVolume:当CSI plugin支持ControllerPublish/ControllerUnpublish操作时,external-attacher负责处理pv对象的finalizer,patch pv对象,去除该external-attacher相关的finalizer(该external-attacher执行attach操作时添加的finalizer)。

2.1.1 trivialHandler.SyncNewOrUpdatedPersistentVolume

trivialHandler.SyncNewOrUpdatedPersistentVolume方法直接返回,可以看出对于pv对象,不做处理。

//external-attcher/pkg/controller/trivial_handler.go

func (h *trivialHandler) SyncNewOrUpdatedPersistentVolume(pv *v1.PersistentVolume) {
return
}
与ceph-csi搭配使用的external-attacher相关日志
I0907 03:30:18.426009       1 main.go:166] CSI driver does not support ControllerPublishUnpublish, using trivial handler

I0907 08:13:38.148672       1 controller.go:198] Started VA processing "csi-706ace7b44a2f618280cba51951595f98893b34a38015e3c4ef7d1160be7f67b"
I0907 08:13:38.148698 1 trivial_handler.go:53] Trivial sync[csi-706ace7b44a2f618280cba51951595f98893b34a38015e3c4ef7d1160be7f67b] started
I0907 08:13:38.148707 1 util.go:35] Marking as attached "csi-706ace7b44a2f618280cba51951595f98893b34a38015e3c4ef7d1160be7f67b"
I0907 08:13:38.156486 1 util.go:48] Marked as attached "csi-706ace7b44a2f618280cba51951595f98893b34a38015e3c4ef7d1160be7f67b"
I0907 08:13:38.156510 1 trivial_handler.go:61] Marked VolumeAttachment csi-706ace7b44a2f618280cba51951595f98893b34a38015e3c4ef7d1160be7f67b as attached

2.1.2 csiHandler.SyncNewOrUpdatedPersistentVolume

csiHandler.SyncNewOrUpdatedPersistentVolume主要是处理pv对象的finalizer,patch pv对象,去除该external-attacher相关的finalizer(该external-attacher执行attach操作时添加的finalizer)。

主要逻辑:

(1)判断pv对象的DeletionTimestamp是否为空,为空则直接返回;

(2)检查pv对象是否含有该external-attacher相关的finalizer,没有则直接返回;

(3)查询volumeAttachment对象列表,遍历查询是否有va对象记录着该pv,有则直接返回;

(4)去除pv对象中该external-attacher相关的finalizer;

(5)patch pv对象。

// pkg/controller/csi_handler.go
func (h *csiHandler) SyncNewOrUpdatedPersistentVolume(pv *v1.PersistentVolume) {
klog.V(4).Infof("CSIHandler: processing PV %q", pv.Name)
// 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
} // Check if the PV has finalizer
finalizer := GetFinalizerName(h.attacherName)
found := false
for _, f := range pv.Finalizers {
if f == finalizer {
found = true
break
}
}
if !found {
// No finalizer -> no action required
klog.V(4).Infof("CSIHandler: processing PV %q: no finalizer, ignoring", pv.Name)
h.pvQueue.Forget(pv.Name)
return
} // Check that there is no VA that requires the PV
vas, err := h.vaLister.List(labels.Everything())
if err != nil {
// Failed listing VAs? Try again with exp. backoff
klog.Errorf("Failed to list VolumeAttachments for PV %q: %s", pv.Name, err.Error())
h.pvQueue.AddRateLimited(pv.Name)
return
}
for _, va := range vas {
if va.Spec.Source.PersistentVolumeName != nil && *va.Spec.Source.PersistentVolumeName == pv.Name {
// This PV is needed by this VA, don't remove finalizer
klog.V(4).Infof("CSIHandler: processing PV %q: VA %q found", pv.Name, va.Name)
h.pvQueue.Forget(pv.Name)
return
}
}
// No VA found -> remove finalizer
klog.V(4).Infof("CSIHandler: processing PV %q: no VA found, removing finalizer", pv.Name)
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 if _, err = h.patchPV(pv, clone); err != nil {
klog.Errorf("Failed to remove finalizer from PV %q: %s", pv.Name, err.Error())
h.pvQueue.AddRateLimited(pv.Name)
return
} klog.V(2).Infof("Removed finalizer from PV %q", pv.Name)
h.pvQueue.Forget(pv.Name) return
}

总结

external-attacher属于external plugin中的一个。

external-attacher作用分析

根据CSI plugin是否支持ControllerPublish/ControllerUnpublish操作,external-attacher的作用分为如下两种:

(1)当CSI plugin不支持ControllerPublish/ControllerUnpublish操作时,AD controller(或kubelet的volume manager)创建VolumeAttachment对象后,external-attacher仅参与VolumeAttachment对象的修改,将attached属性值patch为true;而external-attacher对pv对象无任何同步处理操作。

(2)当CSI plugin支持ControllerPublish/ControllerUnpublish操作时,external-attacher调用csi plugin(ControllerPublishVolume)进行存储的attach操作,然后更改VolumeAttachment对象,将attached属性值patch为true,并patch pv对象,增加该external-attacher相关的Finalizer;对于pv对象,external-attacher负责处理pv对象的finalizer,patch pv对象,去除该external-attacher相关的finalizer(该external-attacher执行attach操作时添加的finalizer)。

external-attacher与ceph-csi rbd结合使用

ceph-csi不支持ControllerPublish/ControllerUnpublish操作,所以external-attacher与ceph-csi rbd结合使用,external-attacher仅参与VolumeAttachment对象的修改,将attached属性值patch为true。

external-attacher源码分析(2)-核心处理逻辑分析的更多相关文章

  1. 第一篇:Spark SQL源码分析之核心流程

    /** Spark SQL源码分析系列文章*/ 自从去年Spark Submit 2013 Michael Armbrust分享了他的Catalyst,到至今1年多了,Spark SQL的贡献者从几人 ...

  2. MyBatis源码分析之核心处理层

    目录 1 传统方式源码剖析 1.1 初始化流程 1.2 执行SQL流程 1.2.1 获取SqlSession 1.2.2 执行SqlSession接口 1.2.3 执行Executor接口 1.2.4 ...

  3. flappy pig小游戏源码分析(4)——核心pig模块(未完待续)

    热身之后,我们要动点真格的了,游戏叫flappy pig,我们的pig终于要出场了. 老规矩,看看目录结构,读者对着目录结构好好回想我们已经讲解的几个模块: 其中game.js是游戏主程序,optio ...

  4. 第十一篇:Spark SQL 源码分析之 External DataSource外部数据源

    上周Spark1.2刚发布,周末在家没事,把这个特性给了解一下,顺便分析下源码,看一看这个特性是如何设计及实现的. /** Spark SQL源码分析系列文章*/ (Ps: External Data ...

  5. 源码分析 | 手写mybait-spring核心功能(干货好文一次学会工厂bean、类代理、bean注册的使用)

    作者:小傅哥 博客:https://bugstack.cn - 汇总系列原创专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言介绍 一个知识点的学习过程基本分为:运行helloworld ...

  6. 【Spark SQL 源码分析系列文章】

    从决定写Spark SQL源码分析的文章,到现在一个月的时间里,陆陆续续差不多快完成了,这里也做一个整合和索引,方便大家阅读,这里给出阅读顺序 :) 第一篇 Spark SQL源码分析之核心流程 第二 ...

  7. 第八篇:Spark SQL Catalyst源码分析之UDF

    /** Spark SQL源码分析系列文章*/ 在SQL的世界里,除了官方提供的常用的处理函数之外,一般都会提供可扩展的对外自定义函数接口,这已经成为一种事实的标准. 在前面Spark SQL源码分析 ...

  8. 《深入理解Spark:核心思想与源码分析》(前言及第1章)

    自己牺牲了7个月的周末和下班空闲时间,通过研究Spark源码和原理,总结整理的<深入理解Spark:核心思想与源码分析>一书现在已经正式出版上市,目前亚马逊.京东.当当.天猫等网站均有销售 ...

  9. ABP源码分析二十六:核心框架中的一些其他功能

    本文是ABP核心项目源码分析的最后一篇,介绍一些前面遗漏的功能 AbpSession AbpSession: 目前这个和CLR的Session没有什么直接的联系.当然可以自定义的去实现IAbpSess ...

随机推荐

  1. Docker Swarm(八)滚动更新、回滚服务

    滚动更新.回滚服务 默认情况下, swarm一次只更新一个副本,并且两个副本之间没有等待时间,我们可以通过: # 定义并行更新的副本数量--update-parallelism# 定义滚动更新的时间间 ...

  2. Linux中的防火墙

    firewalld 一.防火墙安全概述 firewalld支持命令行也支持GUI设置,相对于iptables,firewalld配置更加的方便.在底层的命令都是iptables, firewalld ...

  3. 配置yum仓库的三种方法光盘镜像、nginx、sftp

    方法一: 1.安装ftp服务 [root@oldboy ~]# yum -y install vsftpd 2.查看vsftpd相关的配置文件和目录 rpm -ql vsftpd # 查看vsftpd ...

  4. linux进阶之nmtui和nmcli配置网络

    CentOS7配置网络推荐使用NetworkManager服务(不推荐network服务). 图形化方式:nmtui或Applications->System Tools->Setting ...

  5. python基础之流程控制(if判断和while、for循环)

    程序执行有三种方式:顺序执行.选择执行.循环执行 一.if条件判断 1.语句 (1)简单的 if 语句 (2)if-else 语句 (3)if-elif-else 结构 (4)使用多个 elif 代码 ...

  6. mysql基础之忘掉密码解决办法及恢复root最高权限办法

    如果忘记了mysql的root用户的密码,可以使用如下的方法,重置root密码. 方法一: 1.停止当前mysql进程 systemctl stop mariadb 2.mysql进程停止后,使用如下 ...

  7. MYSQL数据库快速添加测试数据方法

    先添加两条数据,这里最好添加两条不一样的数据 INSERT INTO "表名"(**, **) VALUES (**,**),(**,**) 然后再次添加数据,这次把查询到的数据添 ...

  8. Python数模笔记-Scipy库(1)线性规划问题

    1.最优化问题建模 最优化问题的三要素是决策变量.目标函数和约束条件. (1)分析影响结果的因素是什么,确定决策变量 (2)决策变量与优化目标的关系是什么,确定目标函数 (3)决策变量所受的限制条件是 ...

  9. 路由信息相关 route 网卡

    目录 route命令 1.查看路由表 2.管理路由 基本网络配置 添加网卡地址 修改网卡UUID route命令 路由表管理命令,路由表主要构成: Destination: 目标网络ID,表示可以到达 ...

  10. [算法] 数据结构 splay(伸展树)解析

    前言 splay学了已经很久了,只不过一直没有总结,鸽了好久来写一篇总结. 先介绍 splay:亦称伸展树,为二叉搜索树的一种,部分操作能在 \(O( \log n)\) 内完成,如插入.查找.删除. ...