一. gitlab-runner四大过程
注册runner
首先注册runner,输入url、token、tag等信息,注册时会向GitLab发送一个请求。
GitLab接收到请求后会返回一个token给runner,runner之后和GitLab的请求通讯都会带上这个token。查询job
注册成功后runner会向GitLab轮询请求,查看是否要执行任务。查询到job后构建执行
某位开发者push了代码,代码更新了,这时候流水线pipeline启动,并将第一个stage设置为pending状态。
在步骤3完成没多久,runner的下一次请求已经到达了,这时候会把pending状态的stage发送到runner处,之后runner开始干活,执行gitlab-ci.yml中的脚本。Job Trace日志信息发送给GitLab Server
当执行完了脚本后,runner会发送最终的执行结果,如果完成则为passed,失败则为failed
其中涉及到与gitlab-ci交互的部分
common/network.go
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) (int, UpdateState)
DownloadArtifacts(config JobCredentials, artifactsFile string) DownloadState
UploadRawArtifacts(config JobCredentials, reader io.Reader, options ArtifactsOptions) UploadState
ProcessJob(config RunnerConfig, buildCredentials *JobCredentials) (JobTrace, error)
}
二 注册runner的核心步骤
- commands/register.go#init()
func init() {
common.RegisterCommand2("register", "register a new runner", newRegisterCommand())
}
备注: newRegisterCommand()生成RegisterCommand的实例,中有Execute()方法
- commands/register.go#Execute()
s.askRunner() //询问url token description tags
s.askExecutor()
s.askExecutorOptions()//询问用那个类型的Executor
s.mergeTemplate()
s.addRunner(&s.RunnerConfig)
s.saveConfig() //将配置保存到config.toml
备注:完成配置.
3.network/gitlab.go#RegisterRunner
request := common.RegisterRunnerRequest{
RegisterRunnerParameters: parameters,
Token: runner.Token,
Info: n.getRunnerVersion(common.RunnerConfig{}),
}
var response common.RegisterRunnerResponse
result, statusText, _, _ := n.doJSON(&runner, "POST", "runners", http.StatusCreated, &request, &response)
备注:向server去注册
三 .查询job的核心步骤
commands/multi.go#Run
func (mr *RunCommand) Run() {
//忽略不重要代码
go mr.feedRunners(runners)
//忽略不重要代码
go mr.startWorkers(startWorker, stopWorker, runners)
}
备注:
第一个方法会判断是否在可用的runners,若存在,则遍历所有可用的 runner,并周期性地(CheckInterval / len(runners))往runners 通道中压入runner。
第二个方法中的mr.processRunners方法通过select case结构从runners取前面压入的runner实例,一旦取出成功则执行processRunner方法调用链最终调用network/gitlab.go#RequestJob方法,去请求.
三、四. 查询到job后构建执行并返回日志信息
network/gitlab.go#ProcessJob
func (n *GitLabClient) ProcessJob(config common.RunnerConfig, jobCredentials *common.JobCredentials) (common.JobTrace, error) {
trace, err := newJobTrace(n, config, jobCredentials)
//开启 Job Trace 输出
trace.start()
return trace, nil
}
备注:
trace.start方法来调用trace.watch以周期性地patch Job trace
具体patch Job代码还未来的及看,后面更新.
附上:gitlab-runner 与自己程序交互的demo(只有注册runner部分)
package runners
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
type RegisterResp struct {
Id string `json:"id"`
Token string `json:"token"`
}
func InitServer(port string) error {
mux := &http.ServeMux{}
srv := http.Server{
Addr: ":" + port,
Handler: mux,
}
mux.HandleFunc("/api/v4/runners", func(w http.ResponseWriter, req *http.Request) {
//var closeFlag bool = true
con, _ := ioutil.ReadAll(req.Body) //获取post的数据
fmt.Println(string(con))
resp := RegisterResp{
Id: "55",
Token: "ft7eVVzxa6NrT3XfcKgk",
}
respData, _ := json.Marshal(resp)
//w.Header()
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(201)
fmt.Println(string(respData))
if _, err := w.Write(respData); err != nil {
fmt.Println(err.Error())
}
})
return srv.ListenAndServe()
}
启动
runners.InitServer("80")
查看通信情况抓取http包数据命令:
sudo tcpdump -i vnic0 -A -s 0 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'