掘金 后端 ( ) • 2024-04-14 09:53

实现Kubernetes client

配置加载

加载本地的kubeconfig配置文件

package main

import (
	"context"
	"log"
	"os/exec"
	"os/user"
	"strings"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	// "k8s.io/client-go/rest"
)

// read kube config path from homedir
func NewClient() (*kubernetes.Clientset, error) {
	user, err := user.Current()
	if err != nil {
		return nil, err
	}
	cmd := exec.Command("echo", user.HomeDir+"/.kube/config")
	output, err := cmd.Output()
	if err != nil {
		return nil, err
	}
	return nil, nil
}

找到当前用户的家目录,Kubernetes的集群配置文件路径就是$HOME/.kube/config

当程序运行在集群中后,就无法再使用上述方法读取到配置文件,此时需要使用InClusterConfig加载集群配置文件


// read kube config path from homedir
func NewClient() (*kubernetes.Clientset, error) {

        ...
	configPath := strings.TrimSpace(string(output))
	log.Println("kube config file path: ", configPath)
	var config *rest.Config
	config, err = clientcmd.BuildConfigFromFlags("", configPath)
	if err != nil {
		// initialize from cluster config
		config, err = rest.InClusterConfig()
		if err != nil {
			return nil, err
		}
	}
	return nil, nil
}

初始化client

加载kubeconfig配置文件并初始化client

// read kube config path from homedir
func NewClient() (*kubernetes.Clientset, error) {
	user, err := user.Current()
	if err != nil {
		return nil, err
	}
	cmd := exec.Command("echo", user.HomeDir+"/.kube/config")
	output, err := cmd.Output()
	if err != nil {
		return nil, err
	}

	configPath := strings.TrimSpace(string(output))
	log.Println("kube config file path: ", configPath)
	var config *rest.Config
	config, err = clientcmd.BuildConfigFromFlags("", configPath)
	if err != nil {
		// initialize from cluster config
		config, err = rest.InClusterConfig()
		if err != nil {
			return nil, err
		}
	}
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}
	return clientset, nil
}

获取Pod信息

通过client获取指定namespace下的pod列表信息

package main

import (
	"context"
	"log"
	"os/exec"
	"os/user"
	"strings"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	// "k8s.io/client-go/rest"
)

// read kube config path from homedir
func NewClient() (*kubernetes.Clientset, error) {
    ...
}

func list(clientset *kubernetes.Clientset) {
	// get pod
	podList, err := clientset.CoreV1().Pods("kube-system").List(context.Background(), metav1.ListOptions{})
	if apierrors.IsNotFound(err) {
		log.Println("not found")
	}
	if err != nil {
		log.Fatal(err)
	}
	for _, pod := range podList.Items {
		log.Println(pod.Name)
	}
}

func main() {
	clientset, err := NewClient()
	if err != nil {
		log.Fatal(err)
	}
	list(clientset)
}

获取pod列表打印所有pod的名称

获取Deployment信息

获取名为"nginx-deployment"的deployment实例

func list(clientset *kubernetes.Clientset) {
	// get pod
        ...
	// get a deployment by name
	name := "nginx-deployment"
	deploy, err := clientset.AppsV1().Deployments("default").Get(context.Background(), name, metav1.GetOptions{})
	if apierrors.IsNotFound(err) {
		log.Println("not found")
		return
	}
	if err != nil {
		log.Fatal(err)
	}
	log.Println("deployment name: ", deploy.Name)
}
...

在main中轮询list接口打印相关资源的信息

func main() {
	clientset, err := NewClient()
	if err != nil {
		log.Fatal(err)
	}
	for {
		list(clientset)
		time.Sleep(30 * time.Second)
	}
}

部署应用到K8s集群

编译程序

➜  client-go-app GOPROXY=https://goproxy.io,direct go mod tidy
➜  client-go-app go build .
➜  client-go-app go build -o main .
2024/03/23 11:01:23 kube config file path:  /home/going/.kube/config
2024/03/23 11:01:23 coredns-76f75df574-2xpv8
2024/03/23 11:01:23 coredns-76f75df574-ncccv
2024/03/23 11:01:23 etcd-test-control-plane
2024/03/23 11:01:23 kindnet-cvbck
2024/03/23 11:01:23 kube-apiserver-test-control-plane
2024/03/23 11:01:23 kube-controller-manager-test-control-plane
2024/03/23 11:01:23 kube-proxy-9428x
2024/03/23 11:01:23 kube-scheduler-test-control-plane
2024/03/23 11:01:23 deployment name:  nginx-deployment

编写Dockerifle并构建镜像

编写Dockerfile

FROM golang:1.22

WORKDIR /opt

COPY main.go main.go
COPY go.mod go.mod
COPY go.sum go.sum
RUN GOPROXY=https://goproxy.io,direct go mod tidy
RUN go build -a -o /opt/main .

CMD ["/opt/main"]

构建docker镜像

➜  client-go-app sudo docker build -t jxs1211/lister:v0.0.1 .
 => [internal] load build definition from Dockerfile                                                                                                                                                              0.0s
 => => transferring dockerfile: 211B                                                                                                                                                                              0.0s
 => [internal] load metadata for docker.io/library/golang:1.22                                                                                                                                                    1.8s
 => [auth] library/golang:pull token for registry-1.docker.io                                                                                                                                                     0.0s
 => [internal] load .dockerignore                                                                                                                                                                                 0.0s
 => => transferring context: 2B                                                                                                                                                                                   0.0s
 => [1/7] FROM docker.io/library/golang:1.22@sha256:0b55ab82ac2a54a6f8f85ec8b943b9e470c39e32c109b766bbc1b801f3fa8d3b                                                                                              0.0s
 => [internal] load build context                                                                                                                                                                                 0.0s
 => => transferring context: 1.53kB                                                                                                                                                                               0.0s
 => CACHED [2/7] WORKDIR /opt                                                                                                                                                                                     0.0s
 => [3/7] COPY main.go main.go                                                                                                                                                                                    0.0s
 => [4/7] COPY go.mod go.mod                                                                                                                                                                                      0.1s
 => [5/7] COPY go.sum go.sum                                                                                                                                                                                      0.0s
 => [6/7] RUN GOPROXY=https://goproxy.io,direct go mod tidy                                                                                                                                                      16.5s
 => [7/7] RUN go build .                                                                                                                                                                                        258.6s
 => exporting to image                                                                                                                                                                                            6.5s
 => => exporting layers                                                                                                                                                                                           6.5s
 => => writing image sha256:fa391e8509fcad8770467eaaad64a55839ae49e7608f5aea1abda26c09b02d47                                                                                                                      0.0s
 => => naming to docker.io/jxs1211/lister:v0.0.1                                                                                                                                                                  0.0s
➜  client-go-app 

上传镜像

➜  client-go-app sudo docker push jxs1211/lister:v0.0.1           
The push refers to repository [docker.io/jxs1211/lister]
9cea1fbbab41: Pushed 
5230f0e56593: Pushed 
49cbb87e7a8c: Pushed 
26cf4dbd97ef: Pushed 
eab45223e502: Pushed 
5f70bf18a086: Mounted from library/golang 
09efec90669c: Mounted from library/golang 
ae0862c6a26c: Mounted from library/golang 
e47d37fbe176: Mounted from library/golang 
68866beb2ed2: Mounted from library/golang 
e6e2ab10dba6: Mounted from library/golang 
0238a1790324: Mounted from library/golang 
v0.0.1: digest: sha256:98a4a45165779e218ae050cd485f87803d4566105f5988e6832d62f1e911f6fe size: 3043

部署应用到集群

编写Deployment配置文件

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: lister
  name: lister
spec:
  replicas: 1
  selector:
    matchLabels:
      app: lister
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: lister
    spec:
      containers:
        - image: jxs1211/lister:v0.0.1
          name: lister
          imagePullPolicy: Always
          resources: {}

部署Deployment到集群

➜  client-go-app ka -f lister.yaml          
deployment.apps/lister created
➜  client-go-app kg pod -w        
NAME                     READY   STATUS              RESTARTS   AGE
lister-9f8bf577b-hz42l   0/1     ContainerCreating   0          2s
lister-9f8bf577b-hz42l   1/1     Running             0          27s
lister-9f8bf577b-hz42l   0/1     Error               0          28s

发现问题,处理异常

发现异常,查看日志

➜  client-go-app k logs -f lister-9f8bf577b-hz42l                      
2024/03/23 03:49:40 kube config file path:  /root/.kube/config
2024/03/23 03:49:40 pods is forbidden: User "system:serviceaccount:default:default" cannot list resource "pods" in API group "" in the namespace "kube-system"
➜  client-go-app

报错信息显示:"pod使用的serviceaccount无法获取pod信息",原因是pod在集群中没有权限访问APIServer,所以无法获取Pod信息,于是我们需要配置RBAC为Pod提供相应的权限。

创建ClusterRole设置相关资源的访问权限

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  creationTimestamp: null
  name: lister
rules:
  - apiGroups:
      - ""
    resources:
      - pods
    verbs:
      - list
      - get
  - apiGroups:
      - apps
    resources:
      - deployments
    verbs:
      - list
      - get

编写ClusteRoleBinding并将ClusterRole绑定到Serviceaccount,这个serviceacount名为default,是pod使用的默认serviceaccount

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  creationTimestamp: null
  name: lister
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: lister
subjects:
  - kind: ServiceAccount
    name: default
    namespace: default

部署所有权限相关的配置到集群

➜  client-go-app ka -f role.yaml       
clusterrole.rbac.authorization.k8s.io/lister created
➜  client-go-app ka -f rolebinding.yaml
clusterrolebinding.rbac.authorization.k8s.io/lister created

重新构建镜像并上传到镜像仓库

➜  client-go-app sudo docker build -t jxs1211/lister:v0.0.1 . && sudo docker push jxs1211/lister:v0.0.1
[sudo] password for going: 
[+] Building 273.7s (13/13) FINISHED                                                     docker:default
 => [internal] load build definition from Dockerfile                                               0.0s
 => => transferring dockerfile: 229B                                                               0.0s
 => [internal] load metadata for docker.io/library/golang:1.22                                     1.9s
 => [auth] library/golang:pull token for registry-1.docker.io                                      0.0s
 => [internal] load .dockerignore                                                                  0.0s
 => => transferring context: 2B                                                                    0.0s
 => [1/7] FROM docker.io/library/golang:1.22@sha256:0b55ab82ac2a54a6f8f85ec8b943b9e470c39e32c109b  0.0s
 => [internal] load build context                                                                  0.0s
 => => transferring context: 1.84kB                                                                0.0s
 => CACHED [2/7] WORKDIR /opt                                                                      0.0s
 => [3/7] COPY main.go main.go                                                                     0.0s
 => [4/7] COPY go.mod go.mod                                                                       0.0s
 => [5/7] COPY go.sum go.sum                                                                       0.0s
 => [6/7] RUN GOPROXY=https://goproxy.io,direct go mod tidy                                       10.1s
 => [7/7] RUN go build -a -o /opt/main .                                                         256.6s 
 => exporting to image                                                                             4.9s 
 => => exporting layers                                                                            4.9s 
 => => writing image sha256:418a3f81de6dea5eb16ee16daecef5f32a6359f1efdcb9b5754f7239de8c22c3       0.0s 
 => => naming to docker.io/jxs1211/lister:v0.0.1                                                   0.0s 
The push refers to repository [docker.io/jxs1211/lister]                                                
2f7236794eeb: Pushed 
c37d55347bb1: Pushed 
58e88ebcbf16: Pushed 
8be899e11629: Pushed 
2ebcc941d35e: Pushed 
5f70bf18a086: Layer already exists 
09efec90669c: Layer already exists 
ae0862c6a26c: Layer already exists 
e47d37fbe176: Layer already exists 
68866beb2ed2: Layer already exists 
e6e2ab10dba6: Layer already exists 
0238a1790324: Layer already exists 
v0.0.1: digest: sha256:dfc547c278cd230783efbf0913ac5a5ba97cc9885a49c9dcef19534ffaad43d0 size: 3043
➜  client-go-app 

重新部署应用并查看日志显示成功获取到资源信息

➜  client-go-app kd -f lister.yaml 
deployment.apps "lister" deleted
➜  client-go-app ka -f lister.yaml
deployment.apps/lister created
➜  client-go-app kg pod           
NAME                     READY   STATUS              RESTARTS   AGE
lister-9f8bf577b-dgljv   0/1     ContainerCreating   0          7s
➜  client-go-app kg pod    
NAME                     READY   STATUS    RESTARTS   AGE
lister-9f8bf577b-dgljv   1/1     Running   0          47s
➜  client-go-app k logs -f lister-9f8bf577b-dgljv                      
2024/03/23 05:16:22 kube config file path:  /root/.kube/config
2024/03/23 05:16:22 coredns-76f75df574-2xpv8
2024/03/23 05:16:22 coredns-76f75df574-ncccv
2024/03/23 05:16:22 etcd-test-control-plane
2024/03/23 05:16:22 kindnet-cvbck
2024/03/23 05:16:22 kube-apiserver-test-control-plane
2024/03/23 05:16:22 kube-controller-manager-test-control-plane
2024/03/23 05:16:22 kube-proxy-9428x
2024/03/23 05:16:22 kube-scheduler-test-control-plane
2024/03/23 05:16:22 not found
2024/03/23 05:16:52 coredns-76f75df574-2xpv8
2024/03/23 05:16:52 coredns-76f75df574-ncccv
2024/03/23 05:16:52 etcd-test-control-plane
2024/03/23 05:16:52 kindnet-cvbck
2024/03/23 05:16:52 kube-apiserver-test-control-plane
2024/03/23 05:16:52 kube-controller-manager-test-control-plane
2024/03/23 05:16:52 kube-proxy-9428x
2024/03/23 05:16:52 kube-scheduler-test-control-plane
2024/03/23 05:16:52 not found