bigywater.tech
Kubernetes · 28

kubeconfig:contexts、clusters 与 users 的解析

连错集群、用错账号、命名空间莫名其妙——多半是 kubeconfig 的合并和覆盖规则没搞清。这篇不看文档,用两份内容故意冲突的文件和两个假 apiserver,把环境变量顺序、命令行参数覆盖、相对路径解析都跑成可以直接对照的结果。

ctx-a / ctx-b
同样两个文件,KUBECONFIG 顺序 a:b 与 b:a 时的 current-context
9821 / 9822
同一条 kubectl get pods,默认与 --context=ctx-b 时请求实际打到的两个假 apiserver 端口
REDACTED
kubectl config view 默认对令牌的显示

同名条目:先到先得

实测 · 本机执行输出
  current-context=ctx-a
  clusters:{'c-a': 'https://127.0.0.1:9821', 'c-b': 'https://127.0.0.1:9822', 'c-shared': 'https://127.0.0.1:9821'}
  users(token 在默认视图里被遮掉,这里取自 --raw):{'u-a': 'token-from-A', 'u-b': 'token-from-B'}
  contexts:{'ctx-a': ('c-a', 'u-a', 'ns-a'), 'ctx-b': ('c-b', 'u-b', 'ns-b'), 'ctx-shared': ('c-shared', 'u-a', None)}
  规则:同名的 cluster / user / context,先出现的文件胜出(a 里的 c-shared、u-a、ctx-shared 生效,b 里的被忽略);不同名的合并;current-context 取第一个设了它的文件

a.yaml 和 b.yaml 里都有 c-shared 集群、u-a 用户、ctx-shared context,内容不同。KUBECONFIG=a.yaml:b.yaml 合并后:current-context=ctx-a(第一个设了它的文件),c-shared 指向 a 的服务器,u-a 的令牌是 token-from-A;只在 b 里有的 c-b、u-b、ctx-b 被并进来。规则:同名条目先出现的文件胜出,后面的整条被忽略;不同名的合并。

顺序反过来

实测 · 本机执行输出
  current-context=ctx-b
  clusters:{'c-a': 'https://127.0.0.1:9821', 'c-b': 'https://127.0.0.1:9822', 'c-shared': 'https://127.0.0.1:9822'}
  users:{'u-a': 'token-from-B-for-u-a', 'u-b': 'token-from-B'}
  contexts:{'ctx-a': ('c-a', 'u-a', 'ns-a'), 'ctx-b': ('c-b', 'u-b', 'ns-b'), 'ctx-shared': ('c-shared', 'u-b', None)}
  --minify(只留当前 context 用到的部分):server+namespace = https://127.0.0.1:9822 ns-b
  文件的位置不同,最终的集群地址 / 用户凭据就不同——KUBECONFIG 里的顺序是有意义的

KUBECONFIG=b.yaml:a.yaml 之后:current-context=ctx-b,c-shared 变成 b 的服务器,u-a 的令牌变成 token-from-B-for-u-a,ctx-shared 引用的用户也变成 u-b。同样的两个文件,顺序不同,实际连到哪、用谁的令牌就不同——排查“怎么连到别的集群了”,先看 KUBECONFIG 里的顺序,再用 kubectl config view --minify 看实际生效的那一份。

命令行覆盖

实测 · 本机执行输出
  默认(a.yaml:b.yaml,当前 context=ctx-a):请求打到端口 9821,路径 /api/v1/namespaces/ns-a/pods,Authorization=Bearer token-from-A
  --context=ctx-b:请求打到端口 9822,路径 /api/v1/namespaces/ns-b/pods,Authorization=Bearer token-from-B
  --cluster=c-b(只换集群):请求打到端口 9822,路径 /api/v1/namespaces/ns-a/pods,Authorization=Bearer token-from-A
  --user=u-b(只换用户):请求打到端口 9821,路径 /api/v1/namespaces/ns-a/pods,Authorization=Bearer token-from-B
  --namespace=other(只换命名空间):请求打到端口 9821,路径 /api/v1/namespaces/other/pods,Authorization=Bearer token-from-A
  --cluster=c-b --user=u-b --namespace=other:请求打到端口 9822,路径 /api/v1/namespaces/other/pods,Authorization=Bearer token-from-B
  --token=cli-token(命令行直接给令牌):请求打到端口 9821,路径 /api/v1/namespaces/ns-a/pods,Authorization=Bearer cli-token
  --kubeconfig=b.yaml(环境变量里的 a.yaml:b.yaml 被整个忽略):请求打到端口 9822,路径 /api/v1/namespaces/ns-b/pods,Authorization=Bearer token-from-B
  --context=no-such:退出码 1;Error in configuration: context was not found for specified context: no-such

真的发一次 kubectl get pods,看假 apiserver 收到什么:默认是 9821、/api/v1/namespaces/ns-a/pods、Bearer token-from-A。--context=ctx-b 整套换;--cluster 只换服务器(命名空间和令牌不变),--user 只换令牌,--namespace 只换路径里的命名空间,--token 直接覆盖令牌。--kubeconfig 不是“再合并一个文件”,而是让环境变量里的 KUBECONFIG 被整个忽略。注意:kubectl config view --minify --cluster=… 这类命令只是打印文件内容,不会应用这些覆盖,验证覆盖要看真实请求。

路径与遮掩

实测 · 本机执行输出
  默认视图里的 c-a 集群与用户(a.yaml 的 ctx-a):insecure-skip-tls-verify: true | server: https://127.0.0.1:9821 | token: REDACTED
  ctx-shared(CA 写的是相对路径 sub/ca.pem):certificate-authority: sub/ca.pem | server: https://127.0.0.1:9821
  --flatten 把引用的文件内容嵌进去:certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmYWtlLWNh
  工作目录是 /(与 kubeconfig 不在同一目录)时上面的路径仍然被正确解析:是——解析基准是 a.yaml 所在目录 k22/
  删掉 sub/ca.pem 之后 kubectl get nodes:退出码 1;error: unable to read certificate-authority <ktmp>/k22/sub/ca.pem for c-shared due to open <ktmp>/k22/sub/ca.pem: no such file or directory
  对照:kind 给 kx-main 生成的 kubeconfig:CURRENT   NAME           CLUSTER        AUTHINFO       NAMESPACE | *         kind-kx-main   kind-kx-main   kind-kx-main

kubeconfig 里的相对路径(这里是 certificate-authority: sub/ca.pem)以 kubeconfig 文件所在的目录为基准,不是当前工作目录;--flatten 把引用的文件内容嵌成 certificate-authority-data,搬到别的机器上不会失效。文件缺了,报 unable to read certificate-authority … for c-shared,错误里带着解析后的绝对路径。kubectl config view 默认把令牌显示成 REDACTED,要看原文得加 --raw。

分步演示

kubeconfig 解析
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / kind(Docker 里的单节点或多节点真实集群,节点是 Docker Desktop 虚拟机里的容器)/ kubectl;用空的 kubeconfig 之外的独立 kubeconfig,不碰用户自己的集群;计时用 date +%s%N / kubectl 事件时间戳,数字每次运行会有小幅抖动。两份手写的 kubeconfig(a.yaml、b.yaml,放在实验目录,令牌是假字符串);“集群”是宿主机上两个用 Python http.server 起的 HTTPS 假 apiserver(端口 9821 与 9822,自签证书,只记录收到的路径与 Authorization 头,对 /api、/api/v1、/apis 回最小的发现响应),所以第 3 节看到的是 kubectl 真实发出的请求;合并结果取自 kubectl config view -o json;每条命令都显式设置 KUBECONFIG 或 --kubeconfig,并把 KUBECACHEDIR 指到实验目录,没有读写 ~/.kube;实验后删除了临时文件;每种情形各一次
    • 没有验证:client-go 的 exec 凭据插件(users.user.exec)与 auth-provider;in-cluster 配置(Pod 里没有 kubeconfig 时的回退);KUBECONFIG 里文件不存在或格式错误时的行为;kubectl config set / use-context 对多文件的写入位置(写入第一个文件);proxy-url、tls-server-name 等其他集群字段;默认 ~/.kube/config 的回退(为了不碰用户自己的文件没有测)
    京ICP备2026067502号-1京公网安备11010502063487号