You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go中用JSON服务账号密钥创建Kubernetes Clientset遇ADC报错求解

问题:显式提供Token创建K8s Clientset仍触发ADC查找崩溃?

我从JSON服务账号密钥文件提取Token,想用来创建Kubernetes Clientset。已经在配置里显式指定了Token,但程序还是在找Google默认应用凭据(ADC),找不到就崩溃了。

我的代码

package main

import (
    "context"
    "encoding/base64"
    "fmt"
    "io/ioutil"

    "golang.org/x/oauth2"
    "golang.org/x/oauth2/google"
    gke "google.golang.org/api/container/v1"
    "google.golang.org/api/option"
    "k8s.io/client-go/kubernetes"
    _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
    "k8s.io/client-go/tools/clientcmd"
    "k8s.io/client-go/tools/clientcmd/api"
)

const (
    projectID   = "my_project_id"
    clusterName = "my_cluster_name"
    scope       = "https://www.googleapis.com/auth/cloud-platform"
)

func main() {
    ctx := context.Background()

    // 读取JSON密钥并提取token
    data, err := ioutil.ReadFile("sa_key.json")
    if err != nil {
        panic(err)
    }
    creds, err := google.CredentialsFromJSON(ctx, data, scope)
    if err != nil {
        panic(err)
    }
    token, err := creds.TokenSource.Token()
    if err != nil {
        panic(err)
    }
    fmt.Println("token", token.AccessToken)

    // 创建GKE客户端
    tokenSource := oauth2.StaticTokenSource(token)
    gkeClient, err := gke.NewService(ctx, option.WithTokenSource(tokenSource))
    if err != nil {
        panic(err)
    }

    // 创建内存中的kube配置
    inMemKubeConfig, err := createInMemKubeConfig(ctx, gkeClient, token, projectID)
    if err != nil {
        panic(err)
    }

    // 转换为rest.Config
    config, err := clientcmd.NewNonInteractiveClientConfig(*inMemKubeConfig, clusterName, &clientcmd.ConfigOverrides{CurrentContext: clusterName}, nil).ClientConfig()
    if err != nil {
        panic(err)
    }

    // 创建Clientset(此处崩溃,提示找不到Google ADC)
    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        panic(err)
    }

    fmt.Printf("clientset %+v\n", clientset)
}

func createInMemKubeConfig(ctx context.Context, client *gke.Service, token *oauth2.Token, projectID string) (*api.Config, error) {
    k8sConf := api.Config{
        APIVersion: "v1",
        Kind:       "Config",
        Clusters:   map[string]*api.Cluster{},
        AuthInfos:  map[string]*api.AuthInfo{},
        Contexts:   map[string]*api.Context{},
    }

    // 列出项目下所有可用区的集群
    resp, err := client.Projects.Zones.Clusters.List(projectID, "-").Context(ctx).Do()
    if err != nil {
        return nil, err
    }

    for _, f := range resp.Clusters {
        name := fmt.Sprintf("gke_%s_%s_%s", projectID, f.Zone, f.Name) // 自定义集群命名规则
        cert, err := base64.StdEncoding.DecodeString(f.MasterAuth.ClusterCaCertificate)
        if err != nil {
            return nil, err
        }

        k8sConf.Clusters[name] = &api.Cluster{
            CertificateAuthorityData: cert,
            Server:                   "https://" + f.Endpoint,
        }

        k8sConf.Contexts[name] = &api.Context{
            Cluster:  name,
            AuthInfo: name,
        }

        k8sConf.AuthInfos[name] = &api.AuthInfo{
            Token: token.AccessToken,
            AuthProvider: &api.AuthProviderConfig{
                Name: "gcp",
                Config: map[string]string{
                    "scopes": scope,
                },
            },
        }
    }
    return &k8sConf, nil
}

报错信息

panic: 无法构造google默认token源: google: 找不到默认凭据。


解决方案

问题出在你给AuthInfo同时设置了Token和AuthProvider:当AuthProvider存在时,client-go会优先执行它的认证逻辑,而非直接使用你提供的Token。gcp类型的AuthProvider默认会尝试加载ADC,这就是程序仍在查找ADC的根本原因。

修复步骤

在createInMemKubeConfig函数中,删除AuthInfo里的AuthProvider配置,只保留Token字段:

k8sConf.AuthInfos[name] = &api.AuthInfo{
    Token: token.AccessToken,
}

修改后,client-go会直接使用你提供的Token完成认证,不会再触发ADC查找逻辑。

进阶优化(Token自动刷新)

如果程序需要长期运行,要注意Token的过期问题。可以直接将TokenSource集成到rest.Config中,实现Token自动刷新:

// 替换原有的ClientConfig创建逻辑,直接构建rest.Config
// 注意:根据实际情况选择目标集群
targetCluster := resp.Clusters[0]
cert, _ := base64.StdEncoding.DecodeString(targetCluster.MasterAuth.ClusterCaCertificate)

config := &rest.Config{
    Host:        "https://" + targetCluster.Endpoint,
    TLSClientConfig: rest.TLSClientConfig{
        CAData: cert,
    },
    // 设置TokenSource,Token过期时自动刷新
    BearerTokenSource: creds.TokenSource,
}

// 直接用该config创建clientset
clientset, err := kubernetes.NewForConfig(config)

这样就不需要手动管理Token的过期和刷新了。


内容的提问来源于stack exchange,提问作者Emre Chomko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 22:36:20