kubernetes crd 开发入门指南

文档意义

  入门级基础文档,帮助未入门的同学轻松入门, 初识crd的作用,了解kubebuilder 开发流程以及编写controller的语法。
此文档更多注重于引导在开发中使用的方法和在开发中避免基础的问题,讲解概念以及问题更多从个人理解的角度来进行讲解,避免官方化的对白。

文档内容概括

  • 文档意义
  • 文档内容概括
  • 开发前介绍
  • kubebuilder 安装
  • 初始kubebuilder项目创建
  • 定制自己的crd项目
  • 调试运行crd
  • 正式使用crd
  • 遇到过问题
  • 参考文档

开发前介绍

概念介绍
  • kubernetes
    kubernetes是当前热门的容器编排系统,可以实现自动化部署程序,自动化负载,自动化调度等功能。简单的来说,就是现在我们要运行一个应用,比如tomcat web应用,我们只需要告诉 kubernetes 我们想要一个tomcat,内容在tomcat1的镜像里面, 我们想要的端口是8080,然后 kubernetes 会自动创建一个tomcat web应用,然后给我们一个访问地址(ip:port),我们直接使用访问地址来连接tomcat web应用,而不用自己在tomcat服务器手动上传war包,手动配置。详情请查看kubernetes 中文文档

  • crd
    crd 全称”CustomResourceDefinition“,意思为自定义kubernetes资源,用来满足使用特定功能的需求。用自己的话来说,就是现有的kubernetes没办法满足我的场景,所以自己给自己开发个kubernetes资源来使用。例如本文中举的crd例子 -- 创建一个outLoadBalancer 资源,可以将集群外的内网服务映射到集群中,比如我在自己电脑上启动了一个mysql web工具,但是在公司外的人无法连接进来,所以需要做一个公网地址的转换,我们创建一个outLoadBalancer资源,让kubernetes集群帮我们创建这样一个地址转换。详情参考crd官方文档

  • kubebuilder
    kubebuilder 能帮助我们快速开发一个crd资源,让我们注重逻辑的开发。详情查看kubebuilder 快速开始

  • golang
    golang 是一门高级编程语言,kubernetes 编排软件就是由golang开发的。同学们想学习的话可以免费观看李文周老师的golang 教学视频

  • make介绍
    kubebuilder 调试部署等都会用到make工具, make是linux中用来构建编译软件的工具,我们主要关注Makefile文件的内容

开发环境介绍
本地有kubernetes集群,集群外部开发可以查看kubebuilder官网设置
编辑器选择vscode, 选择理由: 可以安装golang模块,可以查看源代码,免费,使用简单。安装方法可以查看下方连接

golang 教学视频

kubebuilder 安装(linux)

1. 查看本地是否已经正常安装kubebuilder, kustomize
    whereis kubebuilder # 如果没有具体的目录文件,则表示没有安装,如果有,则需要查看是否kubebuilder结构和下方所示一致
    kubebuilder
    └── bin
      ├── etcd
      ├── kube-apiserver
      ├── kubebuilder
      └── kubectl
2. 如果不正确,则需要先卸载旧的kubebuilder
    rm -r /usr/local/kubebuilder # 这里一定要注意是否是正确的路径
3. 如果没有安装,则安装kubebuilder
    os=$(go env GOOS)
    arch=$(go env GOARCH)
    curl -L https://go.kubebuilder.io/dl/2.3.1/${os}/${arch} | tar -xz -C /tmp/
    sudo mv /tmp/kubebuilder_2.3.1_${os}_${arch} /usr/local/kubebuilder
    export PATH=$PATH:/usr/local/kubebuilder/bin
4. 检查是否安装kustomize
    whereis kustomize 
5. 如果没有安装, 则手动安装
    wget https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv3.8.6/kustomize_v3.8.6_linux_amd64.tar.gz-P /tmp/
    tar xvf /tmp/kustomize_v3.8.6_linux_amd64.tar.gz -C /usr/local/kubebuilder/bin

kubebuilder 项目创建

mkdir -p /home/ly/KubernetesCrdSimple/outLoadBalancer (GOPATH为/home/ly/go)
cd /home/ly/KubernetesCrdSimple/outLoadBalancer
go mod init longyi.com # 创建go.mod
kubebuilder init --domain longyi.com # 初始化kubebuilder项目
kubebuilder create api --namespaced=false --group longyi --version v1 --kind OutLoadBalancer # 创建需要的api,包含role等
# 测试项目
make install
make run
# 新开一个终端
new <>: cd /home/ly/KubernetesCrdSimple/outLoadBalancer
new <>: kubectl apply -f config/samples/
# 原来的终端可以看到资源的注册则表示项目初始化已经成功
new <>: kubectl delete -f config/samples/
# 原来的终端
# Ctrl + C 停止make run的运行
# 卸载刚刚创建的crd
make uninstall
# make 支持的命令可以在Makefile中查看
cat ./Makefile
# 格式如下
    # Install CRDs into a cluster
    install: manifests
        kustomize build config/crd | kubectl apply -f - # make install 执行的命令

定制自己的crd项目

现在的项目结构
    ├── api
    │   └── v1 
          ├── groupversion_info.go
          ├── outloadbalancer_types.go # 这里定义我们crd的字段,比如我们需要集群外机器的ip,端口等都在这面定义
          └── zz_generated.deepcopy.go
    ├── bin
    │   └── manager
    ├── config
    │   ├── certmanager
    │   ├── crd
    │   ├── default
    │   ├── manager
    │   ├── prometheus
    │   ├── rbac
    │   ├── samples
    │   └── webhook
    ├── controllers
    │   ├── outloadbalancer_controller.go # 这里主要是写我们需要实现的逻辑,例如创建外网的地址转换逻辑.
    │   └── suite_test.go
    ├── cover.out
    ├── Dockerfile
    ├── go.mod
    ├── go.sum
    ├── hack
    │   └── boilerplate.go.txt
    ├── main.go
    ├── Makefile
    └── PROJECT
outloadbalancer_types.go 自定义字段
// OutLoadBalancerSpec defines the desired state of OutLoadBalancer
// 定义Spec的结构体,这里是我们最终会用到的变量,定义时需要满足json序列化
// 即 变量名 变量类型 `json:"json中显示的变量名,也是我们在kubernetes中要使用的变量名"`
type OutLoadBalancerSpec struct { 
        // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
        // Important: Run "make" to regenerate code after modifying this file

        // Foo is an example field of OutLoadBalancer. Edit OutLoadBalancer_types.go to remove/update
        OutHost  string `json:"outHost"` // 定义ingress中的host字段
        OutPort  int32  `json:"outPort"` // 定义集群外的端口
        OutIP    string `json:"outIP"` // 定义集群外的ip
        LoadName string `json:"loadName"` // 定义此映射的名字
}
outloadbalancer_controller.go 自定义逻辑
// 新导入package,后续创建endpoint svc ingress 会用到
import (
        corev1 "k8s.io/api/core/v1"
        "k8s.io/api/extensions/v1beta1"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        "k8s.io/apimachinery/pkg/util/intstr"
)
// Reconcile controller 这里定义我们要实现的逻辑,参看[https://book.kubebuilder.io/cronjob-tutorial/controller-implementation.html]
func (r *OutLoadBalancerReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
        ctx := context.Background() // 获取上下文
        log := r.Log.WithValues("outloadbalancer", req.NamespacedName) // 获取日志模块

        // your logic 这里写我们的逻辑
        load := &longyiv1.OutLoadBalancer{} // 这里是我们上一步定义的crd 结构体
        if err := r.Get(ctx, req.NamespacedName, load); err != nil { // 获取值注入到我们的load中,之后可以直接通过load来进行获取
                log.Info("unable to fetch load: %v", err)
                return ctrl.Result{}, nil
        }
        // 如果正常获取到数据,则创建我们的网络地址转换
        // 这里有 3 个技巧
        // 1. 可以 ctrl+左键 查看我们需要创建的结构体内容,根据内容填充即可
        // 2. 创建的时候,先创建结构体变量,比如:
        // loadEndpoing :=  corev1.Endpoints{ 
        // } // 鼠标放到左侧,出现黄色小灯的时候,点击小灯可以自动填充结构体内容
        // 3. 如果不知道具体类型,可以查看kubernetes中的集群来进行筛选,例如我们不清楚ingress在哪一个package中
        // 在kubernetes中查看任意一个ingress,获取到apiversion,形如 apiVersion: extensions/v1beta1
        // 由此得知,ingress在extensions/v1beta1中,在vscode中键入v1beta1,
        // 在编辑器提示的import选项中选择extensions/v1beta1即可导入正确的package
        var labels map[string]string // 定义 labels 标签
        labels = make(map[string]string, 1) // 申请地址
        labels["outLoad"] = "true" // 赋值

        var annotations map[string]string // 同label一样,定义annotation注解
        annotations = make(map[string]string, 1)
        annotations["outLoad"] = "true"

        loadMetadata := metav1.ObjectMeta{ // 定义Metadata中的名称
                Name:        load.Spec.LoadName,
                Namespace:   req.Namespace,
                Labels:      labels,
                Annotations: annotations,
        }
        // create Sevcice
        loadServiceSpec := corev1.ServiceSpec{ // 定义service中的Spec字段
                // ServicePort 中的TargetPort 是IntOrString类型, 即从int或者string中解析出值,我们定义了outPort 为int32, 所以这里直接写 IntVal即可,
              // IntOrString 参考 https://docs.lvrui.io/2019/03/06/go%E8%AF%AD%E8%A8%80%E5%B7%A7%E7%94%A8intstr%E6%9D%A5%E5%A4%84%E7%90%86%E6%95%B0%E5%AD%97%E4%B8%8E%E5%AD%97%E7%AC%A6%E7%B1%BB%E5%9E%8B/
                Ports:                    []corev1.ServicePort{{Name: "http", Protocol: corev1.ProtocolTCP, Port: 80, TargetPort: intstr.IntOrString{IntVal: load.Spec.OutPort}, NodePort: 0}},
                Selector:                 nil, // 由于是外部地址,不需要selector,设置为nil
                ClusterIP:                "",
                Type:                     corev1.ServiceTypeClusterIP,
                ExternalIPs:              []string{},
                SessionAffinity:          "",
                LoadBalancerIP:           "",
                LoadBalancerSourceRanges: []string{},
                ExternalName:             "",
                ExternalTrafficPolicy:    "",
                HealthCheckNodePort:      0,
                PublishNotReadyAddresses: false,
                SessionAffinityConfig:    &corev1.SessionAffinityConfig{},
        }

        loadService := corev1.Service{ // 将metadata和spec注入到service中
                Spec:       loadServiceSpec,
                ObjectMeta: loadMetadata,
        }
        r.Create(ctx, &loadService) // 调用Create创建service

        // 使用相同方法创建 Endpoint Ingress
        // 代码查看 github仓库
        // https://github.com/ajax-2/KubernetesCrdSimple
       
        return ctrl.Result{}, nil
}
Dockerifle 定义控制器镜像

由于原生的镜像在国内拉不下来,我们需要改动Dockerfile

# Build the manager binary
FROM golang:1.13.0 as builder # 我开发的go版本是golang:1.13.0, 所以改成 golang:1.13.0

WORKDIR /workspace
# Copy the Go Modules manifests
COPY go.mod go.mod
COPY go.sum go.sum
# cache deps before building and copying source so that we don't need to re-download as much
# and so that source changes don't invalidate our downloaded layer
RUN export GOPROXY="https://goproxy.cn" && go mod download # 增加GOPROXY的国内配置,不然会导致 go mod download失败

# Copy the go source
COPY main.go main.go
COPY api/ api/
COPY controllers/ controllers/

# Build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -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 bubaoxiaoyu/distroless-static:v1 # 更改static镜像, 原生的gcr.io 下载不下来
WORKDIR /
COPY --from=builder /workspace/manager .
USER nonroot:nonroot

ENTRYPOINT ["/manager"]
rbac role
由于我们的需要跨namespace, 所以改动role.yaml, 为了快速调试,直接给最大权限
cat > config/rbac/role.yaml < EOF 
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  creationTimestamp: null
  name: manager-role
rules:
- apiGroups:
  - '*'
  resources:
  - '*'
  verbs:
  - '*'
- nonResourceURLs:
  - '*'
  verbs:
  - '*'
EOF
controller auth-proxy
controller中的auth-proxy 默认是gcr.io的,我们需要改动,查看Makefile
    deploy: manifests
          cd config/manager && kustomize edit set image controller=${IMG}
          kustomize build config/default | kubectl apply -f -
可知我们需要改动的文件在 config/default中
改动 config/default/manager_auth_proxy_patch.yaml 中的image:
image: bubaoxiaoyu/kube-rbac-proxy:v0.5.0
调试部署

make run # 进行本地调试,调试通过后,执行下列步骤进行部署
make docker-build docker-push IMG=bubaoxiaoyu/outloadcrd:v1 # 进行controller镜像构建, IMG换成自己的hub仓库
make deploy IMG=bubaoxiaoyu/outloadcrd:v1 # 构建到本地集群
至此已经完成了crd的开发

遇到的问题

1. 构建的时候提示无法序列化
    定义struct 字段的时候,需要实现json序列化,  Demo String `json:"demo"` // 这里是反引号,不是单引号
2. controller没有创建相应的资源
    rbac role权限不正确,添加正确的权限,可暂时使用cluster-admin 快速查看crd开发结果。
3. 不知道go-client的结构,直接文档少
    借用vscode, 直接查看结构定义
4. kube-rabc-proxy 提示image pull 失败
    更改镜像为 bubaoxiaoyu/kube-rbac-proxy:v0.5.0
5. kubebuilder提示没有etcd等命令
    按照前面的步骤重新安装kubebuilder

总结

1. kubebuilder kustomize 需要安装正确
2. crd Strunct 需要序列化
3. Controller 开发使用client-go来进行开发
4. role 权限需要设置对
5. kube-rbac-porxy 镜像需要更改
6. Dockerfile需要更改
总结: 此文档更过注重于方法以及开发心得的分享,如果有不恰当的地方,各位同学多多包涵。
任何一门知识入门并不难,我们知识缺少好的文档,我愿意分享自己的学习历程,帮助同学们快速入门。
希望为入门的同学们能尽快找到进门的方法,祝好!

参考文档

kubernetes 中文文档
crd 官方文档
kubebuilder 快速开始
IntOrString用法参考
golang 教学视频
kubernetes-simple-controller
kubernetes-client-go-example
kubernetes api
github 代码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • 扩展kubernetes两个最常用最需要掌握的东西:自定义资源CRD 和 adminsion webhook, 本...
    sealyun阅读 703评论 0 0
  • 简介 kubernetes简称k8s。是用于自动部署,扩展和管理容器化应用程序的开源系统。中文官网:https:/...
    Y了个J阅读 1,375评论 0 0
  • 扩展kubernetes两个最常用最需要掌握的东西:自定义资源CRD 和 adminsion webhook, 本...
    sealyun阅读 643评论 0 1
  • 渐变的面目拼图要我怎么拼? 我是疲乏了还是投降了? 不是不允许自己坠落, 我没有滴水不进的保护膜。 就是害怕变得面...
    闷热当乘凉阅读 4,233评论 0 13
  • 夜莺2517阅读 127,711评论 1 9