原文地址: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 Controller
,NodeController
等。
下圖是對這個過程的簡單描述:
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