Files
ProjectAGiPrompt/8-CMII-RMDC/2-rmdc-jenkins-dac/8-jenkins项目层级.md
2026-01-21 16:15:49 +08:00

7.5 KiB
Raw Permalink Blame History

根据Jenkins官方文档Jenkins的实际层级组织结构如下

Jenkins的实际层级结构

Jenkins在使用Organization Folder(组织文件夹)功能时,采用的层级结构为:

Organization Folder组织
  └── Repository/Project仓库/项目)
        └── Branch分支
              └── Build构建

官方文档中明确展示的层级示例:

+--- GitHub Organization
     +--- Project 1
          +--- master
          +--- feature-branch-a
          +--- feature-branch-b
     +--- Project 2
          +--- master
          +--- pull-request-1

关键区别

"Projects → Repository"实际上在Jenkins中是同一层级的概念。 Jenkins使用以下术语

  • Organization Folder对应GitHub Organization、Bitbucket Team、GitLab Organization等组织级别
  • Multibranch Pipeline Project对应单个代码仓库会自动为该仓库的所有包含Jenkinsfile的分支创建构建任务
  • Branch具体的分支或Pull Request
  • Build:针对某个分支的具体构建执行

实现逻辑

Jenkins通过以下机制实现这种层级

  1. Multibranch Pipeline自动扫描单个仓库为每个包含Jenkinsfile的分支创建Pipeline任务
  2. Organization Folder自动扫描整个组织为每个符合条件的仓库创建Multibranch Pipeline项目
  3. 当分支或仓库被创建/删除时可以通过webhook或定期扫描自动更新Jenkins中的任务结构

因此,准确的表述应该是:Organization → Repository(Project) → Branch → Build其中Repository和Project是等价的概念。

根据Jenkins官方文档和API最佳实践您可以通过以下方式获取GitLab Organization Folder的所有分支和构建信息

推荐方案使用Tree参数的单次API调用

最高效的方式是使用tree参数通过一次API调用获取所有数据

https://<JENKINS_HOST>/job/<ORGANIZATION_FOLDER_NAME>/api/json?tree=jobs[name,url,jobs[name,url,builds[number,result,timestamp,duration,url]]]

参数说明

  • 第一层jobsOrganization Folder下的所有RepositoryMultibranch Pipeline项目
  • 第二层jobs每个Repository下的所有Branch分支作为子job存在
  • builds每个Branch的构建历史

限制返回的构建数量

如果构建历史过多可以限制返回数量例如最近50次

https://<JENKINS_HOST>/job/<ORGANIZATION_FOLDER_NAME>/api/json?tree=jobs[name,url,jobs[name,url,builds[number,result,timestamp,duration,url]{0,50}]]

{0,50}表示只返回最近50次构建。

API调用结构层次

基于Organization Folder的结构API调用路径遵循以下层级

  1. Organization Folder/job/<FOLDER_NAME>
  2. Repository (Multibranch Pipeline)/job/<FOLDER_NAME>/job/<REPO_NAME>
  3. Branch/job/<FOLDER_NAME>/job/<REPO_NAME>/job/<BRANCH_NAME>
  4. Build/job/<FOLDER_NAME>/job/<REPO_NAME>/job/<BRANCH_NAME>/<BUILD_NUMBER>

完整示例

假设您的Organization Folder名为CMII-UAV-Cloud-Backend-GITLAB

# 获取所有仓库、分支和最近50次构建
curl -u username:api_token \
  "https://<JENKINS_HOST>/job/CMII-UAV-Cloud-Backend-GITLAB/api/json?tree=jobs[name,url,jobs[name,url,builds[number,result,timestamp,duration,url]{0,50}]]"

性能优化建议

  1. 避免使用depth参数depth会返回大量不必要的数据,建议使用tree参数精确指定所需字段
  2. 限制构建数量:使用{0,N}语法限制每个分支返回的构建数量
  3. 按需获取字段:只请求必要的字段,减少数据传输量

分析您的API调用性能问题主要原因是Jenkins需要遍历整个Organization Folder的所有仓库、分支和构建数据即使您只请求了前3条记录。 以下是性能瓶颈和优化方案:

性能瓶颈分析

主要问题

  1. 一次性加载所有数据Jenkins API的tree参数在返回前必须先遍历整个数据树结构,即使使用{0,3}限制构建数量
  2. Organization Folder扫描开销每个多分支项目的分支发现和元数据加载都会产生大量I/O操作
  3. 深层嵌套查询三层嵌套Organization → Repository → Branch → Builds导致指数级的数据查询

为什么{0,3}不起作用

{0,3}只限制每个分支返回的构建数量但Jenkins仍需要

  • 遍历所有Repository
  • 扫描每个Repository的所有分支
  • 为每个分支加载元数据

高效解决方案

方案1分层逐级获取推荐

不要一次性获取所有层级,而是分步骤获取:

步骤1获取所有Repository

curl -g -u username:token \
  "http://192.168.34.41:27081/job/CMII-UAV-Cloud-Backend-GITLAB/api/json?tree=jobs[name,url]"

返回速度快只获取Repository列表。

步骤2并行获取每个Repository的分支

# 对每个Repository
curl -g -u username:token \
  "http://192.168.34.41:27081/job/CMII-UAV-Cloud-Backend-GITLAB/job/<REPO_NAME>/api/json?tree=jobs[name,url,lastBuild[number,result,timestamp,duration]]"

使用lastBuild只获取最新构建,而非所有构建历史。

步骤3按需获取具体分支的构建历史

curl -g -u username:token \
  "http://192.168.34.41:27081/job/CMII-UAV-Cloud-Backend-GITLAB/job/<REPO_NAME>/job/<BRANCH_NAME>/api/json?tree=builds[number,result,timestamp,duration]{0,10}"

方案2只获取关键信息

如果只需要统计数据,使用最小化字段:

curl -g -u username:token \
  "http://192.168.34.41:27081/job/CMII-UAV-Cloud-Backend-GITLAB/api/json?tree=jobs[name,jobs[name,lastBuild[number,result,timestamp]]]"

关键优化点:

  • 移除url字段(可自行构造)
  • 只用lastBuild替代builds[]
  • 减少返回字段数量

方案3使用Python脚本异步并发获取

import asyncio
import aiohttp
from aiohttp import BasicAuth

async def fetch_repos(session, base_url, auth):
    url = f"{base_url}/api/json?tree=jobs[name]"
    async with session.get(url, auth=auth) as resp:
        data = await resp.json()
        return [job['name'] for job in data.get('jobs', [])]

async def fetch_repo_branches(session, base_url, repo_name, auth):
    url = f"{base_url}/job/{repo_name}/api/json?tree=jobs[name,lastBuild[number,result,timestamp]]"
    async with session.get(url, auth=auth) as resp:
        data = await resp.json()
        return {
            'repo': repo_name,
            'branches': data.get('jobs', [])
        }

async def main():
    base_url = "http://192.168.34.41:27081/job/CMII-UAV-Cloud-Backend-GITLAB"
    auth = BasicAuth('zeaslity', '114f14f0c48fd9dfb4d3092426b0f72913')
    
    async with aiohttp.ClientSession() as session:
        # 1. 获取所有仓库
        repos = await fetch_repos(session, base_url, auth)
        print(f"找到 {len(repos)} 个仓库")
        
        # 2. 并发获取所有仓库的分支信息
        tasks = [fetch_repo_branches(session, base_url, repo, auth) for repo in repos]
        results = await asyncio.gather(*tasks)
        
        # 3. 输出结果
        for result in results:
            print(f"\n仓库: {result['repo']}")
            for branch in result['branches']:
                build = branch.get('lastBuild', {})
                if build != None:
                  print(f"  分支: {branch['name']}, 最新构建: #{build.get('number', 'N/A')}")

asyncio.run(main())

这个脚本将3分钟的串行请求压缩到数秒内。