diff --git a/kubenets/operator/01 简介.md b/kubenets/operator/01 简介.md index e10b4b27..9e5c30b6 100644 --- a/kubenets/operator/01 简介.md +++ b/kubenets/operator/01 简介.md @@ -18,7 +18,7 @@ CRD+Controller=decalartive API,声明式 API 设计是 kubernetes 重要的设 ![](https://img-blog.csdnimg.cn/img_convert/2a84e949bd4662fc61943f27ee67f0bc.png) - +![](image/2023-08-17-21-25-59.png) ## 开发方案 diff --git a/kubenets/operator/03 Kubebuilder.md b/kubenets/operator/03 Kubebuilder.md index aa4884cd..4c4a913b 100644 --- a/kubenets/operator/03 Kubebuilder.md +++ b/kubenets/operator/03 Kubebuilder.md @@ -33,7 +33,7 @@ go mod init contollers.happyhacker.io 2. 定义crd所属的domain,生成一个工程.定义 crd 所属的 domain,这个指令会帮助你生成一个工程。 ``` -kubebuilder init --domain controller.daocloud.io --license apache2 --owner "Holder" +kubebuilder init --domain estom.com --license apache2 --owner "Estom" ``` 创建后的目录结构如下: diff --git a/kubenets/operator/04 处理逻辑.md b/kubenets/operator/04 原理.md similarity index 99% rename from kubenets/operator/04 处理逻辑.md rename to kubenets/operator/04 原理.md index 300a68d5..f85eff86 100644 --- a/kubenets/operator/04 处理逻辑.md +++ b/kubenets/operator/04 原理.md @@ -331,3 +331,7 @@ kubectl get cronjobs.v1.batch.tutorial.kubebuilder.io -o yaml 显然,get命令得到的v1和v2版本的cronjob会存在一些字段上的不同,conversion webhook会负责进行不同版本的cronjob之间的数据转换。 +## 原理解读 + +### manager + diff --git a/kubenets/operator/05 client-go.md b/kubenets/operator/05 client-go.md new file mode 100644 index 00000000..3d214686 --- /dev/null +++ b/kubenets/operator/05 client-go.md @@ -0,0 +1,241 @@ +# Client-go + +## 简介 + +client-go是对K8s集群的二次开发工具,所以client-go是k8s开发者的必备工具之一。client-go实现对kubernetes集群中资源对象(包括deployment、service、ingress、replicaSet、pod、namespace、node等)的增删改查等操作。 + + +client-go主要提供了四种类型的客户端: +1. RESTClient: 是对HTTP Request进行了封装,实现了RESTful风格的API。其他客户端都是在RESTClient基础上的实现。可与用于k8s内置资源和CRD资源 + +2. ClientSet:是对k8s内置资源对象的客户端的集合,默认情况下,不能操作CRD资源,但是通过client-gen代码生成的话,也是可以操作CRD资源的。 + +3. DynamicClient:不仅能对K8S内置资源进行处理,还可以对CRD资源进行处理,不需要client-gen生成代码即可实现。 + +4. DiscoveryClient:用于发现kube-apiserver所支持的资源组、资源版本、资源信息(即Group、Version、Resources) + + +### RestClient + +RESTClient是对HTTP Request 进行了封装,是实现了RESTful风格的API封装。 + +```go +package main + +import ( + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes/scheme" +) + +func main() { + //RESTCLIENT + //config + config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile) + if err != nil { + panic(err) + } + config.GroupVersion = &v1.SchemeGroupVersion + config.NegotiatedSerializer = scheme.Codecs + config.APIPath = "/api" + + //client + restClient, err := rest.RESTClientFor(config) + if err != nil { + panic(err) + } + + //get data + pod := v1.Pod{} + err = restClient.Get().Namespace("default").Resource("pods").Name("test").Do(context.TODO()).Into(&pod) + + if err != nil { + println(err) + }else { + println(pod.Name) + } +} +``` + + +### clientSet + +ClientSet客户端默认是对k8s内置资源对象客户端的集合,通过ClientSet客户端可以操作k8s的内置资源对象。 + +```go +package main + +import ( + "context" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + +) + +func main() { + //clientset + config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile) + if err != nil { + panic(err) + } + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + panic(err) + } + + pod, err := clientset.CoreV1().Pods("default").Get(context.TODO(), "test", v1.GetOptions{}) + if err != nil { + println(err) + }else { + println(pod.Name) + } + +} +``` + + +### DynamicClient + +DynamicClient的特点就是除了可以使用k8s内置资源外,还可以使用CRD资源。dynamicClient的原理就是传入的资源数据都是使用map[string]interface{}结构。dynamicClient内部还是restClient. + +```go +package main + +import ( + "context" + "fmt" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/tools/clientcmd" +) + + +func getDynamicClientExample(kubeconfig string) { + config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + panic(err) + } + client, err := dynamic.NewForConfig(config) + if err != nil { + panic(err) + } + + + deployment := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "demo-deployment", + }, + "spec": map[string]interface{}{ + "replicas": 2, + "selector": map[string]interface{}{ + "matchLabels": map[string]interface{}{ + "app": "demo", + }, + }, + "template": map[string]interface{}{ + "metadata": map[string]interface{}{ + "labels": map[string]interface{}{ + "app": "demo", + }, + }, + + "spec": map[string]interface{}{ + "containers": []map[string]interface{}{ + { + "name": "web", + "image": "nginx:1.12", + "ports": []map[string]interface{}{ + { + "name": "http", + "protocol": "TCP", + "containerPort": 80, + }, + }, + }, + }, + }, + }, + }, + }, + } + deploymentRes := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"} + ctx := context.Background() + // Create Deployment + fmt.Println("Creating deployment...") + result, err := client.Resource(deploymentRes).Namespace("default").Create(ctx,deployment, v1.CreateOptions{}) + if err != nil { + panic(err) + } + fmt.Printf("Created deployment %q.\n", result.GetName()) +} +``` + +### DiscoveryClient + +DiscoveryClient是发现客户端,主要用于发现k8s api-server所支持的资源组、资源版本及资源信息。 + + + +```go +package main + +import ( + "fmt" + "k8s.io/client-go/discovery" + "k8s.io/client-go/tools/clientcmd" +) + +func getDisCoveryClient(kubeconfig string) { + config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + panic(err) + } + discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) + if err != nil { + panic(err) + } + _, APIResourceList, err := discoveryClient.ServerGroupsAndResources() + if err != nil { + panic(err) + } + for _, list := range APIResourceList { + fmt.Println(list) + } +} +``` + +## 2 Informer机制 + +informer在kubernetes系统中,保证各个组件之间消息的实时性、可靠性、顺序性等。k8s各组件通过client-go的informer机制与k8s apiserver通信。 + +Informer:controller机制的基础,循环处理object对象,从Reflector取出数据,然后将数据给到Indexer去缓存,提供对象事件的handler接口 + +Informer机制中的ListAndWatch、DeltaFIFO队列和Indexer等对于实现以上特性非常重要。下面这张图就是Informer机制运行原理图: + +![](image/2023-08-17-21-36-33.png) + +起点是自定义的Controller,Controller会通过ListAndWatch机制从apiserver获取感兴趣的资源对象信息(初始化controller时,会指定感兴趣资源对象的List和watch方法)。controller的内部包含了reflector和DeltaFIFO,controller就是通过reflector中的list和watch将资源对象信息装入DeltaFIFO队列,然后Controller会一直尝试从队列中pop数据,并根据数据中对象的操作类型作事件通知给各Listener并将资源对象存入本地存储Indexer中。 + +### 1、Reflector +用于监控(Watch)指定的kubernetes资源,当监控的资源发生变化时,触发相应的变更事件,如add、update、delete等,并将其资源对象存入本地缓存DeltaFIFO中,然后Informer会从队列里面取数据。 + +https://blog.csdn.net/u013276277/article/details/108592288 + +### 2、DeltaFIFO队列 +DeltaFIFO可以分开理解,FIFO是一个先进先出的队列,它拥有队列操作的基本方法,例如Add、Update、Delete、List、Pop、Close等,而Delta是一个资源对象存储,它可以保存资源对象的操作类型,例如Added(添加)操作类型、Updated(更新)操作类型、Deleted(删除)操作类型、Sync(同步)操作类型等 + +https://blog.csdn.net/u013276277/article/details/108653733 + +### 3、Indexer +是client-go用来存储资源对象冰紫带索引功能的本地存储,Refelctor从DeltaFIFO消费出来的资源对象存储至indexer。indexer与etcd集群保持一致。client-go可以很方便的从本地存储中读取响应的资源对象数据,而无需每次从etcd读取,以减轻kubernetes apiserver对etcd的压力。 + +https://blog.csdn.net/u013276277/article/details/108657739 + +https://mp.weixin.qq.com/s/xCa6yZTk0X76IZhOx6IbHQ \ No newline at end of file diff --git a/kubenets/operator/06 client-go创建资源.md b/kubenets/operator/06 client-go创建资源.md new file mode 100644 index 00000000..a078628f --- /dev/null +++ b/kubenets/operator/06 client-go创建资源.md @@ -0,0 +1,138 @@ + + + +### 创建Ingress方法 + +```go +package jan + +import ( + janv1 "develop-operator/apis/jan/v1" + appv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func NewJan(app *janv1.Jan) *appv1.Deployment { + labels := map[string]string{"app": app.Name} + selector := &metav1.LabelSelector{MatchLabels: labels} + return &appv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + Kind: "apps/v1", + APIVersion: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: app.Name, + Namespace: app.Namespace, + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(app, schema.GroupVersionKind{ + Group: janv1.GroupVersion.Group, + Version: janv1.GroupVersion.Version, + Kind: "Jan", + }), + }, + }, + Spec: appv1.DeploymentSpec{ + Replicas: app.Spec.Replicas, + Selector: selector, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{Labels: labels}, + Spec: corev1.PodSpec{Containers: newContainers(app)}, + }, + MinReadySeconds: 0, + }, + Status: appv1.DeploymentStatus{}, + } + +} + +func newContainers(app *janv1.Jan) []corev1.Container { + containerPorts := []corev1.ContainerPort{} + for _, svcPort := range app.Spec.Ports { + cport := corev1.ContainerPort{} + cport.ContainerPort = svcPort.TargetPort.IntVal + containerPorts = append(containerPorts, cport) + } + return []corev1.Container{ + { + Name: app.Name, + Image: app.Spec.Image, + Resources: app.Spec.Resources, + Ports: containerPorts, + ImagePullPolicy: corev1.PullIfNotPresent, + Env: app.Spec.Envs, + }, + } +} + +func NewService(app *janv1.Jan) *corev1.Service { + return &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: app.Name, + Namespace: app.Namespace, + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(app, schema.GroupVersionKind{ + Group: janv1.GroupVersion.Group, + Version: janv1.GroupVersion.Version, + Kind: "Jan", + }), + }, + }, + Spec: corev1.ServiceSpec{ + Type: app.Spec.Type, + Ports: app.Spec.Ports, + Selector: map[string]string{ + "app": app.Name, + }, + }, + } +} + +const ( + port = 80 +) + +func NewIngress(app *janv1.Jan) *v1.Ingress { + pathType := v1.PathTypePrefix + return &v1.Ingress{ + TypeMeta: metav1.TypeMeta{ + Kind: "Ingress", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: app.Name, + Namespace: app.Namespace, + }, + Spec: v1.IngressSpec{ + IngressClassName: nil, + Rules: []v1.IngressRule{ + { + Host: app.Spec.Host, + IngressRuleValue: v1.IngressRuleValue{ + HTTP: &v1.HTTPIngressRuleValue{ + Paths: []v1.HTTPIngressPath{{ + Path: "/", + PathType: &pathType, + Backend: v1.IngressBackend{ + Service: &v1.IngressServiceBackend{ + Name: app.Name, + Port: v1.ServiceBackendPort{ + Number: int32(port), + }, + }, + Resource: nil, + }, + }, + }}}, + }, + }, + }, + } +} +``` \ No newline at end of file diff --git a/kubenets/operator/image/2023-08-17-21-25-59.png b/kubenets/operator/image/2023-08-17-21-25-59.png new file mode 100644 index 00000000..abfb2d74 Binary files /dev/null and b/kubenets/operator/image/2023-08-17-21-25-59.png differ diff --git a/kubenets/operator/image/2023-08-17-21-36-33.png b/kubenets/operator/image/2023-08-17-21-36-33.png new file mode 100644 index 00000000..b01985ba Binary files /dev/null and b/kubenets/operator/image/2023-08-17-21-36-33.png differ diff --git a/zbin/multi_sync.sh b/zbin/multi_sync.sh deleted file mode 100644 index 9740b37e..00000000 --- a/zbin/multi_sync.sh +++ /dev/null @@ -1,11 +0,0 @@ -git remote add gitee http -git remote add github dsrcosole - - -git pull gitee master - -git pull github master - - -git push gitee -git push github \ No newline at end of file diff --git a/zshell/multi_sync.bat b/zshell/multi_sync.bat new file mode 100644 index 00000000..27620f34 --- /dev/null +++ b/zshell/multi_sync.bat @@ -0,0 +1,19 @@ +git remote remove gitee_notes +git remote remove github_notes +git remote remove coding_notes + +git remote add gitee_notes git@gitee.com:Eyestorm/notes.git +git remote add github_notes git@github.com:Estom/notes.git +git remote add coding_notes git@e.coding.net:cloudengine/notes/notes.git + + +git pull gitee_notes master +git pull github_notes master +git pull coding_notes master + +git add * +git commit -m $date + +git push gitee_notes +git push github_notes +git push coding_notes \ No newline at end of file diff --git a/zshell/multi_sync.sh b/zshell/multi_sync.sh new file mode 100644 index 00000000..f89b1c40 --- /dev/null +++ b/zshell/multi_sync.sh @@ -0,0 +1,17 @@ +git remote remove gitee_notes +git remote remove github_notes +git remote remove coding_notes + +git remote add gitee_notes git@gitee.com:Eyestorm/notes.git +git remote add github_notes git@github.com:Estom/notes.gitgit@github.com:Estom/notes.git +git remote add coding_notes git@e.coding.net:cloudengine/notes/notes.git + + +git pull gitee_notes master +git pull github_notes master +git pull coding_notes master + + +git push gitee_notes +git push github_notes +git push coding_notes \ No newline at end of file diff --git a/zbin/start.sh b/zshell/start.sh similarity index 100% rename from zbin/start.sh rename to zshell/start.sh