K8s 多集群 Kubeconfig 合并填坑记录(Sealos 劫持与合并覆盖)

今天折腾两套独立 K8s 集群的跳板机权限管理,原本以为用原生的 kubectl config view --flatten 打平合并一下就行,结果连踩三个暗坑。记录一下排障过程。

环境说明: 两套独立部署的 K8s 集群(开发 10.20.30.128,生产 10.20.20.40)。跳板机需要通过 use-context 自由切换。

坑一:同名覆盖导致配置丢失

Kubeadm 或 Sealos 默认部署的集群,其 admin.conf 内部命名完全一致(集群名统一为 kubernetes,用户名为 kubernetes-admin)。 如果直接拿这两份原始配置执行 flatten 合并,后者的配置会直接覆盖前者,导致合并后的文件里只剩下一个集群的 IP 和证书。

解决办法:合并前对源文件进行内部对象重命名

切忌直接用 sed 盲替,因为 kubernetes-admin 包含了 kubernetes,替换顺序不对会导致名字变成畸形的 cluster-XXX-admin。正确的替换顺序如下:

# 1. 备份原文件
scp root@10.20.30.128:/etc/kubernetes/admin.conf ~/.kube/config-128-raw
scp root@10.20.20.40:/etc/kubernetes/admin.conf ~/.kube/config-40-raw

# 2. 严格按由长到短的顺序替换 128 集群
sed -e 's/kubernetes-admin@kubernetes/context-128/g' \
    -e 's/kubernetes-admin/admin-128/g' \
    -e 's/kubernetes/cluster-128/g' \
    ~/.kube/config-128-raw > ~/.kube/config-128

# 3. 替换 40 集群
sed -e 's/kubernetes-admin@kubernetes/context-40/g' \
    -e 's/kubernetes-admin/admin-40/g' \
    -e 's/kubernetes/cluster-40/g' \
    ~/.kube/config-40-raw > ~/.kube/config-40

# 4. 执行原生打平合并
export KUBECONFIG=~/.kube/config-128:~/.kube/config-40
kubectl config view --flatten > ~/.kube/config

坑二:Sealos 劫持 API 域名报 dial tcp no such host

合并完执行 kubectl get pods,直接报错: couldn't get current server API group list: Get "https://apiserver.cluster.local:6443/api?timeout=32s": dial tcp: lookup apiserver.cluster.local on 223.5.5.5:53: no such host

原因: Sealos 为了做高可用,会在 kubeconfig 里把 API Server 地址写死为假域名 apiserver.cluster.local,并依赖集群节点的 /etc/hosts 来解析。 在独立的跳板机上,本地没这个 hosts 映射,请求自然被打到了公网 DNS(223.5.5.5)从而解析失败。

解决办法:替换真实 IP 千万别在跳板机的 /etc/hosts 里配解析,因为你有两套集群,共用这一个假域名肯定会冲突。 正确的做法是打开最终的 ~/.kube/config,找到对应集群的 server: 字段,把 https://apiserver.cluster.local:6443 直接改成对应集群的真实 Master IP 或 VIP:

clusters:
- name: cluster-128
  cluster:
    server: https://10.20.30.128:6443  # 这里换成真实 IP

坑三:修改 config 后未生效

排障过程中发现一个诡异现象:修改了 ~/.kube/config 后依然报错,但单独修改原本的 config-40 却能立刻生效。

原因: 第一步合并操作时执行的 export KUBECONFIG=~/.kube/config-128:~/.kube/config-40 在当前终端依然生效。 在 K8s 的底层加载逻辑中,KUBECONFIG 环境变量的优先级绝对高于默认文件路径。此时 kubectl 动态读取的是这两个原始文件,完全无视了刚改好的 ~/.kube/config

解决办法: 执行 unset KUBECONFIG 销毁临时环境变量,或者断开当前 SSH 会话重连即可。

评论