Kubernetes api-server源碼閱讀2(Debug Kubernetes篇)

云原生學習路線導航頁(持續更新中)

本文是 Kubernetes api-server源碼閱讀 系列第二篇,主要講述如何實現 kubernetes api-server 的 debug

  • 參考b站視頻地址:Kubernetes源碼開發之旅二

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版本:
    $ git clone https://github.com/go-delve/delve
    $ cd delve
    $ go install github.com/go-delve/delve/cmd/dlv@v1.9.1
    
  • 安裝完,測試是否已經安裝成功了
    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 shellconnect     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 commandtest        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.官方文檔+命令整體預覽

  • https://github.com/go-delve/delve/tree/master/Documentation/usage
    在這里插入圖片描述

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
    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,則會打印:
        create command is called
        name:  grahamzhu
        
2.3.4.2.使用delve演示本地命令行調試
  • cd delve-study 后,執行命令:

    dlv debug delve-study create --name=grahamzhu
    
    • 發現報錯 Error: unknown flag: --name
    • 這是因為,使用dlv的情況下,指定命令行參數,不能這么寫。需要用一個 -- 分隔開
  • 這么寫就對了

    dlv debug delve-study -- create --name=grahamzhu
    
  • 執行后進入dlv的命令模式

    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行打個斷點

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

    其實就是這里:在這里插入圖片描述

  • 執行 continue ,程序會再下一個斷點位置停下來,就是我們剛才打的斷點

    (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 方法

    (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方法

    (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函數調用方的下一行代碼
    (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,讓程序執行到下一個斷點。不過我們下面沒有斷點了,所以程序直接執行結束了

    (dlv) continue
    Process 351390 has exited with status 0
    
2.3.4.3.使用delve演示遠程命令行調試
  • 使用 dlv --headless debug 進行遠程調試
    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的命令行界面
    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下會干什么

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

      • 把debug執行的語句,從if中取出,這樣不管怎么樣,都會執行
      • 非debug執行的代碼注掉,這樣就不會執行到了
      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 腳本啟動本地集群

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

3.4.使用delve重新啟動API Server

  • 本地集群啟動之后,我們先看一下,目前機器中啟動了kubernetes的哪些組件

    root@graham-virtual-machine:~/zgy/go_project/delve-study# ps -a | grep kube343645 pts/0    00:11:32 kube-apiserver343946 pts/0    00:03:53 kube-controller343948 pts/0    00:00:31 kube-scheduler344111 pts/0    00:03:58 kubelet344604 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

    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保持一致
    /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。你需要把進程號改成你自己的

    kill -9 476450
    
  • 然后再查看下當前有哪些kubernetes的進程。可以看到,kube-apiserver已經沒有了

    root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# ps -a | grep kube476748 pts/0    00:00:27 kube-controller476754 pts/0    00:00:04 kube-scheduler476913 pts/0    00:00:28 kubelet477468 pts/0    00:00:00 kube-proxy
    
  • 此時,在集群啟動終端上,也會提示,API server被意外終止了

    Alternatively, you can write to the default kubeconfig:export KUBERNETES_PROVIDER=localcluster/kubectl.sh config set-cluster local --server=https://localhost:6443 --certificate-authority=/var/run/kubernetes/server-ca.crtcluster/kubectl.sh config set-credentials myself --client-key=/var/run/kubernetes/client-admin.key --client-certificate=/var/run/kubernetes/client-admin.crtcluster/kubectl.sh config set-context local --cluster=local --user=myselfcluster/kubectl.sh config use-context localcluster/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]+)?$"

      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上

    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函數的第一行代碼

    (dlv) break cmd/kube-apiserver/apiserver.go:33
    Breakpoint 1 set at 0x5233854 for main.main() cmd/kube-apiserver/apiserver.go:33
    
  • 使用 continue 執行到斷點位置

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

    (dlv) next
    > main.main() cmd/kube-apiserver/apiserver.go:34 (PC: 0x5233860)
    
  • 使用 args 查看當前函數的參數。輸出空,因為當前是main函數沒有參數

    (dlv) args
    (no args)
    
  • 使用 locals 查看當前的局部變量

    (dlv) locals
    command = ("*k8s.io/kubernetes/vendor/github.com/spf13/cobra.Command")(0xc000890c80)
    
  • 使用 vars 查看當前包級別的變量。可以看到很多,因為apiserver導入了很多包,包再導入包,有很多變量

    (dlv) vars
    .....輸出特別多
    
  • 使用 step 進入當前行的調用函數

    (dlv) step
    > k8s.io/kubernetes/vendor/k8s.io/component-base/cli.Run() vendor/k8s.io/component-base/cli/run.go:45 (PC: 0xcf648f)
    
  • 使用 continue 直接讓程序運行起來

    (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的

      root@graham-virtual-machine:~# ufw status
      狀態: 激活至                          動作          來自
      -                          --          --
      22                         ALLOW       Anywhere
      22 (v6)                    ALLOW       Anywhere (v6)
      
    • 然后開放12345端口

      root@graham-virtual-machine:~# ufw allow 12345
      規則已添加
      規則已添加 (v6)
      
    • 再看一下開放端口,可以看到,12345已經開啟了

      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啟動起來,等待遠程連接
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

      {"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端口

    ufw allow 12345
    
  • 端口開放后,我們還是執行和 4.2.2 中一樣的命令,用 delve 把api-server啟動起來,等待遠程連接

    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

    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

    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了,需要我們手動創建并綁定
    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

    apiVersion: v1
    kind: Secret
    metadata: name: postman-sa-secretannotations:kubernetes.io/service-account.name: forpostman
    type: kubernetes.io/service-account-token
    
  • 我們create一下這個secret,然后get查看一下,已經有這個secret了,輸出yaml能看到自動生成的 證書和 token

    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 createdroot@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      28sroot@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: forpostmankubernetes.io/service-account.uid: 44d471a4-2724-410e-a0e0-838568fd8f9bType:  kubernetes.io/service-account-tokenData
    ====
    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,才能登陸成功
    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

      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

      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 createdroot@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,此即為證書
    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.crtroot@graham-virtual-machine:~/go/src/k8s.io/kubernetes# ls /tmp/ca.*
    /tmp/ca.crt
    
    • 我們將這個證書,復制到postman的主機上

      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:00C:\Users\tmp> ls ca.*目錄: C:\Users\Gesang\AppData\Local\PostmanMode                 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如下:

    {"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端口開啟

    ufw allow 6443
    

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/284372.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/284372.shtml
英文地址,請注明出處:http://en.pswp.cn/news/284372.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

webrtc 視頻 demo

webrtc 視頻 demo webrtc網上封裝的很多&#xff0c;demo很多都是一個頁面里實現的&#xff0c;今天實現了個完整的 &#xff0c; A 發視頻給 BA webrtc.html作為offer <!DOCTYPE html> <html id"home" lang"en"><head><meta http-e…

條件注釋判斷瀏覽器版本!--[if lt IE 9](轉載)

<!--[if !IE]><!--> 除IE外都可識別 <!--<![endif]--> <!--[if IE]> 所有的IE可識別 <![endif]--> <!--[if IE 6]> 僅IE6可識別 <![endif]--> <!--[if lt IE 6]> IE6以及IE6以下版本可識別 <![endif]--> <!--[if …

[轉]阿里開源低代碼引擎LowCodeEngine

一、什么是低代碼引擎 低代碼引擎是具備強大擴展能力的低代碼研發框架&#xff0c;使用者只需要基于低代碼引擎便可以快速定制符合自己業務需求的低代碼平臺。同時&#xff0c;低代碼引擎還在標準低代碼設計器的基礎上提供了簡單易用的定制擴展能力&#xff0c;能夠滿足業務獨特…

Beyond Istio OSS——Istio服務網格的現狀與未來

作者&#xff1a;宋凈超&#xff08;Jimmy Song&#xff09;&#xff0c;原文地址&#xff1a;https://jimmysong.io/blog/beyond-istio-oss/本文根據筆者在 GIAC 深圳 2022 年大會上的的演講《Beyond Istio OSS —— Istio 的現狀及未來》[1] 整理而成&#xff0c;演講幻燈片見…

js多維數組扁平化

數組扁平化&#xff0c;就是將多維數組碾平為一維數組&#xff0c;方便使用。 一&#xff1a;例如&#xff0c;一個二維數組 var arr [a, [b, 2], [c, 3, x]]&#xff0c;將其扁平化&#xff1a; 1. 通過 apply 借用數組的 concat 方法&#xff1a; [].concat.apply([], arr)…

第16講 用戶程序的結構與執行

轉載于:https://www.cnblogs.com/atuo/p/5609843.html

兩種方法清除Excel保護密碼

一、利用VBA腳本直接清除 打Excel&#xff0c;打開腳本編輯器&#xff08;AltF11&#xff09;或者如圖&#xff0c;右鍵sheet名稱 輸入代碼并運行&#xff0c;即可清除密碼保護&#xff1a; Sub DeletePW()ActiveSheet.Protect DrawingObjects:True, Contents:True, AllowFil…

jsonp-反向代理-CORS解決JS跨域問題的個人總結

jsonp-反向代理-CORS解決JS跨域問題的個人總結 網上說了很多很多&#xff0c;但是看完之后還是很混亂&#xff0c;所以我自己重新總結一下。解決 js 跨域問題一共有8種方法&#xff0c; jsonp&#xff08;只支持 get&#xff09;反向代理CORSdocument.domain iframe 跨域windo…

URAL 1682 Crazy Professor (并查集)

【題目鏈接】 http://acm.timus.ru/problem.aspx?space1&num1682 【題目大意】 給出k&#xff0c;從1開始不斷地加一并把這個數寫在黑板上&#xff0c;如果寫上的數字和之前的數字滿足   (ab*b)%k0或者(ba*a)%k0就在他們之間連一條線&#xff0c;如果黑板上出現環就結束…

利用Python隨機或暴力生成密碼

""" Title: python 密碼生成 Author: JackieZheng Date: 2022-04-09 12:47:33 LastEditTime: 2022-04-09 14:00:46 LastEditors: Please set LastEditors Description: FilePath: \\pythonCode\\python_pwd_generater.py """import itertools im…

EasyNetQ-用于使用 RabbitMQ 的 .NET API開源的工具庫

Part1介紹EasyNetQ 的目標是提供一個庫&#xff0c;用于在 .NET 中使用 RabbitMQ 盡可能簡單。為了做到這一點&#xff0c;它通過強制執行一些簡單的約定來以靈活性換取簡單性。這些包括&#xff1a;消息應該由 .NET 類型表示。消息應按其 .NET 類型進行路由。這意味著消息是由…

python 中 __name__ 的使用

1. 如果模塊是被導入&#xff0c;__name__的值為模塊名字2. 如果模塊是被直接執行&#xff0c;__name__的值為’__main__’Py1.py #&#xff01;/usr/bin/env python def test():print __name__ ,__name__ if __name__ __main__:test() Py2.py #&#xff01;/usr/bin/env pyt…

第6章 循環

6.1 range 函數用來創建一個數字列表&#xff0c;它的范圍是從起始數字開始到結束數字之前 1 >>> for x in range(0,5): 2 print(Hello %s % x) 3 4 Hello 0 5 Hello 1 6 Hello 2 7 Hello 3 8 Hello 4 1 >>> print(list(range(10,20))) 2 [10, 11, 12, …

C# 實例解釋面向對象編程中的依賴反轉原則

在面向對象編程中&#xff0c;SOLID 是五個設計原則的首字母縮寫&#xff0c;旨在使軟件設計更易于理解、靈活和可維護。這些原則是由美國軟件工程師和講師羅伯特C馬丁(Robert Cecil Martin)提出的許多原則的子集&#xff0c;在他2000年的論文《設計原則與設計模式》中首次提出…

Linux學習筆記之一————什么是Linux及其應用領域

1.1認識Linux 1&#xff09;什么是操作系統 2&#xff09;現實生活中的操作系統 win7 Mac Android iOS 3&#xff09; 操作系統的發展史 &#xff08;1&#xff09;Unix 1965年之前的時候&#xff0c;電腦并不像現在一樣普遍&#xff0c;它可不是一般人能碰的起的&#xff0c;…

Flex中寬度計算

flex 有三個屬性值&#xff0c;分別是 flex-grow&#xff0c; flex-shrink&#xff0c; flex-basis&#xff0c;默認值是 0 1 auto。 發現網上詳細介紹他們的文章比較少&#xff0c; 今天就詳細說說他們&#xff0c;先一個一個看。 flex-grow 定義項目的放大比例&#xff0c;默…

Lucene詳解

一.lucene原理 Lucene 是apache軟件基金會一個開放源代碼的全文檢索引擎工具包&#xff0c;是一個全文檢索引擎的架構&#xff0c;提供了完整的查詢引擎和索引引擎&#xff0c;部分文本分析引擎。它不是一個完整的搜索應用程序&#xff0c;而是為你的應用程序提供索引和搜索功能…

.NET 6.0中使用Identity框架實現JWT身份認證與授權

原文作者&#xff1a;Sarathlal Saseendran原文鏈接&#xff1a;https://www.c-sharpcorner.com/article/jwt-authentication-and-authorization-in-net-6-0-with-identity-framework/翻譯&#xff1a;沙漠盡頭的狼&#xff08;谷歌翻譯加持&#xff09;介紹微軟于 2021 年 11 …

adb devices 里面有很多 emulator-XXXX的解決方法

2019獨角獸企業重金招聘Python工程師標準>>> adb kill-server 轉載于:https://my.oschina.net/sfshine/blog/700354

MQ(Message Queue)簡介

一、何為MQ&#xff1f; MQ全稱為Message Queue, 消息隊列&#xff08;MQ&#xff09;是一種應用程序對應用程序的通信方法。應用程序通過讀寫出入隊列的消息&#xff08;針對應用程序的數據&#xff09;來通信&#xff0c;而無需專用連接來鏈接它們。消息傳遞指的是程序之間通…