小规模私有化研发流水线:架构、选型与落地方案 对于 JDK 17+、完全私有化部署、小规模团队,我建议采用一套轻量、可恢复、许可证边界清楚的研发流水线,而不是一开始就搭建 Kubernetes、复杂微服务和多套工作流引擎。 核心原则是:代码管理、流水线调度、构建执行、制品存储、质量扫描、部署发布各司其职;构建过程可以重跑,发布过程必须有控制,关键操作必须可审计。 一、先给出推荐方案

Jenkins — 流水线引擎 主选 使用 Jenkins Pipeline(Jenkinsfile)定义构建、测试、扫描、打包和部署流程。Java 生态成熟,插件丰富,适合小规模自建。核心采用 MIT 许可证,但每个插件都要单独审核。

Jenkins +1

Gitea — Git 代码托管 轻量自建 Git 服务,适合小团队。核心为 MIT 许可证。需要注意,部分企业级能力与免费开源版并不相同,采购前应核对所需功能。

Gitea Documentation +1

Harbor — 镜像仓库 如果服务通过容器部署,用 Harbor 保存镜像、版本和摘要。若当前只有普通 JAR 部署,可以先用文件制品仓库或受控存储,不必立刻引入 Harbor。

SonarQube Community Build — 代码质量 负责静态分析、缺陷与部分安全问题检查。但许可证不能只看“开源”两个字:其分析器从 2024 年 11 月 29 日起采用 Sonar Source-Available License v1.0,核心部分仍有 LGPLv3 等不同许可边界,需单独审查。

Sonar +1

另外配套使用:

  • Maven + JDK 17/21:构建与测试。
  • Trivy:镜像和依赖漏洞扫描;正式上线前核对所用版本与数据库更新方式。
  • PostgreSQL:流水线元数据的持久化数据库(仅在对应组件需要时使用)。
  • Nginx 或现有网关:统一 HTTPS 入口。
  • 内部制品仓库:保存不可变的构建产物,避免生产发布时重新构建。
  • 现有日志和监控系统:记录构建、执行器、磁盘、数据库及发布状态。 这里有一个重要边界:Jenkins 是研发流水线引擎,不是通用业务工作流引擎;它也不应成为生产环境的唯一事实来源。 二、组件比较:为什么主选 Jenkins 这里要区分两类工具:一类是完整研发流水线平台,另一类只是流水线中的一个环节。不要因为某个工具支持 DAG、任务调度或 Java,就认为它能直接替代 CI/CD。 组件 定位与技术特点 许可证概况 对当前场景的判断 Jenkins Java 生态成熟;Pipeline、插件、构建节点 核心 MIT;插件逐个核查 首选,能力成熟、可渐进扩展 Woodpecker CI 轻量,服务端与 Agent 分离;以 YAML 定义流水线 Apache-2.0 适合重视轻量与容器化的团队,但服务端不是 Java 技术栈 GitHub +1 Gitea Actions 与 Gitea 代码托管集成,降低系统数量 Gitea 核心 MIT;具体 Actions 组件与依赖应核查 适合优先追求简单集成的团队,但需评估 Runner、权限隔离和运维体验 GitLab CE 代码托管、合并请求和 CI/CD 一体化 CE 为 MIT;EE 使用不同许可 一体化较强,但整体资源和运维复杂度更高 GitLab Docs Tekton Kubernetes 原生任务执行 核实具体仓库及组件许可证 当前规模通常过重,已有 Kubernetes 平台时再考虑 Argo Workflows 容器化 DAG 编排 核实对应版本的仓库许可证 更适合容器任务编排,不是最省事的传统 Java CI 起点 Argo Jenkins 的优势不是“所有方面都最好”,而是:在小团队里,你可以先完成编译、测试、打包和部署,再逐步增加质量门禁、制品管理和审批,而不需要先开发一套自己的流水线平台。Jenkins 的 Agent 模型也支持把实际构建工作从控制器移出去。

Jenkins +1

建议采用的最小组件集合 第一阶段必须有 Gitea(或现有 Git 服务)+ Jenkins LTS + Maven + 独立构建 Agent + 备份存储。

按交付形态添加 容器交付增加 Harbor;JAR 交付先使用受控制品存储。需要代码质量门禁时增加 SonarQube。

上线前必须补齐 权限隔离、凭据管理、流水线审计、离线依赖缓存、备份恢复演练、发布回滚方案。

不建议第一阶段同时引入 GitLab、Jenkins、Tekton、Argo Workflows 和独立任务调度器。它们解决的问题有重叠,维护成本会很快超过实际收益。 三、整体架构 推荐使用“控制面与执行面分离”的结构。小规模不需要 Kubernetes,也不需要为每个组件单独部署高可用集群;但要把构建执行器与控制器隔离。 研发入口 开发者 → Git Push / Pull Request → 代码评审与分支保护

控制面:Jenkins Controller Webhook → Pipeline 编排 → 队列 → 凭据授权 → 执行状态与审计

构建 Agent JDK、Maven、单元测试、静态分析、制品打包

发布 Agent 镜像构建、部署脚本、发布验证、回滚执行

交付与运行环境 制品仓库 / 镜像仓库 → 测试环境 → 审批 → 生产环境

横向基础设施:PostgreSQL(若组件需要)|备份存储|日志与监控|内部 DNS / HTTPS|企业身份认证(可选)

网络与信任边界

  1. 控制器不执行不可信代码。 Jenkins 官方建议将构建工作放到 Agent 上,而不是让控制器承担实际构建任务。

    Jenkins +1

  2. 构建 Agent 不应直接持有生产凭据。 PR 测试、普通构建与生产部署使用不同执行器和凭据范围。
  3. 生产服务器不向构建 Agent 开放任意管理权限。 通过专用发布通道或受限部署账号执行部署。
  4. 构建环境与生产环境分网。 生产环境只接收经过验证的制品,不直接执行开发者分支中的任意脚本。
  5. 所有组件优先使用内部镜像、内部 Maven 仓库和固定版本。 这样断网或外部仓库故障时,已缓存依赖的项目仍可继续构建。 这里最容易忽略的是:Jenkinsfile 本身就是可执行代码。只要它能访问高权限凭据,就可能变成绕过审批的入口。因此,权限隔离必须落实到分支、Job、Agent、凭据和发布目标,而不只是给 Jenkins 页面加登录。 四、部署拓扑:小规模如何部署才合理 建议从 3 个逻辑节点、2 个执行域开始。逻辑节点可以是虚拟机,也可以是同一台宿主机上的独立容器;但生产发布权限必须与普通构建隔离。 建议拓扑 节点 A:研发控制面

2–4 vCPU / 4–8 GB RAM 起步,视插件数量与并发调整

  • Gitea(如果没有现有 Git 服务)
  • Jenkins Controller
  • Nginx / HTTPS
  • Jenkins 配置与运行数据持久化

节点 B:构建执行面

4–8 vCPU / 8–16 GB RAM 起步;主要瓶颈通常是并发测试、磁盘和依赖下载

  • 普通构建 Agent
  • JDK 17 / 21、Maven
  • 单元测试、质量扫描、打包
  • Maven 本地缓存

节点 C:制品与发布

可先作为独立 VM;资源按是否部署 Harbor、SonarQube 及制品保留量决定

  • 制品存储 / Harbor(容器交付时)
  • 受控发布 Agent
  • 备份与恢复数据
  • 到测试、生产环境的受限网络通道

以上是逻辑分层,不是强制要求三台物理机。小团队可合并非敏感组件,但不要把 Jenkins Controller 和不可信构建任务混在同一执行环境。

网络规则 来源 目标 策略 开发者终端 Gitea / Jenkins HTTPS;Git SSH 可选 Gitea Jenkins Webhook 仅开放指定回调入口,验证签名或凭据 Jenkins Controller Agent 仅允许必要的 Agent 通信 构建 Agent Git / Maven 仓库 / 制品仓库 仅允许所需地址与端口 发布 Agent 测试、生产服务器 使用专用账号与受限端口 普通构建 Agent 生产环境管理接口 默认拒绝 所有节点 备份目标、日志系统 仅允许指定目标 如果当前团队很小,不要为了“高可用”一开始就做 Jenkins 多主集群。先保证备份可恢复、Agent 可重建、构建可重跑、制品可追溯。单控制器是合理的起点,但它依然是一个需要制定恢复目标的单点。 五、流水线定义与接口设计 建议以代码仓库中的 Jenkinsfile 为主,以受版本控制的共享流水线库为辅。不要将每个项目的构建步骤都写进 Jenkins 页面,否则后续难以审查、复用和迁移。 5.1 标准 Java 流水线 01

源码触发 Push / Pull Request / Release Tag

02

输入校验 仓库、分支、提交 SHA、触发人、构建参数

03

构建与测试 固定 JDK、Maven Wrapper、单元测试、必要的集成测试

04

质量与安全门禁 静态分析、依赖漏洞、许可证与密钥扫描

05

生成制品 JAR / 镜像、SHA-256、版本号、SBOM(适用时)

06

发布候选版本 保存不可变制品,记录源码与构建环境

07

部署测试环境 部署同一制品,执行健康检查和冒烟测试

08

审批与生产发布 检查审批、目标环境、制品摘要和发布锁

09

验证与记录 健康检查、发布结果、失败处理、审计事件

PR 流水线和生产发布流水线必须有不同的信任等级:

  • PR:验证代码,不接触生产凭据。
  • 主分支:构建、测试、扫描、生成候选制品。
  • Release Tag:对经过批准的源码生成正式版本。
  • 生产发布:部署已经生成的制品,不能在生产发布阶段重新编译。 5.2 推荐的仓库结构 project/ ├── pom.xml ├── mvnw ├── .mvn/ ├── Jenkinsfile ├── src/ └── deploy/ ├── test/ └── prod/

如果有多个项目,建议将公共流水线逻辑放入独立的受保护仓库: devops-pipeline-library/ ├── vars/ │ ├── javaVerify.groovy │ ├── buildArtifact.groovy │ ├── publishArtifact.groovy │ └── deployRelease.groovy └── resources/ └── policy/

公共库应该提供经过评审的标准步骤,而不是允许项目随意传入 Shell 命令并以高权限执行。 5.3 Jenkinsfile 示例 以下是适用于小规模 Java Maven 项目的起点,具体的 JDK 工具名、仓库地址和凭据 ID 需要按实际部署配置。 pipeline { agent { label ‘java-build’ }

options {
    timestamps()
    disableConcurrentBuilds()
    timeout(time: 30, unit: 'MINUTES')
    buildDiscarder(logRotator(
        numToKeepStr: '30',
        artifactNumToKeepStr: '10'
    ))
}

stages {
    stage('Validate') {
        steps {
            sh '''
                set -eu
                java -version
                ./mvnw -version
                git status --short
            '''
        }
    }

    stage('Test') {
        steps {
            sh './mvnw -B -ntp clean verify'
        }
        post {
            always {
                junit(
                    testResults: '**/target/surefire-reports/*.xml,**/target/failsafe-reports/*.xml',
                    allowEmptyResults: false
                )
            }
        }
    }

    stage('Package') {
        when {
            branch 'main'
        }
        steps {
            sh './mvnw -B -ntp package'
            archiveArtifacts(
                artifacts: '**/target/*.jar',
                fingerprint: true
            )
        }
    }
} }

注意三个限制:

  1. disableConcurrentBuilds() 只限制同一个 Pipeline Job 的并发,不能代替跨 Job、跨项目的生产环境发布锁。
  2. archiveArtifacts 适合初期留存,但不等于长期制品仓库。需要正式发布时,应把制品推送到独立仓库并记录摘要。
  3. 示例只包含验证和打包,没有赋予任何生产权限。生产发布应由单独的受保护 Job 或流水线完成。 5.4 需要规范的接口 小规模阶段不需要自行开发一套 Java 流水线平台,但应先把关键接口与数据契约标准化。 接口 / 事件 关键字段 设计要求 Git Webhook eventId、仓库、提交 SHA、事件类型 验证来源;重复事件不能导致重复副作用 Pipeline Run runId、仓库、提交 SHA、触发人、参数摘要 每次执行有唯一标识 Build Result runId、状态、开始/结束时间、日志引用 结果可追踪、可查询 Artifact Published 制品坐标、版本、SHA-256、源码 SHA 制品不可变;禁止只靠文件名识别 Deployment Request releaseId、制品摘要、环境、申请人 发布前检查权限、审批和环境状态 Deployment Result releaseId、状态、目标版本、验证结果 记录成功、失败、未知状态及回滚结果 Audit Event 操作者、动作、目标、时间、结果、关联 ID 追加记录、集中保留、限制修改权限 如果未来需要开发自有的 Java DevOps 门户,可以先通过 Jenkins Remote API 和 Webhook 集成,再逐步封装成公司内部的统一接口。不要一开始就重复实现 Jenkins 已经提供的调度、日志、队列和执行器管理能力。 六、幂等:流水线最重要的工程约束之一 研发流水线不是支付系统,但在部署、发版、镜像推送、数据库变更等环节,同样会遇到“操作已执行,但调用方没有收到结果”的问题。 例如:生产服务器已经完成部署,Agent 却在返回结果前断网。流水线重试时,不能不加判断地再次执行所有发布步骤。 6.1 建立三层幂等机制 第一层:触发幂等 Webhook 事件可能重复到达。可以用: source + eventId 作为事件去重键。如果上游事件没有稳定 ID,则使用经过规范化的事件字段构造去重键,并明确它只是近似去重,而不是严格保证。 第二层:构建幂等 构建的逻辑输入应当可识别: repository + commitSHA + buildProfile + toolchainVersion 如果这些输入相同,系统可以判断是否存在可复用结果。但不能盲目复用:构建脚本、依赖、基础镜像或工具链变化时,结果可能不同。 正式制品应至少关联:
    • 源码提交 SHA。
    • 制品 SHA-256。
    • JDK、Maven 及关键插件版本。
    • 依赖解析结果或 SBOM(适用时)。
    • 构建时间、构建器身份与流水线版本。 第三层:发布幂等 建议为每次发布建立唯一的 releaseId,并以目标环境、应用和发布版本作为互斥范围。 发布服务或部署脚本至少要具备以下语义: releaseId 已成功 -> 返回原有结果

releaseId 正在执行 -> 返回处理中,不重复执行

releaseId 失败,但目标状态不明确 -> 先查询目标环境的真实状态

releaseId 未执行 -> 校验制品、权限与审批后执行

注意:Jenkins 自身的 Build Number 不是跨 Job 的业务幂等键。真正防止重复发布,需要发布端也记录和检查 releaseId。 6.2 不要把所有失败都当作可以自动重试 故障类型 处理方式 下载依赖超时 有界重试,优先使用内部仓库 单元测试失败 失败即终止,不自动重复到“成功为止” Agent 离线 重新调度或人工恢复执行器 Jenkins 重启 恢复 Pipeline 状态;必要时核对执行结果 制品上传超时 先查询制品是否存在并校验摘要,再决定重试 生产部署超时 查询实际运行版本与发布状态,禁止盲目重发 健康检查失败 按预定义策略停止、回滚或转人工处理 回滚自身失败 进入高优先级人工处置,不无限循环 核心原则:可重复执行的构建步骤可以重跑;具有外部副作用的发布步骤必须先确认状态。 七、故障恢复:怎样确保系统坏了还能继续交付 Jenkins Pipeline 支持在控制器重启或异常后恢复执行,但恢复能力取决于 Pipeline 持久化配置、存储状态和具体步骤。官方文档明确说明,性能优化模式会减少持久化写入,从而降低某些异常关机场景下的恢复能力。

Jenkins +1

7.1 故障恢复策略 故障 检测与恢复 必须保留的数据 Jenkins Controller 故障 重启服务,检查 Pipeline 状态;必要时重新执行可重跑任务 Jenkins Home、插件清单、配置、凭据恢复材料 Agent 故障 重建 Agent,重新调度任务 代码与依赖可重新获取;必要的临时数据外置 Git 服务故障 恢复 Git 数据和数据库;构建暂时排队 Git 仓库、数据库、配置和密钥 制品仓库故障 恢复存储;阻止发布引用无法验证的制品 制品、摘要、元数据、版本索引 备份损坏 从独立备份恢复并校验 多份备份、校验信息、恢复手册 发布执行中断 查询目标版本和实际状态,恢复或转人工 发布 ID、步骤记录、前后版本、目标环境 7.2 备份设计 至少备份以下内容:

  • Jenkins Home 中的 Job、Pipeline 运行状态、插件和必要配置。
  • Gitea 的 Git 仓库、数据库、配置文件及所需密钥(如果使用 Gitea)。
  • 制品仓库的元数据和实际制品。
  • 部署配置、流水线共享库、许可证清单。
  • 凭据的安全恢复材料与解密密钥(分开保管)。 不要把所有数据只备份到 Jenkins 所在的同一块磁盘。建议使用独立备份存储,并定期进行恢复演练。 可先制定如下内部目标,再根据团队实际调整: 指标 初始建议 Jenkins 控制面恢复时间(RTO) 4 小时内 Jenkins 元数据恢复点(RPO) 24 小时以内;重要发布记录更严格 Git 与正式制品恢复 优先于普通构建缓存 恢复演练 每季度至少一次,重大升级后补充演练 这些是建议的内部目标,不是工具本身可以保证的指标。 7.3 Pipeline 持久化策略
  • 普通编译、测试流水线:可在验证后评估更高性能的持久化设置。
  • 正式发布、基础设施变更:优先选择高持久性配置。
  • 避免在 Pipeline 中长期保存大型 JSON、日志或复杂对象;这些数据应放入文件、制品或外部系统。
  • 对发布过程中的外部操作设置明确的检查点,并将关键状态写入发布记录。 八、权限与审计:必须落实到执行层 Jenkins 官方将认证和授权区分为两个层面:前者确定用户是谁,后者确定用户能做什么;矩阵授权可以将权限细分到项目和操作级别。

Jenkins +1

8.1 权限矩阵 角色 查看构建 触发构建 修改流水线 管理凭据 生产发布 开发者 是 是 通过代码评审 否 否 项目负责人 是 是 受保护分支审批 否 按规则申请 DevOps 管理员 是 是 是 受控管理 可运维,但不能绕过审批记录 发布审批人 是 否或受限 否 否 审批,不直接获得生产 Shell 发布执行身份 仅必要范围 自动执行 否 仅访问授权凭据 仅指定应用与环境 审计人员 是 否 否 否 只读审计 8.2 凭据设计 Jenkins Credentials 插件提供统一的凭据管理能力,但真正的安全边界还依赖 Job、Folder、Agent 和授权配置。

Jenkins plugin +1

建议:

  1. Git 读取凭据、制品上传凭据、测试环境部署凭据、生产部署凭据分别建立。
  2. 生产凭据仅放在受保护的发布 Job 范围,不允许普通 PR Job 使用。
  3. 凭据只在必要的步骤中短暂注入;不写入源码、日志、制品或构建参数。
  4. 不允许普通开发者修改有权访问生产凭据的 Job 配置。
  5. 构建 Agent 不使用共享管理员账号,不以 root 身份运行不可信代码。
  6. 如果有条件,使用专门的密钥管理系统;没有时也要将凭据恢复材料与普通备份分离。 8.3 审计事件 至少记录以下事件:
    • 谁创建、修改、删除了流水线。
    • 谁触发了构建、重试、取消或重放。
    • 谁批准了生产发布。
    • 哪个制品摘要被部署到哪个环境。
    • 谁修改了凭据、授权策略或插件。
    • 哪次部署失败、回滚或进入人工处置。 审计记录最好同步到独立日志系统,并由独立权限控制其删除和修改。Jenkins 内部日志可以作为一部分证据,但不应是唯一证据来源。 九、许可证:商业使用前需要明确的边界 以下为项目公开许可信息的初步核对,不是对所有依赖及插件的完整法律审查。 组件 公开许可情况 风险与处理建议 Jenkins Core MIT 较宽松;仍需检查插件和依赖 Gitea Core MIT 较宽松;核对需要的企业功能是否属于开源版 Woodpecker CI Apache-2.0 适合自建;第三方插件和镜像仍需核对 Trivy Apache-2.0 可用于漏洞与配置扫描;核对数据库、扫描器及分发方式 SonarQube Community Build 核心部分 LGPLv3;捆绑分析器存在 SSALv1 许可边界 不要仅凭“Community Build”判断全部组件都是传统开源许可 Harbor 需针对实际使用的官方仓库、版本和发行包核对 不要把名称相同或非官方同名项目的许可证直接套用 相关依据:Jenkins 许可证说明、Gitea 官方文档、Woodpecker 官方仓库、Trivy 官方网站、Sonar 官方许可页。

Jenkins +4

许可证治理流程 每次引入组件、插件、构建镜像或升级依赖时:

  1. 固定项目仓库、版本和发行包来源。
  2. 保存 LICENSE、NOTICE 及第三方依赖清单。
  3. 检查运行时插件、构建工具、容器基础镜像和扫描器的许可证。
  4. 记录许可证类型、是否允许商业使用、修改和再分发,以及需要履行的义务。
  5. 对来源不明、许可冲突或限制较强的组件进行人工审核。
  6. 升级后重新生成清单,不沿用旧版本的结论。 需要特别注意:在公司内部使用与向客户分发软件、托管修改版本、对外提供服务,可能触发不同的许可证义务。 如果未来要把这套 DevOps 平台作为产品对外出售或托管,需要再做一次针对实际分发模式的许可证审查。 十、实施路线:按阶段建立能力 P1

第 1 阶段:跑通最小闭环 预计 1 周,视基础设施情况调整

  • 部署 Jenkins LTS 和一个独立构建 Agent。
  • 接入现有 Git 或 Gitea。
  • 实现 Maven 编译、测试、打包。
  • 建立备份和最小权限配置。
  • 使用一个真实 Java 项目验证端到端流程。 验收: 提交代码后自动构建;失败有日志;Agent 可重建;控制器可以备份并恢复。

P2

第 2 阶段:标准化和安全门禁 预计再用 1–2 周

  • 建立共享流水线库。
  • 增加静态分析、依赖漏洞扫描与许可证检查。
  • 建立不可变制品、摘要和构建元数据。
  • 将普通构建与发布 Agent 分开。
  • 配置权限矩阵和审计记录。 验收: 每个制品都能追溯到源码提交、构建记录和依赖信息。

P3

第 3 阶段:受控部署与恢复 预计再用 1–2 周

  • 实现测试环境自动部署。
  • 增加生产发布审批与发布锁。
  • 引入 releaseId、重复请求检测和部署状态查询。
  • 实现健康检查、失败停止与回滚流程。
  • 演练控制器故障、Agent 故障和发布超时。 验收: 重复触发不会导致重复发布;状态未知时先核实,不盲目重试;回滚结果可追踪。

P4

第 4 阶段:按实际瓶颈扩展

  • 增加第二个普通构建 Agent,提高并发。
  • 容器交付时引入 Harbor。
  • 接入统一身份认证、外部审计和监控。
  • 评估是否需要专门的发布平台或审批系统。 验收: 扩容不改变制品语义、权限边界和审计标准。

十一、最终建议与明确的非目标 对当前的小规模团队,我建议最终采用:

  • Jenkins LTS + Jenkinsfile + 共享流水线库:研发流水线核心。
  • 现有 Git 服务或 Gitea:代码托管与评审。
  • Maven + JDK 17/21:Java 构建。
  • 独立 Agent:隔离构建和发布权限。
  • 内部制品存储:不可变版本、摘要与追溯。
  • Trivy 等扫描工具:漏洞和供应链检查;按需添加 SonarQube。
  • 独立备份与审计存储:故障恢复和安全留痕。 暂时不自研流水线调度器,不引入 Kubernetes 专用编排平台,不把 Jenkins 当成生产部署状态的唯一事实来源,也不在第一阶段追求多主高可用。 最值得投入的不是让流水线图看起来更复杂,而是先把三个闭环做扎实:
    1. 代码闭环: 提交 → 评审 → 测试 → 制品。
    2. 发布闭环: 制品 → 审批 → 部署 → 验证 → 回滚。
    3. 治理闭环: 身份 → 权限 → 审计 → 备份 → 恢复演练。 这三个闭环建立后,再根据项目数量、构建耗时、并发量和发布频率决定是否扩容。对于小团队,这通常比先搭建一套“大而全”的 DevOps 平台更有价值。

参考资料