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處理。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM