1. 项目概述与核心价值如果你正在管理一个基于 Jenkins 的持续集成/持续交付CI/CD流水线并且经历过构建队列因资源不足而堆积如山或者为应对突发流量而临时手动扩容物理服务器的痛苦那么jenkinsci/ec2-plugin这个插件很可能就是你一直在寻找的“弹性伸缩”解决方案。简单来说它让 Jenkins 具备了与亚马逊 AWS EC2 云服务深度集成的能力能够根据构建负载动态地创建和销毁计算节点Agent实现构建资源的按需使用和成本优化。我管理过多个从零到一搭建的 Jenkins 集群从最初的几台固定物理机到后来全面上云这个插件是其中最关键的一环。它的核心价值在于将静态的、固定的 Jenkins Agent 资源池转变为一个动态的、可弹性伸缩的云资源池。你只需要维护一个基础的、小型的常驻 Agent 集群甚至可以是零当 Jenkins 检测到有新的构建任务需要执行但所有符合条件的常驻 Agent 都处于忙碌状态时插件会自动通过 AWS API 在指定的区域启动一台新的 EC2 实例将其配置为 Jenkins Agent 并加入集群。一旦任务执行完毕并且该实例空闲时间超过预设的阈值插件又会自动将其终止从而停止计费。这种模式完美解决了构建负载的波峰波谷问题。白天开发活跃构建任务多云上会自动扩容出多个实例并行处理夜晚或周末负载低云上实例自动回收成本几乎为零。对于使用 Spot 实例竞价实例的用户成本甚至可以再降低 60%-90%。这个插件不仅仅是 Jenkins 的一个功能扩展更是一种 DevOps 资源管理理念的落地工具它关乎效率、成本与自动化。2. 插件工作原理与架构设计要玩转这个插件不能只停留在点击配置界面的层面理解其背后的工作流程和设计思路至关重要。这能帮助你在出现问题时快速定位也能让你做出更合理的配置决策。2.1 核心工作流程拆解插件的工作流程是一个典型的“监控-决策-执行”闭环我们可以将其分解为以下几个核心步骤负载监控与决策Jenkins 的队列系统持续监控所有已配置的 Agent包括常驻的和云上的及其标签Label。当一个新的构建任务被触发时调度器会寻找具有匹配标签且空闲的 Agent。如果找不到并且该任务允许在云上执行即其标签与某个 EC2 模板的标签匹配Jenkins 就会向 EC2 插件发出“需要更多资源”的信号。实例启动与初始化插件收到请求后会调用 AWS EC2 的RunInstancesAPI使用你预先配置好的 AMI亚马逊机器镜像、实例类型、安全组、子网等参数启动一台新的 EC2 实例。这里有一个关键点实例启动后它只是一台普通的云服务器还不是 Jenkins Agent。Agent 连接与注册这是最精巧的部分。插件通过 SSH对于 Linux/Unix AMI或 WinRM对于 Windows AMI连接到新启动的实例。连接成功后插件会执行你在 AMI 配置中定义的“Init Script”初始化脚本。这个脚本的典型任务包括安装 Java 运行时、安装构建所需的工具链如 Git, Maven, Docker 等最后最关键的一步是从 Jenkins 控制器Controller下载agent.jar并以 JNLPJava Web Start或 SSH 方式启动完成向 Jenkins 控制器的注册。至此一台云上的 EC2 实例才正式转变为 Jenkins 的一个可用 Agent。任务执行与资源回收注册成功的 Agent 立即开始执行排队中的构建任务。任务完成后Agent 进入空闲状态。插件会为每个云模板SlaveTemplate设置一个“空闲终止时间”Idle Termination Minutes。一旦 Agent 空闲时间超过这个阈值插件就会调用 AWS EC2 的TerminateInstancesAPI 终止该实例。如果配置了“空闲时停止而非终止”则会调用StopInstances下次需要时再启动Resume这适用于需要保留实例存储如 EBS 卷数据的场景。2.2 关键架构组件解析理解插件配置中的几个核心概念有助于你构建一个健壮、高效的云环境Cloud 配置这是插件的顶层配置对应一个 AWS 区域Region。一个 Jenkins 实例可以配置多个 Cloud例如一个对应us-east-1另一个对应eu-west-1。每个 Cloud 需要配置 AWS 凭证Access Key/Secret Key 或 IAM Role、SSH 私钥以及区域信息。AMI 模板SlaveTemplate这是灵魂所在。一个 Cloud 下可以配置多个模板。每个模板定义了启动一个特定类型 Agent 所需的全部蓝图AMI ID使用哪个系统镜像。强烈建议使用自己定制化的、预装了基础依赖的 AMI可以极大缩短 Agent 的启动和初始化时间。实例类型t3.medium,m5.large,c5.xlarge等决定了计算、内存和网络能力。标签Labels这是 Jenkins 调度器将任务分发给云 Agent 的依据。例如你可以创建一个标签为docker-linux的模板另一个标签为windows-vs2019的模板。构建任务只需声明需要的标签插件会自动选择匹配的模板启动实例。初始化脚本Init Script在 Agent 连接后、正式注册前运行的 Shell 脚本Windows 下是 PowerShell。这里是安装软件、配置环境的核心。安全组与子网控制实例的网络访问策略和所处的 VPC 网络位置。空闲终止策略决定实例空闲后是终止省钱还是停止快速恢复。实操心得不要试图用一个“万能”模板来应对所有构建任务。根据你的项目技术栈创建多个精细化的模板。例如一个轻量级模板t3.micro用于前端 npm 构建一个中型模板m5.large用于 Java Maven 项目一个大型 GPU 模板g4dn.xlarge用于机器学习训练。通过标签进行精准调度这样既能保证性能又能最大化成本效益。3. 从零开始详细配置指南与避坑要点纸上得来终觉浅我们直接进入实战环节。假设我们要为一个 Java Docker 项目配置一个基于 Amazon Linux 2 的 EC2 Agent 模板。3.1 前期准备AWS 与 IAM 配置在 Jenkins 里点按钮之前必须在 AWS 端打好基础。创建专用 IAM 用户与策略绝对不要使用根用户Root Account的密钥。遵循最小权限原则创建一个名为jenkins-ec2的 IAM 用户并为其附加一个自定义策略。策略内容可以直接使用插件文档推荐的 JSON它包含了ec2:Describe*,ec2:RunInstances,ec2:TerminateInstances,ec2:StartInstances,ec2:StopInstances等必要的权限。如果你需要为实例附加 IAM 角色Instance Profile则必须包含iam:PassRole权限。保存好生成的Access Key ID和Secret Access Key。准备 SSH 密钥对在目标 AWS 区域例如us-east-1创建或导入一个 SSH 密钥对Key Pair。下载其私钥文件.pem格式。这个私钥将用于 Jenkins 插件通过 SSH 连接到新启动的 EC2 实例。安全提示在 Jenkins 中最佳实践是将这个私钥内容存入 Jenkins 的“凭据”系统类型选择“SSH Username with private key”而不是将私钥文件放在服务器磁盘上。定制化 AMI强烈推荐使用官方的 Amazon Linux 2 AMI 虽然可以工作但每次启动都要从头安装 Java、Docker、Git 等工具会浪费大量时间可能长达5-10分钟。你应该使用 Packer 或直接在 EC2 上启动一个基础实例安装好所有常用工具如java-11-amazon-corretto,docker,git,maven并做好优化如 Docker 用户组配置、SSH 服务调优然后基于此实例创建一个自定义 AMI。记录下这个 AMI 的 ID如ami-0abcdef1234567890。这能将 Agent 就绪时间缩短到1-2分钟以内。3.2 Jenkins 插件配置详解安装好ec2-plugin后进入Manage Jenkins-Configure System滚动到Cloud部分。添加 Amazon EC2 Cloud名称起个有意义的名字如AWS-US-EAST-1。区域选择你创建了密钥对和 AMI 的区域如US East (N. Virginia)。凭据选择你之前存储了 AWSAccess Key和Secret Key的 Jenkins 凭据。SSH 密钥在Advanced中选择你存储了 SSH 私钥的 Jenkins 凭据。或者如果你必须使用文件路径可以设置hudson.plugins.ec2.EC2Cloud.sshPrivateKeyFilePath这个 Jenkins 系统属性例如在容器中通过卷挂载密钥。测试连接点击Test Connection确保 Jenkins 能成功调用 AWS API。如果失败请检查网络连通性、IAM 权限和区域设置。配置第一个 AMI 模板点击Add添加一个模板。描述Java11-Docker-BuilderAMI ID填入你自定义的 AMI ID。实例类型根据项目需求选择例如t3.medium2 vCPU, 4 GiB 内存对于大多数 Java 项目起步足够。安全组填入一个安全组 ID如sg-xxx。关键这个安全组必须允许从 Jenkins 控制器 IP 到实例的SSH端口22入站访问以及 Agent 到 Jenkins 控制器的出站访问用于下载agent.jar和 JNLP 通信。子网选择 VPC 中的一个子网 ID如subnet-xxx。确保子网有路由到互联网通过 NAT 网关或互联网网关以便实例能下载软件和连接 Jenkins。远程用户对于 Amazon Linux 2通常是ec2-user。远程根目录/home/ec2-user或/var/lib/jenkins。这是 Agent 的工作空间根目录。标签java11 docker linux。多个标签用空格分隔。初始化脚本即使 AMI 已预装软件这里也建议放置一个脚本用于更新软件、启动 Docker 服务、检查环境等。例如#!/bin/bash sudo yum update -y sudo systemctl start docker sudo usermod -aG docker ec2-user # 检查Java版本 java -version空闲时间后终止10分钟。这是成本控制的关键。根据你的构建频率调整太短可能导致频繁启停太长则浪费资源。高级选项主机密钥验证策略强烈建议设置为Check New Hard。这是1.50版本后引入的重要安全特性能防止中间人攻击。它通过检查实例系统日志中的 SSH 主机密钥来验证连接。首次连接会稍慢需要等待日志可用但之后会缓存密钥后续连接很快。启动超时秒如果使用Check New Hard需要设置一个足够长的超时如60010分钟以等待实例启动和系统日志就绪。EBS 根卷加密选择Encrypted以符合安全合规要求。使用 Spot 实例如果想节省成本可以勾选并设置一个合理的竞价价格通常可以设置为按需价格的30%-50%。3.3 安全连接深度解析主机密钥验证这是插件配置中最容易被忽略但至关重要的安全环节。早期版本的插件在连接新实例时会无条件接受其提供的 SSH 主机密钥这存在中间人攻击风险。插件现在提供了四种策略策略工作原理安全性适用场景建议Check New Hard首次连接时从实例控制台获取系统输出的 SSH 主机公钥与实例实际提供的进行比对。一致则连接并缓存密钥不一致或找不到则拒绝连接。最高所有新配置的推荐选择。要求 AMI 在启动时输出主机密钥Amazon Linux 2 等主流 AMI 默认支持。务必配合设置足够的“启动超时”如600秒。Check New Soft与 Hard 类似但当控制台找不到密钥时仍会允许连接并缓存提供的密钥。中等从旧版本升级时的默认策略保证兼容性。或用于不输出主机密钥的定制 AMI。作为向Check New Hard迁移的过渡。Accept New无条件接受实例首次连接提供的密钥并缓存后续连接会校验密钥是否变化。较低无法满足上述两种策略要求的极端情况。无法防御首次连接时的中间人攻击。尽量避免使用。Off不进行任何验证每次连接都接受新密钥。无绝对不推荐。仅用于向后兼容或测试环境。生产环境禁用。配置要点对于你的生产环境模板在Advanced-Host Key Verification Strategy中毫不犹豫地选择Check New Hard。这需要你的自定义 AMI 也支持在启动时通过cloud-init或系统服务输出密钥。通常保持 AMI 接近官方镜像的初始化流程即可满足。4. 高级实战Spot 实例与自动化配置4.1 深度利用 Spot 实例降低成本Spot 实例可以节省高达 90% 的成本但有其特殊性可能被 AWS 回收两分钟通知价格浮动启动时间可能稍长。插件对 Spot 实例有良好支持。启用 Spot 实例在 AMI 模板的Advanced中勾选Use Spot Instance。竞价价格你可以设置一个固定的最高价。更常见的做法是设置为“按需价格”On-Demand Price这样只要当前市场价格低于按需价你的请求就会被满足。可以点击Check Current Spot Price查看历史价格。竞价类型Persistent请求是持久的即使实例被中断只要价格仍满足AWS 会尝试重新启动新的实例。适合 Jenkins 场景因为构建任务需要被完成。One-Time请求一次即结束中断后不再重试。为 Spot 实例配置 AMISpot 实例的启动流程与按需实例略有不同。因为 Jenkins 控制器需要等待 Spot 实例启动并主动连接回来所以实例必须预装好自动连接回 Jenkins 的脚本。插件提供了示例脚本ubuntu-ami-setup.sh和ubuntu-init.py其核心逻辑是实例启动时通过 EC2 用户数据User Data获取 Jenkins 的 URL 和分配给它的 Agent 名称。自动从该 URL 下载agent.jar。执行java -jar agent.jar -jnlpUrl ...完成注册。你需要将这些逻辑打包进你的自定义 AMI。一个简单的方法是在创建 AMI 前在基础实例上运行一个设置脚本将ubuntu-init.py这样的脚本安装为系统服务如 systemd 服务并设置为开机自启。Jenkins 控制器配置确保 Jenkins 的Jenkins URL在Configure System中设置的是外部可访问的地址如https://jenkins.yourcompany.com。因为 Spot 实例需要能通过网络访问到这个地址来下载 agent.jar 和建立 JNLP 连接。踩坑记录使用 Spot 实例时在 Jenkins 界面上可能会看到 Agent 启动失败红色叉号。不要立即认为配置错误先查看错误信息。如果提示The spot instance request has been submitted but the instance has not launched yet这通常是正常状态说明 Spot 请求已提交正在等待 AWS 分配资源可能需要几分钟。插件会持续轮询直到实例就绪。4.2 使用 Groovy 脚本实现配置即代码通过 Jenkins 管理界面手动配置虽然直观但不利于版本控制和批量部署。插件支持通过 Groovy 脚本进行全自动化配置这是实现 Jenkins 基础设施即代码IaC的关键。你可以将上一节中提到的庞大 Groovy 脚本示例保存为一个文件如init-ec2-cloud.groovy并通过以下方式执行Jenkins 启动后初始化脚本将脚本放在 Jenkins 的init.groovy.d/目录下Jenkins 启动时会自动执行。Jenkins 脚本控制台直接粘贴到Manage Jenkins-Script Console中运行。脚本核心逻辑解析创建 AWS 凭据对象(AWSCredentialsImpl)将 AK/SK 封装成 Jenkins 可识别的凭据对象。创建从模板对象(SlaveTemplate)用代码定义所有 AMI 模板参数比 UI 更全面、精确。创建云配置对象(EC2Cloud)将区域、凭据ID、私钥、模板列表等组合起来。获取并操作 Jenkins 实例通过Jenkins.getInstance()获取当前实例将凭据添加到全局存储将云配置添加到 Jenkins 的云列表中。保存配置调用jenkins.save()将配置持久化到磁盘。这种方式的优势在于可重复性一键重建整个 Jenkins 的云配置。版本控制脚本可以放入 Git记录每次变更。批量操作轻松创建数十个不同配置的模板。避免手动错误消除在 Web UI 上点击输入可能带来的错误。一个精简的实战脚本片段示例定义模板部分import hudson.plugins.ec2.* import com.cloudbees.jenkins.plugins.awscredentials.* import com.cloudbees.plugins.credentials.* import com.cloudbees.plugins.credentials.domains.Domain // 1. 添加AWS凭据 def awsCreds new AWSCredentialsImpl( CredentialsScope.GLOBAL, jenkins-aws-prod, YOUR_ACCESS_KEY, YOUR_SECRET_KEY, AWS Credentials for Production EC2 Agents ) Jenkins.instance.getExtensionList(com.cloudbees.plugins.credentials.SystemCredentialsProvider)[0].getStore().addCredentials(Domain.global(), awsCreds) // 2. 定义一个Linux构建模板 UnixData unixData new UnixData(, , , 22, ) EC2Tag tag new EC2Tag(Name, jenkins-java-agent) SlaveTemplate template new SlaveTemplate( ami-0c55b159cbfafe1f0, // 你的自定义AMI , // 可用区空表示自动 null, // Spot配置null表示按需实例 sg-0123456789abcdef, // 安全组 /home/ec2-user, // 远程根目录 t3.medium, // 实例类型 false, // EBS优化 java linux docker, // 标签 hudson.model.Node.Mode.NORMAL, Java Builder on t3.medium, sudo yum update -y sudo systemctl start docker, // 初始化脚本 , // 临时目录 , // 用户数据 2, // 执行器数量 ec2-user, // 远程用户 unixData, /usr/bin/java, // Java路径 , // JVM参数 false, // 终止时停止false表示终止 subnet-abcdef01234567890, // 子网 [tag], // 标签列表 10, // 空闲10分钟后终止 0, 0, // 最小实例数最小空闲实例数 5, // 此模板最大实例数 , // IAM实例配置文件 true, // 终止时删除根卷 false, // 使用临时存储 600, // 启动超时600秒 ... ) // 3. 创建EC2 Cloud并添加模板 EC2Cloud ec2Cloud new EC2Cloud( AWS-Prod, false, jenkins-aws-prod, us-east-1, -----BEGIN RSA PRIVATE KEY-----\nYOUR_PRIVATE_KEY\n-----END RSA PRIVATE KEY-----, 10, // 整个Cloud的最大实例数 [template], , ) Jenkins.instance.clouds.add(ec2Cloud) Jenkins.instance.save() println EC2 Cloud configuration added successfully.5. 生产环境运维问题排查与性能调优即使配置正确在生产环境中运行仍可能遇到各种问题。以下是我在多年运维中总结的常见问题与解决方案。5.1 常见问题排查速查表问题现象可能原因排查步骤与解决方案Agent 启动失败状态为Failed to connect1. SSH 密钥不匹配或权限错误。2. 安全组未放行 Jenkins 控制器 IP 到实例 22 端口。3. 实例启动后初始化太慢超时。1. 检查 Jenkins 中配置的 SSH 私钥与 AWS 中密钥对的公钥是否对应。检查Remote User是否正确Amazon Linux 是ec2-user, Ubuntu 是ubuntu。2. 检查实例所在安全组的入站规则。确保源 IP 是 Jenkins 控制器的 IP 或 CIDR。3. 增加 AMI 模板中的Launch Timeout值如设为 600。检查初始化脚本是否有长时间阻塞的操作。Agent 启动成功但立即断开或任务执行失败1. 初始化脚本执行出错如命令不存在、权限不足。2. Java 未正确安装或路径不对。3. Agent 无法连接回 Jenkins 控制器网络问题。1. 登录到 EC2 实例通过 AWS 控制台或 SSH检查/tmp/init.sh脚本内容和执行日志通常在/var/log/cloud-init-output.log。2. 在初始化脚本中加入which java和java -version命令输出日志确保 Java 可用。3. 在实例上尝试curl -I https://your-jenkins-url和telnet your-jenkins-url 50000JNLP 端口检查网络连通性。Spot 实例请求一直处于pending-fulfillment1. 当前 Spot 市场价格高于你的出价。2. 目标区域/可用区所选实例类型容量不足。1. 在 AWS 控制台的 Spot 请求页面查看状态信息。适当提高出价可设置为按需价格。2. 尝试更换实例类型或选择“不指定可用区”以增加灵活性。Agent 空闲后不终止1.Idle Termination Minutes设置过长或为 0。2. 插件调度逻辑认为仍有任务在排队可能是标签不匹配造成的假象。3. Jenkins 与 AWS API 通信故障。1. 检查模板配置。2. 检查构建任务的标签限制是否过于严格导致没有 Agent 能匹配而云 Agent 又因标签不匹配不被调用。3. 查看 Jenkins 日志 (/var/log/jenkins/jenkins.log)搜索EC2Cloud相关错误。检查 IAM 权限是否包含ec2:TerminateInstances。“Authentication Timeout” 错误SSH 连接建立过程超时可能由于实例启动初期系统负载高、SSH 服务启动慢。这是已知问题 JENKINS-30284。可以调整两个 Jenkins 系统属性来增加重试和等待时间-Djenkins.ec2.bootstrapAuthSleepMs45000(每次重试等待45秒)-Djenkins.ec2.bootstrapAuthTries40(重试40次)在JAVA_OPTS中设置这些参数。5.2 性能与成本优化实践AMI 优化是重中之重这是减少 Agent 启动时间即构建排队等待时间最有效的手段。你的自定义 AMI 应该包含所需版本的 JDK/JRE。版本管理工具Git, SVN。构建工具Maven, Gradle, npm, yarn。容器运行时Docker并配置好用户组和镜像加速器。必要的系统工具和库。优化过的 SSH 配置禁用 DNS 反查等。将agent.jar预下载到镜像中并在初始化脚本中直接使用本地文件可以节省几秒到十几秒的网络下载时间。合理设置标签与使用策略精细化标签不要只用一个aws标签。根据 CPU、内存、操作系统、预装软件划分标签如linux-docker-4cpu,windows-msbuild,android-sdk。使用Node和Label参数在 Pipeline 或自由风格项目中使用agent { label linux-docker-4cpu }来精确指定所需的 Agent 类型避免启动不匹配的实例。限制并发在 Cloud 和每个 Template 级别设置合理的Instance Cap实例上限防止因配置错误或异常导致短时间内启动过多实例产生巨额账单。混合使用按需与 Spot 实例创建两个相同的模板一个使用按需实例另一个使用 Spot 实例但赋予相同的标签如java-builder。在 Cloud 配置中将 Spot 模板的顺序放在前面。Jenkins 会优先尝试启动 Spot 实例如果失败如价格或容量问题则会回退到启动按需实例。这能在保证构建可靠性的前提下最大化成本节约。监控与告警CloudWatch 监控为 EC2 实例的关键指标CPUUtilization, NetworkIn/Out设置 CloudWatch 告警监控异常。Jenkins 插件监控使用Monitoring插件或Prometheus插件暴露 Jenkins 和 Agent 的 metrics监控构建队列长度、Agent 在线数量、云实例启动成功率等。成本与使用报告定期通过 AWS Cost Explorer 分析 EC2 成本结合 Jenkins 的构建历史评估 Spot 实例节省比例和 Agent 利用率持续优化模板配置和数量。通过深入理解jenkinsci/ec2-plugin的工作原理遵循安全最佳实践进行配置并运用自动化和监控手段进行管理你可以构建出一个既弹性灵活、又安全可控、同时还能显著优化成本的现代化 Jenkins 构建农场。这套体系能够从容应对从日常开发构建到大规模发布的各种场景真正释放云原生 CI/CD 的潜力。