k8s中设置annotation的方法总结

时间:2024-12-13 13:24:25

k8s中设置annotation的方法总结

annotation是什么

在 Kubernetes 中,Annotations 是一种用于向 Kubernetes 对象附加非标识性元数据的机制。

annotation有什么用

annotation与 Labels 类似,但有一些关键区别和特定用途。 常用于存储与对象相关的配置信息、工具信息、元数据等,但这些信息不会影响 Kubernetes 对象的调度或生命周期管理。

注意相比labels来说, annotation具有不可见性: Annotations 不会被 Kubernetes API 用于选择或路由对象,因此不会影响调度决策或资源管理。

annotation的设置方式

我们可以为常见的资源种类,pod、pvc、pv、deployment、statefulset等设置annotation.

对于 annotation 的操作,只有3种:增加、删除、更新。(如果需要查看可以kubectl get pod -oyaml方式从详细信息中过滤)

方式一:kubectl annotate命令



# 为pod设置一个新的 annotation
root@dg02-k8s-pnode1:~# kubectl annotate pod ubuntu1604 it/city="shenzhen"
pod/ubuntu1604 annotated

# 为pod修改存在的 annotation 对应的key/value
root@dg02-k8s-pnode1:~# kubectl annotate pod ubuntu1604 it/city="shanghai" --overwrite
pod/ubuntu1604 annotated

# 删除 annotation 对应的key/value
root@dg02-k8s-pnode1:~# kubectl annotate pod ubuntu1604 it/city-
pod/ubuntu1604 annotated

方式二: kubectl patch命令

这里需要注意,在patch时,对于json串中的~,/需要分别转义为~0~1

# 增
kubectl patch pod --type=json -p='[{"op": "add", "path": "/metadata/annotations/it~1city", "value": "shenzhen"}]' ubuntu1604
# 改
kubectl patch pod --type=json -p='[{"op": "replace", "path": "/metadata/annotations/it~1city", "value": "shanghai"}]' ubuntu1604
# 删
kubectl patch pod --type=json -p='[{"op": "remove", "path": "/metadata/annotations/it~1city", "value": ""}]' ubuntu1604

方式三: client-go

除了前2种使用kubectl命令的方式,开发人员通常使用client-go对资源操控annotation

使用Patch方式更新K8S的 API Objects 一共有三种方式:strategic merge patch, json-patch,json merge patch。本文介绍最常用的json-patch,其他2种方式,请参考其他文献

本次示例以给一个pod新增一个annotation为例,直接上代码:

package service

import (
	"encoding/json"
	"fmt"
	"github.com/pkg/errors"
	"go.uber.org/zap"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"kubecmdb/internal/buildk8s"
	"kubecmdb/utils"
	"strings"
)

// AnnotationReplace 更新pod的annotation
func AnnotationReplace(clusterName, namespace, name, key string, value interface{}) error {

	// 获取 clientSet
	myClientSet := buildk8s.GetK8SClientSet(clusterName)
	// 判断 pod 是否存在
	_, err := myClientSet.CoreV1().Pods(namespace).Get(name, metav1.GetOptions{})
	if err != nil {
		utils.Error("[annotation]", zap.String("err", err.Error()))
		return errors.Wrapf(err, "AnnotationAdd pod get err.")
	}

	// 符号 ~ 需要转换为 ~0
	if strings.Contains(key, "~") {
		key = strings.Replace(key, "~", "~0", -1)
	}
	if strings.Contains(key, "/") {
		// 符号 / 需要转换为 ~1
		key = strings.Replace(key, "/", "~1", -1)
	}
	// 需要修改的 annotation 的键值对
	patchObj := struct {
		Op    string      `json:"op"`
		Path  string      `json:"path"`
		Value interface{} `json:"value"`
	}{
		Op:    "replace", // 可以使用add 或 replace
		Path:  "/metadata/annotations/" + key,
		Value: value,
	}

	// 转换为json串
	var patchObjs []interface{}
	patchObjs = append(patchObjs, patchObj)

	patchData, err := json.Marshal(patchObjs)
	if err != nil {
		utils.Error("[annotation]", zap.String("err", err.Error()))
		return errors.Wrapf(err, "AnnotationAdd Marshal err.")
	}

	// 使用 Patch 方法修改
	//patchData:=fmt.Sprintf("'[{\"op\": \"replace\", \"path\": \"/metadata/annotations/it~1domain\", \"value\": \"xxx\"}]'")
	fmt.Printf("patchData=%v\n", string(patchData))
	_, err = myClientSet.CoreV1().Pods(namespace).Patch(name, types.JSONPatchType, patchData)
	if err != nil {
		utils.Error("[annotation]", zap.String("err", err.Error()))
		return errors.Wrapf(err, "AnnotationAdd Patch err.")
	}
	return nil

}

// AnnotationAdd 添加pod的annotation
func AnnotationAdd(clusterName, namespace, name, key string,value interface{}) error {
	return AnnotationReplace(clusterName, namespace, name, key, value)
}

// AnnotationRemove 删除pod的annotation
func AnnotationRemove(clusterName, namespace, name, key string) error {

	// 获取 clientSet
	myClientSet := buildk8s.GetK8SClientSet(clusterName)
	pod, err := myClientSet.CoreV1().Pods(namespace).Get(name, metav1.GetOptions{})
	if err != nil {
		utils.Error("[annotation]", zap.String("err", err.Error()))
		return errors.Wrapf(err, "AnnotationAdd pod get err.")
	}

	// 如果不存在,返回操作成功
	if _,ok := pod.ObjectMeta.Annotations[key];!ok {
		return nil
	}

	// 符号 ~ 需要转换为 ~0
	if strings.Contains(key, "~") {
		key = strings.Replace(key, "~", "~0", -1)
	}
	// 符号 / 需要转换为 ~1
	if strings.Contains(key, "/") {

		key = strings.Replace(key, "/", "~1", -1)
	}
	// 需要修改的 annotation 的键值对
	patchObj := struct {
		Op    string      `json:"op"`
		Path  string      `json:"path"`
		Value interface{} `json:"value"`
	}{
		Op:    "remove",
		Path:  "/metadata/annotations/" + key,
		Value: "", // 不需要填 value
	}

	// 转换为json串
	var patchObjs []interface{}
	patchObjs = append(patchObjs, patchObj)

	patchData, err := json.Marshal(patchObjs)
	if err != nil {
		utils.Error("[annotation]", zap.String("err", err.Error()))
		return errors.Wrapf(err, "AnnotationAdd Marshal err.")
	}

	// 使用 Patch 方法修改
	//patchData:=fmt.Sprintf("'[{\"op\": \"replace\", \"path\": \"/metadata/annotations/it~1domain\", \"value\": \"xxx\"}]'")
	fmt.Printf("patchData=%v\n", string(patchData))
	_, err = myClientSet.CoreV1().Pods(namespace).Patch(name, types.JSONPatchType, patchData)
	if err != nil {
		utils.Error("[annotation]", zap.String("err", err.Error()))
		return errors.Wrapf(err, "AnnotationAdd Patch err.")
	}

	return nil
}


单元测试


package service

import (
	"testing"
)

// 更改
// go test -run "^TestAnnotationReplace$" -v
func TestAnnotationReplace(t *testing.T) {

	err := AnnotationReplace("dg11test", "public", "ubuntu1604", "it/city", "shanghai")
	if err != nil {
		t.Fatal(err)
	}
}

// 添加
// go test -run "^TestAnnotationAdd$" -v
func TestAnnotationAdd(t *testing.T) {

	err := AnnotationAdd("dg11test", "public", "ubuntu1604", "it/city", "shenzhen")
	if err != nil {
		t.Fatal(err)
	}
}

//删除
// go test -run "^TestAnnotationRemove$" -v
func TestAnnotationRemove(t *testing.T) {

	err := AnnotationRemove("dg11test", "public", "ubuntu1604", "it/city", )
	if err != nil {
		t.Fatal(err)
	}
}

参考文献

K8S client-go Patch example
kuberntes中文文档