05-go-agent-development by TencentBlueKing
Go Agent 开发指南,涵盖 Agent 架构设计、心跳机制、任务执行、日志上报、升级流程、与 Dispatch 模块交互。当用户开发构建机 Agent、实现任务执行逻辑、处理 Agent 通信或进行 Go 语言开发时使用。
Coding
2.5K Stars
514 Forks
Updated Jan 9, 2026, 09:52 AM
Why Use This
This skill provides specialized capabilities for TencentBlueKing's codebase.
Use Cases
- Developing new features in the TencentBlueKing repository
- Refactoring existing code to follow TencentBlueKing standards
- Understanding and working with TencentBlueKing's codebase structure
Skill Snapshot
Auto scan of skill assets. Informational only.
Valid SKILL.md
Checks against SKILL.md specification
Source & Community
Skill Stats
SKILL.md 338 Lines
Total Files 1
Total Size 0 B
License NOASSERTION
---
name: 05-go-agent-development
description: Go Agent 开发指南,涵盖 Agent 架构设计、心跳机制、任务执行、日志上报、升级流程、与 Dispatch 模块交互。当用户开发构建机 Agent、实现任务执行逻辑、处理 Agent 通信或进行 Go 语言开发时使用。
---
# Skill 05: Go Agent 开发
## 概述
BK-CI 的构建代理(Agent)使用 Go 语言编写,负责与后端服务通信、执行构建任务。
## 技术栈
| 组件 | 版本 | 用途 |
|------|------|------|
| Go | 1.19+ | 编程语言 |
| go.mod | - | 依赖管理 |
| agentcommon/logs | - | 日志框架 |
## 项目结构
```
src/agent/
├── agent/ # 主代理
│ └── src/pkg/
│ ├── api/ # API 调用
│ ├── config/ # 配置管理
│ ├── collector/ # 数据采集
│ ├── job/ # 任务执行
│ ├── pipeline/ # 流水线处理
│ └── util/ # 工具函数
├── agent-slim/ # 轻量版代理
└── common/ # 通用库
```
## 命名规范
### 包命名
- 小写单词,不使用下划线
- 示例:`agent`, `api`, `config`, `collector`, `job`, `pipeline`, `util`
### 结构体命名
使用 PascalCase:
```go
type ThirdPartyAgentStartInfo struct {
HostName string `json:"hostname"`
HostIp string `json:"hostIp"`
DetectOs string `json:"detectOS"`
MasterVersion string `json:"masterVersion"`
SlaveVersion string `json:"version"`
}
type ThirdPartyBuildInfo struct {
ProjectId string `json:"projectId"`
BuildId string `json:"buildId"`
ToDelTmpFiles []string `json:"-"` // 不序列化字段
}
```
### 常量定义
```go
const (
KeyProjectId = "devops.project.id"
KeyAgentId = "devops.agent.id"
KeySecretKey = "devops.agent.secret.key"
KeyDevopsGateway = "landun.gateway"
)
```
### 枚举类型
```go
type BuildJobType string
const (
AllBuildType BuildJobType = "ALL"
DockerBuildType BuildJobType = "DOCKER"
BinaryBuildType BuildJobType = "BINARY"
)
```
## 配置管理
```go
// config/config.go
type AgentConfig struct {
Gateway string
ProjectId string
AgentId string
SecretKey string
ParallelTaskCount int
}
var GAgentConfig *AgentConfig
func Init() {
GAgentConfig = &AgentConfig{
Gateway: getConfigValue(KeyDevopsGateway),
ProjectId: getConfigValue(KeyProjectId),
AgentId: getConfigValue(KeyAgentId),
SecretKey: getConfigValue(KeySecretKey),
}
}
func GetGateWay() string {
return GAgentConfig.Gateway
}
```
## API 调用模式
```go
// api/api.go
func buildUrl(url string) string {
return config.GetGateWay() + url
}
func AgentStartup() (*httputil.DevopsResult, error) {
url := buildUrl("/ms/environment/api/buildAgent/agent/thirdPartyAgent/startup")
startInfo := &ThirdPartyAgentStartInfo{
HostName: systemutil.GetHostName(),
HostIp: systemutil.GetAgentIp(),
DetectOs: systemutil.GetOsName(),
MasterVersion: config.AgentVersion,
SlaveVersion: config.WorkerVersion,
}
return httputil.NewHttpClient().Post(url).Body(startInfo, false).
SetHeaders(config.GAgentConfig.GetAuthHeaderMap()).Execute(nil).IntoDevopsResult()
}
```
## 错误处理模式
### 标准错误检查
```go
if err != nil {
logs.WithError(err).Error("init third_components error")
systemutil.ExitProcess(1)
}
```
### 重试模式
```go
_, err := job.AgentStartup()
if err != nil {
logs.WithError(err).Error("agent startup failed")
for {
_, err = job.AgentStartup()
if err == nil {
break
}
logs.WithError(err).Error("agent startup failed")
time.Sleep(5 * time.Second)
}
}
```
### Panic 恢复
```go
defer func() {
if err := recover(); err != nil {
logs.Error("agent collect panic: ", err)
}
}()
```
## 日志记录规范
```go
// 日志级别使用
logs.Debug("do Collect")
logs.Info("agent collector off")
logs.Infof("collect ip change data: %s", ipData.Data)
logs.Error("agent collect panic: ", err)
logs.WithError(err).Error("init third_components error")
```
## 并发模式
### 启动 goroutine
```go
go collector.Collect()
go cron.CleanJob()
```
### 使用 defer 清理资源
```go
defer config.EBus.Unsubscribe(config.IpEvent, eBusId)
```
### Channel 通信
```go
// 创建 channel
done := make(chan bool)
// 发送数据
done <- true
// 接收数据
<-done
```
## 测试规范
### 测试文件命名
`*_test.go`
### 测试示例
```go
// build_test.go
package job
import (
"testing"
)
func TestBuildJob(t *testing.T) {
// 测试逻辑
}
func TestParseJobType(t *testing.T) {
tests := []struct {
input string
expected BuildJobType
}{
{"ALL", AllBuildType},
{"DOCKER", DockerBuildType},
{"BINARY", BinaryBuildType},
}
for _, tt := range tests {
result := ParseJobType(tt.input)
if result != tt.expected {
t.Errorf("ParseJobType(%s) = %v, want %v", tt.input, result, tt.expected)
}
}
}
```
## HTTP 客户端封装
```go
type HttpClient struct {
client *http.Client
request *http.Request
}
func NewHttpClient() *HttpClient {
return &HttpClient{
client: &http.Client{
Timeout: 30 * time.Second,
},
}
}
func (c *HttpClient) Get(url string) *HttpClient {
req, _ := http.NewRequest("GET", url, nil)
c.request = req
return c
}
func (c *HttpClient) Post(url string) *HttpClient {
req, _ := http.NewRequest("POST", url, nil)
c.request = req
return c
}
func (c *HttpClient) SetHeaders(headers map[string]string) *HttpClient {
for k, v := range headers {
c.request.Header.Set(k, v)
}
return c
}
func (c *HttpClient) Execute(body interface{}) *HttpResponse {
resp, err := c.client.Do(c.request)
// 处理响应
return &HttpResponse{resp: resp, err: err}
}
```
## 系统工具函数
```go
// util/systemutil/systemutil.go
func GetHostName() string {
hostname, _ := os.Hostname()
return hostname
}
func GetAgentIp() string {
addrs, _ := net.InterfaceAddrs()
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String()
}
}
}
return ""
}
func GetOsName() string {
return runtime.GOOS
}
func ExitProcess(code int) {
os.Exit(code)
}
```
## 进程管理
Agent 由两个进程组成:
- **DevopsDaemon**:守护进程,负责启动和监控 Agent
- **DevopsAgent**:主进程,负责与服务端通信和执行任务
```go
// 启动 Worker 进程
func StartWorker(buildInfo *ThirdPartyBuildInfo) error {
cmd := exec.Command(workerPath, buildInfo.BuildId)
cmd.Dir = workDir
cmd.Env = os.Environ()
return cmd.Start()
}
```
Name Size