zoukankan      html  css  js  c++  java
  • pv和pvc状态

    原文地址:https://kubernetes.cn/topics/46

    API Server 和 PVController

    API Server: 这个组件提供对API的支持,响应REST操作,验证API模型和更新etcd中的相应对象

    PVController: 是ontroller.volume.persistentvolume.PersistentVolumeController的简称,负责监听PV和PVC资源的改动Event,取得最新的PV和PVC并维护它们之间的绑定关系。

    通常情况下API Server和PVController是运行在一个进程里的,但它们之间通过Rest API通讯,理论上支持分开部署。

    K8s里的业务逻辑处理都是在不同的Controller组件里进行,例如大家耳熟能详的Replication ControllerNodeController等。

    下图是对这个过程的简单描述:

     

    PV 和 PVC状态图

    在我们开始深入PVController细节之前有必要先了解一下PV和PVC的状态切换。可参考前一篇文章了解所有状态的集合。

    PV的状态图:

     

    PVC的状态图:

     

    实例演示

    为了让大家有更直观的感受,现在我们用一个真实的实例并结合etcd里数据的变化来更好的理解各种状态的切换过程。

    结合上面的PV和PVC状态图,这个实例唯一没有覆盖的路径是PV状态图中的序号3(Released到Available),有兴趣的用户可以自行做实验,只要保证PV中的Storage是真实可用的能被Mount到节点上就行。

    操作PV状态PVC状态
    1. 添加PV Available -
    2. 添加PVC Available Pending
      Bound Bound
    3. 删除PV - Lost
    4. 再次添加PV Bound Bound
    5. 删除PVC Released -
    6. Storage不可用 Failed -
    7. 手动修改PV,删除ClaimRef Available -

    下面我会把每步操作后etcd里PV和PVC的关键信息抽取出来,为了避免冗长,只显示和状态相关的信息。

    1. 添加PV

    刚添加的PV在未被绑定到PVC时是Available状态,为了待会模拟PV由Released变为Failed的状态,这里故意把nfs server ip设置为非法地址。

    [root@dev pv]# kubectl get pv pv3 -o yaml
    apiVersion: v1
    kind: PersistentVolume
    metadata:
    creationTimestamp: 2017-02-16T08:33:43Z
    name: pv3
    ...
    spec:
    accessModes:
    - ReadWriteOnce
    - ReadWriteMany
    - ReadOnlyMany
    capacity:
    storage: 5Gi
    nfs:
    path: /var/nfsshare/v1
    server: 172.16.51.58.1
    persistentVolumeReclaimPolicy: Recycle
    status:
    phase: Available
    
    2. 添加PVC

    刚添加的PVC的状态是Pending,如果有合适的PV,这个Pending状态会立刻变为Bound,同时相应的PVC也会变为Bound。 你也可以先添加PVC,后添加PV,这样就能保证看到Pending状态。

    [root@dev pv]# kubectl get pvc myclaim -o yaml
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    creationTimestamp: 2017-02-16T08:45:53Z
    name: myclaim
    namespace: test
    ...
    spec:
    accessModes:
    - ReadWriteOnce
    resources:
    requests:
      storage: 5Gi
    status:
    phase: Pending
    

    然后PV和PVC都变为Bound:

    [root@dev pv]# kubectl get pvc myclaim -o yaml
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    annotations:
    pv.kubernetes.io/bind-completed: "yes"
    ...
    uid: 534338ef-f424-11e6-8ab8-000c29a5bb07
    spec:
    ...
    status:
    ...
    phase: Bound
    
    [root@dev pv]# kubectl get pv pv3 -o yaml
    apiVersion: v1
    kind: PersistentVolume
    metadata:
    annotations:
    pv.kubernetes.io/bound-by-controller: "yes"
    ...
    name: pv3
    ...
    spec:
    ...
    claimRef:
    apiVersion: v1
    kind: PersistentVolumeClaim
    name: myclaim
    namespace: test
    uid: 534338ef-f424-11e6-8ab8-000c29a5bb07
    persistentVolumeReclaimPolicy: Recycle
    status:
    phase: Bound
    
    3. 删除PV

    删除PV后,PVC状态变为Lost。请看PVC状态图,Lost状态是没办法变回Pending的,这是即使有其它可用的PV,也不能被PVC使用,因为PVC中还保留对PV的引用(volumeName:pv3)。

    [root@dev pv]# kubectl delete pv pv3
    persistentvolume "pv3" deleted
    [root@dev pv]# kubectl get pvc myclaim -o yaml
    ...
    spec:
    volumeName: pv3
    status:
    phase: Lost
    
    4. 再次添加PV

    再次把刚才的PV添加回来后,PV会被自动绑定到PVC,PVC再次变为Bound。

    [root@dev pv]# kubectl create -f 1.yaml
    persistentvolume "pv3" created
    [root@dev pv]# kubectl get pvc myclaim -o yaml
    ...
    phase: Bound
    
    5. 删除PVC

    删除PVC后,PV状态变为Released,请注意这时PV中还保留对PVC的引用(spec.claimRef)。

    [root@dev pv]# kubectl delete pvc myclaim
    persistentvolumeclaim "myclaim" deleted
    [root@dev pv]# kubectl get pv pv3 -o yaml
    ...
    spec:
    accessModes:
    - ReadWriteOnce
    - ReadWriteMany
    - ReadOnlyMany
    capacity:
    storage: 5Gi
    claimRef:
    apiVersion: v1
    kind: PersistentVolumeClaim
    name: myclaim
    ...
    persistentVolumeReclaimPolicy: Recycle
    status:
    phase: Released
    
    6. Storage不可用

    如果PV的ReclaimPolicy是Recycle,系统会试图mount真实的存储并做删除操作(rm -rm /volume/*),因为我们配置了错误的存储server,Recycle会失败。

    [root@dev pv]# kubectl get pv pv3 -o yaml
    ...
    claimRef:
    apiVersion: v1
    kind: PersistentVolumeClaim
    name: myclaim
    ...
    phase: Failed
    
    7. 手动修改PV,删除ClaimRef

    这时就需要手工干预了,真实生产环境下管理员会把数据备份或迁移出来,然后修改PV,删除claimRef对PVC的引用,k8s会接受到PV变化的Event,将PV状态修改为Available。

    [root@dev pv]# kubectl edit pv pv3
    persistentvolume "pv3" edited
    [root@dev pv]# kubectl get pv pv3 -o yaml
    ...
    status:
    phase: Available
    

    PVController 构建和启动

    构建

    构建过程会创建PVController运行所需要的所有子组件,为了便于理解,这里省去了参数的传递过程,在代码片段里也只是保留了主要的逻辑处理,有兴趣的用户可以自行下载代码研究。

    func NewPersistentVolumeController(
    kubeClient clientset.Interface,
    syncPeriod time.Duration,
    alphaProvisioner vol.ProvisionableVolumePlugin,
    volumePlugins []vol.VolumePlugin,
    cloud cloudprovider.Interface,
    clusterName string,
    volumeSource, claimSource, classSource cache.ListerWatcher,
    eventRecorder record.EventRecorder,
    enableDynamicProvisioning bool,
    ) *PersistentVolumeController {
    
    // eventRecorder 用于记录异常和关键信息,以Event资源形式记录在API Server并和event的来源建立绑定关系,
    // 用kubectl describe 可以看到某种Resource上的event。
    
    if eventRecorder == nil {
        broadcaster := record.NewBroadcaster()
        broadcaster.StartRecordingToSink(&unversioned_core.EventSinkImpl{Interface: kubeClient.Core().Events("")})
        eventRecorder = broadcaster.NewRecorder(api.EventSource{Component: "persistentvolume-controller"})
    }
    
    // 初始化一个新的PVController实例,volumes用于缓存最新的PV,claims用于缓存最新的PVC。
    // kubeClient用于和API Server交互。
    
    controller := &PersistentVolumeController{
        volumes:           newPersistentVolumeOrderedIndex(),
        claims:            cache.NewStore(framework.DeletionHandlingMetaNamespaceKeyFunc),
        kubeClient:        kubeClient,
        eventRecorder:     eventRecorder,
    ...
    }
    
    // volumeSource 主要有两个功能:1. 用于请求API Server 得到最新的PV列表。 2. 用于接收API Server发过来的PV变动的Event
    //
    if volumeSource == nil {
        volumeSource = &cache.ListWatch{
            ListFunc: func(options api.ListOptions) (runtime.Object, error) {
                return kubeClient.Core().PersistentVolumes().List(options)
            },
            WatchFunc: func(options api.ListOptions) (watch.Interface, error) {
                return kubeClient.Core().PersistentVolumes().Watch(options)
            },
        }
    }
    controller.volumeSource = volumeSource
    
    //claimSource 主要有两个功能:1. 用于请求API Server 得到最新的PVC列表。 2. 用于接收API Server发过来的PVC变动的Event
    //
    if claimSource == nil {
        claimSource = &cache.ListWatch{
            ListFunc: func(options api.ListOptions) (runtime.Object, error) {
                return kubeClient.Core().PersistentVolumeClaims(api.NamespaceAll).List(options)
            },
            WatchFunc: func(options api.ListOptions) (watch.Interface, error) {
                return kubeClient.Core().PersistentVolumeClaims(api.NamespaceAll).Watch(options)
            },
        }
    }
    controller.claimSource = claimSource
    
    ...
    
    // volumeController会被启动为一个GoRoutine(类似线程,但更轻量),接收并处理PV的add/delete/update Event
    //
    _, controller.volumeController = framework.NewIndexerInformer(
        volumeSource,
        &api.PersistentVolume{},
        syncPeriod,
        framework.ResourceEventHandlerFuncs{
            AddFunc:    controller.addVolume,
            UpdateFunc: controller.updateVolume,
            DeleteFunc: controller.deleteVolume,
        },
        cache.Indexers{"accessmodes": accessModesIndexFunc},
    )
    
    // claimController会被启动为一个GoRoutine,接收并处理PVC的add/delete/update Event
    //  
    _,controller.claimController=framework.NewInformer(claimSource,&api.PersistentVolumeClaim{},syncPeriod,framework.ResourceEventHandlerFuncs{AddFunc:controller.addClaim,UpdateFunc:controller.updateClaim,DeleteFunc:controller.deleteClaim,},)...returncontroller}
    启动

    启动过程比较简单,只是把volumeController和claimController启动到新的Goroutine里,这两个子Controller开始接收PV和PVC更新的Event并会触发相应的处理。 从构建过程可以看出:这两个子Controller会处理六种Event通知,分别是PV的add/delete/update和PVC的add/delete/update.

    // Run starts all of this controller's control loops
    func (ctrl *PersistentVolumeController) Run(stopCh <-chan struct{}) {
    glog.V(4).Infof("starting PersistentVolumeController")
    ctrl.initializeCaches(ctrl.volumeSource, ctrl.claimSource)
    go ctrl.volumeController.Run(stopCh)
    go ctrl.claimController.Run(stopCh)
    }

    PVController 工作流程解析

    这部分会介绍PVController的主要职责,处理上面提到的六种Event通知,重新计算PV和PVC的绑定关系并把绑定关系通过API Server持久化。

    add/delete/update PV

    这三个方法都是先把Event中的最新PV更新到缓存中,然后调用syncVolume处理。可见syncVolume是一个通用的方法,逻辑比较复杂,因为要考虑到所有可能的PV更新类型。

     

    说明:

    unbindPV 会删除PV中对PV的引用然后调用API Server持久化,API Server会再生成一个PV更新的Event并通知PVController,从而使syncVolume再次被调用,但是这次会走不同的分支并把PV设置为Released

    Fun:storeObjectUpdate 会更新本地缓存和API Server

    Fun: reclaimVolume 的详情如下图:

     
    add/delete/update PVC

    这三个方法都是先把Event中的最新PVC更新到缓存中,然后调用syncClaim处理。

  • 相关阅读:
    洛谷
    洛谷
    洛谷
    洛谷
    模板
    .
    洛谷
    洛谷
    洛谷
    poj 2955"Brackets"(区间DP)
  • 原文地址:https://www.cnblogs.com/boshen-hzb/p/6520496.html
Copyright © 2011-2022 走看看