您现在的位置是:首页 >技术交流 >k8s client-go 程序实现kubernetes Controller & Operator 使用CRD 学习总结网站首页技术交流

k8s client-go 程序实现kubernetes Controller & Operator 使用CRD 学习总结

寂寞的4角钱 2023-04-23 19:16:52
简介k8s client-go 程序实现kubernetes Controller & Operator 使用CRD 学习总结

k8s client-go 程序实现kubernetes Controller & Operator 使用CRD 学习总结

大纲

  • 1 定义CRD
  • 2 client-go自动代码生成
  • 3 client-go操作CR
  • 4 创建镜像
  • 5 配置权限
  • 6 部署到k8s

基础流程

这里使用client-go实现编写,相对于kubebuiler这些工具生成脚手架工程要麻烦一些,但是可以理解完整的原理。

k8s 自定义operator 基本流程

  • 1 定义crd
  • 2 使用code-generator 生成自定义clientset
  • 3 编写代码
  • 4 配置权限
  • 5 发布到k8s集群

定义CRD

此例子中使用的CRD自定义资源定义基本和 《k8s java程序实现kubernetes Controller & Operator 使用CRD 学习总结》 文章中使用的CRD一致

CRD自定义资源定义yaml文件内容如下 (yaml/crd-liuyijiang.yaml )

# 定义自定义的 MyCrdGolangTest 资源
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # 名字必需与下面的 spec 字段匹配,并且格式为 '<名称的复数形式>.<组名>'
  name: mycrdgolangtests.liuyjiang.com

spec:   
   # 组名称,用于 REST API: /apis/<组>/<版本>
  group: liuyjiang.com
  names:
    # 名称的复数形式,用于 URL:/apis/<组>/<版本>/<名称的复数形式>
   plural: mycrdgolangtests
    # 名称的单数形式,作为命令行使用时和显示时的别名
   singular: mycrdgolangtest
    # kind 通常是单数形式的驼峰命名(CamelCased)形式。你的资源清单会使用这一形式。
   kind: MyCrdGolangTest
    # shortNames 允许你在命令行使用较短的字符串来匹配资源
   shortNames:
     - mcgt
  # 可以是 Namespaced 或 Cluster   
  scope: Namespaced       
  versions:
    - name: v1
      # 每个版本都可以通过服务标志启用/禁用。
      served: true
      # 必须将一个且只有一个版本标记为存储版本。
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:  
            #自定义CRD中的spec
            spec:
              type: object
              properties:
                # 自定义的资源spec 中的属性 mymsg
                mymsg: 
                  type: string
                # 自定义的资源spec 中的属性 myarray  
                myarray:    
                  type: array
                  items:
                    type: string 
                # 自定义的资源spec 中的属性 mynumber         
                mynumber:  
                  type: integer
              #自定义CRD中的status    
            status:
              type: object    
              properties:  
                mystatus: 
                  type: string
                myip: 
                  type: string  

资源定义完成后使用 kubectl apply -f crd-liuyijiang.yaml 在集群内部先创建好CRD

kubectl apply -f crd-liuyijiang.yaml
kubectl get crd mycrdgolangtests.liuyjiang.com

在这里插入图片描述

crd 创建成功

kubectl describe crd mycrdgolangtests.liuyjiang.com 查看crd内容

在这里插入图片描述

client-go code-generator 自动代码生成

如果不选择使用自动代码生成,可以直接使用client-go 提供的 dynamicClient实现操作CR,但是使用起来相对麻烦

例如需要使用 unstructured.Unstructured实现创建资源

在这里插入图片描述

需要使用 watch方法实现监听,无法使用Informer

在这里插入图片描述

使用 code-generator 可以生成对应的clientset, Informer, lister等编码时更方便

code-generator地址:
https://github.com/kubernetes/kubernetes/tree/master/staging/src/k8s.io/code-generator

测试时发现code-generator 生成代码需要在linux环境下才能正常运行,在window环境下生成会有如下错误 unknown escape sequence(and 3 more errors) 或者 illegal character U+005C ‘’ (and 14 more errors)

errors in package "crdtest\pkg\client\clientset\versioned":
unable to format file "..\crdtest\pkg\client\clientset\versioned\clientset.go" (23:24: unknown escape sequence (and 3 more errors)).

errors in package "crdtest\pkg\client\versioned\typed\liuyijiang.com\v1":
unable to format file "..\crdtest\pkg\client\versioned\typed\liuyijiang.com\v1\mycrdgolangtest.go" (27:9: illegal character U+005C '' (and 14 more errors)).

使用gitbash 报错

在这里插入图片描述

使用 cygwin 也报错

在这里插入图片描述

看生成的代码,是文件分割符号有问题 目前还未解决此问题!所以只有在linux环境下执行code-generator 脚本生成代码

client-go code-generator 生成代码流程

基本流程:

  • 1 需要一台配置了go环境的linux系统机器
  • 2 安装code-generator 代码生成工具
  • 3 创建一个最简单的go项目
  • 4 go项目需要使用git管理 (否则报错 fatal: not a git repository (or any of the parent directories): .git)
  • 5 编写 doc.go types.go register.go 文件用于生成代码

linux搭建go环境

golang 下载地址 https://golang.google.cn/dl/

本次测试使用golang 版本 go1.19.3.linux-amd64.tar.gz

例如在把 golang 安装到 /ops/go

在这里插入图片描述

修改环境变量 path

vi /etc/profile

把go命令和gopath 添加到环境变量中
export PATH=/ops/go/go/bin:$PATH
export GOPATH=/root/go

source /etc/profile

在这里插入图片描述

配置代理和开启go mod

go env -w GO111MODULE=on  
go env -w GOPROXY=https://goproxy.cn,direct

在这里插入图片描述

进入到/root 文件夹 目前还没有go文件夹 手动创建

mkdir -p ./go/pkg

在这里插入图片描述

此时 linux go环境搭建完成

安装 code-generator

code-generator
官方地址 https://github.com/kubernetes/code-generator

在 $GOPTAH/pkg (即/root/go/pkg)文件夹下拉取code-generator

git clone https://github.com/kubernetes/code-generator

在这里插入图片描述

进入code-generator安装代码生成需要的工具

go install ./cmd/{applyconfiguration-gen,client-gen,deepcopy-gen,informer-gen,lister-gen}

在这里插入图片描述

当执行generate-groups.sh all 时会分别调用applyconfiguration-gen,client-gen,deepcopy-gen,informer-gen,lister-gen生成工具

工具说明

  • client-gen 用于生成clientset相关代码
  • deepcopy-gen 用于实现对象deepcopy
  • informer-gen 用于生成Informer相关代码
  • lister-gen 用于生成Lister相关代码
  • applyconfiguration-gen 生成配置相关的的代码 generate-groups.sh all 时会调用

此时code-generator 代码生产需要的环境已经完成

go项目搭建

在开发环境 window机器上的创建项目

注意:项目需要使用git管理 否则使用 deepcopy-gen生成深拷贝代码时会出现以下异常

fatal: not a git repository (or any of the parent directories): .git

step1 在gitee上创建一个项目 方便等下在linux环境下 pull push 生成后的代码

例如已经在gitee上创建好一个空的项目crdtest。并在 D:giteecode 拉取项目

git clone  https://gitee.com/liuyijiang/crdtest.git
cd crdtest
go mod init crdtest

在crdtest文件夹中创建 pkg/apis/liuyijiang.com/v1 文件夹用于存放代码生成需要的模板文件

其中 liuyijiang.com文件夹和自己CRM配置文件中的组名称一致

step2 添加模板代码

需要三个模板文件 doc.go types.go register.go

doc.go 内容如下

// +k8s:deepcopy-gen=package

// +groupName=liuyjiang.com
package v1

+k8s:deepcopy-gen=package用来告诉生成器来生成我们自定义资源类型的deepcopy方法,+groupName=liuyjiang.com是指定我们的group名称

types.go 内容如下

package v1

import (
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +genclient
// +genclient:noStatus
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type MyCrdGolangTest struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`
	Spec              MyCrdGolangTestSpec   `json:"spec"`
	Status            MyCrdGolangTestStatus `json:"status"`
}

type MyCrdGolangTestSpec struct {
	Mymsg    string   `json:"mymsg"`
	Mynumber int64    `json:"mynumber"`
	Myarray  []string `json:"myarray"`
}

type MyCrdGolangTestStatus struct {
	Mystatus string `json:"mystatus"`
	Myip     string `json:"myip"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// StudentList is a list of Student resources
type MyCrdGolangTestList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata"`

	Items []MyCrdGolangTest `json:"items"`
}

types.go主要就是编写自定义CRD结构体Spec Status等

register.go 内容如下

package v1

import (
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

var SchemeGroupVersion = schema.GroupVersion{
	Group:   "liuyjiang.com",
	Version: "v1",
}

var (
	SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
	AddToScheme   = SchemeBuilder.AddToScheme
)

func Resource(resource string) schema.GroupResource {
	return SchemeGroupVersion.WithResource(resource).GroupResource()
}

func Kind(kind string) schema.GroupKind {
	return SchemeGroupVersion.WithKind(kind).GroupKind()
}

func addKnownTypes(scheme *runtime.Scheme) error {
	scheme.AddKnownTypes(
		SchemeGroupVersion,
		&MyCrdGolangTest{},
		&MyCrdGolangTestList{},
	)

	// register the type in the scheme
	metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
	return nil
}

register.go 就是一个模板代码,只需要注意SchemeGroupVersion 使用自己的组名和版本名称,addKnownTypes中添加自己创建的CRD结构体指针

step3 最后执行 go mod tidy 添加依赖

使用 go mod tidy 添加依赖

在这里插入图片描述

此时项目结构如下 注意此时是 未生成代码前的项目结构

在这里插入图片描述

到此 go项目搭建完成 使用git push 到远端服务上

到linux机器上生成代码

拉取刚才提交的代码 注意此时在linux机器上,任意文件夹下都可以

进入go项目内执行生成代码脚本

/root/go/pkg/code-generator/generate-groups.sh all crdtest/pkg/client crdtest/pkg/apis liuyjiang.com:v1 --go-header-file=/root/go/pkg/code-generator/examples/hack/boilerplate.go.txt --output-base ../

在这里插入图片描述

生成命令说明:

  • /root/go/pkg/code-generator/generate-groups.sh
这里使用code-generator项目中的generate-groups.sh
  • all
是使用 applyconfiguration,client,deepcopy,informer,lister 的简写
  • crdtest/pkg/client
是生成的文件的包名,简单说就是会在 crdtest/pkg/client文件下创建生成的文件,并且包名以crdtest/pkg/client为前缀
注意这里要配合--output-base 指定输出的根路径
例如在crdtest文件夹内执行命令 那么--output-base需要指定为 ../ 即输出根路径是当前文件夹的父文件夹
  • crdtest/pkg/apis
是指定模板文件的位置,简单说就是doc.go  types.go  register.go这几个文件放置的最上层文件夹名称
  • liuyjiang.com:v1
指定组名和版本与CRD配置中的组名和版本一致即可
  • –go-header-file
直接使用code-generator项目中的现有的boilerplate.go.txt文件
/root/go/pkg/code-generator/examples/hack/boilerplate.go.txt 
  • –output-base
这里使用 ../ 即输出根路径是当前文件夹的父文件夹 ,这样输出文件就可以在pkg文件夹下了

generate-groups.sh 脚本参数与使用方式如下

在这里插入图片描述

此时代码生成完成 git push后回到window环境上查看

在这里插入图片描述

如有报错再执行一下 go mod tidy

client-go操作CR

有了生成代码后 就可以操作自定义的资源了,现在编写一个简单的web服务,可以创建并查询自定义的CRD,同时启动一个线程监听CRD

注意: 集群内部需要使用 rest.InClusterConfig() 获取权限信息

config, err := rest.InClusterConfig()
	if err != nil {
		panic(err.Error())
	}

相关代码如下

web.go

package crdweb

import (
	"context"
	crdv1 "crdtest/pkg/apis/liuyijiang.com/v1"
	crdclientset "crdtest/pkg/client/clientset/versioned"
	"fmt"
	"net/http"
	"time"

	crdexternalversions "crdtest/pkg/client/informers/externalversions"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
)

func StartWeb() {
	go watchMyCrdGolangTest()
	fmt.Println("start")
	http.HandleFunc("/", index)
	http.HandleFunc("/get", get)
	http.HandleFunc("/list", list)
	http.HandleFunc("/create", create)
	//http.HandleFunc("/update", index)
	//http.HandleFunc("/delete", index)

	http.ListenAndServe(":8000", nil)

}

// 对应首页
func index(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("hello this is MyCrdGolangTest 
"))

}

func get(w http.ResponseWriter, r *http.Request) {
	crdName := r.FormValue("name")
	w.Write([]byte(getMyCrdGolangTestStr(crdName)))

}

func list(w http.ResponseWriter, r *http.Request) {
	//crdName := r.FormValue("name")
	w.Write([]byte(listMyCrdGolangTest()))

}

func create(w http.ResponseWriter, r *http.Request) {
	crdName := r.FormValue("name")
	createMyCrdGolangTest(crdName)
	w.Write([]byte("createMyCrdGolangTest success 
"))

}

func getClientSet() *crdclientset.Clientset {
	//外部访问集群的方式
	// config, err := clientcmd.BuildConfigFromFlags("", "./config")
	// if err != nil {
	// 	fmt.Println(err)
	// }

	//使用集群内部访问k8s的方式
	config, err := rest.InClusterConfig()
	if err != nil {
		panic(err.Error())
	}

	clientset, err := crdclientset.NewForConfig(config)
	if err != nil {
		fmt.Println(err)
	}
	return clientset
}

/*
创建自定义资源  MyCrdGolangTest
*/
func createMyCrdGolangTest(name string) {

	clientset := getClientSet()
	namespace := "crd-golang-test"

	typeMeta := metav1.TypeMeta{
		Kind:       "MyCrdGolangTest",
		APIVersion: "v1",
	}

	labelMap := make(map[string]string)
	labelMap["app"] = "this-is-my-crd"

	//可以配置pod的名字 Labels 等
	objectMeta := metav1.ObjectMeta{
		Name:   name,
		Labels: labelMap,
	}

	spec := crdv1.MyCrdGolangTestSpec{
		Mymsg:    "hello liuyijiang222",
		Mynumber: 123,
		Myarray:  []string{"AFFF", "BCCC", "CEEE"},
	}

	myCrdGolangTest := crdv1.MyCrdGolangTest{
		TypeMeta:   typeMeta,
		ObjectMeta: objectMeta,
		Spec:       spec,
	}
	crd, _ := clientset.LiuyjiangV1().MyCrdGolangTests(namespace).Create(context.TODO(), &myCrdGolangTest, metav1.CreateOptions{})
	fmt.Println(crd.GetName())
}

/*
查询所有自定义资源  MyCrdGolangTest
*/
func listMyCrdGolangTest() string {
	clientset := getClientSet()
	namespace := "crd-golang-test"

	info := ""

	list, _ := clientset.LiuyjiangV1().MyCrdGolangTests(namespace).List(context.TODO(), metav1.ListOptions{})
	for _, cr := range list.Items {
		fmt.Println(cr.GetName())
		info = info + cr.GetName() + "
 "
	}
	return info
}

/*
查询单个自定义资源  MyCrdGolangTest
*/
func getMyCrdGolangTest(name string) *crdv1.MyCrdGolangTest {
	clientset := getClientSet()
	namespace := "crd-golang-test"

	cr, _ := clientset.LiuyjiangV1().MyCrdGolangTests(namespace).Get(context.TODO(), name, metav1.GetOptions{})
	//返回的cr 不会为空 只能根据名字是否有值去判断
	fmt.Println(cr.GetName())
	fmt.Println(cr.Labels)
	fmt.Println("=============Spec===============")
	fmt.Println("Mymsg: ", cr.Spec.Mymsg)
	fmt.Println("Mynumber: ", cr.Spec.Mynumber)
	fmt.Println("Myarray: ", cr.Spec.Myarray)
	fmt.Println("=============Status===============")
	fmt.Println("Mystatus: ", cr.Status.Mystatus)
	fmt.Println("Myip: ", cr.Status.Myip)
	return cr
}

func getMyCrdGolangTestStr(name string) string {
	clientset := getClientSet()
	namespace := "crd-golang-test"

	cr, _ := clientset.LiuyjiangV1().MyCrdGolangTests(namespace).Get(context.TODO(), name, metav1.GetOptions{})
	info := "        " + cr.GetName() + " 
 "
	info = info + "=============Spec===============
 Mymsg:" + cr.Spec.Mymsg + "
 Mynumber: " + fmt.Sprintf("%d", cr.Spec.Mynumber)
	info = info + "
=============Status===============
 Mystatus: " + cr.Status.Mystatus + " 
"
	return info
}

/*
更新自定义资源  MyCrdGolangTest
*/
func updateMyCrdGolangTest(name string) {
	fmt.Printf("=== before update === 

")
	cr := getMyCrdGolangTest(name)

	clientset := getClientSet()
	namespace := "default"
	cr.Spec.Mymsg = "ffffff"
	cr.Spec.Mynumber = 456
	cr.Spec.Myarray = []string{"TTTT", "GGGGG"}

	cr.Status.Myip = "127.0.0.1"
	cr.Status.Mystatus = "runing"
	clientset.LiuyjiangV1().MyCrdGolangTests(namespace).Update(context.TODO(), cr, metav1.UpdateOptions{})

	fmt.Printf("=== after update === 

")
	getMyCrdGolangTest(name)
}

/*
监听资源
*/
func watchMyCrdGolangTest() {
	// 生成clientSet
	clientSet := getClientSet()

	/*
	   使用Informer 实现watch操作
	*/
	//使用NewSharedInformerFactory无法过滤 命名空间  无法根细粒度的选择哪个Crd
	//crdiformerFactory := crdexternalversions.NewSharedInformerFactory(clientSet, time.Second*30)

	/*
		NewFilteredSharedInformerFactory 可以过滤命名空间  和  Crd粒度
	*/
	namespace := "crd-golang-test"
	tweakListOptions := func(opt *metav1.ListOptions) {
		fmt.Println("=======tweakListOptions========")
		/**
		这里还可以对pod进行筛选
		app=my-quarkus-demo
		*/
		//opt.LabelSelector = "app=my-quarkus-demo"

	}

	crdiformerFactory := crdexternalversions.NewFilteredSharedInformerFactory(clientSet, time.Second*30, namespace, tweakListOptions)

	crdInformer := crdiformerFactory.Liuyjiang().V1().MyCrdGolangTests()

	crdInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			//fmt.Println(obj)
			crd := obj.(*crdv1.MyCrdGolangTest)
			fmt.Println("======AddFunc=====", crd.GetName())

		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			//fmt.Println(oldObj)

			//fmt.Println(newObj)
			crd := newObj.(*crdv1.MyCrdGolangTest)
			fmt.Println("======UpdateFunc=====", crd.GetName())
		},
		DeleteFunc: func(obj interface{}) {
			crd := obj.(*crdv1.MyCrdGolangTest)
			fmt.Println("======DeleteFunc=====", crd.GetName())
		},
	})

	crdiformerFactory.Start(wait.NeverStop)
	crdiformerFactory.WaitForCacheSync(wait.NeverStop)

	//time.Sleep(time.Hour * 3)

}

main.go

package main

import (
	"context"
	crdweb "crdtest/pkg"
	crdv1 "crdtest/pkg/apis/liuyijiang.com/v1"
	crdclientset "crdtest/pkg/client/clientset/versioned"
	crdexternalversions "crdtest/pkg/client/informers/externalversions"
	"fmt"
	"time"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/clientcmd"
)

//crdinformers "crdtest/pkg/client/informers/externalversions/liuyijiang.com/v1"
//crdexternalversions "crdtest/pkg/client/informers/externalversions"

func main() {
	crdweb.StartWeb()
}

代码编写完成后使用交叉编译的方式,生成linux环境下可执行文件

GOOS=linux GOARCH=amd64 go build main.go

在这里插入图片描述

镜像创建

编写一个Dockerfile内容如下

FROM ubuntu
VOLUME ["/data/service/logs","/data/service/tmp"] 
WORKDIR "/data/service"
ENV LANG en_US.UTF-8  
ENV LANGUAGE en_US:en  
ENV LC_ALL en_US.UTF-8
COPY main main 
RUN chmod 711 main
ENTRYPOINT ["./main"]

使用 ubuntu作为基础镜像

然后创建镜像并推送到私库

docker build -t crd-go .
docker tag crd-go registry.cn-hangzhou.aliyuncs.com/jimliu/crd-go
docker push registry.cn-hangzhou.aliyuncs.com/jimliu/crd-go

在这里插入图片描述

此时得到可以使用的crd-go 版本镜像

权限配置与发布配置

权限和发布参考 《k8s java程序实现kubernetes Controller & Operator 使用CRD 学习总结》 文章中使用的CRD一致

直接贴出deploy.yml内容

# 创建命名空间
apiVersion: v1
kind: Namespace
metadata:
  name: crd-golang-test
  labels:
    liuyijiang.com: crd-golang-test
  
---

# 创建阿里云私库秘钥
apiVersion: v1
kind: Secret
metadata:
  name: myaliyunsecret-crd-golang-test
  namespace: crd-golang-test
  labels:
    liuyijiang.com: crd-golang-test
data:
  .dockerconfigjson: eyJhdXRocyI6eyJyZWd-省略
type: kubernetes.io/dockerconfigjson


---

# 创建ServiceAccount 用于 程序中访问自定义资源
apiVersion: v1
kind: ServiceAccount
metadata:
  name: crd-golang-test-serviceaccount
  namespace: crd-golang-test
  labels:
    liuyijiang.com: crd-golang-test
imagePullSecrets:
    - name: myaliyunsecret-crd-golang-test


---

# 需要操作自定义的 CRD MyCrdGolangTest  需要配置对MyCrdGolangTest资源的操作权限
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: crd-golang-test-clusterrole
  labels:
    liuyijiang.com: crd-golang-test
rules:
  - apiGroups:
      - "liuyjiang.com"  #apiGroups crd-liuyijiang.yaml中定义的 group
    resources: 
      - mycrdgolangtests  #MyCrdGolangTest  注意为crd-liuyijiang.yaml中配置的复数名称
    verbs:   #可以操作的类型
      - list
      - watch
      - get
      - create
      - delete 
      - update 
      - edit 
      - exec
      
---

# 让ServiceAccount 与 ClusterRole绑定
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: crdtest-cluster-role-binding
  labels:
    liuyijiang.com: crd-golang-test
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: crd-golang-test-clusterrole
subjects:
  - kind: ServiceAccount
    name: crd-golang-test-serviceaccount
    namespace: crd-golang-test 


---

# 创建项目容器pod 
apiVersion: v1
kind: Pod
metadata: 
   name: crd-go
   namespace: crd-golang-test 
   labels: 
     app: crd-go
     liuyijiang.com: crd-golang-test
     
spec: 
  # 注意指定serviceAccount
  serviceAccountName: crd-golang-test-serviceaccount
  restartPolicy: Always
  containers: 
    - image:  registry.cn-hangzhou.aliyuncs.com/jimliu/crd-go:latest
      name: crd-go-runtime  


---

# 创建service
apiVersion: v1
kind: Service
metadata:
  name: crd-go-service
  namespace: crd-golang-test 
spec:
  ports:
    - protocol: TCP
      port: 8000
      targetPort: 8000
      nodePort: 18000
      name: http
  selector:
    app: crd-go
  type: NodePort

部署与测试

kubectl apply -f deploy.yml 部署程序

在这里插入图片描述

调用创建接口 http://192.168.0.160:18000/create?name=jimtest

在这里插入图片描述

调用查询接口 http://192.168.0.160:18000/get?name=jimtest

在这里插入图片描述

手动修改一下cr

kubectl -n crd-golang-test edit mcgt jimtest

在这里插入图片描述

在这里插入图片描述

**删除cr **

在这里插入图片描述

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。