kubernetes CRD开发指南
扩展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变得异常简单。
安装
通过源码安装:
git clone https://github.com/kubernetes-sigs/kubebuilder
cd kubebuilder
make build
cp bin/kubebuilder $GOPATH/bin
或者下载二进制:
os=$(go env GOOS)
arch=$(go env GOARCH)
# download kubebuilder and extract it to tmp
curl -sL https://go.kubebuilder.io/dl/2.0.0-beta.0/${os}/${arch} | tar -xz -C /tmp/
# move to a long-term location and put it on your path
# (you'll need to set the KUBEBUILDER_ASSETS env var if you put it somewhere else)
sudo mv /tmp/kubebuilder_2.0.0-beta.0_${os}_${arch} /usr/local/kubebuilder
export PATH=$PATH:/usr/local/kubebuilder/bin
还需要装下kustomize 这可是个渲染yaml的神器,让helm颤抖。
go install sigs.k8s.io/kustomize/v3/cmd/kustomize
使用
注意你得先有个kubernetes集群,一步安装走你
创建CRD
kubebuilder init --domain sealyun.com --license apache2 --owner "fanux"
kubebuilder create api --group infra --version v1 --kind VirtulMachine
安装CRD并启动controller
make install # 安装CRD
make run # 启动controller
然后我们就可以看到创建的CRD了
# kubectl get crd
NAME AGE
virtulmachines.infra.sealyun.com 52m
来创建一个虚拟机:
# kubectl apply -f config/samples/
# kubectl get virtulmachines.infra.sealyun.com
NAME AGE
virtulmachine-sample 49m
看一眼yaml文件:
# cat config/samples/infra_v1_virtulmachine.yaml
apiVersion: infra.sealyun.com/v1
kind: VirtulMachine
metadata:
name: virtulmachine-sample
spec:
# Add fields here
foo: bar
这里仅仅是把yaml存到etcd里了,我们controller监听到创建事件时啥事也没干。
把controller部署到集群中
make docker-build docker-push IMG=fanux/infra-controller
make deploy
我是连的远端的kubenetes, make docker-build时test过不去,没有etcd的bin文件,所以先把test关了。
修改Makefile:
# docker-build: test
docker-build:
Dockerfile里的gcr.io/distroless/static:latest
这个镜像你也可能拉不下来,随意改改就行,我改成了golang:1.12.7
也有可能构建时有些代码拉不下来,启用一下go mod vendor 把依赖打包进去
go mod vendor
如果你本地有些代码拉不下来,可以用proxy:
export GOPROXY=https://goproxy.io
再改下Dockerfile, 注释掉download:
修改后:
# Build the manager binary
FROM golang:1.12.7 as builder
WORKDIR /go/src/github.com/fanux/sealvm
# Copy the Go Modules manifests
COPY . .
# Build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o manager main.go
# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
# FROM gcr.io/distroless/static:latest
FROM golang:1.12.7
WORKDIR /
COPY --from=builder /go/src/github.com/fanux/sealvm/manager .
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已经跑起来了:
kubectl get deploy -n sealvm-system
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
sealvm-controller-manager 1 1 1 0 3m
kubectl get svc -n sealvm-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
sealvm-controller-manager-metrics-service ClusterIP 10.98.71.199 <none> 8443/TCP 4m
开发
增加对象数据参数
看下config/samples下面的yaml文件:
apiVersion: infra.sealyun.com/v1
kind: VirtulMachine
metadata:
name: virtulmachine-sample
spec:
# Add fields here
foo: bar
这里参数里有foo:bar
, 那我们来加个虚拟CPU,内存信息:
直接api/v1/virtulmachine_types.go
即可
// VirtulMachineSpec defines the desired state of VirtulMachine
// 在这里加信息
type VirtulMachineSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
CPU string `json:"cpu"` // 这是我增加的
Memory string `json:"memory"`
}
// VirtulMachineStatus defines the observed state of VirtulMachine
// 在这里加状态信息,比如虚拟机是启动状态,停止状态啥的
type VirtulMachineStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
然后make一下:
make && make install && make run
这时再去渲染一下controller的yaml就会发现CRD中已经带上CPU和内存信息了:
kustomize build config/default
properties:
cpu:
description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
Important: Run "make" to regenerate code after modifying this file'
type: string
memory:
type: string
修改一下yaml:
apiVersion: infra.sealyun.com/v1
kind: VirtulMachine
metadata:
name: virtulmachine-sample
spec:
cpu: "1"
memory: "2G"
# kubectl apply -f config/samples
virtulmachine.infra.sealyun.com "virtulmachine-sample" configured
# kubectl get virtulmachines.infra.sealyun.com virtulmachine-sample -o yaml
apiVersion: infra.sealyun.com/v1
kind: VirtulMachine
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"infra.sealyun.com/v1","kind":"VirtulMachine","metadata":{"annotations":{},"name":"virtulmachine-sample","namespace":"default"},"spec":{"cpu":"1","memory":"2G"}}
creationTimestamp: 2019-07-26T08:47:34Z
generation: 2
name: virtulmachine-sample
namespace: default
resourceVersion: "14811698"
selfLink: /apis/infra.sealyun.com/v1/namespaces/default/virtulmachines/virtulmachine-sample
uid: 030e2b9a-af82-11e9-b63e-5254bc16e436
spec: # 新的CRD已生效
cpu: "1"
memory: 2G
Status 同理,就不再赘述了,比如我把status里加一个Create, 表示controller要去创建虚拟机了(主要一些控制层面的逻辑),创建完了把状态改成Running
Reconcile 唯一需要实现的接口
controller把轮训与事件监听都封装在这一个接口里了.你不需要关心怎么事件监听的.
获取虚拟机信息
func (r *VirtulMachineReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
ctx = context.Background()
_ = r.Log.WithValues("virtulmachine", req.NamespacedName)
vm := &v1.VirtulMachine{}
if err := r.Get(ctx, req.NamespacedName, vm); err != nil { # 获取VM信息
log.Error(err, "unable to fetch vm")
} else {
fmt.Println(vm.Spec.CPU, vm.Spec.Memory) # 打印CPU内存信息
}
return ctrl.Result{}, nil
}
make && make install && make run
这个时候去创建一个虚拟机kubectl apply -f config/samples
,日志里就会输出CPU内存了. List接口同理,我就不赘述了
r.List(ctx, &vms, client.InNamespace(req.Namespace), client.MatchingField(vmkey, req.Name))
更新状态
在status结构体中加入状态字段:
type VirtulMachineStatus struct {
Status string `json:"status"`
}
controller里去更新状态:
vm.Status.Status = "Running"
if err := r.Status().Update(ctx, vm); err != nil {
log.Error(err, "unable to update vm status")
}
如果出现:the server could not find the requested resource
这个错误,那么在CRD结构体上需要加个注释 // +kubebuilder:subresource:status
:
// +kubebuilder:subresource:status
// +kubebuilder:object:root=true
type VirtulMachine struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec VirtulMachineSpec `json:"spec,omitempty"`
Status VirtulMachineStatus `json:"status,omitempty"`
}
这样就好了
编译启动后再去apply发现状态已经变成running:
# kubectl get virtulmachines.infra.sealyun.com virtulmachine-sample -o yaml
...
status:
status: Running
删除
time.Sleep(time.Second * 10)
if err := r.Delete(ctx, vm); err != nil {
log.Error(err, "unable to delete vm ", "vm", vm)
}
10s之后我们将GET不到
删除回收器 Finalizers
如果不使用Finalizers,kubectl delete 时直接就删了etcd数据,controller再想去拿CRD时已经拿不到了:
ERRO[0029] VirtulMachine.infra.sealyun.com "virtulmachine-sample" not foundunable to fetch vm source="virtulmachine_controller.go:48"
所以在创建时我们需要给CRD加上Finalizer:
vm.ObjectMeta.Finalizers = append(vm.ObjectMeta.Finalizers, "virtulmachine.infra.sealyun.com")
然后删除时就只会给CRD打上一个删除时间戳,供我们做后续处理, 处理完了我们删除掉Finalizers:
如果 DeleteionTimestamp不存在
如果没有Finalizers
加上Finalizers,并更新CRD
要不然,说明是要被删除的
如果存在Finalizers,删除Finalizers,并更新CRD
看个完整的代码示例:
if cronJob.ObjectMeta.DeletionTimestamp.IsZero() {
if !containsString(cronJob.ObjectMeta.Finalizers, myFinalizerName) {
cronJob.ObjectMeta.Finalizers = append(cronJob.ObjectMeta.Finalizers, myFinalizerName)
if err := r.Update(context.Background(), cronJob); err != nil {
return ctrl.Result{}, err
}
}
} else {
if containsString(cronJob.ObjectMeta.Finalizers, myFinalizerName) {
if err := r.deleteExternalResources(cronJob); err != nil {
return ctrl.Result{}, err
}
cronJob.ObjectMeta.Finalizers = removeString(cronJob.ObjectMeta.Finalizers, myFinalizerName)
if err := r.Update(context.Background(), cronJob); err != nil {
return ctrl.Result{}, err
}
}
}
失败重试
假设我们A依赖B而B又后创建,那么在处理A CRD时直接返回失败即可,这样很快会重试
webhook
kuberentes有三种webhook,admission webhook, authorization webhook and CRD conversion webhook.
这里比如我们要给CRD设置一些默认值,又或者是用户创建时少填了一些参数,那么我们得禁止创建等等这些事。
使用webhook也非常的简单,只需给定义的结构体实现 Defaulter
和 Validator
接口即可.
其它接口
Reconcile结构体聚合了Client接口,所以client的所有方法都是可以直接调用,大部分是对CRD object的相关操作
type Client interface {
Reader
Writer
StatusClient
}
// Reader knows how to read and list Kubernetes objects.
type Reader interface {
// Get retrieves an obj for the given object key from the Kubernetes Cluster.
// obj must be a struct pointer so that obj can be updated with the response
// returned by the Server.
Get(ctx context.Context, key ObjectKey, obj runtime.Object) error
// List retrieves list of objects for a given namespace and list options. On a
// successful call, Items field in the list will be populated with the
// result returned from the server.
List(ctx context.Context, list runtime.Object, opts ...ListOptionFunc) error
}
// Writer knows how to create, delete, and update Kubernetes objects.
type Writer interface {
// Create saves the object obj in the Kubernetes cluster.
Create(ctx context.Context, obj runtime.Object, opts ...CreateOptionFunc) error
// Delete deletes the given obj from Kubernetes cluster.
Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error
// Update updates the given obj in the Kubernetes cluster. obj must be a
// struct pointer so that obj can be updated with the content returned by the Server.
Update(ctx context.Context, obj runtime.Object, opts ...UpdateOptionFunc) error
// Patch patches the given obj in the Kubernetes cluster. obj must be a
// struct pointer so that obj can be updated with the content returned by the Server.
Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOptionFunc) error
}
// StatusClient knows how to create a client which can update status subresource
// for kubernetes objects.
type StatusClient interface {
Status() StatusWriter
}
扫码关注sealyun
探讨可加QQ群:98488045
kubernetes CRD开发指南的更多相关文章
- kubernetes CRD 开发指南
扩展kubernetes两个最常用最需要掌握的东西:自定义资源CRD 和 adminsion webhook, 本文教你如何十分钟掌握CRD开发. kubernetes允许用户自定义自己的资源对象,就 ...
- kuberenetes CRD开发指南
扩展kubernetes两个最常用最需要掌握的东西:自定义资源CRD 和 adminsion webhook, 本文教你如何十分钟掌握CRD开发. kubernetes允许用户自定义自己的资源对象,就 ...
- 开放下载 | 《Knative 云原生应用开发指南》开启云原生时代 Serverless 之门
点击下载<Knative 云原生应用开发指南> 自 2018 年 Knative 项目开源后,就得到了广大开发者的密切关注.Knative 在 Kubernetes 之上提供了一套完整的应 ...
- ASP.NET Aries 开源开发框架:开发指南(一)
前言: 上周开源了Aries开发框架后,好多朋友都Download了源码,在运行过程里,有一些共性的问题会问到. 所以本篇打算写一下简单的开发指南,照顾一下不是太看的懂源码的同学,同时也会讲解一下框架 ...
- FreeMarker模板开发指南知识点梳理
freemarker是什么? 有什么用? 怎么用? (问得好,这些都是我想知道的问题) freemarker是什么? FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生 ...
- Jetty使用教程(四:21-22)—Jetty开发指南
二十一.嵌入式开发 21.1 Jetty嵌入式开发HelloWorld 本章节将提供一些教程,通过Jetty API快速开发嵌入式代码 21.1.1 下载Jetty的jar包 Jetty目前已经把所有 ...
- JVM 平台上的各种语言的开发指南
JVM 平台上的各种语言的开发指南 为什么我们需要如此多的JVM语言? 在2013年你可以有50中JVM语言的选择来用于你的下一个项目.尽管你可以说出一大打的名字,你会准备为你的下一个项目选择一种新的 ...
- iOS原生地图开发指南续——大头针与自定义标注
iOS原生地图开发指南续——大头针与自定义标注 出自:http://www.sxt.cn/info-6042-u-7372.html 在上一篇博客中http://my.oschina.net/u/23 ...
- Angularjs中文版本开发指南发布
从本人开始在写关于Angularjs的文章开始,也算是见证了Angularjs在国内慢慢的火起来,如今的Angularjs正式如日中天.想知道为什么Angularjs会这么火,请移步angularjs ...
随机推荐
- CentOS7使用firewalld防火墙
firewalld的基本使用 启动: systemctl start firewalld 关闭: systemctl stop firewalld 查看状态: systemctl status fir ...
- Angular中input和output使用
// 写法一: 1 @Components({ 2 ...., 3 inputs:['init'], 4 outputs:['finish'] 5 }) 6 export class xxx(){ 7 ...
- 十年后,我又开始研究SEO了
2009年的时候,我就小打小闹式地要创业了,其实就是和同学在北京阜成门开了一家特产店.但我们的想法不止在这个店,有更大的目标,目前也不便在这里展开了.反正当时我就注意到了SEO,即搜索引擎优化.通俗地 ...
- Python 爬虫从入门到进阶之路(十四)
之前的文章我们已经可以根据 re 模块,Xpath 模块和 BeautifulSoup4 模块来爬取网站上我们想要的数据并且存储在本地,但是我们并没有对存储数据的格式有要求,本章我们就来看数据的存储格 ...
- Codeforces 730I:Olympiad in Programming and Sports(最小费用流)
http://codeforces.com/problemset/problem/730/I 题意:有n个人参加两种比赛,其中每个人有两个参加比赛的属性,如果参加了其中的一个比赛,那么不能参加另一个比 ...
- 详解FIX协议的原理、消息格式及配置开发
一.定义 FIX协议是由国际FIX协会组织提供的一个开放式协议,目的是推动国际贸易电子化的进程,在各类参与者之间,包括投资经理.经纪人,买方.卖方建立起实时的电子化通讯协议.FIX协议的目标是把各类证 ...
- 线性表的顺序存储C++代码实现
关于线性表的概念,等相关描述请参看<大话数据结构>第三章的内容, 1 概念 线性表list:零个或多个数据的有限序列. 可以这么理解:糖葫芦都吃过吧,它就相当于一个线性表,每个 ...
- django基础知识之上传图片:
上传图片 当Django在处理文件上传的时候,文件数据被保存在request.FILES FILES中的每个键为<input type="file" name="& ...
- RSYNC部署
1 rsync简介 1.1 什么是rsync rsync: - a fast, versatile, remote (and local) file-copying toolrsync:是一种快速,多 ...
- 2017《java技术预备作业》
2017<java技术预备作业> 1.阅读邹欣老师的博客,谈谈你期望的师生关系是什么样的? 亦师亦友,很多人这样说,确实,倘若师生之间如果中间有些隔阂最终吃亏的始终是学生.我认为师生之间应 ...