在 Go 中使用 Kubernetes 对象

简介: 通常,在某些情况下,我们需要通用的方法去使用 Kubernetes 资源对象,而不是编写代码来处理特定类型。


作者 | Jason Snouffer

译者 | Luga Lee    

策划 | Luga Lee

    通常,在某些情况下,我们需要通用的方法去使用 Kubernetes 资源对象,而不是编写代码来处理特定类型。 比如,如下一些简单的用例参考场景:

    1、使用来自没有关联 Golang 结构的插件的 K8s API 对象。

    2、使用 JsonPath、JMESPath、jq 等对 K8s 对象执行通用 CRUD(创建/读取/更新/删除)操作。需要一种通用方法以避免必须编写显式代码来处理每种可能的资源类型。

API Machinery SIG

    Kubernetes 社区中的许多项目都由特别兴趣小组 (SIG) 管理。API Machinery SIG 用于维护与 K8s API 服务器接口的客户端库,包括用于通用 API CRUD 语义的包。API Machinery 的一个著名子项目 client-go,其是一个用于与 K8s API 服务器交互的官方 Go API。

    client-go 最常见的入口点是 kubernetes.Clientset,一组类型化的客户端,为每个核心资源类型(Pod、部署、服务等)提供预先生成的本地 API 对象。基于其易用性,建议大家尽可能使用此入口点。但是,使用类型化客户端可能会受到很大限制,因为代码往往与所使用的特定类型及版本紧密耦合。

client-go/dynamic 和非结构化对象

    API Machinery 的 universal-machinery 子项目维护了一个共享依赖库,供服务器和客户端使用 Kubernetes API 基础设施,没有直接的类型依赖。非结构化包是这个共享依赖库的一部分,允许对 K8s 资源对象进行通用操作。

  struct unstructured.Unstructured 是一种简单类型,它使用一组嵌套的 map[string]interface{} 值来创建一个内部结构,该结构与来自 K8s API 服务器的 REST 负载非常相似。

    client-go/dynamic 包提供了一个动态客户端,可以对任意 API 资源执行 RESTful 操作。struct dynamic.Interface 使用 unstructured.Unstructured 来表示来自 API 服务器的所有对象值。动态包将所有数据绑定推迟到运行时。

基本示例

    以下代码示例需要依赖项 k8s.io/client-go/kubernetes 和 sigs.k8s.io/controller-runtime。controller-runtime 项目是一组用于构建 Kubernetes 操作符的库。可以在没有控制器运行时的情况下使用 client-go,但简化了为 K8s API 服务器访问配置 client-go 客户端。

    在为 API 访问配置 client-go 时,有两种常见的配置方法。在 Pod 内运行时使用集群内配置,并使用挂载到 Pod 的服务帐户令牌。在集群外运行时使用集群外配置,并使用提供的 kubeconfig 文件或当前用户的默认 kubeconfig 文件。控制器运行时库提供了一个方便的多合一函数 GetConfig(),它首先尝试集群外配置,如果失败,则尝试集群内配置。

    要将所需的依赖项添加到 Go 项目,请执行以下命令:

go get k8s.io/client-go/kubernetes
go get sigs.k8s.io/controller-runtime

    以下示例在功能上等效,但演示了使用类型化客户端与动态客户端时的语义差异。

使用 kubernetes.Clientset 获取 K8s 对象

    以下代码片段定义了一个函数,用于使用来自 kubernetes.Clientset 的类型化部署客户端检索 K8s 部署对象。

package main
import (
  "context"
  "fmt"
  v1 "k8s.io/api/apps/v1"
  metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  "k8s.io/client-go/kubernetes"
  ctrl "sigs.k8s.io/controller-runtime"
)
func main() {
  ctx := context.Background()
  config := ctrl.GetConfigOrDie()
  clientset := kubernetes.NewForConfigOrDie(config)
  namespace := "default"
  items, err := GetDeployments(clientset, ctx, namespace)
  if err != nil {
    fmt.Println(err)
  } else {
    for _, item := range items {
      fmt.Printf("%+v\n", item)
    }
  }
}
func GetDeployments(clientset *kubernetes.Clientset, ctx context.Context,
      namespace string) ([]v1.Deployment, error) {
  list, err := clientset.AppsV1().Deployments(namespace).
    List(ctx, metav1.ListOptions{})
  if err != nil {
    return nil, err
  }
  return list.Items, nil
}

使用 dynamic.Interface 获取 K8s 对象

    以下代码片段定义了一个使用动态客户端检索 K8s 对象的函数。 调用该函数以检索默认命名空间中的部署列表。

import (
  "context"
  "fmt"
  metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
  "k8s.io/apimachinery/pkg/runtime/schema"
  "k8s.io/client-go/dynamic"
  ctrl "sigs.k8s.io/controller-runtime"
)
func main() {
  ctx := context.Background()
  config := ctrl.GetConfigOrDie()
  dynamic := dynamic.NewForConfigOrDie(config)
  namespace := "default"
  items, err := GetResourcesDynamically(dynamic, ctx,
        "apps", "v1", "deployments", namespace)
  if err != nil {
    fmt.Println(err)
  } else {
    for _, item := range items {
      fmt.Printf("%+v\n", item)
    }
  }
}
func GetResourcesDynamically(dynamic dynamic.Interface, ctx context.Context,
  group string, version string, resource string, namespace string) (
  []unstructured.Unstructured, error) {
  resourceId := schema.GroupVersionResource{
    Group:    group,
    Version:  version,
    Resource: resource,
  }
  list, err := dynamic.Resource(resourceId).Namespace(namespace).
    List(ctx, metav1.ListOptions{})
  if err != nil {
    return nil, err
  }
  return list.Items, nil
}

    在这两个示例中,我们很明显看到,使用类型化客户端来处理 K8s 对象更简单,代码更少。不过,动态方法更加强大和灵活,尤其是当资源类型事先未知或需要使用缺少关联 Golang 结构的自定义资源定义时。

高级示例

    真正受益于动态客户端提供的灵活性的用例是使用 jq 评估或改变 K8s 对象。 对于 JSON 数据,Jq 就像 sed、awk 和 grep。 它是 Kubectl 的有用伴侣,简化了 K8s 对象的读取、解析和变异。

    在这种情况下,为遇到的每个资源类型编写显式类型处理可能会很乏味。此外,可能无法提前知道所有可能遇到的资源类型。

    此代码示例使用 github.com/itchyny/gojq,这是 jq 的纯 Go 实现。要将所需的依赖项添加到 Go 项目,请执行以下命令:

go get github.com/itchyny/gojq

检查特定标签的 Kubernetes 对象

    以下代码片段重用了上一个示例中的 GetResourcesDynamically 函数来获取默认命名空间中的部署列表。然后检查每个部署是否使用 jq 将标签 app.kubernetes.io/managed-by 设置为 Helm 值。

    为了能够进行 jq 评估,必须将从 API 服务器返回的对象转换为 JSON。k8s.io/apimachinery/pkg/runtime 包通过在 runtime.DefaultUnstructuredConverter 提供一个非结构化到 JSON 的转换器来简化这个过程。一旦转换为 JSON,就会执行 jq 评估,如果它返回一个布尔结果并且结果为“true”,则将 K8s 对象添加到函数返回的切片中。

package main
import (
  "context"
  "fmt"
  "github.com/itchyny/gojq"
  "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
  "k8s.io/apimachinery/pkg/runtime"
  "k8s.io/client-go/dynamic"
  ctrl "sigs.k8s.io/controller-runtime"
)
func main() {
  ctx := context.Background()
  config := ctrl.GetConfigOrDie()
  dynamic := dynamic.NewForConfigOrDie(config)
  namespace := "default"
  query := ".metadata.labels[\"app.kubernetes.io/managed-by\"] == \"Helm\""
  items, err := GetResourcesByJq(dynamic, ctx, "apps", "v1", "deployments", namespace, query)
  if err != nil {
    fmt.Println(err)
  } else {
    for _, item := range items {
      fmt.Printf("%+v\n", item)
    }
  }
}
func GetResourcesByJq(dynamic dynamic.Interface, ctx context.Context, group string,
  version string, resource string, namespace string, jq string) (
  []unstructured.Unstructured, error) {
  resources := make([]unstructured.Unstructured, 0)
  query, err := gojq.Parse(jq)
  if err != nil {
    return nil, err
  }
  items, err := GetResourcesDynamically(dynamic, ctx, group, version, resource, namespace)
  if err != nil {
    return nil, err
  }
  for _, item := range items {
    // Convert object to raw JSON
    var rawJson interface{}
    err = runtime.DefaultUnstructuredConverter.FromUnstructured(item.Object, &rawJson)
    if err != nil {
      return nil, err
    }
    // Evaluate jq against JSON
    iter := query.Run(rawJson)
    for {
      result, ok := iter.Next()
      if !ok {
        break
      }
      if err, ok := result.(error); ok {
        if err != nil {
          return nil, err
        }
      } else {
        boolResult, ok := result.(bool)
        if !ok {
          fmt.Println("Query returned non-boolean value")
        } else if boolResult {
          resources = append(resources, item)
        }
      }
    }
  }
  return resources, nil
}

    再一次,使用类型化客户端可以更简单地完成上面的示例,并使用更少的代码。不过,这是因为我们知道我们正在处理部署并查看 Kubernetes 元数据,这在所有对象类型中都很常见。然而,设想一下,如果我们正在编写一个可以评估任何对象类型中的任何字段的函数,我们将需要多少代码。如果没有动态客户端的能力、对底层 JSON 内容的访问以及 jq,那将是一项无法完成的任务。

    概括

    在这篇文章中,我们使用 API machinery 子项目 client-go 提供的类型化和动态客户端评估了在 Go 中使用实时 Kubernetes 对象的情况。对于基本用例,类型化客户端提供对 K8s 对象的简单、优雅的访问。但是,如果对象类型很多,或者在类型之前不知道特定的对象类型,或者对象类型来自缺乏关联 Golang 结构体的第三方资源,那么动态客户端则提供了所需的灵活性。


  # 参考

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
1月前
|
存储 安全 Java
go语言重用对象
【10月更文挑战第19天】
29 1
|
7月前
|
Kubernetes API 调度
Kubernetes详解(十五)——Pod对象创建过程
Kubernetes详解(十五)——Pod对象创建过程
66 4
|
7月前
|
Kubernetes API 调度
Kubernetes详解(十四)——Pod对象生命周期
Kubernetes详解(十四)——Pod对象生命周期
60 3
|
3月前
|
Kubernetes Go Docker
在K8s编程中如何使用Go
一文带你了解在K8s编程中如何使用Go
93 3
|
4月前
|
Kubernetes 监控 Cloud Native
"解锁K8s新姿势!Cobra+Client-go强强联手,打造你的专属K8s监控神器,让资源优化与性能监控尽在掌握!"
【8月更文挑战第14天】在云原生领域,Kubernetes以出色的扩展性和定制化能力引领潮流。面对独特需求,自定义插件成为必要。本文通过Cobra与Client-go两大利器,打造一款监测特定标签Pods资源使用的K8s插件。Cobra简化CLI开发,Client-go则负责与K8s API交互。从初始化项目到实现查询逻辑,一步步引导你构建个性化工具,开启K8s集群智能化管理之旅。
63 2
|
4月前
|
运维 Kubernetes Go
"解锁K8s二开新姿势!client-go:你不可不知的Go语言神器,让Kubernetes集群管理如虎添翼,秒变运维大神!"
【8月更文挑战第14天】随着云原生技术的发展,Kubernetes (K8s) 成为容器编排的首选。client-go作为K8s的官方Go语言客户端库,通过封装RESTful API,使开发者能便捷地管理集群资源,如Pods和服务。本文介绍client-go基本概念、使用方法及自定义操作。涵盖ClientSet、DynamicClient等客户端实现,以及lister、informer等组件,通过示例展示如何列出集群中的所有Pods。client-go的强大功能助力高效开发和运维。
439 1
|
7月前
|
Kubernetes API 调度
Kubernetes详解(十五)——Pod对象创建过程
Kubernetes详解(十五)——Pod对象创建过程
106 5
|
7月前
|
Kubernetes API 调度
Kubernetes详解(十四)——Pod对象生命周期
Kubernetes详解(十四)——Pod对象生命周期
59 2
|
7月前
|
运维 Kubernetes Linux
Kubernetes详解(七)——Service对象部署和应用
Kubernetes详解(七)——Service对象部署和应用
79 3
|
7月前
|
Kubernetes 应用服务中间件 nginx
Kubernetes详解(六)——Pod对象部署和应用
在Kubernetes系列中,本文聚焦Pod对象的部署和管理。首先,通过`kubectl run`命令创建Pod,如`kubectl run pod-test --image=nginx:1.12 --port=80 --replicas=1`。接着,使用`kubectl get deployment`或`kubectl get pods`查看Pod信息,添加`-o wide`参数获取详细详情。然后,利用Pod的IP地址进行访问。最后,用`kubectl delete pods [Pod名]`删除Pod,但因Controller控制器,删除后Pod可能自动重建。了解更多细节,请参阅原文链接。
122 5
下一篇
DataWorks