使用 clientcmd 实现统一的 API 服务器访问

发布: (2026年1月20日 GMT+8 02:00)
11 min read

Source: Kubernetes Blog

如果你曾想为 Kubernetes API 开发一个命令行客户端——尤其是可以作为 kubectl 插件使用的——你可能会想如何让你的客户端对 kubectl 用户来说感觉熟悉。
快速浏览 kubectl options 的输出可能会让人望而生畏:

“我真的需要实现所有这些选项吗?”

别担心!Kubernetes 项目已经提供了两个库,帮你完成大部分繁重的工作:

  • clientcmd – 解析类似 kubectl 的命令行参数并构建 restclient.Config
  • cli‑runtime – 基于 clientcmd 构建。

本文聚焦于 clientcmd 库。

通用理念

因为 clientcmdclient‑go 的一部分,它的最终目标是生成一个可以向 API 服务器发送请求的 restclient.Config。它遵循与 kubectl 相同的语义:

SourceDescription
~/.kube/config (or equivalent)默认配置
KUBECONFIG environment variableKUBECONFIG 环境变量——一个或多个文件,其内容将被合并
Command‑line arguments命令行参数——覆盖上述任意设置

clientcmd 不会 自动添加 --kubeconfig 标志;您将在 “绑定标志” 部分看到如何添加它。

可用功能

clientcmd 让程序能够处理:

  • kubeconfig 选择(via KUBECONFIG
  • 上下文选择
  • 命名空间选择
  • 客户端证书 & 私钥
  • 用户冒充
  • HTTP Basic 认证(username/password)

配置合并

  • KUBECONFIG 可以包含一个 冒号分隔的文件列表;它们的内容会被合并。
  • 基于映射的设置第一次 定义生效,后续的会被忽略。
  • 非映射设置最后一次 定义生效。
  • KUBECONFIG 中引用的缺失文件仅会产生 警告
  • 如果用户显式提供路径(例如 --kubeconfig),则该文件 必须存在
  • KUBECONFIG 未设置时,使用 ~/.kube/config(如果存在)。

整体流程

典型的使用模式在 clientcmd 包文档中有概述:

// 1️⃣ Build loading rules (defaults to KUBECONFIG or ~/.kube/config)
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
//   – you can customise the order / files here if needed

// 2️⃣ Prepare overrides (populated from flags later)
configOverrides := &clientcmd.ConfigOverrides{}

// 3️⃣ Create a deferred‑loading client config
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
    loadingRules,
    configOverrides,
)

// 4️⃣ Resolve the final REST config
config, err := kubeConfig.ClientConfig()
if err != nil {
    // handle error
}

// 5️⃣ Build a client (example using the dynamic client)
client, err := dynamic.NewForConfig(config)
if err != nil {
    // handle error
}

在本文中,我们将逐步演示 六个步骤,对应上面代码中的编号注释:

  1. 配置加载规则
  2. 配置覆盖项
  3. 构建一组标志(flags)
  4. 绑定这些标志
  5. 生成合并后的配置
  6. 获取 API 客户端

1️⃣ 配置加载规则

loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
  • 如果已设置 KUBECONFIG,则使用它;否则回退到 ~/.kube/config
  • 如果使用默认文件,它还能从非常旧的默认位置(~/.kube/.kubeconfig)迁移设置。

如果需要非标准的顺序或额外的文件,可以创建自定义的 ClientConfigLoadingRules,但默认设置已能满足大多数情况。

2️⃣ 配置覆盖

clientcmd.ConfigOverrides 是一个结构体,用于存储将 覆盖 从 kubeconfig 文件加载的任何值。在本指南中,覆盖将通过命令行标志(使用 pflag 库,它是 Go 的 flag 包的直接替代)进行填充。

configOverrides := &clientcmd.ConfigOverrides{}

通常你不需要在这里手动设置任何内容;你将在下一步将结构体字段绑定到标志。

3️⃣ 构建一组 Flag

Flag 代表一个命令行参数(例如 --namespace-n)。clientcmd 提供了三组 Flag,每组都封装在 FlagInfo 结构体中:

Flag 组别常用参数
Authentication(认证)--certificate-authority--token--as--username--password
Cluster(集群)--server--certificate-authority--insecure-skip-tls-verify--proxy-url--tls-server-name--compress
Context(上下文)--context--cluster--user--namespace

推荐 的 Flag 集合包括 全部三组,再加上 --timeout Flag。

推荐的 Flag 构造函数

// No prefix → flags like --context, --namespace, etc.
flags := clientcmd.RecommendedConfigOverrideFlags("")
// With a prefix → flags like --from-context, --from-namespace, etc.
flags := clientcmd.RecommendedConfigOverrideFlags("from-")
  • --timeout Flag 的默认值为 0
  • --namespace Flag 还会获得一个短别名 -n

注意: 前缀 不会 影响短名称。如果创建多个带前缀的 Flag 集合,只有 一个 能保留 -n 别名。需要在其他集合上清除短名称:

kflags := clientcmd.RecommendedConfigOverrideFlags(prefix)
kflags.ContextOverrideFlags.Namespace.ShortName = "" // removes -n

4️⃣ 绑定标志

现在将标志定义连接到 ConfigOverrides 结构体,使得在命令行中输入的值能够填充到覆盖配置中。

// Assume you have a pflag.FlagSet called fs
fs := pflag.NewFlagSet("myclient", pflag.ExitOnError)

// Build the flag structs (choose a prefix if you like)
flags := clientcmd.RecommendedConfigOverrideFlags("")

// Register the flags
flags.BindFlags(fs)                     // registers the flags
flags.BindFlagSet(fs, configOverrides) // populates configOverrides when parsed

如果你还想要一个 --kubeconfig 标志(与 kubectl 保持一致),可以手动添加:

fs.StringVar(&configOverrides.ClusterInfo.KubeConfigPath, "kubeconfig", "", "Path to the kubeconfig file")

最后,解析命令行参数:

if err := fs.Parse(os.Args[1:]); err != nil {
    // handle parse error
}

5️⃣ 构建合并后的配置

在加载规则和覆盖已准备好的情况下,创建延迟加载的客户端配置并解析最终的 rest.Config

kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
    loadingRules,
    configOverrides,
)

restConfig, err := kubeConfig.ClientConfig()
if err != nil {
    // handle error (e.g., missing kubeconfig, invalid overrides)
}

restConfig 现在包含了以下来源的合并设置:

  • KUBECONFIG(或默认文件)引用的文件
  • 通过标志提供的覆盖项

6️⃣ 获取 API 客户端

使用 rest.Config 实例化所需的任意 Kubernetes 客户端。以下是一些常见示例:

// CoreV1 client
coreClient, err := kubernetes.NewForConfig(restConfig)

// Dynamic client (works with any resource)
dynClient, err := dynamic.NewForConfig(restConfig)

// Discovery client
discClient, err := discovery.NewDiscoveryClientForConfig(restConfig)

从这里你就可以像 kubectl 那样与集群交互。

TL;DR – 六步检查清单

步骤代码片段
1️⃣ 加载规则loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
2️⃣ 覆盖configOverrides := &clientcmd.ConfigOverrides{}
3️⃣ 标志flags := clientcmd.RecommendedConfigOverrideFlags("")
4️⃣ 绑定flags.BindFlags(fs); flags.BindFlagSet(fs, configOverrides)
5️⃣ 合并kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides); restConfig, _ := kubeConfig.ClientConfig()
6️⃣ 客户端client, _ := kubernetes.NewForConfig(restConfig)

使用这些步骤,你可以让你的 Go CLI 拥有与 kubectl 相同的外观和体验,而无需自行重新实现每个标志。祝编码愉快!

禁用标志

如果你想完全禁用某个标志,请清空它的长名称:

kflags.ContextOverrideFlags.Namespace.LongName = ""

Bind the Flags (alternative)

一旦定义了一组标志,就可以使用 clientcmd.BindOverrideFlags 将命令行参数绑定到覆盖。这需要一个 pflag FlagSet(而不是 Go 标准的 flag 包)。

如果你还想绑定 --kubeconfig,现在就通过在加载规则中绑定 ExplicitPath 来实现:

flags.StringVarP(&loadingRules.ExplicitPath,
    "kubeconfig", "", "", "absolute path(s) to the kubeconfig file(s)")

Build the Merged Configuration (alternative)

有两个函数可用于构建合并后的配置:

  • clientcmd.NewInteractiveDeferredLoadingClientConfig
  • clientcmd.NewNonInteractiveDeferredLoadingClientConfig

交互式版本可以通过提供的读取器交互式地请求身份验证信息,而 非交互式版本仅使用调用者提供的信息。两者都是“延迟”的:你可以在解析命令行参数之前调用它们;实际构建时,生成的配置会合并截至那时已解析的所有标志值。

获取 API 客户端(替代方案)

合并后的配置会作为 ClientConfig 实例返回。您可以通过调用 ClientConfig() 方法从中获取 API 客户端。

如果未找到配置(例如 KUBECONFIG 为空或指向不存在的文件,~/.kube/config 不存在,且未提供命令行覆盖),默认设置会返回一个提及 KUBERNETES_MASTER 的模糊错误。此遗留行为仅在 kubectl--local--dry-run 标志下保留。

提示: 使用 clientcmd.IsEmptyConfig(err) 检测 “空配置” 错误,并向用户展示更清晰的提示信息。

Namespace() 方法也很方便——它返回应使用的命名空间,并指示该命名空间是否被用户(通过 --namespace)覆盖。

完整示例

以下是一个完整的、可运行的示例:

package main

import (
	"context"
	"fmt"
	"os"

	"github.com/spf13/pflag"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
)

func main() {
	// Loading rules – no configuration yet
	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()

	// Overrides and flag (command‑line argument) setup
	configOverrides := &clientcmd.ConfigOverrides{}
	flags := pflag.NewFlagSet("clientcmddemo", pflag.ExitOnError)

	// Bind the standard override flags
	clientcmd.BindOverrideFlags(configOverrides, flags,
		clientcmd.RecommendedConfigOverrideFlags(""))

	// Bind the --kubeconfig flag
	flags.StringVarP(&loadingRules.ExplicitPath,
		"kubeconfig", "", "", "absolute path(s) to the kubeconfig file(s)")

	// Parse the command‑line arguments
	flags.Parse(os.Args)

	// Construct the client configuration (non‑interactive)
	kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
		loadingRules, configOverrides)

	// Get a REST config for the client‑go libraries
	config, err := kubeConfig.ClientConfig()
	if err != nil {
		if clientcmd.IsEmptyConfig(err) {
			panic("Please provide a configuration pointing to the Kubernetes API server")
		}
		panic(err)
	}

	// Build the typed clientset
	client, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err)
	}

	// Determine which namespace to use
	namespace, overridden, err := kubeConfig.Namespace()
	if err != nil {
		panic(err)
	}
	fmt.Printf("Chosen namespace: %s; overridden: %t\n", namespace, overridden)

	// Use the client – list all nodes
	nodeList, err := client.CoreV1().Nodes().List(context.TODO(), v1.ListOptions{})
	if err != nil {
		panic(err)
	}
	for _, node := range nodeList.Items {
		fmt.Println(node.Name)
	}
}

祝编码愉快,感谢您对使用熟悉的使用模式构建工具的兴趣!

Back to Blog

相关文章

阅读更多 »

StatefulSet 项目

先决条件:StatefulSet 需要以下组件: - Headless Service —— 为每个 pod 提供稳定的 DNS。 - StatefulSet manifest —— 定义 pod……