Kubernetes api-server源码阅读2(Debug Kubernetes篇)

云原生学习路线导航页(持续更新中)

本文是 Kubernetes api-server源码阅读 系列第二篇,主要讲述如何实现 kubernetes api-server 的 debug

1.本篇章任务

  • Go-Delve:go语言的调试工具
  • Debug模式启动集群:为了能调试,需要使用Debug的方式启动集群
  • 命令行调试:在没有IDE的情况下,迅速调试kubernetes,就可以使用命令行的调试功能
  • VS Code中调试:为了方便我们学习,使用VS Code实现远程调试线上的kubernetes
  • Goland中调试:现在有很多人喜欢使用Goland(包括我),所以这里也给出了Goland的远程调试方法
  • Postman请求Api-Server:像kubectl这种客户端,很多都有cache的机制,通过informer的cache机制,会把apiserver的很多api对象都在本地缓存下来,所以我们执行kubectl命令来调试的话,可能一条命令没有触发到apiserver上,所以我们直接使用postman发送http请求,这样apiserver就一定会被触发执行

2.Go-Delve

2.1.go-delve简介

2.1.1.go-delve是什么

  • go-delve 是一个开源项目,为go语言提供debug能力,简单易用,github地址:https://github.com/go-delve/delve
  • go-delve 属于golang语言基础设施的一部分,像vscode的go语言插件、goland调试功能等,底层都是使用了delve。
  • go的plugin好像也用到了go-delve(go的plugin是什么,可以参考我的另一篇博客:知识点积累 的1.7)

2.1.2.go-delve的能力

  • 支持本地调试:本地写了一个go项目,可以使用delve的dlv命令,直接在命令行进行调试。(本地vscode调试也属于这种)
  • 也支持远程调试 :可以使用远程的IDE,调试另一台机器上的go程序

2.1.3.go-delve的调试方法

  • delve是一个单独的进程,可以直接使用delve去启动一个go程序,这样delve就是套在go程序外边的壳,可以直接进行调试
  • 也可以单独先去启动go程序,然后delve根据go程序的进程号,接管go程序,然后实现调试

2.2.安装go-delve

  • 官方文档里已经给了安装方法:https://github.com/go-delve/delve/tree/master/Documentation/installation

  • 不过我们使用的go版本是1.18.2,不可以直接安装最新版的delve,会提示golang版本太低,至少需要go1.19

  • 经过我的测试,大家可以使用delve1.9.1版本:

    sh 复制代码
    $ git clone https://github.com/go-delve/delve
    $ cd delve
    $ go install github.com/go-delve/delve/cmd/dlv@v1.9.1
  • 安装完,测试是否已经安装成功了

    sh 复制代码
    root@graham-virtual-machine:~/Download/delve# dlv --help
    Delve is a source level debugger for Go programs.
    
    Delve enables you to interact with your program by controlling the execution of the process,
    evaluating variables, and providing information of thread / goroutine state, CPU register state and more.
    
    The goal of this tool is to provide a simple yet powerful interface for debugging Go programs.
    
    Pass flags to the program you are debugging using `--`, for example:
    
    `dlv exec ./hello -- server --config conf/config.toml`
    
    Usage:
      dlv [command]
    
    Available Commands:
      attach      Attach to running process and begin debugging.
      completion  Generate the autocompletion script for the specified shell
      connect     Connect to a headless debug server with a terminal client.
      core        Examine a core dump.
      dap         Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP).
      debug       Compile and begin debugging main package in current directory, or the package specified.
      exec        Execute a precompiled binary, and begin a debug session.
      help        Help about any command
      test        Compile test binary and begin debugging program.
      trace       Compile and begin tracing program.
      version     Prints version.
    
    Additional help topics:
      dlv backend    Help about the --backend flag.
      dlv log        Help about logging flags.
      dlv redirect   Help about file redirection.
    
    Use "dlv [command] --help" for more information about a command.

2.3.go-delve使用方法

2.3.1.官方文档+命令整体预览

2.3.2.启动delve接管目标进程 的 命令

2.3.2.1.本地调试
  • delve debug [package]
    • delve debug,会先将package里的程序 build 成可执行文件,然后启动起来,并使用delve接管,接下来就可以在delve的命令行使用过命令调试了
  • delve test [package]
    • 将package里的单元测试函数启动起来,并使用delve接管,接下来就可以在delve的命令行使用过命令调试了
  • delve exec <exec>
    • 如果事先已经编译好了可执行文件,可以直接使用 delve exec 启动,就不用像 delve debug 再build了
  • delve attach <pid>
    • 接管已经启动的go进程,指定进程id
2.3.2.2.远程调试
  • dlv --headless <command> <target> <args>
    • 将目标程序以一个serve的方式启动起来,等待远程连接
    • 我们接下来主要使用这种方式
  • dlv dap
    • dap:Debug Adaptor Protocol
    • 也是支持远程连接的方式,不过我们这里没有使用

2.3.3.调试过程中用到的命令

  • 这里大约列出来90%可用命令

2.3.4.通过一个demo演示delve使用方法

2.3.4.1.项目github地址
  • 这是根据 视频中老师讲的项目,编写的demo,大家可以克隆下来直接用

  • https://github.com/graham924/delve-study

    sh 复制代码
    git clone https://github.com/graham924/delve-study.git
  • demo内容简介:

    • 使用cobra写了两个命令:rootCmd、createCmd
    • 其中createCmd是rootCmd的子命令
    • cd到项目目录下,运行项目
      • 直接 go run main.go 执行的是rootCmd,会打印"hello world"

      • 执行子命令,并指定参数--name,如 go run main.go create --name grahamzhu,则会打印:

        sh 复制代码
        create command is called
        name:  grahamzhu
2.3.4.2.使用delve演示本地命令行调试
  • cd delve-study 后,执行命令:

    sh 复制代码
    dlv debug delve-study create --name=grahamzhu
    • 发现报错 Error: unknown flag: --name
    • 这是因为,使用dlv的情况下,指定命令行参数,不能这么写。需要用一个 -- 分隔开
  • 这么写就对了

    sh 复制代码
    dlv debug delve-study -- create --name=grahamzhu
  • 执行后进入dlv的命令模式

    sh 复制代码
    root@graham-virtual-machine:~/zgy/go_project/delve-study# dlv debug delve-study -- create --name=grahamzhu
    Type 'help' for list of commands.
    (dlv)
  • 我们在 cmd/create.go 中,在第19行打个断点

    sh 复制代码
    (dlv) break cmd/create.go:19
    Breakpoint 1 set at 0x5f1238 for delve-study/cmd.glob..func1() ./cmd/create.go:19

    其实就是这里:

  • 执行 continue ,程序会再下一个断点位置停下来,就是我们刚才打的断点

    sh 复制代码
    (dlv) continue
    > delve-study/cmd.glob..func1() ./cmd/create.go:19 (hits goroutine(1):1 total:1) (PC: 0x5f1238)
        14: var createCmd = &cobra.Command{
        15:         Use:   "create",
        16:         Short: "子命令",
        17:         Long:  "做一个子命令 - create",
        18:         Run: func(cmd *cobra.Command, args []string) {
    =>  19:                 fmt.Println("create command is called")
        20:                 name, _ := cmd.Flags().GetString("name")
        21:                 create(name)
        22:         },
        23: }
        24:
  • 执行两次 next,让程序 将要 去执行 create 方法

    sh 复制代码
    (dlv) next
    create command is called
    > delve-study/cmd.glob..func1() ./cmd/create.go:20 (PC: 0x5f128a)
        15:         Use:   "create",
        16:         Short: "子命令",
        17:         Long:  "做一个子命令 - create",
        18:         Run: func(cmd *cobra.Command, args []string) {
        19:                 fmt.Println("create command is called")
    =>  20:                 name, _ := cmd.Flags().GetString("name")
        21:                 create(name)
        22:         },
        23: }
        24:
        25: func create(name string) {
    (dlv) next
    > delve-study/cmd.glob..func1() ./cmd/create.go:21 (PC: 0x5f12cc)
        16:         Short: "子命令",
        17:         Long:  "做一个子命令 - create",
        18:         Run: func(cmd *cobra.Command, args []string) {
        19:                 fmt.Println("create command is called")
        20:                 name, _ := cmd.Flags().GetString("name")
    =>  21:                 create(name)
        22:         },
        23: }
        24:
        25: func create(name string) {
        26:         fmt.Println("name: ", name)
  • 执行 step 进入create方法

    sh 复制代码
    (dlv) step
    > delve-study/cmd.create() ./cmd/create.go:25 (PC: 0x5f132a)
        20:                 name, _ := cmd.Flags().GetString("name")
        21:                 create(name)
        22:         },
        23: }
        24:
    =>  25: func create(name string) {
        26:         fmt.Println("name: ", name)
        27: }
  • 执行 stepout,会执行完当前的create方法,跳到create函数调用方的下一行代码

    • 可以看到,create函数执行完毕,打印出来了 name: grahamzhu
    • 并且跳到了create函数调用方的下一行代码
    sh 复制代码
    (dlv) stepout
    name:  grahamzhu
    > delve-study/cmd.glob..func1() ./cmd/create.go:22 (PC: 0x5f12db)
    Values returned:
    
        17:         Long:  "做一个子命令 - create",
        18:         Run: func(cmd *cobra.Command, args []string) {
        19:                 fmt.Println("create command is called")
        20:                 name, _ := cmd.Flags().GetString("name")
        21:                 create(name)
    =>  22:         },
        23: }
        24:
        25: func create(name string) {
        26:         fmt.Println("name: ", name)
        27: }
  • 执行 continue,让程序执行到下一个断点。不过我们下面没有断点了,所以程序直接执行结束了

    sh 复制代码
    (dlv) continue
    Process 351390 has exited with status 0
2.3.4.3.使用delve演示远程命令行调试
  • 使用 dlv --headless debug 进行远程调试

    sh 复制代码
    root@graham-virtual-machine:~/zgy/go_project/delve-study# dlv --headless debug delve-study -- create --name=grahamzhu
    API server listening at: 127.0.0.1:38437
  • 可以看到,在端口 38437 启动了一个serve,这就是delve启动的一个进程,终端没有结束,正在等待我们远程连接

  • 然后我们开启另一个终端,模拟是远程,使用 dlv connect 127.0.0.1:38437 远程连接调试。可以看到,已经进入了dlv的命令行界面

    sh 复制代码
    root@graham-virtual-machine:~# dlv connect 127.0.0.1:38437
    Type 'help' for list of commands.
    (dlv)
  • 接下来就和上面本地调试一样了,使用break打断点,continue、next、step等调试

  • 如果你本机上也安装了delve,也可以在本机上连接,这样也是远程。

3.Debug模式启动集群

3.1.Debug模式启动集群分3步

  • 修改 kubernetes/hack/lib/golang.sh 的编译参数,使得每次编译都不会优化掉debug的东西
  • 重新启动本地集群
  • 使用delve重新启动API Server

3.2.修改编译参数,使得每次编译都不会优化掉debug的东西

  • 修改 kubernetes/hack/lib/golang.sh 的编译参数,使得每次编译都不会优化掉debug的东西。改的内容实际上就是两点:

    • 禁止-w -s,保留文件名,行号

    • 加上-gcflags= "all=-N-I",禁止优化和内联

  • 编辑 kubernetes/hack/lib/golang.sh,修改方式如下:

    • 找到下面这段,可以看到,两个 if语句,第一个是debug下会干什么、第二个是非debug下会干什么

      sh 复制代码
      gogcflags="all=-trimpath=${trimroot} ${GOGCFLAGS:-}"
        if [[ "${DBG:-}" == 1 ]]; then
            # Debugging - disable optimizations and inlining.
            gogcflags="${gogcflags} -N -l"
        fi
      
        goldflags="all=$(kube::version::ldflags) ${GOLDFLAGS:-}"
        if [[ "${DBG:-}" != 1 ]]; then
            # Not debugging - disable symbols and DWARF.
            goldflags="${goldflags} -s -w"
        fi
    • 我们改成下面这样就好了

      • 把debug执行的语句,从if中取出,这样不管怎么样,都会执行
      • 非debug执行的代码注掉,这样就不会执行到了
      sh 复制代码
      gogcflags="all=-trimpath=${trimroot} ${GOGCFLAGS:-}"
        # if [[ "${DBG:-}" == 1 ]]; then
        #     # Debugging - disable optimizations and inlining.
        #     gogcflags="${gogcflags} -N -l"
        # fi
      gogcflags="${gogcflags} -N -l"
        goldflags="all=$(kube::version::ldflags) ${GOLDFLAGS:-}"
        # if [[ "${DBG:-}" != 1 ]]; then
        #     # Not debugging - disable symbols and DWARF.
        #     goldflags="${goldflags} -s -w"
        # fi
    • 这样以后再执行 make all,或者执行hack/local-up-cluster.sh,编译出来的东西,就都可以debug了

3.3.重新启动本地集群

  • 修改完 hack/lib.golang.sh 后,因为修改了源码,所以需要执行make clean,清除已编译的旧的可执行程序。

  • 然后通过 hack/local-up-cluster.sh 脚本启动本地集群

    sh 复制代码
    cd ~/go/src/k8s.io/kubernetes
    make clean
    hack/local-up-cluster.sh

3.4.使用delve重新启动API Server

  • 本地集群启动之后,我们先看一下,目前机器中启动了kubernetes的哪些组件

    sh 复制代码
    root@graham-virtual-machine:~/zgy/go_project/delve-study# ps -a | grep kube
     343645 pts/0    00:11:32 kube-apiserver
     343946 pts/0    00:03:53 kube-controller
     343948 pts/0    00:00:31 kube-scheduler
     344111 pts/0    00:03:58 kubelet
     344604 pts/0    00:00:04 kube-proxy
  • 然后我们 以API Server举例,讲解一下:如何使用delve重新启动kubernetes的一个组件,进而可以进行远程调试

3.4.1.杀掉 kube-apiserver 进程

  • 之所以杀掉 kube-apiserver 进程,是因为现在启动的kube-apiserver没有使用delve启动,无法进行远程调试

  • 我们先查看一下当前 kube-apiserver 的一些信息。可以看到:进程号pid是 476450

    sh 复制代码
    root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# ps -ef | grep kube-apiserver
    root      476450  448854 10 12月22 pts/0  00:00:49 /root/go/src/k8s.io/kubernetes/_output/local/bin/linux/amd64/kube-apiserver --authorization-mode=Node,RBAC  --cloud-provider= --cloud-config=   --v=3 --vmodule= --audit-policy-file=/tmp/kube-audit-policy-file --audit-log-path=/tmp/kube-apiserver-audit.log --authorization-webhook-config-file= --authentication-token-webhook-config-file= --cert-dir=/var/run/kubernetes --egress-selector-config-file=/tmp/kube_egress_selector_configuration.yaml --client-ca-file=/var/run/kubernetes/client-ca.crt --kubelet-client-certificate=/var/run/kubernetes/client-kube-apiserver.crt --kubelet-client-key=/var/run/kubernetes/client-kube-apiserver.key --service-account-key-file=/tmp/kube-serviceaccount.key --service-account-lookup=true --service-account-issuer=https://kubernetes.default.svc --service-account-jwks-uri=https://kubernetes.default.svc/openid/v1/jwks --service-account-signing-key-file=/tmp/kube-serviceaccount.key --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,Priority,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,NodeRestriction --disable-admission-plugins= --admission-control-config-file= --bind-address=0.0.0.0 --secure-port=6443 --tls-cert-file=/var/run/kubernetes/serving-kube-apiserver.crt --tls-private-key-file=/var/run/kubernetes/serving-kube-apiserver.key --storage-backend=etcd3 --storage-media-type=application/vnd.kubernetes.protobuf --etcd-servers=http://127.0.0.1:2379 --service-cluster-ip-range=10.0.0.0/24 --feature-gates=AllAlpha=false --external-hostname=localhost --requestheader-username-headers=X-Remote-User --requestheader-group-headers=X-Remote-Group --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-client-ca-file=/var/run/kubernetes/request-header-ca.crt --requestheader-allowed-names=system:auth-proxy --proxy-client-cert-file=/var/run/kubernetes/client-auth-proxy.crt --proxy-client-key-file=/var/run/kubernetes/client-auth-proxy.key --cors-allowed-origins=/127.0.0.1(:[0-9]+)?$,/localhost(:[0-9]+)?$
    root      483326  468865  0 00:00 pts/1    00:00:00 grep --color=auto kube-apiserver
  • 其中,这部分信息,就是当前kube-apiserver进程启动的命令。记录一下,我们等会在用delve启动apiserver的时候,也需要用(你需要记录你自己的,不能直接用我的)

    • 这部分内容,前面是/root/go/src/k8s.io/kubernetes/_output/local/bin/linux/amd64/kube-apiserver apiserver可执行文件的路径,我们等会也使用这个可执行文件
    • -- 后面 就是 启动的命令行参数,我们等会也要用这个命令行参数,和原来启动的apiserver保持一致
    sh 复制代码
    /root/go/src/k8s.io/kubernetes/_output/local/bin/linux/amd64/kube-apiserver --authorization-mode=Node,RBAC  --cloud-provider= --cloud-config=   --v=3 --vmodule= --audit-policy-file=/tmp/kube-audit-policy-file --audit-log-path=/tmp/kube-apiserver-audit.log --authorization-webhook-config-file= --authentication-token-webhook-config-file= --cert-dir=/var/run/kubernetes --egress-selector-config-file=/tmp/kube_egress_selector_configuration.yaml --client-ca-file=/var/run/kubernetes/client-ca.crt --kubelet-client-certificate=/var/run/kubernetes/client-kube-apiserver.crt --kubelet-client-key=/var/run/kubernetes/client-kube-apiserver.key --service-account-key-file=/tmp/kube-serviceaccount.key --service-account-lookup=true --service-account-issuer=https://kubernetes.default.svc --service-account-jwks-uri=https://kubernetes.default.svc/openid/v1/jwks --service-account-signing-key-file=/tmp/kube-serviceaccount.key --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,Priority,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,NodeRestriction --disable-admission-plugins= --admission-control-config-file= --bind-address=0.0.0.0 --secure-port=6443 --tls-cert-file=/var/run/kubernetes/serving-kube-apiserver.crt --tls-private-key-file=/var/run/kubernetes/serving-kube-apiserver.key --storage-backend=etcd3 --storage-media-type=application/vnd.kubernetes.protobuf --etcd-servers=http://127.0.0.1:2379 --service-cluster-ip-range=10.0.0.0/24 --feature-gates=AllAlpha=false --external-hostname=localhost --requestheader-username-headers=X-Remote-User --requestheader-group-headers=X-Remote-Group --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-client-ca-file=/var/run/kubernetes/request-header-ca.crt --requestheader-allowed-names=system:auth-proxy --proxy-client-cert-file=/var/run/kubernetes/client-auth-proxy.crt --proxy-client-key-file=/var/run/kubernetes/client-auth-proxy.key --cors-allowed-origins=/127.0.0.1(:[0-9]+)?$,/localhost(:[0-9]+)?$
  • 杀掉kube-apiserver。你需要把进程号改成你自己的

    sh 复制代码
    kill -9 476450
  • 然后再查看下当前有哪些kubernetes的进程。可以看到,kube-apiserver已经没有了

    sh 复制代码
    root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# ps -a | grep kube
     476748 pts/0    00:00:27 kube-controller
     476754 pts/0    00:00:04 kube-scheduler
     476913 pts/0    00:00:28 kubelet
     477468 pts/0    00:00:00 kube-proxy
  • 此时,在集群启动终端上,也会提示,API server被意外终止了

    sh 复制代码
    Alternatively, you can write to the default kubeconfig:
    
      export KUBERNETES_PROVIDER=local
    
      cluster/kubectl.sh config set-cluster local --server=https://localhost:6443 --certificate-authority=/var/run/kubernetes/server-ca.crt
      cluster/kubectl.sh config set-credentials myself --client-key=/var/run/kubernetes/client-admin.key --client-certificate=/var/run/kubernetes/client-admin.crt
      cluster/kubectl.sh config set-context local --cluster=local --user=myself
      cluster/kubectl.sh config use-context local
      cluster/kubectl.sh
    hack/local-up-cluster.sh:行 1223: 476450 已杀死               ${CONTROLPLANE_SUDO} "${GO_OUT}/kube-apiserver" "${authorizer_arg}" "${priv_arg}" ${runtime_config} ${cloud_config_arg} "${advertise_address}" "${node_port_range}" --v="${LOG_LEVEL}" --vmodule="${LOG_SPEC}" --audit-policy-file="${AUDIT_POLICY_FILE}" --audit-log-path="${LOG_DIR}/kube-apiserver-audit.log" --authorization-webhook-config-file="${AUTHORIZATION_WEBHOOK_CONFIG_FILE}" --authentication-token-webhook-config-file="${AUTHENTICATION_WEBHOOK_CONFIG_FILE}" --cert-dir="${CERT_DIR}" --egress-selector-config-file="${EGRESS_SELECTOR_CONFIG_FILE:-}" --client-ca-file="${CERT_DIR}/client-ca.crt" --kubelet-client-certificate="${CERT_DIR}/client-kube-apiserver.crt" --kubelet-client-key="${CERT_DIR}/client-kube-apiserver.key" --service-account-key-file="${SERVICE_ACCOUNT_KEY}" --service-account-lookup="${SERVICE_ACCOUNT_LOOKUP}" --service-account-issuer="https://kubernetes.default.svc" --service-account-jwks-uri="https://kubernetes.default.svc/openid/v1/jwks" --service-account-signing-key-file="${SERVICE_ACCOUNT_KEY}" --enable-admission-plugins="${ENABLE_ADMISSION_PLUGINS}" --disable-admission-plugins="${DISABLE_ADMISSION_PLUGINS}" --admission-control-config-file="${ADMISSION_CONTROL_CONFIG_FILE}" --bind-address="${API_BIND_ADDR}" --secure-port="${API_SECURE_PORT}" --tls-cert-file="${CERT_DIR}/serving-kube-apiserver.crt" --tls-private-key-file="${CERT_DIR}/serving-kube-apiserver.key" --storage-backend="${STORAGE_BACKEND}" --storage-media-type="${STORAGE_MEDIA_TYPE}" --etcd-servers="http://${ETCD_HOST}:${ETCD_PORT}" --service-cluster-ip-range="${SERVICE_CLUSTER_IP_RANGE}" --feature-gates="${FEATURE_GATES}" --external-hostname="${EXTERNAL_HOSTNAME}" --requestheader-username-headers=X-Remote-User --requestheader-group-headers=X-Remote-Group --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-client-ca-file="${CERT_DIR}/request-header-ca.crt" --requestheader-allowed-names=system:auth-proxy --proxy-client-cert-file="${CERT_DIR}/client-auth-proxy.crt" --proxy-client-key-file="${CERT_DIR}/client-auth-proxy.key" --cors-allowed-origins="${API_CORS_ALLOWED_ORIGINS}" > "${APISERVER_LOG}" 2>&1
    W1223 00:07:55]: API server terminated unexpectedly, see /tmp/kube-apiserver.log

3.4.2.使用delve命令,重新启动 kube-apiserver

  • 因为已经有了kube-apiserver的可执行文件,所以可以直接使用 dlv --headless exec 启动delve的debug server。

    • 我们需要指定 apiserver 的 可执行文件路径

    • 另外,我们这次就不让delve给我们自动选择端口号了,我们直接指定一个确定的端口号:--listen=:12345,不写ip默认使用localhost

    • 还有,调试apiserver,必须使用delve API版本2,否则会出错。即:--api-version=2

    • 另外,为了方便查看调试过程中的错误,我们将日志打印出来,即:--log --log-output=debugger,gdbwire,lldbout,debuglineerr,rpc,dap,fncall,minidump --log-dest=/root/delve-log/log

    • 最后,添加 -- 分隔符,后面就直接把 原API server 的 命令行参数 拷贝过来。不过需要注意,最后一个参数 --cors-allowed-origins,它的value有特殊符号,需要用引号包裹上:"/127.0.0.1(:[0-9]+)?$,/localhost(:[0-9]+)?$"

      sh 复制代码
      dlv --headless exec /root/go/src/k8s.io/kubernetes/_output/local/bin/linux/amd64/kube-apiserver --listen=:12345 --api-version=2 --log --log-output=debugger,gdbwire,lldbout,debuglineerr,rpc,dap,fncall,minidump --log-dest=/root/delve-log/log -- --authorization-mode=Node,RBAC  --cloud-provider= --cloud-config=   --v=3 --vmodule= --audit-policy-file=/tmp/kube-audit-policy-file --audit-log-path=/tmp/kube-apiserver-audit.log --authorization-webhook-config-file= --authentication-token-webhook-config-file= --cert-dir=/var/run/kubernetes --egress-selector-config-file=/tmp/kube_egress_selector_configuration.yaml --client-ca-file=/var/run/kubernetes/client-ca.crt --kubelet-client-certificate=/var/run/kubernetes/client-kube-apiserver.crt --kubelet-client-key=/var/run/kubernetes/client-kube-apiserver.key --service-account-key-file=/tmp/kube-serviceaccount.key --service-account-lookup=true --service-account-issuer=https://kubernetes.default.svc --service-account-jwks-uri=https://kubernetes.default.svc/openid/v1/jwks --service-account-signing-key-file=/tmp/kube-serviceaccount.key --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,Priority,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,NodeRestriction --disable-admission-plugins= --admission-control-config-file= --bind-address=0.0.0.0 --secure-port=6443 --tls-cert-file=/var/run/kubernetes/serving-kube-apiserver.crt --tls-private-key-file=/var/run/kubernetes/serving-kube-apiserver.key --storage-backend=etcd3 --storage-media-type=application/vnd.kubernetes.protobuf --etcd-servers=http://127.0.0.1:2379 --service-cluster-ip-range=10.0.0.0/24 --feature-gates=AllAlpha=false --external-hostname=localhost --requestheader-username-headers=X-Remote-User --requestheader-group-headers=X-Remote-Group --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-client-ca-file=/var/run/kubernetes/request-header-ca.crt --requestheader-allowed-names=system:auth-proxy --proxy-client-cert-file=/var/run/kubernetes/client-auth-proxy.crt --proxy-client-key-file=/var/run/kubernetes/client-auth-proxy.key --cors-allowed-origins="/127.0.0.1(:[0-9]+)?$,/localhost(:[0-9]+)?$"
  • 命令执行之后,程序会停在光标位置,但是没有结束。这就是在等待远程连接

4.使用delve远程连接Debug Server

4.1.演示 命令行 远程连接kube-apiserver

  • 打开我们ubuntu的另一个终端,或者在你的物理机上打开一个终端(物理机上需要安装delve)

  • 使用 dlv connect localhost:12345 远程连接到kube-apiserver上

    sh 复制代码
    root@graham-virtual-machine:~# dlv connect 127.0.0.1:12345
    Type 'help' for list of commands.
    (dlv)
  • 使用 break 在cmd/kube-apiserver/apiserver.go文件的33行打个断点,也就是main函数的第一行代码

    sh 复制代码
    (dlv) break cmd/kube-apiserver/apiserver.go:33
    Breakpoint 1 set at 0x5233854 for main.main() cmd/kube-apiserver/apiserver.go:33
  • 使用 continue 执行到断点位置

    sh 复制代码
    (dlv) continue
    > main.main() cmd/kube-apiserver/apiserver.go:33 (hits goroutine(1):1 total:1) (PC: 0x5233854)
  • 使用 next 执行到下一行

    sh 复制代码
    (dlv) next
    > main.main() cmd/kube-apiserver/apiserver.go:34 (PC: 0x5233860)
  • 使用 args 查看当前函数的参数。输出空,因为当前是main函数没有参数

    sh 复制代码
    (dlv) args
    (no args)
  • 使用 locals 查看当前的局部变量

    sh 复制代码
    (dlv) locals
    command = ("*k8s.io/kubernetes/vendor/github.com/spf13/cobra.Command")(0xc000890c80)
  • 使用 vars 查看当前包级别的变量。可以看到很多,因为apiserver导入了很多包,包再导入包,有很多变量

    sh 复制代码
    (dlv) vars
    .....输出特别多
  • 使用 step 进入当前行的调用函数

    sh 复制代码
    (dlv) step
    > k8s.io/kubernetes/vendor/k8s.io/component-base/cli.Run() vendor/k8s.io/component-base/cli/run.go:45 (PC: 0xcf648f)
  • 使用 continue 直接让程序运行起来

    sh 复制代码
    (dlv) continue
  • 在 delve 的 debug server 端,可以看到打出来的日志,api-server已经运行起来了

4.2.演示 VS Code 远程连接 kube-apiserver

4.2.1.开放12345端口,否则VSCode会连接失败

  • 因为要远程连接,而且我们固定使用12345端口,所以我们事先把ubuntu的12345端口开启

    • 查看一下当前开放了哪些端口,发现12345没有开启。所以我们本机上的VSCode肯定是连不上这里delve启动的debug server的

      sh 复制代码
      root@graham-virtual-machine:~# ufw status
      状态: 激活
      
      至                          动作          来自
      -                          --          --
      22                         ALLOW       Anywhere
      22 (v6)                    ALLOW       Anywhere (v6)
    • 然后开放12345端口

      sh 复制代码
      root@graham-virtual-machine:~# ufw allow 12345
      规则已添加
      规则已添加 (v6)
    • 再看一下开放端口,可以看到,12345已经开启了

      sh 复制代码
      root@graham-virtual-machine:~# ufw status
      状态: 激活
      
      至                          动作          来自
      -                          --          --
      22                         ALLOW       Anywhere
      12345                      ALLOW       Anywhere
      22 (v6)                    ALLOW       Anywhere (v6)
      12345 (v6)                 ALLOW       Anywhere (v6)

4.2.2.使用delve启动api-server等待连接

  • 端口开放后,我们还是执行和 3.5 中一样的命令,用 delve 把api-server启动起来,等待远程连接
sh 复制代码
dlv --headless exec /root/go/src/k8s.io/kubernetes/_output/local/bin/linux/amd64/kube-apiserver --listen=:12345 --api-version=2 --log --log-output=debugger,gdbwire,lldbout,debuglineerr,rpc,dap,fncall,minidump --log-dest=/root/delve-log/log -- --authorization-mode=Node,RBAC  --cloud-provider= --cloud-config=   --v=3 --vmodule= --audit-policy-file=/tmp/kube-audit-policy-file --audit-log-path=/tmp/kube-apiserver-audit.log --authorization-webhook-config-file= --authentication-token-webhook-config-file= --cert-dir=/var/run/kubernetes --egress-selector-config-file=/tmp/kube_egress_selector_configuration.yaml --client-ca-file=/var/run/kubernetes/client-ca.crt --kubelet-client-certificate=/var/run/kubernetes/client-kube-apiserver.crt --kubelet-client-key=/var/run/kubernetes/client-kube-apiserver.key --service-account-key-file=/tmp/kube-serviceaccount.key --service-account-lookup=true --service-account-issuer=https://kubernetes.default.svc --service-account-jwks-uri=https://kubernetes.default.svc/openid/v1/jwks --service-account-signing-key-file=/tmp/kube-serviceaccount.key --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,Priority,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,NodeRestriction --disable-admission-plugins= --admission-control-config-file= --bind-address=0.0.0.0 --secure-port=6443 --tls-cert-file=/var/run/kubernetes/serving-kube-apiserver.crt --tls-private-key-file=/var/run/kubernetes/serving-kube-apiserver.key --storage-backend=etcd3 --storage-media-type=application/vnd.kubernetes.protobuf --etcd-servers=http://127.0.0.1:2379 --service-cluster-ip-range=10.0.0.0/24 --feature-gates=AllAlpha=false --external-hostname=localhost --requestheader-username-headers=X-Remote-User --requestheader-group-headers=X-Remote-Group --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-client-ca-file=/var/run/kubernetes/request-header-ca.crt --requestheader-allowed-names=system:auth-proxy --proxy-client-cert-file=/var/run/kubernetes/client-auth-proxy.crt --proxy-client-key-file=/var/run/kubernetes/client-auth-proxy.key --cors-allowed-origins="/127.0.0.1(:[0-9]+)?$,/localhost(:[0-9]+)?$"

4.2.3.使用VSCode连接delve的debug server

  • 然后来到物理机的VS Code上,使用VSCode打开kubernetes的源代码,然后在kubernetes项目的 .vscode目录 下,创建一个launch.json

    • launch.json 是什么,可以看我的另一篇博客 知识点积累9.1.1

    • 注意:request必须是attach、mode必须是remote

      json 复制代码
      {
          "version": "0.2.0",
          "configurations": {
              "name": "Connect to server",
              "type": "go",
              "request": "attach",
              "mode": "remote",
              "port": 12345,
              "host": "192.168.245.146"
          }
      }
  • 并在 VS Code 上,给程序打一个断点。我们还把断点打在 apiserver.go的main函数第一句,也就是33行

  • 然后,点开VSCode的左侧Debug页面,点击Debug按钮,稍等一下后,发现程序已经运行,并且停在了我们的断点处,之后就可以使用VSCode进行调试了

  • 我们在VSCode把程序放行后,可以在Debug Server终端看到打印信息,ApiServer已经启动起来了

  • 如果你发现终端报错,启动失败了,报错:Error: "kube-apiserver" does not take any arguments

    • 大概率是有可能是 dlv --headless exec 命令的 -- 后面的apiserver参数有问题。
    • 因为我们执行的这条命令特别长,屏幕一般又比较小,命令在终端显示的时候,肯定会发生换行
    • 有些ssh终端工具,会在命令的换行部分,处理的有问题。比如我使用的MobaXterm工具,就多次遇到这个问题。
    • 解决方法:先把命令复制到一个文本编辑器里,确保这条命令能显示成一行,再复制到终端去执行,就可以启动成功了
    • kubernetes issues中也有人遇到过这个问题:https://github.com/kubernetes/kubernetes/issues/94758

4.3.演示 Goland 远程连接 kube-apiserver

4.3.1.和VS Code一样,先开放12345端口,并使用delve启动Api-server

  • 开放12345端口

    sh 复制代码
    ufw allow 12345
  • 端口开放后,我们还是执行和 4.2.2 中一样的命令,用 delve 把api-server启动起来,等待远程连接

    sh 复制代码
    dlv --headless exec /root/go/src/k8s.io/kubernetes/_output/local/bin/linux/amd64/kube-apiserver --listen=:12345 --api-version=2 --log --log-output=debugger,gdbwire,lldbout,debuglineerr,rpc,dap,fncall,minidump --log-dest=/root/delve-log/log -- --authorization-mode=Node,RBAC  --cloud-provider= --cloud-config=   --v=3 --vmodule= --audit-policy-file=/tmp/kube-audit-policy-file --audit-log-path=/tmp/kube-apiserver-audit.log --authorization-webhook-config-file= --authentication-token-webhook-config-file= --cert-dir=/var/run/kubernetes --egress-selector-config-file=/tmp/kube_egress_selector_configuration.yaml --client-ca-file=/var/run/kubernetes/client-ca.crt --kubelet-client-certificate=/var/run/kubernetes/client-kube-apiserver.crt --kubelet-client-key=/var/run/kubernetes/client-kube-apiserver.key --service-account-key-file=/tmp/kube-serviceaccount.key --service-account-lookup=true --service-account-issuer=https://kubernetes.default.svc --service-account-jwks-uri=https://kubernetes.default.svc/openid/v1/jwks --service-account-signing-key-file=/tmp/kube-serviceaccount.key --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,Priority,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,NodeRestriction --disable-admission-plugins= --admission-control-config-file= --bind-address=0.0.0.0 --secure-port=6443 --tls-cert-file=/var/run/kubernetes/serving-kube-apiserver.crt --tls-private-key-file=/var/run/kubernetes/serving-kube-apiserver.key --storage-backend=etcd3 --storage-media-type=application/vnd.kubernetes.protobuf --etcd-servers=http://127.0.0.1:2379 --service-cluster-ip-range=10.0.0.0/24 --feature-gates=AllAlpha=false --external-hostname=localhost --requestheader-username-headers=X-Remote-User --requestheader-group-headers=X-Remote-Group --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-client-ca-file=/var/run/kubernetes/request-header-ca.crt --requestheader-allowed-names=system:auth-proxy --proxy-client-cert-file=/var/run/kubernetes/client-auth-proxy.crt --proxy-client-key-file=/var/run/kubernetes/client-auth-proxy.key --cors-allowed-origins="/127.0.0.1(:[0-9]+)?$,/localhost(:[0-9]+)?$"

4.3.2.使用 Goland 连接 delve的debug server

  • goland打开kubernetes项目,记得将分支切到1.24.0版本
  • 然后按照下面的步骤操作即可



5.使用Postman请求API Server

5.1.为什么要用Postman请求API server

  • 由于kubectl是存在informer机制的,会把从apiserver获取到了资源数据缓存下来,所以我们很多时候使用kubectl请求api-server,实际上都是走的缓存,并没有触发到kube-apiserver
  • 所以,我们使用postman这种无缓存机制的工具,直接发送https请求到apiserver,这样每个请求都能触发到apiserver

5.2.Postman请求API Server需要准备什么?

5.2.1.搞清楚我们需要做的事情

  • 由于 ApiServer 对外提供的访问方式,只有HTTPS协议的安全端口443。所以我们需要完成
  • 要保证ApiServer对到达的请求:
    • 验证 登陆信息 通过
    • 验证 鉴权 通过
  • 所以,最繁琐的配置就是,如何配置 登陆和鉴权

5.2.2.配置 登陆和鉴权 需要做什么

  • 由于在kubernetes中,所有的组件,包括正在运行的Pod,与API Server交互的方式都是使用Service Account
  • 所以,我们要想让 kube-apiserver 能认识外部的客户端,也需要为我们的客户端创建一个ServiceAccount,创建好ServiceAccount后,我们需要为这个Service Account做两件事
    • 为这个Service Account绑定一个Secret,提取这个Secret的证书 ,并从Secret中获取Token,都交给Postman,每次请求的时候都携带过来
    • 为这个Service Account授予权限,把它的权限扩大到能够访问API对象。这里使用的是ClusterRole、RoleBinding的方式

5.2.3.配置 登陆和鉴权 操作 主要分6步

5.3.在集群中 配置 登陆和鉴权

5.3.1.创建一个ServiceAccount

  • 先查看一下当前集群中有哪些资源。可以看到,环境很干净,只有一个默认的sa

    sh 复制代码
    root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# cd ~/go/src/k8s.io.kubernetes
    root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# cluster/kubectl.sh get sa
    NAME      SECRETS   AGE
    default   0         162m
    root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# cluster/kubectl.sh get secret
    No resources found in default namespace.
  • 然后我们创建一个ServiceAccount,名称为 forpostman

    sh 复制代码
    root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# cluster/kubectl.sh create sa forpostman
    serviceaccount/forpostman created
  • 然后我们describe一个这个sa。可以看到,这个sa的Tokens为空,没有绑定任何的Secret。

    • 因为kubernetes1.24及以后,就不再自动为sa创建并绑定secret了,需要我们手动创建并绑定
    sh 复制代码
    root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# cluster/kubectl.sh describe sa forpostman
    Name:                forpostman
    Namespace:           default
    Labels:              &lt;none&gt;
    Annotations:         &lt;none&gt;
    Image pull secrets:  &lt;none&gt;
    Mountable secrets:   &lt;none&gt;
    Tokens:              &lt;none&gt;
    Events:              &lt;none&gt;创建

5.3.2.创建一个Secret,并绑定到ServiceAccount上去

  • 我们给出了一个Secret的yaml文件。创建一个名称为 postman-sa-secret 的secret,并使用annotations 的方式,将之绑定到 一个指定的service-account上,即forpostman这个sa

    yaml 复制代码
    apiVersion: v1
    kind: Secret
    metadata: 
      name: postman-sa-secret
      annotations:
        kubernetes.io/service-account.name: forpostman
    type: kubernetes.io/service-account-token
  • 我们create一下这个secret,然后get查看一下,已经有这个secret了,输出yaml能看到自动生成的 证书和 token

    sh 复制代码
    root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# cluster/kubectl.sh create -f ~/zgy/go_yaml/postman-sa-secret.yaml
    secret/postman-sa-secret created
    
    root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# cluster/kubectl.sh get secret
    NAME                TYPE                                  DATA   AGE
    postman-sa-secret   kubernetes.io/service-account-token   3      28s
    
    root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# cluster/kubectl.sh describe secret postman-sa-secret
    Name:         postman-sa-secret
    Namespace:    default
    Labels:       <none>
    Annotations:  kubernetes.io/service-account.name: forpostman
                  kubernetes.io/service-account.uid: 44d471a4-2724-410e-a0e0-838568fd8f9b
    
    Type:  kubernetes.io/service-account-token
    
    Data
    ====
    ca.crt:     1310 bytes
    namespace:  7 bytes
    token:      eyJhbGciOiJSUzI1NiIsImtpZCI6IjdEeERLbXpSVHlFd2FOMUpUVTNaTXl3TkNBeE5nQ28xbXpIUkxESGVQa1UifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InBvc3RtYW4tc2Etc2VjcmV0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImZvcnBvc3RtYW4iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI0NGQ0NzFhNC0yNzI0LTQxMGUtYTBlMC04Mzg1NjhmZDhmOWIiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpmb3Jwb3N0bWFuIn0.m9Jo_ycqz3j6J0TkTHpiQSGbE1Z2QjVTxcYP2dge7YxJn5Adl3r_K9Lt-1-WFs2d3CsBIEWFJX8z1IQNiogyy41nr2DWyepll5vlafDgVh9eTlrz7ktVX6hRshVBQOz4v1qrcPFnbFxdtqXWr_W0Y_7viEuQNX4Yv9P4PqWGUawlQuUQoI0hKzC8pXYMQr_VSneQ3Uh_lqotOLrkf4H4L4b13eTg7La0C4lDWdsssPJQhv_VcW-m8H_jso6tfTQFQ5YQK-_r6gmbZayX_Xi4KnYHa2g13pyJy_xeJ57UlZYs2Wr7057FLChNXoui8-pHF4jby3d0-kusZkMJWAXiuw
  • 再describe一下forpostman这个sa,可以看到已经有一个token绑定上来了,就是postman-sa-secret

    • 这个其实就是 登陆 的配置,postman发送请求的时候,需要在http header里携带这个 secret 的 token,才能登陆成功
    sh 复制代码
    root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# cluster/kubectl.sh describe sa forpostman
    Name:                forpostman
    Namespace:           default
    Labels:              <none>
    Annotations:         <none>
    Image pull secrets:  <none>
    Mountable secrets:   <none>
    Tokens:              postman-sa-secret
    Events:              <none>

5.3.3.为ServiceAccount授权

  • 虽然secret已经有token了,根据token能找到 对应namespace下的ServiceAccount,即default:forpostman,

  • 但是forpostman还没有任何权限,我们需要给 forpostman 授权。

    • 我们先看一下系统中有哪些权限,我们直接选一个权限大的,设置给这个secret就可以了,不用自己再创建了。从下面来看,集群角色还真不少,我们选一个权限大的:cluster-admin

      sh 复制代码
      root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# cluster/kubectl.sh get clusterrole
      NAME                                                                   CREATED AT
      admin                                                                  2023-12-23T08:11:11Z
      cluster-admin                                                          2023-12-23T08:11:11Z
      edit                                                                   2023-12-23T08:11:11Z
      system:aggregate-to-admin                                              2023-12-23T08:11:11Z
      system:aggregate-to-edit                                               2023-12-23T08:11:11Z
      system:aggregate-to-view                                               2023-12-23T08:11:11Z
      system:auth-delegator                                                  2023-12-23T08:11:11Z
      ........
    • 创建一个rolebinding,名称为forpostmanadmin,其中的集群角色是cluster-admin,绑定的对象是ns=default下的serviceAccount:forpostman

      sh 复制代码
      root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# cluster/kubectl.sh create rolebinding forpostmanadmin --clusterrole cluster-admin --serviceaccount default:forpostman
      rolebinding.rbac.authorization.k8s.io/forpostmanadmin created
      
      root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# cluster/kubectl.sh get rolebinding
      NAME              ROLE                        AGE
      forpostmanadmin   ClusterRole/cluster-admin   38s
  • 至此,我们完成了 集群中 登录鉴权的配置

    • Secret 中 的token有了,实现了登陆的目标
    • ServiceAccount 也具有了 cluster-admin 的权限

5.4.为Postman设置证书和token

  • 从我们创建的secret:postman-sa-secret 中,提出证书

    • 将secret的data中,ca.crt 的内容,以 base64 的编码,写到 /tmp/ca.crt,此即为证书
    sh 复制代码
    root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# cluster/kubectl.sh get secret postman-sa-secret -o jsonpath="{.data['ca\.crt']}" | base64 -d > /tmp/ca.crt
    
    root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# ls /tmp/ca.*
    /tmp/ca.crt
    • 我们将这个证书,复制到postman的主机上

      cmd 复制代码
      C:\Users\tmp> scp root@192.168.245.146:/tmp/ca.crt ./ca.crt
      root@192.168.245.146's password:
      ca.crt                                                                                100% 1310   638.4KB/s   00:00
      
      C:\Users\tmp> ls ca.*
      
          目录: C:\Users\Gesang\AppData\Local\Postman
          
      Mode                 LastWriteTime         Length Name
      ----                 -------------         ------ ----
      -a----        2023/12/23     20:21           1310 ca.crt
  • postman创建一个请求,填写url:https://192.168.245.146:6443/apis ,请求方式是Get

    • 6443:启动本地集群的话,暴漏的API端口就是 6443

    • /apis:获取apiserver的api object的endpoints

    • 为请求设置token

    • 设置证书

  • 发送请求,已经有响应了,response如下:

    json 复制代码
    {
        "kind": "APIGroupList",
        "apiVersion": "v1",
        "groups": [
            {
                "name": "apiregistration.k8s.io",
                "versions": [
                    {
                        "groupVersion": "apiregistration.k8s.io/v1",
                        "version": "v1"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "apiregistration.k8s.io/v1",
                    "version": "v1"
                }
            },
            {
                "name": "apps",
                "versions": [
                    {
                        "groupVersion": "apps/v1",
                        "version": "v1"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "apps/v1",
                    "version": "v1"
                }
            },
            {
                "name": "events.k8s.io",
                "versions": [
                    {
                        "groupVersion": "events.k8s.io/v1",
                        "version": "v1"
                    },
                    {
                        "groupVersion": "events.k8s.io/v1beta1",
                        "version": "v1beta1"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "events.k8s.io/v1",
                    "version": "v1"
                }
            },
            {
                "name": "authentication.k8s.io",
                "versions": [
                    {
                        "groupVersion": "authentication.k8s.io/v1",
                        "version": "v1"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "authentication.k8s.io/v1",
                    "version": "v1"
                }
            },
            {
                "name": "authorization.k8s.io",
                "versions": [
                    {
                        "groupVersion": "authorization.k8s.io/v1",
                        "version": "v1"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "authorization.k8s.io/v1",
                    "version": "v1"
                }
            },
            {
                "name": "autoscaling",
                "versions": [
                    {
                        "groupVersion": "autoscaling/v2",
                        "version": "v2"
                    },
                    {
                        "groupVersion": "autoscaling/v1",
                        "version": "v1"
                    },
                    {
                        "groupVersion": "autoscaling/v2beta1",
                        "version": "v2beta1"
                    },
                    {
                        "groupVersion": "autoscaling/v2beta2",
                        "version": "v2beta2"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "autoscaling/v2",
                    "version": "v2"
                }
            },
            {
                "name": "batch",
                "versions": [
                    {
                        "groupVersion": "batch/v1",
                        "version": "v1"
                    },
                    {
                        "groupVersion": "batch/v1beta1",
                        "version": "v1beta1"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "batch/v1",
                    "version": "v1"
                }
            },
            {
                "name": "certificates.k8s.io",
                "versions": [
                    {
                        "groupVersion": "certificates.k8s.io/v1",
                        "version": "v1"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "certificates.k8s.io/v1",
                    "version": "v1"
                }
            },
            {
                "name": "networking.k8s.io",
                "versions": [
                    {
                        "groupVersion": "networking.k8s.io/v1",
                        "version": "v1"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "networking.k8s.io/v1",
                    "version": "v1"
                }
            },
            {
                "name": "policy",
                "versions": [
                    {
                        "groupVersion": "policy/v1",
                        "version": "v1"
                    },
                    {
                        "groupVersion": "policy/v1beta1",
                        "version": "v1beta1"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "policy/v1",
                    "version": "v1"
                }
            },
            {
                "name": "rbac.authorization.k8s.io",
                "versions": [
                    {
                        "groupVersion": "rbac.authorization.k8s.io/v1",
                        "version": "v1"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "rbac.authorization.k8s.io/v1",
                    "version": "v1"
                }
            },
            {
                "name": "storage.k8s.io",
                "versions": [
                    {
                        "groupVersion": "storage.k8s.io/v1",
                        "version": "v1"
                    },
                    {
                        "groupVersion": "storage.k8s.io/v1beta1",
                        "version": "v1beta1"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "storage.k8s.io/v1",
                    "version": "v1"
                }
            },
            {
                "name": "admissionregistration.k8s.io",
                "versions": [
                    {
                        "groupVersion": "admissionregistration.k8s.io/v1",
                        "version": "v1"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "admissionregistration.k8s.io/v1",
                    "version": "v1"
                }
            },
            {
                "name": "apiextensions.k8s.io",
                "versions": [
                    {
                        "groupVersion": "apiextensions.k8s.io/v1",
                        "version": "v1"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "apiextensions.k8s.io/v1",
                    "version": "v1"
                }
            },
            {
                "name": "scheduling.k8s.io",
                "versions": [
                    {
                        "groupVersion": "scheduling.k8s.io/v1",
                        "version": "v1"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "scheduling.k8s.io/v1",
                    "version": "v1"
                }
            },
            {
                "name": "coordination.k8s.io",
                "versions": [
                    {
                        "groupVersion": "coordination.k8s.io/v1",
                        "version": "v1"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "coordination.k8s.io/v1",
                    "version": "v1"
                }
            },
            {
                "name": "node.k8s.io",
                "versions": [
                    {
                        "groupVersion": "node.k8s.io/v1",
                        "version": "v1"
                    },
                    {
                        "groupVersion": "node.k8s.io/v1beta1",
                        "version": "v1beta1"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "node.k8s.io/v1",
                    "version": "v1"
                }
            },
            {
                "name": "discovery.k8s.io",
                "versions": [
                    {
                        "groupVersion": "discovery.k8s.io/v1",
                        "version": "v1"
                    },
                    {
                        "groupVersion": "discovery.k8s.io/v1beta1",
                        "version": "v1beta1"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "discovery.k8s.io/v1",
                    "version": "v1"
                }
            },
            {
                "name": "flowcontrol.apiserver.k8s.io",
                "versions": [
                    {
                        "groupVersion": "flowcontrol.apiserver.k8s.io/v1beta2",
                        "version": "v1beta2"
                    },
                    {
                        "groupVersion": "flowcontrol.apiserver.k8s.io/v1beta1",
                        "version": "v1beta1"
                    }
                ],
                "preferredVersion": {
                    "groupVersion": "flowcontrol.apiserver.k8s.io/v1beta2",
                    "version": "v1beta2"
                }
            }
        ]
    }
  • 如果postman报错:Could not send request Error: Request timed out,请把服务器的6443端口开启

    sh 复制代码
    ufw allow 6443
相关推荐
元气满满的热码式8 小时前
K8S中Service详解(一)
云原生·容器·kubernetes
元气满满的热码式12 小时前
K8S中ingress详解
云原生·容器·kubernetes
jcrose258013 小时前
Ubuntu二进制部署K8S 1.29.2
linux·ubuntu·kubernetes
lozhyf13 小时前
基于 JFinal 的国产微服务框架
微服务·云原生·架构
matrixlzp13 小时前
K8S 启动探测、就绪探测、存活探测
云原生·容器·kubernetes
Dusk_橙子13 小时前
在K8S中,如何使用EFK实现日志的统一管理?
云原生·容器·kubernetes
Tony115413 小时前
Kubernetes v1.28.0安装dashboard v2.6.1(k8s图形化操作界面)
云原生·容器·kubernetes
龙胖不下锅13 小时前
k8s资源预留
云原生·容器·kubernetes
超级阿飞13 小时前
利用Kubespray安装生产环境的k8s集群-排错篇
docker·容器·kubernetes
喝醉酒的小白13 小时前
在 Kubernetes 上快速安装 KubeSphere v4.1.2
云原生·容器·kubernetes