一、前言
Kubernetes平台对于分布式服务部署的很多重要的模块都有系统性的支持,借助如下一些平台资源可以满足大多数分布式系统部署和管理的需求:
但是在不同应用业务环境下,对于平台可能有一些特殊的需求,这些需求可以抽象为Kubernetes的扩展资源,而Kubernetes的CRD(CustomResourceDefinition)为这样的需求提供了轻量级的机制,保证新的资源的快速注册和使用。在更老的版本中,TPR(ThirdPartyResource)是与CRD类似的概念,但是在1.9以上的版本中被弃用,而CRD则进入的beta状态。
转载自https://blog.csdn.net/cloudvtech
二、CRD的基本概念
CRD的工作步骤如下:
用户向Kubernetes API服务注册一个带特定schema的资源,并定义相关API
从基本原理上来讲,CRD定义的Kubernetes扩展资源:
在实现自定义controller的时候,蓝色的部分是client-go中经为用户提供的框架和逻辑,可以直接使用,红色的部分就是用户需要实现的关于该扩展资源的业务逻辑。informer会借助APIServer跟踪该扩展资源定义的变化,一旦被触发就会调用回调函数,并把变更的具体内容放到Workqueue中,自定义controller里面的worker会获取Workqueue里面内容,并进行相应的业务处理。
转载自https://blog.csdn.net/cloudvtech
三、CRD的配置和使用
借助kubernetes的例子可以了解一下CRD配置的方式
3.1 crd的定义
crd.yaml
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: foos.samplecontroller.k8s.io
spec:
group: samplecontroller.k8s.io
version: v1alpha1
names:
kind: Foo
plural: foos
scope: Namespaced
运行如下命令建立一个种类为Foo的CRD
kubectl create -f crd.yaml
3.2 定义这个种类的CRD的验证schema
crd-validation.yaml
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: foos.samplecontroller.k8s.io
spec:
group: samplecontroller.k8s.io
version: v1alpha1
names:
kind: Foo
plural: foos
scope: Namespaced
validation:
openAPIV3Schema:
properties:
spec:
properties:
replicas:
type: integer
minimum: 1
maximum: 10
kubectl create -f crd-validation.yaml
3.3 定义一个Foo CRD的资源实例
example-foo.yaml
apiVersion: samplecontroller.k8s.io/v1alpha1
kind: Foo
metadata:
name: example-foo
spec:
deploymentName: example-foo
replicas: 1
kubectl create -f example-foo.yaml
转载自https://blog.csdn.net/cloudvtech
四、CRD contorller的实现
这个例子主要的作用时定义一个Foo CRD,这个CRD的资源实例可以定义一个replica的值,后端controller读取这个值,并且建立包含这个数目replica POD的nginx deployment。两个主要的实现controller的文件是:
https://github.com/kubernetes/sample-controller/blob/master/main.go
https://github.com/kubernetes/sample-controller/blob/master/controller.go
4.1 初始化CDR controller
在main.go里面使用了如下package:
kubeinformers "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
实例化kubeinformer、informer等对象,运行CRD controller:
...
kubeInformerFactory := kubeinformers.NewSharedInformerFactory(kubeClient, time.Second*30)
exampleInformerFactory := informers.NewSharedInformerFactory(exampleClient, time.Second*30)
controller := NewController(kubeClient, exampleClient, kubeInformerFactory, exampleInformerFactory)
...
go kubeInformerFactory.Start(stopCh)
go exampleInformerFactory.Start(stopCh)
...
controller.Run(2, stopCh)
4.2 获取informer并注册处理函数
在controller初始化的时候获取Deployment和Foo两个资源的informer并注册处理函数:
// obtain references to shared index informers for the Deployment and Foo
// types.
deploymentInformer := kubeInformerFactory.Apps().V1().Deployments()
fooInformer := sampleInformerFactory.Samplecontroller().V1alpha1().Foos()
...
// Set up an event handler for when Foo resources change
fooInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: controller.enqueueFoo,
UpdateFunc: func(old, new interface{}) {
controller.enqueueFoo(new)
},
})
...
deploymentInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: controller.handleObject,
UpdateFunc: func(old, new interface{}) {
newDepl := new.(*appsv1.Deployment)
oldDepl := old.(*appsv1.Deployment)
if newDepl.ResourceVersion == oldDepl.ResourceVersion {
// Periodic resync will send update events for all known Deployments.
// Two different versions of the same Deployment will always have different RVs.
return
}
controller.handleObject(new)
},
DeleteFunc: controller.handleObject,
})
Foo的informer的处理函数借助enqueueFoo将状态变动事件加入到队列,deployment的Informer借助handleObject函数处理状态变动事件,可以看到最后也是将必要的事件放入队列。这里参考了controller模式的开发指导,借助这个开发模式框架,CDR的controller可以获取任何感兴趣的Kubernetes资源的事件进行相关处理;在这个例子中,CDR controller感兴趣的只有两个资源的事件,一个是Foo这个资源的实例的加入事件,另外一个是deployment的变化事件(应为这里的Foo CRD的作用实际上是新建一个Foo资源实例所定义的replica的nginx deployment),处理这个事件的handleObject要对deployment进行过滤,只将Foo资源实例对应的nginx部署过滤出来,并将对应的事件加入到队列。
4.3 队列处理
是一个队列驱动的loop:
// processNextWorkItem will read a single work item off the workqueue and
// attempt to process it, by calling the syncHandler.
func (c *Controller) processNextWorkItem() bool {
...
// Run the syncHandler, passing it the namespace/name string of the
// Foo resource to be synced.
if err := c.syncHandler(key); err != nil {
return fmt.Errorf("error syncing '%s': %s", key, err.Error())
}
...
每个事件object都要经由syncHandler进行处理,主要包括建立deployment、更新deployment等行为:
...
deployment, err = c.kubeclientset.AppsV1().Deployments(foo.Namespace).Create(newDeployment(foo))
...
deployment, err = c.kubeclientset.AppsV1().Deployments(foo.Namespace).Update(newDeployment(foo))
...
err = c.updateFooStatus(foo, deployment)
...
每次的deployment事件操作都会先产生一个新的deployment配置:
// newDeployment creates a new Deployment for a Foo resource. It also sets
// the appropriate OwnerReferences on the resource so handleObject can discover
// the Foo resource that 'owns' it.
func newDeployment(foo *samplev1alpha1.Foo) *appsv1.Deployment {
labels := map[string]string{
"app": "nginx",
"controller": foo.Name,
}
return &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: foo.Spec.DeploymentName,
Namespace: foo.Namespace,
OwnerReferences: []metav1.OwnerReference{
*metav1.NewControllerRef(foo, schema.GroupVersionKind{
Group: samplev1alpha1.SchemeGroupVersion.Group,
Version: samplev1alpha1.SchemeGroupVersion.Version,
Kind: "Foo",
}),
},
},
Spec: appsv1.DeploymentSpec{
Replicas: foo.Spec.Replicas,
Selector: &metav1.LabelSelector{
MatchLabels: labels,
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "nginx",
Image: "nginx:latest",
},
},
},
},
},
}
}
4.4 总结
所以这个example-controller可以根据Foo资源实例定义的replica,创建或者更新由controller管理的一个nginx deployment的replica。
转载自https://blog.csdn.net/cloudvtech