kubernetes发展到1.7以后,就朝着平台去开发了,上一个讨论的点是kubernetes把metrics单独出去,成为一个kubernetes的监控标准,这个我们以后再慢慢说,先看存储这个孵化的项目。github.com/kubernetes-incubator/external-storage第三方存储对接。
它和上一篇介绍的custom-metrics-apiserver项目一样都是孵化的项目,我们先看看他是如何工作的。
核心在lib/controller/controller.go
//启动了pvc的listwatch
controller.claimSource = &cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
return client.Core().PersistentVolumeClaims(v1.NamespaceAll).List(options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
return client.Core().PersistentVolumeClaims(v1.NamespaceAll).Watch(options)
},
}
controller.claims, controller.claimController = cache.NewInformer(
controller.claimSource,
&v1.PersistentVolumeClaim{},
controller.resyncPeriod,
cache.ResourceEventHandlerFuncs{
AddFunc: controller.addClaim,
UpdateFunc: controller.updateClaim,
DeleteFunc: nil,
},
)
//启动了pv的listwatch
controller.volumeSource = &cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
return client.Core().PersistentVolumes().List(options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
return client.Core().PersistentVolumes().Watch(options)
},
}
controller.volumes, controller.volumeController = cache.NewInformer(
controller.volumeSource,
&v1.PersistentVolume{},
controller.resyncPeriod,
cache.ResourceEventHandlerFuncs{
AddFunc: nil,
UpdateFunc: controller.updateVolume,
DeleteFunc: nil,
},
)
//启动了storageclass的listwatch
controller.classSource = &cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
return client.StorageV1().StorageClasses().List(options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
return client.StorageV1().StorageClasses().Watch(options)
},
}
controller.classReflector = cache.NewReflector(
controller.classSource,
&storage.StorageClass{},
controller.classes,
controller.resyncPeriod,
)
上面启动服务主要是watch k8s对存储管理的各种事件,只要是pvc、pv和storageclass的事件的监听。
先看如果pvc创建了改如何处理
func (ctrl *ProvisionController) addClaim(obj interface{}) {
claim, ok := obj.(*v1.PersistentVolumeClaim)
if !ok {
glog.Errorf("Expected PersistentVolumeClaim but addClaim received %+v", obj)
return
}
if ctrl.shouldProvision(claim) {
ctrl.leaderElectorsMutex.Lock()
le, ok := ctrl.leaderElectors[claim.UID]
ctrl.leaderElectorsMutex.Unlock()
if ok && le.IsLeader() {
opName := fmt.Sprintf("provision-%s[%s]", claimToClaimKey(claim), string(claim.UID))
ctrl.scheduleOperation(opName, func() error {
err := ctrl.provisionClaimOperation(claim)
ctrl.updateProvisionStats(claim, err)
return err
})
} else {
opName := fmt.Sprintf("lock-provision-%s[%s]", claimToClaimKey(claim), string(claim.UID))
ctrl.scheduleOperation(opName, func() error {
ctrl.lockProvisionClaimOperation(claim)
return nil
})
}
}
}
关于选主的方法在此先跳过,主要看看,如何执行的
err := ctrl.provisionClaimOperation(claim)
ctrl.updateProvisionStats(claim, err)
先看provisionClaimOperation这个方法,这个方法比较长,挑选核心介绍
先获取到pv的名称通过pvc+pvc的uid拼接而成
pvName := ctrl.getProvisionedVolumeNameForClaim(claim)
获取pv是否存在,存在则跳过
volume, err := ctrl.client.Core().PersistentVolumes().Get(pvName, metav1.GetOptions{})
如果不存在则执行Provision,创建pv对象
volume, err = ctrl.provisioner.Provision(options)
调用k8s创建pv
ctrl.client.Core().PersistentVolumes().Create(volume)
核心是上面的Provision,它是通过pvc创建pv的入口。
它是一个接口
type Provisioner interface {
//创建pv对象
Provision(VolumeOptions) (*v1.PersistentVolume, error)
// 删除pv
Delete(*v1.PersistentVolume) error
}
这个就是让第三方去实现的接口,去创建自己的pv对象。这样存储就创建好了。
如果是pv被删除呢?当然按照一开始listwatch方式也是监听到这个消息,会执行
func (ctrl *ProvisionController) updateVolume(oldObj, newObj interface{}) {
volume, ok := newObj.(*v1.PersistentVolume)
if !ok {
glog.Errorf("Expected PersistentVolume but handler received %#v", newObj)
return
}
if ctrl.shouldDelete(volume) {
opName := fmt.Sprintf("delete-%s[%s]", volume.Name, string(volume.UID))
ctrl.scheduleOperation(opName, func() error {
err := ctrl.deleteVolumeOperation(volume)
ctrl.updateDeleteStats(volume, err)
return err
})
}
}
deleteVolumeOperation这个方法原理和之前的一样会调用删除接口
err = ctrl.provisioner.Delete(volume)
释放存储。