kubernetes源码调试体验




源码是k8s的release-1.10分支,为啥没用master?因为我的虚拟机里面安装的golang版本是1.9.4的,不满足最新版的要求,也懒得更新了。

编译环境是CentOS 7.2 x86_64。

总的来说,Go的调试与C比较接近,调试工具和调试命令都很像,尤其是都可以用gdb调试,但我这次没有用gdb,而是用了网上说的更适合Go的delve

要调试k8s,首先得编译出来debug版本的二进制程序,正常编译肯定是make all就好了,debug版本的要去掉编译优化选项并打开调试选项,看了下k8s的Makefile:

可以看到 81 # make all GOGCFLAGS="-N -l" 这行和下面的几行注释,提到了-N -l这两个编译选项,分别是指禁用编译优化和禁用内联优化,最终目的是可以在调试代码的时候单步执行可以看到实际对应的每行源码,具体支持的选项列表可以通过如下命令查看:

 

要编译首先得下载源码, git clone https://github.com/kubernetes/kubernetes.git ,之后切到你想要编译的分支,这里以release-1.10为例, git checkout origin/release-1.10 -b release-1.10 ,根据官方文档准备编译环境:https://github.com/kubernetes/community/blob/master/contributors/devel/development.md#building-kubernetes-on-a-local-osshell-environment

我这里选的是Linux本地编译,不是Docker编译。etcd和go的安装就不多说了。接下来就是编译了,命令上面已经说过了 make all GOGCFLAGS="-N -l" ,在kubernetes目录(源码根目录)下执行就好了。然后就是等待编译结束,第一次会比较慢,编译好之后会把编译好的二进制文件放到kubernetes目录下的 _output/bin/ ,对应源码则是在 _output/local/go/src/ ,进入目录可以看出, _output/local/go/src/k8s.io/ 下面的kubernetes目录其实是一个软链接,链接到kubernetes源码根目录。

接下来是调试过程,首先要安装delve,安装也比较简单,我是用的 go get github.com/derekparker/delve/cmd/dlv 直接下载编译的,自动编译好的二进制文件放在 ~/go/bin/ 目录下(也就是是$GOPATH/bin目录),接下来要么你把这个dlv工具copy到系统变量$PATH里任何目录下(比如/usr/local/bin),要么也可以建个软链接过去,或者把它所在的目录也加到系统变量$PATH下,比如在dlv工具所在目录执行: export PATH=$PATH:`pwd` (注意这种方式没有持久化,退出shell窗口之后就失效了,需要再次执行),之后就可以在任何地方运行dlv命令了。

首先看下dlv用法,目前看起来比较常用的是attach、exec、debug三个子命令:

简单调试下kube-apiserver:

这里我没有给kube-apiserver传参数,执行c之后就异常退出了,我们这次调试的目标就是分析为啥退出?首先在入口的main函数处设置断点(可以不设置):

之后根据错误日志”–etcd-servers must be specified”找到报错的代码位置处继续设置断点(注意源文件路径,是相对路径,相对于/root/k8s/kubernetes/_output/local/go/src/目录的,其中kubernetes是git clone的源码根目录):

bt命令可以看到整个调用栈:

这里简单调试下,主要是为后续深入分析k8s代码做准备,有了调试工具,所有代码流程都可以分析清楚,只需要在你关心的代码或者函数哪里加上断点,之后等代码执行过来执行bt命令就能看到整个调用栈了,非常省事省力快速有效,拿到调用栈之后就是一步一步的分析源码了,这样可以保证分析流程不出错(尤其是在大型项目里,很多同名函数、回调函数,很容易绕晕,调用栈基本就相当于是分析代码流程的指南针了)。

dlv的attach和debug,也试了下,也比较简单,只是使用场景不太一样而已,attach是挂载到运行中的进程进行debug,debug是直接针对go源码调试的,命令执行起来之后跟exec调试过程就没有区别了。这里就不贴执行流程了。