k8s开发(二) client-go简单使用

169 阅读3分钟

client-go是kubernetes官方出的go语言操作k8s对象的sdk

一,基本操作

1.1 初始化配置

// 读取k8s配置,
config, err := clientcmd.BuildConfigFromFlags("", "/Users/guqingbo/.kube/config")
if err != nil {
   klog.Fatal(err)
}

//根据配置创建客户端对象
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
  klog.Fatal(err)
}

k8s配置放在用户目录下的.kube下面,我的k8s配置是上文启动minikube集群配置

1.2 操作资源

k8s的接口设计遵循了restful原则,对于各种资源,一般都有create,delete,update,get,patch这些标准的操作,并且client-go对各种内置资源进行了对象封装,如图:

image.png

1.2.1 pod Get

func (k kubernetesExample) getPod(ctx context.Context) {
   pod, err := k.clientSet.CoreV1().Pods("kube-system").Get(ctx, "kube-apiserver-minikube", metav1.GetOptions{})
   if err != nil {
      k.logger.Error(err, "getPod")
      return
   }

   k.logger.Info("getPod:%v", pod)
}

kubernetesExample是我demo封装的对象,无需关注,上述代码在minikube集群正常运行的情况下,可以轻松的获得apiserver的pod所有属性值

1.2.2 pod Patch

patch操作相对复杂,并且分为4中操作类型,详细解释可以看这篇文章:k8s.iswbm.com/extra/p03_k…

const (
   JSONPatchType           PatchType = "application/json-patch+json"
   MergePatchType          PatchType = "application/merge-patch+json"
   StrategicMergePatchType PatchType = "application/strategic-merge-patch+json"
   ApplyPatchType          PatchType = "application/apply-patch+yaml"
)
//先添加标签,然后删除标签
func (k kubernetesExample) patchPod(ctx context.Context) {
   //给pod的/metadata/annotations,增加一个alex:gu的标签
   pod, err := k.clientSet.CoreV1().Pods("kube-system").
      Patch(ctx, "kube-apiserver-minikube", types.JSONPatchType,
         []byte(`[{"op": "add", "path": "/metadata/annotations/alex", "value": "gu"}]`),
         metav1.PatchOptions{})
   if err != nil {
      k.logger.Error(err, "patch add")
      return
   }
   k.logger.Info("patch add:", "alex", pod.GetObjectMeta().GetAnnotations()["alex"])

   //给pod的/metadata/annotations,移除alex:gu的标签
   pod, err = k.clientSet.CoreV1().Pods("kube-system").
      Patch(ctx, "kube-apiserver-minikube", types.JSONPatchType,
         []byte(`[{"op": "remove", "path": "/metadata/annotations/alex"}]`),
         metav1.PatchOptions{})
   if err != nil {
      k.logger.Error(err, "patch remove")
      return
   }

   k.logger.Info("patch remove:", "alex", pod.GetObjectMeta().GetAnnotations()["alex"])
}

如果只执行上面代码中的添加标签,不删除标签,然后在控制台执行

kubectl get pod kube-apiserver-minikube -oyaml -n kube-system|grep alex -C 5

可得到

image.png

一般k8s对象属性值很多,使用patch能节省非必要属性值传递,达到节省网络开销的目的,因此在k8s代码中,能在很多地方找到patch的应用,例如下面这段代码就是k8s源码中为pod添加ownerReferences属性

image.png

1.2.3 pod list,watch操作

func (k kubernetesExample) listWatchPod(ctx context.Context) {
   podList, err := k.clientSet.CoreV1().Pods("kube-system").List(ctx, metav1.ListOptions{})
   if err != nil {
      k.logger.Error(err, "list err")
      return
   }
   k.logger.Info("list", "len", len(podList.Items))

   //从list获得的最后记录开始watch
   watcher, err := k.clientSet.CoreV1().Pods("kube-system").Watch(ctx, metav1.ListOptions{ResourceVersion: podList.GetResourceVersion()})
   if err != nil {
      k.logger.Error(err, "watch err")
      return
   }

   go func() {
      for {
         select {
         case <-ctx.Done():
            return
         case event := <-watcher.ResultChan():
            k.logger.Info("watcher", "event", event)
         }
      }
   }()
}

程序启动,首先会打印出在kube-system空间下pod数量为7,并会阻塞,然后给pod:kube-apiserver-minikube的annotation加key,value,保存后会收到MODIFIED的event事件

image.png

二,dynamic操作

对于上面的操作方式,资源都是有具体类型声明的,属于强类型,对于确定的k8s资源有很好的代码规范能力,但是无法操作自定义资源,因此client-go还有一套dynamic的操作资源方式

2.1 创建自定义资源

func (d dynamicExample) create(ctx context.Context) {
   rs, err := d.dynamicClient.Resource(schema.GroupVersionResource{
      Group: "examples.alex.com", Version: "v1", Resource: "alexdynamics",
   }).
      Namespace(metav1.NamespaceDefault).
      Create(ctx, &unstructured.Unstructured{
         Object: map[string]interface{}{
            "apiVersion": "examples.alex.com/v1",
            "kind":       "AlexDynamic",
            "metadata": map[string]interface{}{
               "name": "alex-demo",
            },
         },
      }, metav1.CreateOptions{})
   if err != nil {
      d.logger.Error(err, "create")
      return
   }

   d.logger.Info("create:", "rs", rs)
}

注意对象GVR,GVK值的填写,具体意义可以参考这篇文章: blog.csdn.net/qq_24433609…

但是直接运行这段代码会报错:

image.png

实际上我们要在k8s中管理新的自定义资源,首先需要把自定义资源定义告诉k8s

2.2 CRD定义

#resourcedefinition.yaml

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # 名字必需与下面的 spec 字段匹配,并且格式为 '<名称的复数形式>.<组名>'
  name: alexdynamics.examples.alex.com
spec:
  # 组名称,用于 REST API: /apis/<组>/<版本>
  group: examples.alex.com
  # 列举此 CustomResourceDefinition 所支持的版本
  versions:
    - name: v1
      # 每个版本都可以通过 served 标志来独立启用或禁止
      served: true
      # 其中一个且只有一个版本必需被标记为存储版本
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                image:
                  type: string
                replicas:
                  type: integer
  # 可以是 Namespaced 或 Cluster
  scope: Namespaced
  names:
    # 名称的复数形式,用于 URL:/apis/<组>/<版本>/<名称的复数形式>
    plural: alexdynamics
    # 名称的单数形式,作为命令行使用时和显示时的别名
    singular: alexdynamic
    # kind 通常是单数形式的驼峰命名(CamelCased)形式。你的资源清单会使用这一形式。
    kind: AlexDynamic
    # shortNames 允许你在命令行使用较短的字符串来匹配资源
    shortNames:
      - ad

注意这里定义的GVK,GVR要与上面代码的对应,详细文档见: kubernetes.io/zh-cn/docs/…

然后执行:

kubectl apply -f resourcedefinition.yaml

然后运行代码,创建自定义资源,就不会报错了,接着执行:

kubectl get alexdynamic

image.png

即可像操作k8s原生资源一样,操作自定义资源

三,上述示例代码

github.com/RainAlways/… ,tag:client-go-example-1