1. 安装client-go
client-go 安装很简单,前提是本机已经安装并配置好了 Go 环境,安装之前,我们需要先查看下其版本针对 k8s 版本 兼容性列表,针对自己本机安装的 k8s 版本选择对应的 client-go 版本,当然也可以默认选择最新版本,来兼容所有。
client-go 安装方式有多种,比如 go get、Godep、Glide 方式。如果我们本地没有安装 Godep 和 Glide 依赖管理工具的话,可以使用最简单的 go get 下载安装。
1 |
$ go get k8s.io/client-go/... |
执行该命令将会自动将 k8s.io/client-go 下载到本机 $GOPATH,默认下载的源码中只包含了大部分依赖,并将其放在 k8s.io/client-go/vendor 路径,但是如果想成功运行的话,还需要另外两个依赖库 k8s.io/client-go/vendor 和 glog,所以还需要接着执行如下命令。
1 |
$ go get -u k8s.io/apimachinery/... |
说明一下,为什么要使用 -u 参数来拉取最新的该依赖库呢?那是因为最新的 client-go 库只能保证跟最新的 apimachinery 库一起运行。
2. 在k8s集群外操作资源示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# cat main.go package main import ( "flag" "fmt" "os" "time" 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/kubernetes/admin.conf", "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()) } 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 列表信息 namespace := "default" pods, err = clientset.CoreV1().Pods(namespace).List(metav1.ListOptions{}) if err != nil { panic(err) } fmt.Printf("\nThere are %d pods in namespaces %s\n", len(pods.Items), namespace) for _, pod := range pods.Items { fmt.Printf("Name: %s, Status: %s, CreateTime: %s\n", pod.ObjectMeta.Name, pod.Status.Phase, pod.ObjectMeta.CreationTimestamp) } time.Sleep(10 * time.Second) } } func prettyPrint(maps map[string]interface{}) { lens := 0 for k, _ := range maps { if lens <= len(k) { lens = len(k) } } for key, values := range maps { spaces := lens - len(key) v := "" for i := 0; i < spaces; i++ { v += " " } fmt.Printf("%s: %s%v\n", key, v, values) } } func homeDir() string { if h := os.Getenv("HOME"); h != "" { return h } return os.Getenv("USERPROFILE") // windows } |
执行程序
1 2 3 4 5 6 7 |
# go run main.go There are 15 pods in the k8s cluster There are 2 pods in namespaces default Name: podinfo-7b8c9bc5c9-64g8k, Status: Running, CreateTime: 2019-01-10 22:40:18 +0800 CST Name: podinfo-7b8c9bc5c9-bx7ml, Status: Running, CreateTime: 2019-01-10 22:40:18 +0800 CST There are 15 pods in the k8s cluster |
3.在k8s集群内操作资源示例
除以上方法外,还可以在 k8s 集群内运行客户端操作资源类型。既然是在 k8s 集群内运行,那么就需要将编写的代码放到镜像内,然后在 k8s 集群内以 Pod 方式运行该镜像容器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# 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。
编译一下,看下是否通过。
1 2 3 |
# go build main2.go # ls main2 main2.go |
接下来,在同级目录创建一个 Dockerfile 文件如下
1 2 3 |
FROM debian COPY ./main2 /opt ENTRYPOINT /opt/main2 |
构建docker镜像
1 2 3 4 |
# ls Dockerfile main2 # docker build -t client-go/in-cluster:1.0 . |
因为本机 k8s 默认开启了 RBAC 认证的,所以需要创建一个 clusterrolebinding 来赋予 default 账户 view 权限。
1 2 |
$ kubectl create clusterrolebinding default-view --clusterrole=view --serviceaccount=default:default clusterrolebinding.rbac.authorization.k8s.io "default-view" created |
最后,在 Pod 中运行该镜像即可,可以使用 yaml 方式或运行 kubectl run 命令来创建。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 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 |
运行正常,简单验证一下吧!
1 2 3 4 5 |
# 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 |
4. k8s各资源对象CRUD操作
上边演示了,在 k8s 集群内外运行客户端操作资源类型,但是仅仅是 Read 相关读取操作,接下来简单演示下如何进行 Create、Update、Delete 操作。创建 main.go 文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# 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/kubernetes/admin.conf", "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) } |
执行程序
1 2 3 4 5 6 7 |
# 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 |