- 后端
- DevOps
- 云原生
- 微服务
【免费下载链接】spinnaker
Spinnaker is an open source, multi-cloud continuous delivery platform for releasing software changes with high velocity and confidence.
Rosco 是 Spinnaker 持续交付平台中的“烘焙坊”(bakery)微服务:它负责调用 Hashicorp Packer 把基础镜像与待发布软件包合成为全新的机器镜像,并调用 Helm、Kustomize 等模板引擎渲染 Kubernetes 部署清单。本文以仓库中的 rosco/README.md 为主干,结合源码、配置模板与测试用例,系统讲解 Rosco 的定位、REST API、请求字段、执行链路、配置方式与本地开发调试方法,读完即可独立上手验证、运行与扩展这一服务。
Rosco 在 Spinnaker 中的定位
Rosco is Spinnaker's bakery, producing machine images with Hashicorp Packer and rendered manifests with templating engines Helm and Kustomize.
Rosco 的职责包含两个层面:
- 机器镜像烘焙(Machine Image Baking):以指定的基础镜像(base image)为起点,通过 Packer 启动临时实例、执行软件包安装脚本,产出新的机器镜像(AMI、GCE 镜像、Azure 镜像等)。
- 清单渲染(Manifest Rendering):将模板化的 Kubernetes 部署清单结合输入参数渲染为最终可发布清单,交付给下游部署流程。
镜像烘焙链路中,Rosco 目前支持以下云平台(见 rosco/README.md 与 BakeRequest.groovy 中的CloudProviderType枚举):Alibaba Cloud(alicloud)、AWS(aws)、Azure(azure)、Google Compute Engine(gce)、Huawei Cloud(huaweicloud)、Oracle(oracle)、Tencent Cloud(tencentcloud)以及 Docker(docker)。其扩展性来自CloudProviderBakeHandler 抽象:新增平台只需实现一个 bake handler 并注册到CloudProviderBakeHandlerRegistry,无需改动核心烘焙流程。
从代码结构看,Rosco 是典型的多模块 Gradle 工程,位于 monorepo 的rosco/目录下,核心模块包括:
rosco-core:领域模型(api包)、任务执行(jobs)、状态持久化(persistence)与各云平台 bake handler(providers);rosco-web:Spring Boot 入口与 REST 控制器(controllers);rosco-manifests:Helm / Kustomize / Helmfile / CloudFoundry 清单渲染服务;rosco-bom:依赖物料清单。
对外 REST API 与 Swagger UI
Rosco 以 REST API 形式提供服务,默认端口8087(见 rosco-web/config/rosco.yml 中的server.port)。启动后可访问 Swagger UI 交互式调试:
http://localhost:8087/swagger-ui.htmlSwagger 文档的启用与扫描范围由 rosco-web/config/rosco.yml 末尾的swagger段控制,默认覆盖/api/v1.*、/api/v2.*、/bakeOptions.*、/status.*等路径模式。
v1 烘焙 API:BakeryController
BakeryController(BakeryController.groovy)暴露了镜像烘焙的完整端点集合:
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /bakeOptions | 列出所有已注册云平台的可烘焙选项(含基础镜像清单) |
| GET | /bakeOptions/{cloudProvider} | 查询指定云平台的烘焙选项 |
| GET | /bakeOptions/{cloudProvider}/baseImages/{imageId} | 查询指定基础镜像详情 |
| POST | /api/v1/{region}/bake | 发起一次镜像烘焙,支持rebake=1强制重烤 |
| GET | /api/v1/{region}/status/{statusId} | 按状态 ID 查询烘焙进度 |
| GET | /api/v1/{region}/bake/{bakeId} | 查询烘焙结果详情(含产出的镜像引用) |
| GET | /api/v1/{region}/logs/{statusId} | 获取烘焙日志 |
| GET | /api/v1/{region}/logs/image/{imageId} | 按镜像 ID 反查烘焙日志 |
| DELETE | /api/v1/{region}/bake | 按 bakeKey 删除烘焙记录并取消对应任务 |
| POST | /api/v1/bakes/delete-requests | 按 pipeline execution id 批量清理烘焙记录 |
| GET | /api/v1/{region}/cancel/{statusId} | 取消一个进行中的烘焙 |
其中核心的createBake流程值得展开:控制器先校验/补齐cloud_provider_type(缺省时使用default-cloud-provider-type配置,默认aws,见源码@Value('${default-cloud-provider-type:aws}')),再通过produceBakeKey(region, bakeRequest)生成 bakeKey,随后用 Redis 分布式锁(acquireBakeLock)保证同一 bakeKey 同时只有一个烘焙任务在跑:
- 若请求携带
rebake=1,先删除旧烘焙记录并cancelJob取消旧进程; - 否则查询同 bakeKey 下已有的 RUNNING 或 SUCCESS 状态任务,命中则直接复用并返回既有
BakeStatus(避免重复烘焙),并累加duplicate计数器; - 获得锁后调用
runBake:jobExecutor.startJob(jobRequest)启动 Packer 子进程,然后以rosco.polling.wait-for-job-start-timeout-millis(默认 5000ms)为窗口、每rosco.polling.wait-for-job-start-polling-interval-millis(默认 500ms)轮询一次任务是否已启动,实现“快速失败”;任务确认启动后写入BakeStatus到 Redis。
v2 清单渲染 API:V2BakeryController
针对 Helm / Kustomize 渲染场景,V2BakeryController(V2BakeryController.java)提供统一入口:
POST /api/v2/manifest/bake/{type}{type}即模板渲染器类型,控制器根据BakeManifestService.handles(type)从注入的List<BakeManifestService>中挑选对应实现,把请求体转换为该服务的请求类型后调用bake()并返回渲染产物Artifact。
BakeRequest:一次烘焙请求的完整字段
请求体模型定义于 BakeRequest.groovy,字段带 Swagger Schema 注解,可作为 API 文档与调试参考。核心字段如下:
| 字段 | 类型 | 含义 |
|---|---|---|
request_id | String | 自动生成的 UUID,作为烘焙任务的 jobId(只读字段) |
user | String | 发起请求的用户 |
package | String | 要安装的软件包名,空格分隔 |
package_artifacts | List<Artifact> | 以 Spinnaker artifact 形式给出的软件包 |
build_host/job/build_number/commit_hash/build_info_url | String | CI 构建信息(构建主机、任务名、构建号、提交哈希、构建地址) |
cloud_provider_type | 枚举 | alicloud / aws / azure / docker / gce / huaweicloud / oracle / tencentcloud |
base_label | 枚举 | release / candidate / previous / unstable / foundation |
base_os | String | 从 Rosco 配置中解析的命名基础镜像 ID |
base_name | String | 基础镜像名 |
base_ami | String | 显式指定基础机器镜像,绕过配置解析 |
vm_type | 枚举 | pv / hvm |
store_type | 枚举 | ebs / s3 / docker |
enhanced_networking | Boolean | 是否启用增强网络 |
ami_name/ami_suffix | String | 目标镜像名/后缀 |
upgrade | Boolean | 是否先升级系统再装包 |
instance_type | String | 烘焙实例规格 |
organization | String | 镜像所属组织 |
template_file_name | String | 显式指定 Packer 模板文件 |
extended_attributes | Map | 追加到 Packer 命令的键值对 |
var_file_name | String | 追加的变量文件(JSON,须与模板同目录) |
account_name | String | 烘焙所用云账号 |
publisher/offer/sku | String | Azure 市场镜像三元组 |
os_type | 枚举 | linux / windows |
package_type | 枚举 | RPM / DEB / NUPKG,各自绑定对应的PackageUtil(见 PackageUtil.java 的实现 Rpm/Deb/Nupkg) |
custom_managed_image_name | String | 自定义托管镜像名 |
响应的BakeStatus(BakeStatus.groovy)包含id、state(RUNNING / COMPLETED / CANCELED)、result(SUCCESS / FAILURE)与resource_id——后者可传给lookupBake获取新镜像的引用信息。
从请求到镜像:一次烘焙的完整执行链路
1. 生成 BakeRecipe
控制器拿到BakeRequest后,调用对应云平台的CloudProviderBakeHandler.produceBakeRecipe(region, bakeRequest),把请求翻译成一条可执行的命令。BakeRecipe(BakeRecipe.groovy)由name、version、command(tokenized 命令序列)和可选的env(环境变量)组成。
2. 构造 Packer 命令
PackerCommandFactory(PackerCommandFactory.groovy)负责把参数序列化为packer build命令行;其默认实现LocalJobFriendlyPackerCommandFactory(LocalJobFriendlyPackerCommandFactory.groovy)生成的命令形态为:
packer build -color=false [-timestamp-ui] [-var key=value ...] [-var-file=<path>] <template-file>其中-timestamp-ui仅当配置packer.timestamp: true时追加(适用于 Packer >= 1.4.0),additionalParameters可注入每次构建都携带的固定参数(例如-on-error=abort),配置位置见 rosco-web/config/rosco.yml 的packer段。
3. 本地任务执行与状态持久化
命令通过JobExecutor的本地实现JobExecutorLocal(JobExecutorLocal.groovy)以子进程方式执行,超时默认 30 分钟(rosco.jobs.local.timeoutMinutes: 30,见 rosco-web/config/rosco.yml)。
任务状态与日志持久化在 Redis:RedisBackedBakeStore(RedisBackedBakeStore.groovy)实现BakeStore接口,负责 bakeKey 锁、状态存取、日志存取与按 pipeline execution id 清理。BakePoller(BakePoller.groovy)后台周期性地轮询进行中的任务并把最新状态回写 Redis。这也是 README 强调“Rosco 需要本地 Redis 实例”的原因。
4. 镜像内软件包安装
无论是 AWS 的aws-ebs.json、GCE 的gce.json还是其他模板,Packer 的 provisioner 都会执行统一的安装脚本 install_packages.sh。该脚本按package_type分发:
- deb:把
repository写入/etc/apt/sources.list.d/spinnaker.list,apt-get update后按序安装packages与 artifact 引用的包;upgrade=true时先执行unattended-upgrade;支持disable_services时创建/usr/sbin/policy-rc.d抑制服务自启(适配 chroot 构建); - rpm:生成
/tmp/spinnaker-<ts>.repo并移动到/etc/yum.repos.d/,随后yum install; - 脚本会先用
jq解析/tmp/artifacts.json中 artifact 的reference(形如“仓库 包名”),解析后卸载 jq,避免把临时工具遗留进镜像。
5. 镜像产物回传
构建成功后,Packer 的manifestpost-processor(见 gce.json 中的post-processors)输出 JSON manifest,Rosco 通过PackerManifestService(PackerManifestService.java)解析并入库,最终由lookupBake端点把对应 region 的镜像引用(AMI ID 等)返回给上层编排(如 Orca / Deck)。
Manifest 渲染:Helm 与 Kustomize 的实现细节
清单渲染统一继承抽象类BakeManifestService(BakeManifestService.java),其doBake与 v1 镜像烘焙共用JobExecutor:启动任务后每秒轮询BakeStatus,直至非 RUNNING;失败则抛出携带日志的异常,成功则返回渲染输出。
- Helm:
HelmBakeManifestService(HelmBakeManifestService.java)同时支持HELM2与HELM3两种渲染器,输入HelmBakeManifestRequest(HelmBakeManifestRequest.java)可携带apiVersions、kubeVersion、namespace、rawOverrides、includeCRDs(Helm v3 是否包含 CRD 清单)以及helmChartFilePath(chart 位于 git/repo artifact 子目录时指定Chart.yaml路径)等参数;渲染结果以embedded/base64类型的 artifact 返回,并剥离tests/目录下的模板。 - Kustomize:
KustomizeBakeManifestService(KustomizeBakeManifestService.java)支持KUSTOMIZE(v3)、KUSTOMIZE4、KUSTOMIZE5三类渲染器,并配套KustomizationFileReader与 ConfigMapGenerator 等映射工具(见 kustomize/mapping)。 - 此外还内置
helmfile与cloudfoundry渲染服务,分别位于 helmfile 与 cloudfoundry。
容器镜像中这些工具的版本由 Dockerfile.ubuntu 统一固定:Packer 1.15.4、Helm 2.17.0 与 Helm 3、kustomize 3.9.4 / 4.5.7 / 5.8.1、helmfile 1.7.0,且以packer plugins install方式安装 amazon / azure / googlecompute 插件——若在本地手动复现,需按相同方式准备 Packer 及其插件。
本地开发与运行指南
Rosco 位于本 monorepo 的rosco/目录,但所有./gradlew命令都必须从monorepo 根目录执行,而不是从rosco/内部执行(完整构建/测试/运行命令见根目录 CLAUDE.md)。
1. 准备本地 Redis
Rosco 依赖 Redis 存放烘焙状态与锁,先启动一个本地实例:
docker run -d -p 6379:6379 redisRedis 连接地址可在 halconfig/rosco.yml 中通过redis.connection配置覆盖(默认redis://localhost:6379)。需要注意的是,Rosco 并不复用 Halyard 默认部署的 Redis,而是独立连接redis://localhost:6379,因此本地调试时必须自行启动 Redis。
2. IDE 环境准备
生成 IntelliJ Gradle 工程文件:
./gradlew idea应用 Groovy 代码格式化方案:
- Preferences -> Editor -> Code Style -> Manage ... -> Import,选择项目根目录的 codestyle.xml;
- 应用
spinnaker代码风格方案。
3. 启动服务
./gradlew rosco4. 调试模式
通过 Java 系统属性DEBUG=true以调试模式启动 JVM:
./gradlew rosco -DDEBUG=trueJVM 会在8187 端口监听调试器连接(注意:不会等待调试器接入才启动 Rosco);相关 JVM 参数可在rosco-web/build.gradle中查看与调整。
5. 验证服务
服务启动后,用 curl 验证烘焙选项端点:
curl -v localhost:8087/bakeOptions该端点由BakeryController.bakeOptions()实现,遍历CloudProviderBakeHandlerRegistry中所有已注册 handler,返回各自getBakeOptions()(包含cloudProvider与baseImages列表,模型见 BakeOptions.groovy)。
6. 收尾:停止 Docker 容器
调试结束后,停止并清理本地 Redis 容器:
docker stop <redis-container-id>配置要点:启用云平台、基础镜像与软件源
各云平台的启用开关与基础镜像定义集中在 rosco-web/config/rosco.yml(halconfig 侧另有 images.yml 作为参考样本)。关键配置项:
- 启用开关:每个云平台默认禁用,如
aws.enabled: ${AWS_ENABLED:false}、google.enabled: ${GOOGLE_ENABLED:false}。若在预构建 Spinnaker 镜像中使用,需将AWS_ENABLED等替换为SPINNAKER_AWS_ENABLED形式的环境变量,或显式置为true。 - 默认模板:如
aws.bakeryDefaults.templateFile: aws-ebs.json;若要使用镜像共享/复制能力,可切换为aws-multi-ebs.json或aws-multi-chroot.json,此时还需设置SPINNAKER_AWS_DEFAULT_ACCOUNT环境变量为 Spinnaker 实例所在 AWS 账号 ID。相关模板文件位于 rosco/halconfig/packer。 - 基础镜像(baseImages):每个条目包含
id、shortDescription、detailedDescription、packageType、可选的templateFile、osType与customRepository,以及按区域细分的virtualizationSettings(如 AWS 的sourceAmi/instanceType/sshUserName/spotPrice,Azure 的publisher/offer/sku,GCE 的sourceImage或sourceImageFamily)。GCE 支持isImageFamily: true表示按镜像族解析,Deck 会在 UI 上据此标注。 - Packer Spot 价格:AWS
spotPrice设为auto时自动探测最优价格,此时必须同时配置spotPriceAutoProduct(取值如Linux/UNIX (Amazon VPC)、Windows (Amazon VPC)等);设为0则使用按需实例(默认)。 - 软件源仓库:可在
rosco.yml顶层配置debianRepository/yumRepository/chocolateyRepository(每个类型支持分号分隔的多个仓库),烘焙 deb/rpm/nupkg 镜像时注入;也可在单条 baseImage 上通过customRepository覆盖。 - 需要 root 的模板:
templatesNeedingRoot: aws-chroot.json列出的模板要求/usr/bin/packer以 sudo 运行。若使用此类模板,需在/etc/sudoers.d/spinnaker中添加spinnaker ALL=(ALL) NOPASSWD: /usr/bin/packer;该文件同时给出安全警告——为 spinnaker 用户授予 packer 的 sudo 权限可能带来被恶意利用的风险。 - Packer 版本适配:
packer.timestamp在 Packer >= 1.4.0 时建议置true以获得带时间戳前缀的输出;packer.additionalParameters可追加每次构建都生效的参数。
扩展新云平台:CloudProviderBakeHandler 抽象
Rosco 的云平台支持是可插拔的。核心抽象接口CloudProviderBakeHandler(CloudProviderBakeHandler.groovy)定义了getBakeOptions()、produceBakeKey(region, request)、produceBakeRecipe(region, request)与getMaskedPackerParameters()等方法;现有实现(如 AWSBakeHandler.groovy、GCEBakeHandler.groovy、AliCloudBakeHandler.java、TencentCloudBakeHandler.java 等)均通过DefaultCloudProviderBakeHandlerRegistry注册,并被BakeryController通过lookup(cloudProvider)按类型查找。每个 handler 配套的 Spock 测试(如 AWSBakeHandlerSpec.groovy、GCEBakeHandlerSpec.groovy)与控制器测试(BakeryControllerSpec.groovy)验证了 bakeKey 生成、Packer 命令组装与去重/重烤语义,可作为新增平台时的实现范本。
若要接入新平台,从源码结构看需要:新增一个实现CloudProviderBakeHandler的 handler 类,编写对应的 Packer 模板放入 rosco/halconfig/packer,在 rosco-web/config/rosco.yml 中补充bakeryDefaults与enabled开关,并在BakeRequest.CloudProviderType枚举中登记新类型。核心的烘焙、轮询、存储链路无需改动。
小结
Rosco 以“Packer 烘焙镜像 + 模板引擎渲染清单”双能力支撑 Spinnaker 的持续交付:对外通过/bakeOptions、/api/v1/{region}/bake与/api/v2/manifest/bake/{type}提供统一 REST 入口,对内以CloudProviderBakeHandler实现多云抽象、以 Redis 实现任务状态与锁、以JobExecutor驱动本地 Packer 子进程。开发与调试时只需一条 Redis 容器、一个./gradlew rosco命令即可快速起服,并通过curl -v localhost:8087/bakeOptions或 Swagger UI(http://localhost:8087/swagger-ui.html)完成交互验证。
- 后端
- DevOps
- 云原生
- 微服务
【免费下载链接】spinnaker
Spinnaker is an open source, multi-cloud continuous delivery platform for releasing software changes with high velocity and confidence.
相关推荐
Spinnaker Rosco halconfig 配置骨架解析:Halyard 拼接机制、弃用迁移与烘焙默认值配置指南
Spinnaker Rosco halconfig 配置骨架解析:Halyard 拼接机制、弃用迁移与烘焙默认值配置指南 本文围绕 Spinnaker 开源仓库
后端DevOps云原生微服务Entities Graphics URP 光照探针(Light Probes)示例深度解析:基于 URPSamples Lightprobes 场景的烘焙与渲染实践
Entities Graphics URP 光照探针(Light Probes)示例深度解析:基于 URPSamples Lightprobes 场景的烘焙与渲
示例工程深入解读 HashiCorp Packer:从单一配置构建多平台机器镜像
深入解读 HashiCorp Packer:从单一配置构建多平台机器镜像 导读 本文以本仓库(HashiCorp Packer 官方开源仓库)的 README.
云原生DevOps运维
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考