网站注册凡科,东莞常平电镀工业园,wordpress结构图数据库图,怎么把html文件生成网址云原生学习路线导航页#xff08;持续更新中#xff09; 本文是 Kubernetes api-server源码阅读 系列第二篇#xff0c;主要讲述如何实现 kubernetes api-server 的 debug 参考b站视频地址#xff1a;Kubernetes源码开发之旅二 1.本篇章任务
Go-Delve#xff1a;go语言的…云原生学习路线导航页持续更新中 本文是 Kubernetes api-server源码阅读 系列第二篇主要讲述如何实现 kubernetes api-server 的 debug 参考b站视频地址Kubernetes源码开发之旅二 1.本篇章任务
Go-Delvego语言的调试工具Debug模式启动集群为了能调试需要使用Debug的方式启动集群命令行调试在没有IDE的情况下迅速调试kubernetes就可以使用命令行的调试功能VS Code中调试为了方便我们学习使用VS Code实现远程调试线上的kubernetesGoland中调试现在有很多人喜欢使用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/delvego-delve 属于golang语言基础设施的一部分像vscode的go语言插件、goland调试功能等底层都是使用了delve。go的plugin好像也用到了go-delvego的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/dlvv1.9.1安装完测试是否已经安装成功了rootgraham-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.tomlUsage: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 dapDebug Adaptor Protocol也是支持远程连接的方式不过我们这里没有使用
2.3.3.调试过程中用到的命令
这里大约列出来90%可用命令
2.3.4.通过一个demo演示delve使用方法
2.3.4.1.项目github地址
这是根据 视频中老师讲的项目编写的demo大家可以克隆下来直接用https://github.com/graham924/delve-studygit clone https://github.com/graham924/delve-study.gitdemo内容简介 使用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: grahamzhu2.3.4.2.使用delve演示本地命令行调试 cd delve-study 后执行命令 dlv debug delve-study create --namegrahamzhu发现报错 Error: unknown flag: --name这是因为使用dlv的情况下指定命令行参数不能这么写。需要用一个 -- 分隔开 这么写就对了 dlv debug delve-study -- create --namegrahamzhu执行后进入dlv的命令模式 rootgraham-virtual-machine:~/zgy/go_project/delve-study# dlv debug delve-study -- create --namegrahamzhu
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) continuedelve-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 calleddelve-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) nextdelve-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) stepdelve-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: grahamzhudelve-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 02.3.4.3.使用delve演示远程命令行调试
使用 dlv --headless debug 进行远程调试rootgraham-virtual-machine:~/zgy/go_project/delve-study# dlv --headless debug delve-study -- create --namegrahamzhu
API server listening at: 127.0.0.1:38437 可以看到在端口 38437 启动了一个serve这就是delve启动的一个进程终端没有结束正在等待我们远程连接然后我们开启另一个终端模拟是远程使用 dlv connect 127.0.0.1:38437 远程连接调试。可以看到已经进入了dlv的命令行界面rootgraham-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下会干什么 gogcflagsall-trimpath${trimroot} ${GOGCFLAGS:-}if [[ ${DBG:-} 1 ]]; then# Debugging - disable optimizations and inlining.gogcflags${gogcflags} -N -lfigoldflagsall$(kube::version::ldflags) ${GOLDFLAGS:-}if [[ ${DBG:-} ! 1 ]]; then# Not debugging - disable symbols and DWARF.goldflags${goldflags} -s -wfi我们改成下面这样就好了 把debug执行的语句从if中取出这样不管怎么样都会执行非debug执行的代码注掉这样就不会执行到了 gogcflagsall-trimpath${trimroot} ${GOGCFLAGS:-}# if [[ ${DBG:-} 1 ]]; then# # Debugging - disable optimizations and inlining.# gogcflags${gogcflags} -N -l# fi
gogcflags${gogcflags} -N -lgoldflagsall$(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.sh3.4.使用delve重新启动API Server 本地集群启动之后我们先看一下目前机器中启动了kubernetes的哪些组件 rootgraham-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 rootgraham-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-modeNode,RBAC --cloud-provider --cloud-config --v3 --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-lookuptrue --service-account-issuerhttps://kubernetes.default.svc --service-account-jwks-urihttps://kubernetes.default.svc/openid/v1/jwks --service-account-signing-key-file/tmp/kube-serviceaccount.key --enable-admission-pluginsNamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,Priority,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,NodeRestriction --disable-admission-plugins --admission-control-config-file --bind-address0.0.0.0 --secure-port6443 --tls-cert-file/var/run/kubernetes/serving-kube-apiserver.crt --tls-private-key-file/var/run/kubernetes/serving-kube-apiserver.key --storage-backendetcd3 --storage-media-typeapplication/vnd.kubernetes.protobuf --etcd-servershttp://127.0.0.1:2379 --service-cluster-ip-range10.0.0.0/24 --feature-gatesAllAlphafalse --external-hostnamelocalhost --requestheader-username-headersX-Remote-User --requestheader-group-headersX-Remote-Group --requestheader-extra-headers-prefixX-Remote-Extra- --requestheader-client-ca-file/var/run/kubernetes/request-header-ca.crt --requestheader-allowed-namessystem: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 --colorauto 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-modeNode,RBAC --cloud-provider --cloud-config --v3 --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-lookuptrue --service-account-issuerhttps://kubernetes.default.svc --service-account-jwks-urihttps://kubernetes.default.svc/openid/v1/jwks --service-account-signing-key-file/tmp/kube-serviceaccount.key --enable-admission-pluginsNamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,Priority,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,NodeRestriction --disable-admission-plugins --admission-control-config-file --bind-address0.0.0.0 --secure-port6443 --tls-cert-file/var/run/kubernetes/serving-kube-apiserver.crt --tls-private-key-file/var/run/kubernetes/serving-kube-apiserver.key --storage-backendetcd3 --storage-media-typeapplication/vnd.kubernetes.protobuf --etcd-servershttp://127.0.0.1:2379 --service-cluster-ip-range10.0.0.0/24 --feature-gatesAllAlphafalse --external-hostnamelocalhost --requestheader-username-headersX-Remote-User --requestheader-group-headersX-Remote-Group --requestheader-extra-headers-prefixX-Remote-Extra- --requestheader-client-ca-file/var/run/kubernetes/request-header-ca.crt --requestheader-allowed-namessystem: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已经没有了 rootgraham-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_PROVIDERlocalcluster/kubectl.sh config set-cluster local --serverhttps://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 --clusterlocal --usermyselfcluster/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-issuerhttps://kubernetes.default.svc --service-account-jwks-urihttps://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-servershttp://${ETCD_HOST}:${ETCD_PORT} --service-cluster-ip-range${SERVICE_CLUSTER_IP_RANGE} --feature-gates${FEATURE_GATES} --external-hostname${EXTERNAL_HOSTNAME} --requestheader-username-headersX-Remote-User --requestheader-group-headersX-Remote-Group --requestheader-extra-headers-prefixX-Remote-Extra- --requestheader-client-ca-file${CERT_DIR}/request-header-ca.crt --requestheader-allowed-namessystem: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} 21
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-version2 另外为了方便查看调试过程中的错误我们将日志打印出来即–log --log-outputdebugger,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-version2 --log --log-outputdebugger,gdbwire,lldbout,debuglineerr,rpc,dap,fncall,minidump --log-dest/root/delve-log/log -- --authorization-modeNode,RBAC --cloud-provider --cloud-config --v3 --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-lookuptrue --service-account-issuerhttps://kubernetes.default.svc --service-account-jwks-urihttps://kubernetes.default.svc/openid/v1/jwks --service-account-signing-key-file/tmp/kube-serviceaccount.key --enable-admission-pluginsNamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,Priority,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,NodeRestriction --disable-admission-plugins --admission-control-config-file --bind-address0.0.0.0 --secure-port6443 --tls-cert-file/var/run/kubernetes/serving-kube-apiserver.crt --tls-private-key-file/var/run/kubernetes/serving-kube-apiserver.key --storage-backendetcd3 --storage-media-typeapplication/vnd.kubernetes.protobuf --etcd-servershttp://127.0.0.1:2379 --service-cluster-ip-range10.0.0.0/24 --feature-gatesAllAlphafalse --external-hostnamelocalhost --requestheader-username-headersX-Remote-User --requestheader-group-headersX-Remote-Group --requestheader-extra-headers-prefixX-Remote-Extra- --requestheader-client-ca-file/var/run/kubernetes/request-header-ca.crt --requestheader-allowed-namessystem: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上 rootgraham-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) continuemain.main() cmd/kube-apiserver/apiserver.go:33 (hits goroutine(1):1 total:1) (PC: 0x5233854)使用 next 执行到下一行 (dlv) nextmain.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) stepk8s.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的 rootgraham-virtual-machine:~# ufw status
状态 激活至 动作 来自
- -- --
22 ALLOW Anywhere
22 (v6) ALLOW Anywhere (v6)然后开放12345端口 rootgraham-virtual-machine:~# ufw allow 12345
规则已添加
规则已添加 (v6)再看一下开放端口可以看到12345已经开启了 rootgraham-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-version2 --log --log-outputdebugger,gdbwire,lldbout,debuglineerr,rpc,dap,fncall,minidump --log-dest/root/delve-log/log -- --authorization-modeNode,RBAC --cloud-provider --cloud-config --v3 --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-lookuptrue --service-account-issuerhttps://kubernetes.default.svc --service-account-jwks-urihttps://kubernetes.default.svc/openid/v1/jwks --service-account-signing-key-file/tmp/kube-serviceaccount.key --enable-admission-pluginsNamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,Priority,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,NodeRestriction --disable-admission-plugins --admission-control-config-file --bind-address0.0.0.0 --secure-port6443 --tls-cert-file/var/run/kubernetes/serving-kube-apiserver.crt --tls-private-key-file/var/run/kubernetes/serving-kube-apiserver.key --storage-backendetcd3 --storage-media-typeapplication/vnd.kubernetes.protobuf --etcd-servershttp://127.0.0.1:2379 --service-cluster-ip-range10.0.0.0/24 --feature-gatesAllAlphafalse --external-hostnamelocalhost --requestheader-username-headersX-Remote-User --requestheader-group-headersX-Remote-Group --requestheader-extra-headers-prefixX-Remote-Extra- --requestheader-client-ca-file/var/run/kubernetes/request-header-ca.crt --requestheader-allowed-namessystem: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-version2 --log --log-outputdebugger,gdbwire,lldbout,debuglineerr,rpc,dap,fncall,minidump --log-dest/root/delve-log/log -- --authorization-modeNode,RBAC --cloud-provider --cloud-config --v3 --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-lookuptrue --service-account-issuerhttps://kubernetes.default.svc --service-account-jwks-urihttps://kubernetes.default.svc/openid/v1/jwks --service-account-signing-key-file/tmp/kube-serviceaccount.key --enable-admission-pluginsNamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,Priority,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,NodeRestriction --disable-admission-plugins --admission-control-config-file --bind-address0.0.0.0 --secure-port6443 --tls-cert-file/var/run/kubernetes/serving-kube-apiserver.crt --tls-private-key-file/var/run/kubernetes/serving-kube-apiserver.key --storage-backendetcd3 --storage-media-typeapplication/vnd.kubernetes.protobuf --etcd-servershttp://127.0.0.1:2379 --service-cluster-ip-range10.0.0.0/24 --feature-gatesAllAlphafalse --external-hostnamelocalhost --requestheader-username-headersX-Remote-User --requestheader-group-headersX-Remote-Group --requestheader-extra-headers-prefixX-Remote-Extra- --requestheader-client-ca-file/var/run/kubernetes/request-header-ca.crt --requestheader-allowed-namessystem: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 rootgraham-virtual-machine:~/go/src/k8s.io/kubernetes# cd ~/go/src/k8s.io.kubernetes
rootgraham-virtual-machine:~/go/src/k8s.io/kubernetes# cluster/kubectl.sh get sa
NAME SECRETS AGE
default 0 162m
rootgraham-virtual-machine:~/go/src/k8s.io/kubernetes# cluster/kubectl.sh get secret
No resources found in default namespace.然后我们创建一个ServiceAccount名称为 forpostman rootgraham-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了需要我们手动创建并绑定 rootgraham-virtual-machine:~/go/src/k8s.io/kubernetes# cluster/kubectl.sh describe sa forpostman
Name: forpostman
Namespace: default
Labels: lt;nonegt;
Annotations: lt;nonegt;
Image pull secrets: lt;nonegt;
Mountable secrets: lt;nonegt;
Tokens: lt;nonegt;
Events: lt;nonegt;创建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 rootgraham-virtual-machine:~/go/src/k8s.io/kubernetes# cluster/kubectl.sh create -f ~/zgy/go_yaml/postman-sa-secret.yaml
secret/postman-sa-secret createdrootgraham-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 28srootgraham-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-tokenDataca.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才能登陆成功 rootgraham-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: none5.3.3.为ServiceAccount授权 虽然secret已经有token了根据token能找到 对应namespace下的ServiceAccount即default:forpostman 但是forpostman还没有任何权限我们需要给 forpostman 授权。 我们先看一下系统中有哪些权限我们直接选一个权限大的设置给这个secret就可以了不用自己再创建了。从下面来看集群角色还真不少我们选一个权限大的cluster-admin rootgraham-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绑定的对象是nsdefault下的serviceAccountforpostman rootgraham-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 createdrootgraham-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 从我们创建的secretpostman-sa-secret 中提出证书 将secret的data中ca.crt 的内容以 base64 的编码写到 /tmp/ca.crt此即为证书 rootgraham-virtual-machine:~/go/src/k8s.io/kubernetes# cluster/kubectl.sh get secret postman-sa-secret -o jsonpath{.data[ca\.crt]} | base64 -d /tmp/ca.crtrootgraham-virtual-machine:~/go/src/k8s.io/kubernetes# ls /tmp/ca.*
/tmp/ca.crt我们将这个证书复制到postman的主机上 C:\Users\tmp scp root192.168.245.146:/tmp/ca.crt ./ca.crt
root192.168.245.146s 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.crtpostman创建一个请求填写urlhttps://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