前言
gitlab-runner是当前炙手可热的一个CICD工具,和Gitlab集成非常好。之前在工作中应用了一下,效果相当理想。
为了进一步了解gitlab-runner的运行原理,打算学习一下gitlab-runner源码。
由于是零基础,所以打算把整个过程记录下来,包括每一步的思路,以便复盘;贻笑大方之处,欢迎评论区打脸。
学习思路
一开始,我也不知道这堆代码是怎么划分模块的,所以从main.go
开始,顺藤摸瓜,先对整个系统有个大致的了解,之后再有针对的学习gitlab-runner
中核心的代码。
第一步:main.go
第一步先看main.go
,主要有以下内容:
- 异常处理:panic + recover + defer
- cli_helpers:顾名思义,辅助
cli
的,不用关注。 - 命令行框架:
github.com/urfave/cli
是一个golang
的常见的命令行框架。用他可以很快捷的创建一整套命令体系。我们通过代码目录可以看到commands/
下,保存了大部分command代码。这些command通过init
,注册到common/command
中的变量var commands []cli.Command
。
运行go run main.go
,显示如下:
$ go run main.go
Runtime platform arch=amd64 os=linux pid=19900 revision=HEAD version=development version
NAME:
main - a GitLab Runner
USAGE:
main [global options] command [command options] [arguments...]
VERSION:
development version (HEAD)
AUTHOR:
GitLab Inc. <[email protected]>
COMMANDS:
......
你可以手动试试go run main.go register
,看看是什么。
第二步:register
register命令应该就是我们注册runner使用的命令,输入gitlab的token和url,然后在gitlab上注册一个runner实例。
使用命令go run main.go register
,界面如下:
查看register.go代码:
register
所需要的全部参数定义在type RegisterCommand struct {}
- 结构体tag,如下``包括起来的内容,就是TagList的tag。看他tag的定义,可能会在反射中使用。
TagList string `long:"tag-list" env:"RUNNER_TAG_LIST" description:"Tag list"`
register
除了一堆和输入界面互动的代码,剩下的就是和gitlab通信,注册runner了。
func (s *RegisterCommand) askRunner() {
......
result := s.network.RegisterRunner(s.RunnerCredentials, parameters)
......
}
我们看看network实现了什么。
common/network
network的接口定义如下,看上去他是专门负责和gitlab
通信的。
type Network interface {
RegisterRunner(config RunnerCredentials, parameters RegisterRunnerParameters) *RegisterRunnerResponse
VerifyRunner(config RunnerCredentials) bool
UnregisterRunner(config RunnerCredentials) bool
RequestJob(config RunnerConfig, sessionInfo *SessionInfo) (*JobResponse, bool)
UpdateJob(config RunnerConfig, jobCredentials *JobCredentials, jobInfo UpdateJobInfo) UpdateState
PatchTrace(config RunnerConfig, jobCredentials *JobCredentials, content []byte, startOffset int) PatchTraceResult
DownloadArtifacts(config JobCredentials, artifactsFile string, directDownload *bool) DownloadState
UploadRawArtifacts(config JobCredentials, reader io.Reader, options ArtifactsOptions) UploadState
ProcessJob(config RunnerConfig, buildCredentials *JobCredentials) (JobTrace, error)
}
根据这个接口,我找到了实现类./network/gitlab.go
,RegisterRunner
流程如下:
//gitlab.go
func (n *GitLabClient) RegisterRunner(
......
result, statusText, resp := n.doJSON(
&runner,
http.MethodPost,
"runners",
http.StatusCreated,
&request,
&response,
)
......
}
func (n *GitLabClient) doJSON(
credentials requestCredentials,
method, uri string,
statusCode int,
request interface{
},
response interface{
},
) (int, string, *http.Response) {
......
return c.doJSON(uri, method, statusCode, request, response)
}
//client.go
func (n *client) doJSON(
uri, method string,
statusCode int,
request interface{
},
response interface{
},
) (int, string, *http.Response) {
......
res, err := n.do(uri, method, body, jsonMimeType, headers)
......
}
func (n *client) do(
uri, method string,
request io.Reader,
requestType string,
headers http.Header,
) (*http.Response, error) {
......
req, err := http.NewRequest(method, url.String(), request)
......
}
第三步:调试
下面我们使用VSCode,对上面的代码简单的进行一个跟踪调试。
修改launch.json
进入vscode的debug页面,点击小齿轮,配置一个launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "register",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}",
"args": [
"register"
],
"showLog": true
}
]
}
debug
选中register(gitlab-runner),点击绿色箭头,开始debug:
就可以轻松愉快的跟踪代码了:
然后,好事多磨,这种调试模式,输出信息在Debug console
中,不能模拟stdin
,换句话说,无法进行信息的交互。
查找资料后,发现现有的VSCode插件都不支持debug
模式下的stdin
!!!
我们只能绕过这个坎,使用下面的debug模式:
attach 模式
祭出dlv
工具。我们的解决方案是
- 用
dlv
在终端启动一个debug server
- VSCode采用remote模式,attach到上面的这个
debug server
- 在
dlv
的终端输入命令,在VSCode进行代码的跟踪和调试。
在终端启动一个debug server
# 取消编译优化
go build -gcflags='all=-N -l'
# 启动debug server;
# --headless 指明是debug server
# --listen 指明运行的 IP:port
# -- log 记录日志
# --api-version=2 新版本都是2
# -- register 带入 os.args
dlv debug --headless --listen=127.0.0.1:9999 --log --api-version=2 -- register
在VSCode新建一个debug config,配置如下:
{
"version": "0.2.0",
"configurations": [
{
"name": "attach",
"type": "go",
"request": "attach",
"mode": "remote",
"port": 9999,
"host": "127.0.0.1",
}
]
}
VSCode启动Debug,如下图:
一开始,因为dlv还没开始运行呢,所以Vscode也是没有跟踪到代码。按下F5,Debug Server
就开始跑起来了。如下图:
在这个终端输入runner
的相关参数,你就可以跟踪代码了。
总结
今天摸瓜了一天,大致看明白了gitlab-runner
这套源代码的一个结构。
main.go
:创建一个commands交互框架。./common
:公共方法,通过外观模式,将几大模块对外开放。./commands
:具体的命令行实现模块。./network
:网络相关的,主要是和gitlab的通信。
另外,今天算是有了一个很好的Debug方案。
完毕。