【问题标题】:GO get K8S api server health statusGO 获取 K8S api 服务器健康状态
【发布时间】:2021-01-14 17:31:01
【问题描述】:

我有一个 golang 程序,我需要向K8S API server status (livez) api 添加一个新调用以获取运行状况。

https://kubernetes.io/docs/reference/using-api/health-checks/

程序应该在api服务器的同一个集群上运行并且需要获得/livez状态,我试图在client-go lib中找到这个API但没有找到实现它的方法...

https://github.com/kubernetes/client-go

有没有办法从运行在 API 服务器运行的同一个集群上的 Go 程序中做到这一点?

【问题讨论】:

    标签: go kubernetes


    【解决方案1】:

    更新(最终答案)

    附加

    OP 要求我修改我的答案以显示“微调”或“特定”服务帐户的配置,而不使用集群管理员..

    据我所知,默认情况下,每个 pod 都具有从 /healthz 读取的权限。例如,下面的CronJob 完全不使用ServiceAccount 就可以正常工作:

    # cronjob
    apiVersion: batch/v1beta1
    kind: CronJob
    metadata:
      name: is-healthz-ok-no-svc
    spec:
      schedule: "*/5 * * * *" # at every fifth minute
      jobTemplate:
        spec:
          template:
            spec:
    ######### serviceAccountName: health-reader-sa
              containers:
                - name: is-healthz-ok-no-svc
                  image: oze4/is-healthz-ok:latest
              restartPolicy: OnFailure
    

    原创

    我继续为此写了一个概念证明。 You can find the full repo here,但代码如下。

    main.go

    package main
    
    import (
        "os"
        "errors"
        "fmt"
    
        "k8s.io/client-go/kubernetes"
        "k8s.io/client-go/rest"
    )
    
    func main() {
        client, err := newInClusterClient()
        if err != nil {
            panic(err.Error())
        }
    
        path := "/healthz"
        content, err := client.Discovery().RESTClient().Get().AbsPath(path).DoRaw()
        if err != nil {
            fmt.Printf("ErrorBadRequst : %s\n", err.Error())
            os.Exit(1)
        }
    
        contentStr := string(content)
        if contentStr != "ok" {
            fmt.Printf("ErrorNotOk : response != 'ok' : %s\n", contentStr)
            os.Exit(1)
        }
    
        fmt.Printf("Success : ok!")
        os.Exit(0)
    }
    
    func newInClusterClient() (*kubernetes.Clientset, error) {
        config, err := rest.InClusterConfig()
        if err != nil {
            return &kubernetes.Clientset{}, errors.New("Failed loading client config")
        }
        clientset, err := kubernetes.NewForConfig(config)
        if err != nil {
            return &kubernetes.Clientset{}, errors.New("Failed getting clientset")
        }
        return clientset, nil
    }
    

    码头文件

    FROM golang:latest
    RUN mkdir /app
    ADD . /app
    WORKDIR /app
    RUN go build -o main .
    CMD ["/app/main"]
    

    deploy.yaml

    (作为 CronJob)

    # cronjob
    apiVersion: batch/v1beta1
    kind: CronJob
    metadata:
      name: is-healthz-ok
    spec:
      schedule: "*/5 * * * *" # at every fifth minute
      jobTemplate:
        spec:
          template:
            spec:
              serviceAccountName: is-healthz-ok
              containers:
                - name: is-healthz-ok
                  image: oze4/is-healthz-ok:latest
              restartPolicy: OnFailure
    ---
    # service account
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: is-healthz-ok
      namespace: default
    ---
    # cluster role binding
    kind: ClusterRoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: is-healthz-ok
    subjects:
      - kind: ServiceAccount
        name: is-healthz-ok
        namespace: default
    roleRef:
      kind: ClusterRole
      ##########################################################################
      # Instead of assigning cluster-admin you can create your own ClusterRole #
      # I used cluster-admin because this is a homelab                         #
      ##########################################################################
      name: cluster-admin
      apiGroup: rbac.authorization.k8s.io
    ---
    

    截图

    成功的 CronJob 运行


    更新 1

    OP 询问如何部署“in-cluster-client-config”,所以我提供了一个示例部署(我正在使用的)..

    你可以找到repo here

    示例部署(我使用的是 CronJob,但它可以是任何东西):

    cronjob.yaml

    apiVersion: batch/v1beta1
    kind: CronJob
    metadata:
      name: remove-terminating-namespaces-cronjob
    spec:
      schedule: "0 */1 * * *" # at minute 0 of each hour aka once per hour
      #successfulJobsHistoryLimit: 0
      #failedJobsHistoryLimit: 0
      jobTemplate:
        spec:
          template:
            spec:
              serviceAccountName: svc-remove-terminating-namespaces
              containers:
              - name: remove-terminating-namespaces
                image: oze4/service.remove-terminating-namespaces:latest
              restartPolicy: OnFailure
    

    rbac.yaml

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: svc-remove-terminating-namespaces
      namespace: default
    ---
    kind: ClusterRoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: crb-namespace-reader-writer
    subjects:
    - kind: ServiceAccount
      name: svc-remove-terminating-namespaces
      namespace: default
    roleRef:
      kind: ClusterRole
      ##########################################################################
      # Instead of assigning cluster-admin you can create your own ClusterRole #
      # I used cluster-admin because this is a homelab                         #
      ##########################################################################
      name: cluster-admin
      apiGroup: rbac.authorization.k8s.io
    ---
    

    原答案

    听起来您正在寻找的是来自 client-go 的“in-cluster-client-config”。

    请务必记住,当使用“in-cluster-client-config”时,Go 代码中的 API 调用会使用“that”pod 的服务帐户。只是想确保您使用有权读取“/livez”的帐户进行测试。

    我测试了以下代码,我能够获得“livez”状态..

    package main
    
    import (
        "errors"
        "flag"
        "fmt"
        "path/filepath"
    
        "k8s.io/client-go/kubernetes"
        "k8s.io/client-go/tools/clientcmd"
        "k8s.io/client-go/rest"
        "k8s.io/client-go/util/homedir"
    )
    
    func main() {
        // I find it easiest to use "out-of-cluster" for tetsing
        // client, err := newOutOfClusterClient()
    
        client, err := newInClusterClient()
        if err != nil {
            panic(err.Error())
        }
    
        livez := "/livez"
        content, _ := client.Discovery().RESTClient().Get().AbsPath(livez).DoRaw()
    
        fmt.Println(string(content))
    }
    
    func newInClusterClient() (*kubernetes.Clientset, error) {
        config, err := rest.InClusterConfig()
        if err != nil {
            return &kubernetes.Clientset{}, errors.New("Failed loading client config")
        }
        clientset, err := kubernetes.NewForConfig(config)
        if err != nil {
            return &kubernetes.Clientset{}, errors.New("Failed getting clientset")
        }
        return clientset, nil
    }
    
    // I find it easiest to use "out-of-cluster" for tetsing
    func newOutOfClusterClient() (*kubernetes.Clientset, error) {
        var kubeconfig *string
        if home := homedir.HomeDir(); home != "" {
            kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
        } else {
            kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
        }
        flag.Parse()
    
        // use the current context in kubeconfig
        config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
        if err != nil {
            return nil, err
        }
    
        // create the clientset
        client, err := kubernetes.NewForConfig(config)
        if err != nil {
            return nil, err
        }
    
        return client, nil
    }
    

    【讨论】:

    • 您好!我想对其进行测试,但出现两个错误:1. 运行 go mod init 和 vendor 时出现错误“go: finding module for package k8s.io/api/auditregistration/v1alpha1 apiserver imports k8s.io/client-go/kubernetes imports k8s.io/client-go/kubernetes/typed/auditregistration/v1alpha1 imports k8s.io/api/auditregistration/v1alpha1: module k8s.io/api@latest found (v0.19.2), but does not contain package k8s.io/api/auditregistration/v1alpha1 what I should do?
    • 第二个错误在这个api中client.Discovery().RESTClient().Get().AbsPath(livez).DoRaw()错误是:Not enough arguments in call to 'client.Discovery().RESTClient().Get().AbsPath(livez).DoRaw'
    • 这是SDK的问题。该问题特定于您的机器,因此您需要对其进行故障排除。确保您的 go.mod 看起来像这样 require ( k8s.io/api v0.17.0 k8s.io/apimachinery v0.17.0 k8s.io/client-go v0.17.0 )
    • 好吧,我能够解决这个问题,当我使用 outClusterConfig 运行它时,我正在获取 api `fmt.Println(string(content))` 的列表,但我想 ping到 api 服务器"/healthz", 端点并检查是否存在,我们如何实现这一点?这是输出{ "paths": [ "/apis", "/apis/", "/apis/apiextensions.k8s.io", "/apis/apiextensions.k8s.io/v1beta1", "/healthz", "/healthz/etcd", "/healthz/log", "/healthz/ping", "/healthz/poststarthook/crd-informer-synced", ] }
    • 顺便说一句,如果我将值 livez := "/livez" 更改为 livez := "/foo" 我会得到相同的结果/api 列表
    猜你喜欢
    • 2022-11-07
    • 2014-02-09
    • 1970-01-01
    • 1970-01-01
    • 2021-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多