本文介绍利用go语言实现对k8s中pod/service/deployment资源的增删改查操作。(个人实际操作的记录,有错误欢迎指正呀~)

K8s中有哪些资源?

资源对象:deployment、service、ingress、replicaSet、pod、namespace、node等

核心资源:

  1. pod
  2. service
  3. deployment

client-go源码

——示例一:对deployment资源实现增删改查

准备工作:

已安装kubernetes集群和本地IDE环境配置完成。

根据自己的kubernetes版本,下载对应源码:client-go

kubernetes版本>1.17的推荐使用v0.x.y tags, 直接git clone的就是最新版本,我的kubernetes为1.21,go版本1.17,直接git clone client-go代码可以使用。

查看kubernetes版本:

kubectl version

查看go版本

go version

本例通过 client-go 来在 k8s 集群外运行客户端来操作各资源类型。

以/client-go/examples/create-update-delete-deployment/main.go为示例:

在终端输入命令运行:

go run main.go

(go run命令会编译源码,并且直接执行源码的 main() 函数,不会在当前目录留下可执行文件,在该文件夹下有readme.md,也可按照里面步骤执行)

但是,直接运行会提示error,找不到.kube/config文件: 

解决办法:

 网上教程:

sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config

但是自己所在集群的kubeconfig文件不在上述路径下,而在/root/.kube/config目录下,且需要切换到root用户身份,才可以复制文件,否则permission denied.

# 1.切换到root权限
sudo su
# 2.复制文件到$HOME/.kube/config
# 若.kube文件夹不存在要先创建
mkdir .kube
sudo cp /root/.kube/config $HOME/.kube/config
# 3.退出root身份
exit
# 4.查看,
cd $HOME
ls -a
# 此时可以看到已存在.kube文件夹,里面有config文件

 .kube/config文件复制完成后,执行go run main.go时提示config文件还是没有权限:

sudo chown -R $USER $HOME/.kube

此时再go run main.go, 可以成功运行main.go了!!!结果如下,对deployment实现了增删改查:

源码:

/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Note: the example only works with the code within the same release/branch.
package main 


// fmt 包实现了格式化 IO(输入/输出)的函数。
import (
	"bufio"
	"context"
	"flag"
	"fmt"
	"os"
	"path/filepath"

	appsv1 "k8s.io/api/apps/v1"
	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"
	"k8s.io/client-go/util/homedir"
	"k8s.io/client-go/util/retry"
	//
	// Uncomment to load all auth plugins
	// _ "k8s.io/client-go/plugin/pkg/client/auth"
	//
	// Or uncomment to load specific auth plugins
	// _ "k8s.io/client-go/plugin/pkg/client/auth/azure"
	// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
	// _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
	// _ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
)

func main() {
	var kubeconfig *string
	if home := homedir.HomeDir(); home != "" {
		kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
	} else {
		kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
	}
	flag.Parse()

	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		panic(err)
	}

	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err)
	}

	deploymentsClient := clientset.AppsV1().Deployments(apiv1.NamespaceDefault)

	deployment := &appsv1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name: "demo-deployment",
		},
		Spec: appsv1.DeploymentSpec{
			Replicas: int32Ptr(2),
			Selector: &metav1.LabelSelector{
				MatchLabels: map[string]string{
					"app": "demo",
				},
			},
			Template: apiv1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						"app": "demo",
					},
				},
				Spec: apiv1.PodSpec{
					Containers: []apiv1.Container{
						{
							Name:  "web",
							Image: "nginx:1.12",
							Ports: []apiv1.ContainerPort{
								{
									Name:          "http",
									Protocol:      apiv1.ProtocolTCP,
									ContainerPort: 80,
								},
							},
						},
					},
				},
			},
		},
	}

	// Create Deployment
	fmt.Println("Creating deployment...")
	result, err := deploymentsClient.Create(context.TODO(), deployment, metav1.CreateOptions{})
	if err != nil {
		panic(err)
	}
	fmt.Printf("Created deployment %q.\n", result.GetObjectMeta().GetName())

	// Update Deployment
	prompt()
	fmt.Println("Updating deployment...")
	//    You have two options to Update() this Deployment:
	//
	//    1. Modify the "deployment" variable and call: Update(deployment).
	//       This works like the "kubectl replace" command and it overwrites/loses changes
	//       made by other clients between you Create() and Update() the object.
	//    2. Modify the "result" returned by Get() and retry Update(result) until
	//       you no longer get a conflict error. This way, you can preserve changes made
	//       by other clients between Create() and Update(). This is implemented below
	//			 using the retry utility package included with client-go. (RECOMMENDED)
	//
	// More Info:
	// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency

	retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
		// Retrieve the latest version of Deployment before attempting update
		// RetryOnConflict uses exponential backoff to avoid exhausting the apiserver
		result, getErr := deploymentsClient.Get(context.TODO(), "demo-deployment", metav1.GetOptions{})
		if getErr != nil {
			panic(fmt.Errorf("Failed to get latest version of Deployment: %v", getErr))
		}

		result.Spec.Replicas = int32Ptr(1)                           // reduce replica count
		result.Spec.Template.Spec.Containers[0].Image = "nginx:1.13" // change nginx version
		_, updateErr := deploymentsClient.Update(context.TODO(), result, metav1.UpdateOptions{})
		return updateErr
	})
	if retryErr != nil {
		panic(fmt.Errorf("Update failed: %v", retryErr))
	}
	fmt.Println("Updated deployment...")

	// List Deployments
	prompt()
	fmt.Printf("Listing deployments in namespace %q:\n", apiv1.NamespaceDefault)
	list, err := deploymentsClient.List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		panic(err)
	}
	for _, d := range list.Items {
		fmt.Printf(" * %s (%d replicas)\n", d.Name, *d.Spec.Replicas)
	}

	// Delete Deployment
	prompt()
	fmt.Println("Deleting deployment...")
	deletePolicy := metav1.DeletePropagationForeground
	if err := deploymentsClient.Delete(context.TODO(), "demo-deployment", metav1.DeleteOptions{
		PropagationPolicy: &deletePolicy,
	}); err != nil {
		panic(err)
	}
	fmt.Println("Deleted deployment.")
}

func prompt() {
	fmt.Printf("-> Press Return key to continue.")
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		break
	}
	if err := scanner.Err(); err != nil {
		panic(err)
	}
	fmt.Println()
}

func int32Ptr(i int32) *int32 { return &i }

1.//定义包名,必须在源文件中非注释的第一行指明这个文件属于哪个包,main表示是一个可独立执行的程序,每个Go应用程序都包含一个名为main的包

package main

2.程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数
 注意下面的“{”不能独占一行,否则报错

func main() {

3. 获取kubernetes配置文件kubeconfig的绝对路径。一般路径为$HOME/.kube/config。该文件主要用来配置本地连接的kubernetes集群。

 var kubeconfig *string
    if home := homedir.HomeDir(); home != "" {
        kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
    } else {
        kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
    }

4.通过参数(master的url或者kubeconfig路径)和BuildConfigFromFlags方法来获取rest.Config对象。包含apiserver地址、用户名、密码、token等信息

    config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)

5. 通过*rest.Config参数和NewForConfig方法来获取clientset对象,clientset是多个client的集合,每个client可能包含不同版本的方法调用。
    NewForConfig函数就是初始化clientset中的每个client

clientset, err := kubernetes.NewForConfig(config)

参考:

Go语言flag包:命令行参数解析 (biancheng.net)

开发指南 - client-go的使用及源码分析 - 《胡伟煌 Kubernetes 学习笔记》 - 书栈网 · BookStack

【kubernets】——client-go库使用(k8s四种客户端介绍)_Teingi的博客-CSDN博客

Logo

开源、云原生的融合云平台

更多推荐