云原生学习路线导航页(持续更新中)
本文是 Kubernetes api-server源码阅读 系列第二篇,主要讲述如何实现 kubernetes api-server 的 debug
- 参考b站视频地址:Kubernetes源码开发之旅二
$ git clone https://github.com/go-delve/delve
$ cd delve
$ go install github.com/go-delve/delve/cmd/[email protected]
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.
git clone https://github.com/graham924/delve-study.git
go run main.go
执行的是rootCmd,会打印"hello world"go run main.go create --name grahamzhu
,则会打印:create command is called
name: grahamzhu
cd delve-study
后,执行命令:
dlv debug delve-study create --name=grahamzhu
Error: unknown flag: --name
--
分隔开这么写就对了
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函数调用方的下一行代码
name: grahamzhu
(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
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
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)
修改 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"
fi
goldflags="all=$(kube::version::ldflags) ${GOLDFLAGS:-}"
if [[ "${DBG:-}" != 1 ]]; then
# Not debugging - disable symbols and DWARF.
goldflags="${goldflags} -s -w"
fi
我们改成下面这样就好了
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了
修改完 hack/lib.golang.sh 后,因为修改了源码,所以需要执行make clean,清除已编译的旧的可执行程序。
然后通过 hack/local-up-cluster.sh 脚本启动本地集群
cd ~/go/src/k8s.io/kubernetes
make clean
hack/local-up-cluster.sh
本地集群启动之后,我们先看一下,目前机器中启动了kubernetes的哪些组件
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的一个组件,进而可以进行远程调试
之所以杀掉 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 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被意外终止了
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
因为已经有了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]+)?$"
命令执行之后,程序会停在光标位置,但是没有结束。这就是在等待远程连接
打开我们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已经运行起来了
因为要远程连接,而且我们固定使用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)
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]+)?$"
然后来到物理机的VS Code上,使用VSCode打开kubernetes的源代码,然后在kubernetes项目的 .vscode目录 下,创建一个launch.json
并在 VS Code 上,给程序打一个断点。我们还把断点打在 apiserver.go的main函数第一句,也就是33行
然后,点开VSCode的左侧Debug页面,点击Debug按钮,稍等一下后,发现程序已经运行,并且停在了我们的断点处,之后就可以使用VSCode进行调试了
如果你发现终端报错,启动失败了,报错:Error: “kube-apiserver” does not take any arguments
--
后面的apiserver参数有问题。开放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]+)?$"
先查看一下当前集群中有哪些资源。可以看到,环境很干净,只有一个默认的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。
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: <none>
Events: <none>创建
我们给出了一个Secret的yaml文件。创建一个名称为 postman-sa-secret
的secret,并使用annotations 的方式,将之绑定到 一个指定的service-account上,即forpostman这个sa
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
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
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>
虽然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 created
root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# cluster/kubectl.sh get rolebinding
NAME ROLE AGE
forpostmanadmin ClusterRole/cluster-admin 38s
至此,我们完成了 集群中 登录鉴权的配置
从我们创建的secret:postman-sa-secret 中,提出证书
/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.crt
root@graham-virtual-machine:~/go/src/k8s.io/kubernetes# ls /tmp/ca.*
/tmp/ca.crt
我们将这个证书,复制到postman的主机上
C:\Users\tmp> scp [email protected]:/tmp/ca.crt ./ca.crt
[email protected]'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
发送请求,已经有响应了,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