扩展kubernetes两个最常用最需要掌握的东西:自定义资源CRD 和 adminsion webhook, 本文教你如何十分钟掌握CRD开发.

kubernetes允许用户自定义自己的资源对象,就如同deployment statefulset一样,这个应用非常广泛,比如prometheus opterator就自定义Prometheus对象,再加上一个自定义的controller监听到kubectl create Prometheus时就去创建Pod组成一个pormetheus集群。rook等等同理。

我需要用kubernetes调度虚拟机,所以这里自定义一个 VirtualMachine 类型

kubebuilder

kubebuilder能帮我们节省大量工作,让开发CRD和adminsion webhook变得异常简单。

安装

通过源码安装:

  1. git clone https://github.com/kubernetes-sigs/kubebuilder
  2. cd kubebuilder
  3. make build
  4. cp bin/kubebuilder $GOPATH/bin

或者下载二进制:

  1. os=$(go env GOOS)
  2. arch=$(go env GOARCH)
  3. # download kubebuilder and extract it to tmp
  4. curl -sL https://go.kubebuilder.io/dl/2.0.0-beta.0/${os}/${arch} | tar -xz -C /tmp/
  5. # move to a long-term location and put it on your path
  6. # (you'll need to set the KUBEBUILDER_ASSETS env var if you put it somewhere else)
  7. sudo mv /tmp/kubebuilder_2.0.0-beta.0_${os}_${arch} /usr/local/kubebuilder
  8. export PATH=$PATH:/usr/local/kubebuilder/bin

还需要装下kustomize 这可是个渲染yaml的神器,让helm颤抖。

  1. go install sigs.k8s.io/kustomize/v3/cmd/kustomize

使用

注意你得先有个kubernetes集群,一步安装走你

创建CRD

  1. kubebuilder init --domain sealyun.com --license apache2 --owner "fanux"
  2. kubebuilder create api --group infra --version v1 --kind VirtulMachine

安装CRD并启动controller

  1. make install # 安装CRD
  2. make run # 启动controller

然后我们就可以看到创建的CRD了

  1. # kubectl get crd
  2. NAME AGE
  3. virtulmachines.infra.sealyun.com 52m

来创建一个虚拟机:

  1. # kubectl apply -f config/samples/
  2. # kubectl get virtulmachines.infra.sealyun.com
  3. NAME AGE
  4. virtulmachine-sample 49m

看一眼yaml文件:

  1. # cat config/samples/infra_v1_virtulmachine.yaml
  2. apiVersion: infra.sealyun.com/v1
  3. kind: VirtulMachine
  4. metadata:
  5. name: virtulmachine-sample
  6. spec:
  7. # Add fields here
  8. foo: bar

这里仅仅是把yaml存到etcd里了,我们controller监听到创建事件时啥事也没干。

把controller部署到集群中

  1. make docker-build docker-push IMG=fanux/infra-controller
  2. make deploy

我是连的远端的kubenetes, make docker-build时test过不去,没有etcd的bin文件,所以先把test关了。

修改Makefile:

  1. # docker-build: test
  2. docker-build:

Dockerfile里的gcr.io/distroless/static:latest 这个镜像你也可能拉不下来,随意改改就行,我改成了golang:1.12.7

也有可能构建时有些代码拉不下来,启用一下go mod vendor 把依赖打包进去

  1. go mod vendor
  2. 如果你本地有些代码拉不下来,可以用proxy:

export GOPROXY=https://goproxy.io

再改下Dockerfile, 注释掉download:

修改后:

  1. # Build the manager binary
  2. FROM golang:1.12.7 as builder
  3. WORKDIR /go/src/github.com/fanux/sealvm
  4. # Copy the Go Modules manifests
  5. COPY . .
  6. # Build
  7. RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o manager main.go
  8. # Use distroless as minimal base image to package the manager binary
  9. # Refer to https://github.com/GoogleContainerTools/distroless for more details
  10. # FROM gcr.io/distroless/static:latest
  11. FROM golang:1.12.7
  12. WORKDIR /
  13. COPY --from=builder /go/src/github.com/fanux/sealvm/manager .
  14. ENTRYPOINT ["/manager"]

make deploy 时报错: Error: json: cannot unmarshal string into Go struct field Kustomization.patches of type types.Patch

config/default/kustomization.yaml 中的 patches: 改成 patchesStrategicMerge: 即可

kustomize build config/default 这个命令就渲染出了controller的yaml文件,可以体验下

看 你的controller已经跑起来了:

  1. kubectl get deploy -n sealvm-system
  2. NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
  3. sealvm-controller-manager 1 1 1 0 3m
  4. kubectl get svc -n sealvm-system
  5. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  6. sealvm-controller-manager-metrics-service ClusterIP 10.98.71.199 <none> 8443/TCP 4m

开发

增加对象数据参数

看下config/samples下面的yaml文件:

  1. apiVersion: infra.sealyun.com/v1
  2. kind: VirtulMachine
  3. metadata:
  4. name: virtulmachine-sample
  5. spec:
  6. # Add fields here
  7. foo: bar

这里参数里有foo:bar, 那我们来加个虚拟CPU,内存信息:

直接api/v1/virtulmachine_types.go即可

  1. // VirtulMachineSpec defines the desired state of VirtulMachine
  2. // 在这里加信息
  3. type VirtulMachineSpec struct {
  4. // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
  5. // Important: Run "make" to regenerate code after modifying this file
  6. CPU string `json:"cpu"` // 这是我增加的
  7. Memory string `json:"memory"`
  8. }
  9. // VirtulMachineStatus defines the observed state of VirtulMachine
  10. // 在这里加状态信息,比如虚拟机是启动状态,停止状态啥的
  11. type VirtulMachineStatus struct {
  12. // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
  13. // Important: Run "make" to regenerate code after modifying this file
  14. }

然后make一下:

  1. make && make install && make run

这时再去渲染一下controller的yaml就会发现CRD中已经带上CPU和内存信息了:

kustomize build config/default

  1. properties:
  2. cpu:
  3. description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
  4. Important: Run "make" to regenerate code after modifying this file'
  5. type: string
  6. memory:
  7. type: string

修改一下yaml:

  1. apiVersion: infra.sealyun.com/v1
  2. kind: VirtulMachine
  3. metadata:
  4. name: virtulmachine-sample
  5. spec:
  6. cpu: "1"
  7. memory: "2G"
  1. # kubectl apply -f config/samples
  2. virtulmachine.infra.sealyun.com "virtulmachine-sample" configured
  3. # kubectl get virtulmachines.infra.sealyun.com virtulmachine-sample -o yaml
  4. apiVersion: infra.sealyun.com/v1
  5. kind: VirtulMachine
  6. metadata:
  7. annotations:
  8. kubectl.kubernetes.io/last-applied-configuration: |
  9. {"apiVersion":"infra.sealyun.com/v1","kind":"VirtulMachine","metadata":{"annotations":{},"name":"virtulmachine-sample","namespace":"default"},"spec":{"cpu":"1","memory":"2G"}}
  10. creationTimestamp: 2019-07-26T08:47:34Z
  11. generation: 2
  12. name: virtulmachine-sample
  13. namespace: default
  14. resourceVersion: "14811698"
  15. selfLink: /apis/infra.sealyun.com/v1/namespaces/default/virtulmachines/virtulmachine-sample
  16. uid: 030e2b9a-af82-11e9-b63e-5254bc16e436
  17. spec: # 新的CRD已生效
  18. cpu: "1"
  19. memory: 2G

Status 同理,就不再赘述了,比如我把status里加一个Create, 表示controller要去创建虚拟机了(主要一些控制层面的逻辑),创建完了把状态改成Running

Reconcile 唯一需要实现的接口

controller把轮训与事件监听都封装在这一个接口里了.你不需要关心怎么事件监听的.

获取虚拟机信息

  1. func (r *VirtulMachineReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
  2. ctx = context.Background()
  3. _ = r.Log.WithValues("virtulmachine", req.NamespacedName)
  4. vm := &v1.VirtulMachine{}
  5. if err := r.Get(ctx, req.NamespacedName, vm); err != nil { # 获取VM信息
  6. log.Error(err, "unable to fetch vm")
  7. } else {
  8. fmt.Println(vm.Spec.CPU, vm.Spec.Memory) # 打印CPU内存信息
  9. }
  10. return ctrl.Result{}, nil
  11. }

make && make install && make run这个时候去创建一个虚拟机kubectl apply -f config/samples,日志里就会输出CPU内存了. List接口同理,我就不赘述了

  1. r.List(ctx, &vms, client.InNamespace(req.Namespace), client.MatchingField(vmkey, req.Name))

更新状态

在status结构体中加入状态字段:

  1. type VirtulMachineStatus struct {
  2. Status string `json:"status"`
  3. }

controller里去更新状态:

  1. vm.Status.Status = "Running"
  2. if err := r.Status().Update(ctx, vm); err != nil {
  3. log.Error(err, "unable to update vm status")
  4. }

如果出现:the server could not find the requested resource 这个错误,那么在CRD结构体上需要加个注释 // +kubebuilder:subresource:status

  1. // +kubebuilder:subresource:status
  2. // +kubebuilder:object:root=true
  3. type VirtulMachine struct {
  4. metav1.TypeMeta `json:",inline"`
  5. metav1.ObjectMeta `json:"metadata,omitempty"`
  6. Spec VirtulMachineSpec `json:"spec,omitempty"`
  7. Status VirtulMachineStatus `json:"status,omitempty"`
  8. }

这样就好了

编译启动后再去apply发现状态已经变成running:

  1. # kubectl get virtulmachines.infra.sealyun.com virtulmachine-sample -o yaml
  2. ...
  3. status:
  4. status: Running

删除

  1. time.Sleep(time.Second * 10)
  2. if err := r.Delete(ctx, vm); err != nil {
  3. log.Error(err, "unable to delete vm ", "vm", vm)
  4. }

10s之后我们将GET不到

删除回收器 Finalizers

如果不使用Finalizers,kubectl delete 时直接就删了etcd数据,controller再想去拿CRD时已经拿不到了:

  1. ERRO[0029] VirtulMachine.infra.sealyun.com "virtulmachine-sample" not foundunable to fetch vm source="virtulmachine_controller.go:48"

所以在创建时我们需要给CRD加上Finalizer:

  1. vm.ObjectMeta.Finalizers = append(vm.ObjectMeta.Finalizers, "virtulmachine.infra.sealyun.com")

然后删除时就只会给CRD打上一个删除时间戳,供我们做后续处理, 处理完了我们删除掉Finalizers:

  1. 如果 DeleteionTimestamp不存在
  2. 如果没有Finalizers
  3. 加上Finalizers,并更新CRD
  4. 要不然,说明是要被删除的
  5. 如果存在Finalizers,删除Finalizers,并更新CRD

看个完整的代码示例:

  1. if cronJob.ObjectMeta.DeletionTimestamp.IsZero() {
  2. if !containsString(cronJob.ObjectMeta.Finalizers, myFinalizerName) {
  3. cronJob.ObjectMeta.Finalizers = append(cronJob.ObjectMeta.Finalizers, myFinalizerName)
  4. if err := r.Update(context.Background(), cronJob); err != nil {
  5. return ctrl.Result{}, err
  6. }
  7. }
  8. } else {
  9. if containsString(cronJob.ObjectMeta.Finalizers, myFinalizerName) {
  10. if err := r.deleteExternalResources(cronJob); err != nil {
  11. return ctrl.Result{}, err
  12. }
  13. cronJob.ObjectMeta.Finalizers = removeString(cronJob.ObjectMeta.Finalizers, myFinalizerName)
  14. if err := r.Update(context.Background(), cronJob); err != nil {
  15. return ctrl.Result{}, err
  16. }
  17. }
  18. }

失败重试

假设我们A依赖B而B又后创建,那么在处理A CRD时直接返回失败即可,这样很快会重试

webhook

kuberentes有三种webhook,admission webhook, authorization webhook and CRD conversion webhook.

这里比如我们要给CRD设置一些默认值,又或者是用户创建时少填了一些参数,那么我们得禁止创建等等这些事。

使用webhook也非常的简单,只需给定义的结构体实现 DefaulterValidator接口即可.

其它接口

Reconcile结构体聚合了Client接口,所以client的所有方法都是可以直接调用,大部分是对CRD object的相关操作

  1. type Client interface {
  2. Reader
  3. Writer
  4. StatusClient
  5. }
  1. // Reader knows how to read and list Kubernetes objects.
  2. type Reader interface {
  3. // Get retrieves an obj for the given object key from the Kubernetes Cluster.
  4. // obj must be a struct pointer so that obj can be updated with the response
  5. // returned by the Server.
  6. Get(ctx context.Context, key ObjectKey, obj runtime.Object) error
  7. // List retrieves list of objects for a given namespace and list options. On a
  8. // successful call, Items field in the list will be populated with the
  9. // result returned from the server.
  10. List(ctx context.Context, list runtime.Object, opts ...ListOptionFunc) error
  11. }
  12. // Writer knows how to create, delete, and update Kubernetes objects.
  13. type Writer interface {
  14. // Create saves the object obj in the Kubernetes cluster.
  15. Create(ctx context.Context, obj runtime.Object, opts ...CreateOptionFunc) error
  16. // Delete deletes the given obj from Kubernetes cluster.
  17. Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error
  18. // Update updates the given obj in the Kubernetes cluster. obj must be a
  19. // struct pointer so that obj can be updated with the content returned by the Server.
  20. Update(ctx context.Context, obj runtime.Object, opts ...UpdateOptionFunc) error
  21. // Patch patches the given obj in the Kubernetes cluster. obj must be a
  22. // struct pointer so that obj can be updated with the content returned by the Server.
  23. Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOptionFunc) error
  24. }
  25. // StatusClient knows how to create a client which can update status subresource
  26. // for kubernetes objects.
  27. type StatusClient interface {
  28. Status() StatusWriter
  29. }

扫码关注sealyun



探讨可加QQ群:98488045

kubernetes CRD开发指南的更多相关文章

  1. kubernetes CRD 开发指南

    扩展kubernetes两个最常用最需要掌握的东西:自定义资源CRD 和 adminsion webhook, 本文教你如何十分钟掌握CRD开发. kubernetes允许用户自定义自己的资源对象,就 ...

  2. kuberenetes CRD开发指南

    扩展kubernetes两个最常用最需要掌握的东西:自定义资源CRD 和 adminsion webhook, 本文教你如何十分钟掌握CRD开发. kubernetes允许用户自定义自己的资源对象,就 ...

  3. 开放下载 | 《Knative 云原生应用开发指南》开启云原生时代 Serverless 之门

    点击下载<Knative 云原生应用开发指南> 自 2018 年 Knative 项目开源后,就得到了广大开发者的密切关注.Knative 在 Kubernetes 之上提供了一套完整的应 ...

  4. ASP.NET Aries 开源开发框架:开发指南(一)

    前言: 上周开源了Aries开发框架后,好多朋友都Download了源码,在运行过程里,有一些共性的问题会问到. 所以本篇打算写一下简单的开发指南,照顾一下不是太看的懂源码的同学,同时也会讲解一下框架 ...

  5. FreeMarker模板开发指南知识点梳理

    freemarker是什么? 有什么用? 怎么用? (问得好,这些都是我想知道的问题) freemarker是什么? FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生 ...

  6. Jetty使用教程(四:21-22)—Jetty开发指南

    二十一.嵌入式开发 21.1 Jetty嵌入式开发HelloWorld 本章节将提供一些教程,通过Jetty API快速开发嵌入式代码 21.1.1 下载Jetty的jar包 Jetty目前已经把所有 ...

  7. JVM 平台上的各种语言的开发指南

    JVM 平台上的各种语言的开发指南 为什么我们需要如此多的JVM语言? 在2013年你可以有50中JVM语言的选择来用于你的下一个项目.尽管你可以说出一大打的名字,你会准备为你的下一个项目选择一种新的 ...

  8. iOS原生地图开发指南续——大头针与自定义标注

    iOS原生地图开发指南续——大头针与自定义标注 出自:http://www.sxt.cn/info-6042-u-7372.html 在上一篇博客中http://my.oschina.net/u/23 ...

  9. Angularjs中文版本开发指南发布

    从本人开始在写关于Angularjs的文章开始,也算是见证了Angularjs在国内慢慢的火起来,如今的Angularjs正式如日中天.想知道为什么Angularjs会这么火,请移步angularjs ...

随机推荐

  1. 渗透测试工具SQLmap

    一.简介 SQLmap 是一款用 Python 编写的开源渗透测试工具,用来自动检测和利用 SQL 注入漏洞. 二.Windows 下安装 2.1 安装 Python 环境 注:Python 3.0会 ...

  2. JAVA String类型的一些小操作

    String类型是否包含某个String类型的函数:源字符串.contains(包含字符串)  返回值为:boolean类型(true或false) String类型把某个字符替换成另一个字符:源字符 ...

  3. HDU 2795:Billboard(线段树)

    http://acm.hdu.edu.cn/showproblem.php?pid=2795 Billboard Problem Description   At the entrance to th ...

  4. php中对象类型与数组之间的转换

    1.刚看视频学习的时候看到一个困扰很久的问题, 有时候我们在进行做项目的时候会碰到的一个小问题.举一个小例子.  获取一个xml文件里面的数据. xml.xml文件如下: <?xml versi ...

  5. Java 垃圾收集总结

    概述 垃圾收集(Garbage Collection,GC),它不是Java语言的伴生产物,它的历史比Java还要久远. 人们主要思考GC需要完成的3件事情: 哪些内存需要回收? 什么时候回收? 如何 ...

  6. springboot+druid连接池及监控配置

    1. 问题描述 阿里巴巴的数据库连接池Druid在效率与稳定性都很高,被很多开发团队使用,并且自带的Druid监控也很好用,本章简单介绍下springboot+druid配置连接池及监控. 2. 解决 ...

  7. CDQZ集训DAY0 日记

    貌似没发生什么事…… 按照教练员的交代,写一下流水账…… 早上5:30到了机场,然后就默默地坐着飞机到了成都.然后就按预定好的被GXY的父亲的朋友接机(貌似因为觉得GXY和他爸的同学挺像被批判一番). ...

  8. ServiceFabric极简文档-1.3删除群集

    删除群集 若要删除群集,请运行包文件夹中的 RemoveServiceFabricCluster.ps1 Powershell 脚本,并传入 JSON 配置文件的路径. 可以选择性地指定删除日志的位置 ...

  9. 个人永久性免费-Excel催化剂功能第100波-透视多行数据为多列数据结构

    在数据处理过程中,大量的非预期格式结构需要作转换,有大家熟知的多维转一维(准确来说应该是交叉表结构的数据转二维表标准数据表结构),也同样有一些需要透视操作的数据源,此篇同样提供更便捷的方法实现此类数据 ...

  10. 个人永久性免费-Excel催化剂功能第64波-多级数据如省市区联动输入,自由配置永不失效

    日常使用各大系统过程中,数据录入的规范性一般做得都很不错,本来系统的存在很大范畴就是为了数据和管理的规范性.在Excel环境中,想得到规范性的数据录入,除非是自行对数据有很深的认识,知道哪些数据是脏乱 ...