本文是胡涛大佬所出版的《Kubernetes Operator 进阶开发》,强烈推荐各位阅读原书,本文仅仅留作个人心得,如有侵权立马删除。
1 简单认识 client-go
对于 client-go 的版本的理解:
Kubernetes大于的时候使用v0.x.yKubernetes小于的时候使用Kubernetes-1.x.y
然后应该如何获取 client-go:
go get k8s.io/client-go@latest
go get k8s.io/client-go@<version>
此时当前的集群的 Kubernetes 的 version 是:
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 是什么回事:
List:负责调用资源中的Kubernetes APIServer中的RESTful API获取全局数据列表,并且同步缓存到本地缓存中Watch:负责监听资源的变化,并且调用对应事件的处理函数进行处理,同时更新本地缓存、使得本地缓存和Etcd中保持一致
其中 List 是 HTTP 中的短链方案;Watch 是 HTTP 中的长链方案。
然后记录一下作者所描述的各个模块的信息:
Reflector:从apiserver中watch特定资源的类型,在得到变更通知之后,将其丢到DeltaFIFO队列中Informer:Informer从DeltaFIFO中pop出相应的对象,然后通过Indexer将对象和索引丢到本地的cache中,然后再触发Resource Event Handlers。Indexer:主要提供一个对象根据一定条件检索的能力WorkQueue:使用的延迟队列,在Resource Event Handler中完成将对象的Key放入WorkQueue中,然后在逻辑代码部分消费这些keyResource Event Handler:一般添加一些过滤功能,判断那些需要进一步处理Worker:是指自己的业务代码可以从WorkQueue中获得key,然后通过Indexer获得obj