原文地址: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