0pR3gx.png

早期我已经在本地搭建了一个两台机器的k3s集群,并部署了Crawlab( 一个使用Golang 开发的分布式爬虫管理平台),实现了本地的分布式集群服务环境的搭建。服务起来以后,动态的控制容器节点,监控容器运行状态,实现即使的扩容,这都需要我们Kubernetes进行控制。幸运的是Kubernetes直接提供了python、golang的客户端,可以方便的实现Kubernetes API 操作。所以接下来我们一起熟悉一下,基于golng客户端Kubernetes控制。

起步

访问 API 和查看列表

在了解 Kubernetes 的基本架构和提供 API 的方式后,接下来我们需要知道 Kubernetes 到底提供了哪些 API。为了方便调试,首先我们需要在本地运行 kubectl proxy 命令,kube-apiserver 就会在本地的 8001 端口上进行监听,也就是提供了一个 Kubernetes API 服务的 HTTP 代理。

这个时候我们可以访问:

$ curl https://127.0.0.1:6443/api/v1

查看所提供的对应 API‘s:

{
  "kind": "APIResourceList",
  "groupVersion": "v1",
  "resources": [
    {
      "name": "bindings",
      "singularName": "",
      "namespaced": true,
      "kind": "Binding",
      "verbs": [
        "create"
      ]
    },
    {
      "name": "componentstatuses",
      "singularName": "",
      "namespaced": false,
      "kind": "ComponentStatus",
      "verbs": [
        "get",
        "list"
      ],
      "shortNames": [
        "cs"
      ]
    },
    ...
  ]
}

访问 api/v1/pods 路径,获取所有 Pods

$ curl https://127.0.0.1:6443/api/v1/pods

访问结果:

{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "selfLink": "/api/v1/pods",
    "resourceVersion": "614376"
  },
  "items": [
    {
      "metadata": {
        "name": "awesome-project-76788db95b-7ztwr",
        "generateName": "awesome-project-76788db95b-",
        "namespace": "default",
        "selfLink": "/api/v1/namespaces/default/pods/awesome-project-76788db95b-7ztwr",
        "uid": "4fdb6661-edbd-4fc6-bf71-1d2dadb3ffc1",
        "resourceVersion": "608545",
        "creationTimestamp": "2020-05-03T02:29:32Z",
        "labels": {
          "app": "awesome-project",
          "pod-template-hash": "76788db95b"
        },
        ...
        ]
      },
    ]

更多的 API 列表和介绍可查看官方文档

Kubernetes 官方提供了 Go 语言的 Client SDK,也就是client-go

SDK集群外访问:

package main

import (
	"context"
	"flag"
	"fmt"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	"os"
)

func main() {
	// 配置 k8s 集群外 kubeconfig 配置文件
	var kubeconfig *string
	kubeconfig = flag.String("kubeconfig", "/etc/rancher/k3s/k3s.yaml", "absolute path to the kubeconfig file")
	flag.Parse()

	//在 kubeconfig 中使用当前上下文环境,config 获取支持 url 和 path 方式
	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		panic(err.Error())
	}

	// 根据指定的 config 创建一个新的 clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err.Error())
	}

		// 通过实现 clientset 的 CoreV1Interface 接口列表中的 PodsGetter 接口方法 Pods(namespace string)返回 PodInterface
		// PodInterface 接口拥有操作 Pod 资源的方法,例如 Create、Update、Get、List 等方法
		// 注意:Pods() 方法中 namespace 不指定则获取 Cluster 所有 Pod 列表
		pods, err := clientset.CoreV1().Pods("").List(context.TODO(),metav1.ListOptions{})
		if err != nil {
			panic(err.Error())
		}
		fmt.Printf("There are %d pods in the k8s cluster\n", len(pods.Items))
		fmt.Println(clientset.CoreV1().Namespaces().List(context.TODO(),metav1.ListOptions{}))

		// 获取指定 namespace 中的 Pod 列表信息
		namespace := "default"
		pods, err = clientset.CoreV1().Pods(namespace).List(context.TODO(),metav1.ListOptions{})

		if err != nil {
			panic(err)
		}
		fmt.Printf("\nThere are %d pods in namespaces %s\n", len(pods.Items), namespace)

}
  • kubeconfig默认是在/etc/kubernetes/admin.conf,由于我装的是K3s这里的路径是/etc/rancher/k3s/k3s.yaml

执行程序

There are 15 pods in the k8s cluster

There are 5 pods in namespaces crawlab

SDK集群内访问

除以上方法外,还可以在 k8s 集群内运行客户端操作资源类型。既然是在 k8s 集群内运行,那么就需要将编写的代码放到镜像内,然后在 k8s 集群内以 Pod 方式运行该镜像容器。

# cat main2.go
package main

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

func main() {
	// 通过集群内部配置创建 k8s 配置信息,通过 KUBERNETES_SERVICE_HOST 和 KUBERNETES_SERVICE_PORT 环境变量方式获取
	// 若集群使用 TLS 认证方式,则默认读取集群内部 tokenFile 和 CAFile
	// tokenFile  = "/var/run/secrets/kubernetes.io/serviceaccount/token"
	// rootCAFile = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
	config, err := rest.InClusterConfig()
	if err != nil {
		panic(err.Error())
	}

	// 根据指定的 config 创建一个新的 clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err.Error())
	}
	for {
		// 通过实现 clientset 的 CoreV1Interface 接口列表中的 PodsGetter 接口方法 Pods(namespace string)返回 PodInterface
		// PodInterface 接口拥有操作 Pod 资源的方法,例如 Create、Update、Get、List 等方法
		// 注意:Pods() 方法中 namespace 不指定则获取 Cluster 所有 Pod 列表
		pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{})
		if err != nil {
			panic(err.Error())
		}
		fmt.Printf("There are %d pods in the k8s cluster\n", len(pods.Items))

		// 获取指定 namespace 中的 Pod 列表信息
		namespce := "default"
		pods, err = clientset.CoreV1().Pods(namespce).List(metav1.ListOptions{})
		if err != nil {
			panic(err)
		}
		fmt.Printf("\nThere are %d pods in namespaces %s\n", len(pods.Items), namespce)
		for _, pod := range pods.Items {
			fmt.Printf("Name: %s, Status: %s, CreateTime: %s\n", pod.ObjectMeta.Name, pod.Status.Phase, pod.ObjectMeta.CreationTimestamp)
		}

		// 获取所有的 Namespaces 列表信息
		ns, err := clientset.CoreV1().Namespaces().List(metav1.ListOptions{})
		if err != nil {
			panic(err)
		}
		nss := ns.Items
		fmt.Printf("\nThere are %d namespaces in cluster\n", len(nss))
		for _, ns := range nss {
			fmt.Printf("Name: %s, Status: %s, CreateTime: %s\n", ns.ObjectMeta.Name, ns.Status.Phase, ns.CreationTimestamp)
		}

		time.Sleep(10 * time.Second)
	}
}

该示例主要演示如何在 k8s 集群内操作 Pod 和 Namespaces 资源类型,包括获取集群所有 Pod 列表数量,获取指定 Namespace 中的 Pod 列表信息,获取集群内所有 Namespace 列表信息。这里,该方式获取 k8s 集群配置的方式跟上边方式不同,它通过集群内部创建的 k8s 配置信息,通过 KUBERNETES_SERVICE_HOST 和 KUBERNETES_SERVICE_PORT 环境变量方式获取,来跟 k8s 建立连接,进而来操作其各个资源类型。如果 k8s 开启了 TLS 认证方式,那么默认读取集群内部指定位置的 tokenFile 和 CAFile。

编译一下,看下是否通过。

# go build main2.go
# ls
main2  main2.go

接下来,在同级目录创建一个 Dockerfile 文件如下

FROM debian
COPY ./main2 /opt
ENTRYPOINT /opt/main2

构建docker镜像

# ls
Dockerfile  main2

# docker build -t client-go/in-cluster:1.0 .

因为本机 k8s 默认开启了 RBAC 认证的,所以需要创建一个 clusterrolebinding 来赋予 default 账户 view 权限。

$ kubectl create clusterrolebinding default-view --clusterrole=view --serviceaccount=default:default
clusterrolebinding.rbac.authorization.k8s.io "default-view" created

最后,在 Pod 中运行该镜像即可,可以使用 yaml 方式或运行 kubectl run 命令来创建。

# kubectl run --rm -i client-go-in-cluster-demo --image=client-go/in-cluster:1.0 --image-pull-policy=Never

There are 3 pods in namespaces default
Name: client-go-in-cluster-demo-58d9b5bd79-7w5ds, Status: Running, CreateTime: 2019-02-13 14:25:38 +0000 UTC
Name: podinfo-7b8c9bc5c9-64g8k, Status: Running, CreateTime: 2019-01-10 14:40:18 +0000 UTC
Name: podinfo-7b8c9bc5c9-bx7ml, Status: Running, CreateTime: 2019-01-10 14:40:18 +0000 UTC

There are 5 namespaces in cluster
Name: custom-metrics, Status: Active, CreateTime: 2019-01-10 09:01:52 +0000 UTC
Name: default, Status: Active, CreateTime: 2019-01-05 09:18:02 +0000 UTC
Name: kube-public, Status: Active, CreateTime: 2019-01-05 09:18:02 +0000 UTC
Name: kube-system, Status: Active, CreateTime: 2019-01-05 09:18:02 +0000 UTC
Name: monitoring, Status: Active, CreateTime: 2019-01-08 15:00:41 +0000 UTC
There are 16 pods in the k8s cluster

运行正常,简单验证一下吧!

# kubectl get pods -n default
NAME                                         READY   STATUS    RESTARTS   AGE
client-go-in-cluster-demo-58d9b5bd79-7w5ds   1/1     Running   0          10m
podinfo-7b8c9bc5c9-64g8k                     1/1     Running   1          33d
podinfo-7b8c9bc5c9-bx7ml                     1/1     Running   1          33d

SDK对k8s各资源对象操作

上边演示了,在 k8s 集群内外运行客户端操作资源类型,但是仅仅是 Read 相关读取操作,接下来简单演示下如何进行 Create、Update、Delete 操作。创建 main.go 文件如下:

# cat main3.go
package main

import (
	"flag"
	"fmt"
	apiv1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
)

func main() {
	// 配置 k8s 集群外 kubeconfig 配置文件
	var kubeconfig *string
	    kubeconfig = flag.String("kubeconfig", "/etc/rancher/k3s/k3s.yaml", "absolute path to the kubeconfig file")
	flag.Parse()

	//在 kubeconfig 中使用当前上下文环境,config 获取支持 url 和 path 方式
	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		panic(err)
	}

	// 根据指定的 config 创建一个新的 clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err)
	}

	// 通过实现 clientset 的 CoreV1Interface 接口列表中的 NamespacesGetter 接口方法 Namespaces 返回 NamespaceInterface
	// NamespaceInterface 接口拥有操作 Namespace 资源的方法,例如 Create、Update、Get、List 等方法
	name := "client-go-test"
	namespacesClient := clientset.CoreV1().Namespaces()
	namespace := &apiv1.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
		Status: apiv1.NamespaceStatus{
			Phase: apiv1.NamespaceActive,
		},
	}

	// 创建一个新的 Namespaces
	fmt.Println("Creating Namespaces...")
	result, err := namespacesClient.Create(namespace)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Created Namespaces %s on %s\n", result.ObjectMeta.Name, result.ObjectMeta.CreationTimestamp)

	// 获取指定名称的 Namespaces 信息
	fmt.Println("Getting Namespaces...")
	result, err = namespacesClient.Get(name, metav1.GetOptions{})
	if err != nil {
		panic(err)
	}
	fmt.Printf("Name: %s, Status: %s, selfLink: %s, uid: %s\n",
		result.ObjectMeta.Name, result.Status.Phase, result.ObjectMeta.SelfLink, result.ObjectMeta.UID)

	// 删除指定名称的 Namespaces 信息
	fmt.Println("Deleting Namespaces...")
	deletePolicy := metav1.DeletePropagationForeground
	if err := namespacesClient.Delete(name, &metav1.DeleteOptions{
		PropagationPolicy: &deletePolicy,
	}); err != nil {
		panic(err)
	}
	fmt.Printf("Deleted Namespaces %s\n", name)
}

执行程序

# go run main3.go
Creating Namespaces...
Created Namespaces client-go-test on 2019-02-13 21:44:52 +0800 CST
Getting Namespaces...
Name: client-go-test, Status: Active, selfLink: /api/v1/namespaces/client-go-test, uid: 8a2de86e-2f95-11e9-b2e0-a0369f3f0404
Deleting Namespaces...
Deleted Namespaces client-go-test

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

K3S本地测试环境搭建 下一篇