4. 认识client-go

131 阅读4分钟

本文是胡涛大佬所出版的《Kubernetes Operator 进阶开发》,强烈推荐各位阅读原书,本文仅仅留作个人心得,如有侵权立马删除。


1 简单认识 client-go

对于 client-go 的版本的理解:

  1. Kubernetes 大于的时候使用 v0.x.y
  2. Kubernetes 小于的时候使用 Kubernetes-1.x.y

然后应该如何获取 client-go

go get k8s.io/client-go@latest

go get k8s.io/client-go@<version>

此时当前的集群的 Kubernetesversion 是:

docker image ls 
application-operator   v0.0.1     1ae3c6493fbe   13 hours ago   55.1MB
kindest/node           v1.26.14   0621212e565b   7 weeks ago    914MB

所以我们选择和 v1.26.14 对应的版本:v0.26.0

go get k8s.io/client-go@v0.26.0

2 集群内认证

然后编写一段简单的程序:

package main

import (
	"context"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"log"
	"time"
)

func main() {
	
	// 在Kubernetes中,pod创建的时候会自动将 sa 挂载在 /var/run/secrets中/kubernetes.io/serviceaccount目录下
	// 然后利用 InClusterConfig() 方法来获取集群中的配置信息
	config, err := rest.InClusterConfig()
	if err != nil {
		log.Fatal(err)
	}

	// 实现clientSet的各种资源的CURD操作
	clientSet, err := kubernetes.NewForConfig(config)
	if err != nil {
		log.Fatal(err)
	}

	for {
		pods, err := clientSet.CoreV1().Pods("default").List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			log.Fatal(err)
		}

		log.Printf("There are %d pods in the cluster\n", len(pods.Items))
		for i, pod := range pods.Items {
			log.Printf("Pod %d: %s\n", i, pod.Name)
		}
		<-time.Tick(5 * time.Second)
	}
}

go mod tidy

然后就接着编写 Dockerfile

FROM busybox  
COPY ./app /app  
ENTRYPOINT app

此时的项目结构:

base ❯ tree       
.
├── Makefile
├── b01-incluster-config
│   ├── Dockerfile
│   ├── app
│   └── main.go
├── b02-outcluster-config
├── go.mod
└── go.sum

然后可以编译 Makefile 代码:

B01_PATH="./b01-incluster-config"
B01_IMG="b01-incluster-config:v1"
KIND_NAME="dev"

.PHONY: all
all: build


.PHONY: b01
b01:
	echo "start compiling b01d"
	GOOS=linux go build -o $(B01_PATH)/app  $(B01_PATH)
	docker build -t $(B01_IMG) $(B01_PATH)
	echo "end compiling b01d"
	kind load docker-image $(B01_IMG) --name=$(KIND_NAME)
	echo "loaded b01d image to $(KIND_NAME) cluster"

.PHONY: b02
b02: echo "b02"

然后编译:

make b01

然后给的 default sa 查看 Pod 的权限:

kubectl create clusterrolebinding default-view --clusterrole=view --serviceaccount=default:default

然后就可以得到正确的输出了:

kubectl run -i b01-incluster --image=b01-incluster-config:v1
If you don't see a command prompt, try pressing enter.
2024/04/05 00:22:07 There are 1 pods in the cluster
2024/04/05 00:22:07 Pod 0: b01-incluster
2024/04/05 00:22:12 There are 1 pods in the cluster
2024/04/05 00:22:12 Pod 0: b01-incluster

3 集群外认证

如果我们需要在自己的电脑上访问服务器上的集群,那么就可以使用下面的方式进行认证:

package main

import (
	"context"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
	"log"
	"path/filepath"
	"time"
)

func main() {
	homePath := homedir.HomeDir()
	if homePath == "" {
		log.Fatal("HomeDir() returned empty string")
	}

	kubeconfig := filepath.Join(homePath, ".kube", "config")
	config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
	if err != nil {
		log.Fatal(err)
	}

	clientSet, err := kubernetes.NewForConfig(config)
	if err != nil {
		log.Fatal(err)
	}

	for {
		pods, err := clientSet.CoreV1().Pods("default").List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			log.Fatal(err)
		}

		log.Printf("There are %d pods in the cluster\n", len(pods.Items))
		for i, pod := range pods.Items {
			log.Printf("Pod %d: %s\n", i, pod.Name)
		}
		<-time.Tick(5 * time.Second)
	}
}

然后就直接运行就可以:

go run .

2024/04/05 08:29:57 There are 2 pods in the cluster
2024/04/05 08:29:57 Pod 0: b01-incluster
2024/04/05 08:29:57 Pod 1: b02-incluster
2024/04/05 08:30:02 There are 2 pods in the cluster
2024/04/05 08:30:02 Pod 0: b01-incluster
2024/04/05 08:30:02 Pod 1: b02-incluster
2024/04/05 08:30:07 There are 2 pods in the cluster
2024/04/05 08:30:07 Pod 0: b01-incluster
2024/04/05 08:30:07 Pod 1: b02-incluster

4 list-watch 机制

如果需要学习 client-go,那么就得好好明白一下 list-watch 是什么回事:

  1. List:负责调用资源中的 Kubernetes APIServer 中的 RESTful API 获取全局数据列表,并且同步缓存到本地缓存中
  2. Watch:负责监听资源的变化,并且调用对应事件的处理函数进行处理,同时更新本地缓存、使得本地缓存和 Etcd 中保持一致

其中 ListHTTP 中的短链方案;WatchHTTP 中的长链方案。

然后记录一下作者所描述的各个模块的信息:

  1. Reflector:从 apiserverwatch 特定资源的类型,在得到变更通知之后,将其丢到 DeltaFIFO 队列中
  2. InformerInformerDeltaFIFOpop 出相应的对象,然后通过 Indexer 将对象和索引丢到本地的 cache 中,然后再触发 Resource Event Handlers
  3. Indexer:主要提供一个对象根据一定条件检索的能力
  4. WorkQueue:使用的延迟队列,在 Resource Event Handler 中完成将对象的 Key 放入 WorkQueue 中,然后在逻辑代码部分消费这些 key
  5. Resource Event Handler:一般添加一些过滤功能,判断那些需要进一步处理
  6. Worker:是指自己的业务代码可以从 WorkQueue 中获得 key,然后通过 Indexer 获得 obj