蓝盾 Agent 安装流程

蓝鲸持续集成平台(蓝盾)是一个免费并开源的 CI 服务。本文会介绍蓝盾 Agent 安装的整体流程。

1. 背景

在蓝盾中,构建机 Agent 是执行具体构建任务的核心组件。本文以蓝盾社区版 7.1 为例,详细梳理 Agent 的安装流程。

2. 手动安装流程

按照官方文档的说明,手动安装 Agent 的步骤如下:

  1. 进入蓝盾页面:服务 -> 环境管理 -> 节点
  2. 点击右上角的”导入私有构建机”按钮
  3. 在弹窗中选择目标机器的操作系统(支持 Linux/Windows/MacOS)
  4. 复制安装命令到目标机器执行
  5. 安装完成后点击刷新,确认节点状态
  6. 点击导入完成节点接入

3. 自动化安装方案

除了手动操作外,我们也可以通过调用接口实现 Agent 的自动化安装。主要涉及以下步骤:

3.1. 生成安装命令

GET /ms/environment/api/user/environment/thirdPartyAgent/projects/${projectId}/os/${os}/generateLink

请求示例:

curl -X GET \
'https://devops.example.com/ms/environment/api/user/environment/thirdPartyAgent/projects/demo/os/LINUX/generateLink' \
-H 'X-DEVOPS-UID: admin'

返回结果:

{
"status": 0,
"data": {
"agentId": "njmkqvml",
"link": "curl -H \"X-DEVOPS-PROJECT-ID: demo\" https://devops.example.com/environment/api/external/thirdPartyAgent/njmkqvml/install | bash"
}
}

3.2. 执行安装并查询状态

  1. 在目标机器执行安装命令:

    curl -H \"X-DEVOPS-PROJECT-ID: demo\" https://devops.example.com/environment/api/external/thirdPartyAgent/njmkqvml/install | bash
  2. 查询 Agent 状态:

    GET /ms/environment/api/user/environment/thirdPartyAgent/projects/${projectId}/agents/${agentId}/status
  3. 导入节点:

    POST /ms/environment/api/user/environment/thirdPartyAgent/projects/${projectId}/agents/${agentId}/import

4. Agent 安装实现分析

生成安装命令的实现主要分为三层:

  1. API层 - 接口定义:

    @ApiOperation("生成链接")
    @GET
    @Path("/projects/{projectId}/os/{os}/generateLink")
    fun generateLink(
    @HeaderParam(AUTH_HEADER_USER_ID) userId: String,
    @PathParam("projectId") projectId: String,
    @PathParam("os") os: OS,
    @QueryParam("zoneName") zoneName: String?
    ): Result<ThirdPartyAgentLink>
  2. Service层 - 参数校验:

    override fun generateLink(userId: String, projectId: String, os: OS, zoneName: String?): Result<ThirdPartyAgentLink> {
    checkUserId(userId)
    checkProjectId(projectId)
    return Result(thirdPartyAgentService.generateAgent(userId, projectId, os, zoneName))
    }
  3. 具体实现 - 核心逻辑:

    fun generateAgent(userId: String, projectId: String, os: OS, zoneName: String?): ThirdPartyAgentLink {
    // 1. 获取网关信息
    val gateway = slaveGatewayService.getGateway(zoneName)

    // 2. 检查未导入的agent
    val unimportAgent = thirdPartyAgentDao.listUnimportAgent(
    dslContext = dslContext,
    projectId = projectId,
    userId = userId,
    os = os
    )

    // 3. 生成或复用agent记录
    val agentRecord = if (unimportAgent.isEmpty()) {
    // 生成新agent
    val secretKey = generateSecretKey()
    val id = thirdPartyAgentDao.add(...)
    thirdPartyAgentDao.getAgent(dslContext, id)!!
    } else {
    // 复用已有agent
    unimportAgent[0]
    }

    // 4. 根据操作系统生成安装命令
    return if (os == OS.WINDOWS) {
    ThirdPartyAgentLink(
    agentId = agentHashId,
    link = agentUrlService.genAgentUrl(agentRecord)
    )
    } else {
    ThirdPartyAgentLink(
    agentId = agentHashId,
    link = agentUrlService.genAgentInstallScript(agentRecord)
    )
    }
    }

4.2. 获取安装脚本

蓝盾通过模板渲染的方式生成不同操作系统的安装脚本。主要流程:

  1. API接口定义:

    @ApiOperation("下载agent安装脚本")
    @GET
    @Path("/{agentId}/install")
    @Produces(MediaType.APPLICATION_OCTET_STREAM)
    fun downloadAgentInstallScript(
    @ApiParam("Agent ID", required = true)
    @PathParam("agentId")
    agentId: String
    ): Response
  2. 核心实现:

    fun downloadInstallScript(agentId: String): Response {
    // 1. 获取Agent记录
    val agentRecord = getAgentRecord(agentId)

    // 2. 根据OS选择安装脚本模板
    val fileName = if (agentRecord.os == OS.WINDOWS.name) {
    "install.bat"
    } else {
    "install.sh"
    }
    val scriptFile = File(agentPackage, "script/${agentRecord.os.toLowerCase()}/$fileName")

    // 3. 替换模板变量
    val map = getAgentReplaceProperties(agentRecord) // 获取替换变量
    var result = scriptFile.readText(Charset.forName("UTF-8"))
    map.forEach { (t, u) ->
    result = result.replace("##$t##", u)
    }

    // 4. 返回生成的脚本
    return Response.ok(StreamingOutput { output ->
    output.write(result.toByteArray())
    output.flush()
    }, MediaType.APPLICATION_OCTET_STREAM_TYPE)
    .header("content-disposition", "attachment; filename = $fileName")
    .build()
    }
  3. 安装脚本目录结构:

    script/
    ├── linux/
    │ ├── install.sh # Linux安装脚本模板
    │ ├── start.sh # 启动脚本
    │ ├── stop.sh # 停止脚本
    │ └── uninstall.sh # 卸载脚本
    ├── macos/
    │ ├── install.sh # MacOS安装脚本模板
    │ └── ...
    └── windows/
    ├── install.bat # Windows安装脚本模板
    ├── devopsctl.vbs # VBS控制脚本
    └── ...
  4. 模板变量说明:

    ##agent_url##       - Agent包下载地址
    ##projectId## - 项目ID
    ##agentId## - AgentID
    ##agentSecretKey## - Agent密钥
    ##gateWay## - 网关地址
    ##fileGateway## - 文件网关地址

4.3. Linux安装脚本实现

install.sh脚本是Agent安装的核心,主要实现以下功能:

4.3.1. 初始化环境
workspace=`pwd`              # 工作目录
user=${USER} # 当前用户
agent_id='##agentId##' # Agent ID(模板变量)

# 检测系统架构
function initArch() {
ARCH=$(uname -m)
case $ARCH in
aarch64) ARCH="arm64";;
arm64) ARCH="arm64";;
mips64) ARCH="mips64";;
*) ARCH="";;
esac
}
4.3.2. 下载并解压Agent包
function download_agent() {
if [[ -f "agent.zip" ]]; then
echo "agent.zip already exist, skip download"
return
fi

# 优先使用curl,失败则尝试wget
if exists curl; then
curl -H "X-DEVOPS-PROJECT-ID: ##projectId##" -o agent.zip "##agent_url##"
if [[ $? -ne 0 ]]; then
wget --header="X-DEVOPS-PROJECT-ID: ##projectId##" -O agent.zip "##agent_url##"
fi
elif exists wget; then
wget --header="X-DEVOPS-PROJECT-ID: ##projectId##" -O agent.zip "##agent_url##"
else
echo "Curl & wget command don't exist, download fail"
exit 1
fi
}

# 解压JDK
function unzip_jdk() {
if [[ ! -d "jdk" ]]; then
unzip -q -o jre.zip -d jdk
fi
}
4.3.3. Agent包下载实现

Agent包下载接口:

@ApiOperation("下载agent.zip")
@GET
@Path("/{agentId}/agent")
fun downloadAgent(
@PathParam("agentId") agentId: String,
@QueryParam("eTag") eTag: String?,
@QueryParam("arch") arch: String?
): Response

主要实现步骤:

  1. 根据操作系统准备文件:
  • JAR包: worker-agent.jar
  • JRE: 对应系统的jre.zip
  • 二进制文件: devopsAgent、devopsDaemon等
  • 安装脚本: install.sh/bat等
  • 配置文件
  1. 打包成agent.zip返回

注意:macOS JDK 必须带 Contents/Home 结构。若 Intel 版 jre.zip 缺失该路径,临时可以执行命令 mkdir -p Contents/Home && unzip -d Contents/Home jre.zip && zip -r jre.zip Contents 修正;后续需更新 environment 模块中的 jre.zip,并制作新的镜像。

4.3.4. 各系统服务注册实现

Linux (通过 rc.local)

function installAgentService() {
# 添加到rc.local实现开机自启
echo "cd ${workspace} && ./devopsDaemon & # ${service_name}" >> /etc/rc.d/rc.local
${workspace}/start.sh
}

MacOS (通过 launchd)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>$(getServiceName)</string>

<key>Program</key>
<string>${workspace}/devopsDaemon</string>

<key>RunAtLoad</key>
<true/>

<key>WorkingDirectory</key>
<string>${workspace}</string>

<key>KeepAlive</key>
<false/>
</dict>
</plist>

Windows (通过 Windows Service)

sc create %service_name% binPath= "%work_dir%\devopsDaemon.exe" start= auto
sc start %service_name%
4.3.5. Agent进程启动

主要通过start.sh/bat脚本实现:

  1. 创建工作目录
  2. 解压配置JDK环境
  3. 启动devopsDaemon守护进程
  4. 写入pid文件
  5. 检查进程状态

核心启动代码:

function start() {
# 创建必要目录
mkdir -p ${workspace}/workspace
mkdir -p ${workspace}/logs

# 启动守护进程
nohup ${workspace}/devopsDaemon > /dev/null 2>&1 &

# 检查进程状态
pid=`cat ${workspace}/runtime/daemon.pid`
if isPidExists ${pid}; then
echo "agent daemon is running, pid: $pid"
fi
}

5. 总结

蓝盾 Agent 安装方式:

  • 手动安装:通过 Web 界面操作,适合少量节点
  • 自动化安装:通过 API 接口,适合批量部署

了解底层实现原理后,我们可以根据实际需求选择合适的安装方式,并进行二次开发和流程优化。

6. 参考