Floci 本地模拟 AWS Batch:从任务编排到 Docker 执行的控制面实现解析
【免费下载链接】flociLight, fluffy, and always free - The AWS Local Emulator alternative项目地址: https://gitcode.com/gh_mirrors/fl/floci
Floci(Light, fluffy, and always free —— AWS Local Emulator)在本地实现了 AWS Batch 控制面(REST JSON 协议),支持计算环境、任务队列、任务定义与任务提交等核心编排能力,并可通过immediate与docker两种运行器模式完成本地契约测试或真实容器执行。本文以 docs/services/batch.md 为主线,结合 BatchService.java、BatchDockerRunner.java、BatchController.java 等源码与 BatchIntegrationTest.java 测试,带你掌握在 Floci 中配置与使用 AWS Batch 的完整实战方案,包括数组任务(Array Jobs)、多节点并行任务(MNP)、EventBridge 联动与 CloudFormation 资源供给。
Batch 服务的定位与端点约定
Floci 的 Batch 实现面向本地集成测试的控制面模拟:它完整保存队列、任务定义与任务的元数据,让 SDK 调用可以像连真实 AWS 一样完成"创建→提交→查询"的闭环,同时刻意简化为测试服务的调度语义(如 immediate 模式下的即时成功)。整体协议为REST JSON,默认端点前缀为http://localhost:4566/v1/...。
在源码层面,所有端点由 BatchController.java 暴露,统一走一个handle模板方法:解析请求体(空请求体视为{}),通过 RegionResolver 解析请求区域,然后将异常映射为带X-Amzn-Errortype头的 AWS 风格错误响应(400ClientException、500ServerException)。这意味着你的客户端错误处理逻辑在本地与云端行为一致。
支持的操作(Supported Operations)
Batch 控制面共提供 12 个操作,均以POST调用,端点即 API 名小写化后的路径:
| Operation | Endpoint | Description |
|---|---|---|
CreateComputeEnvironment | POST /v1/createcomputeenvironment | 存储本地计算环境并返回其 ARN |
DescribeComputeEnvironments | POST /v1/describecomputeenvironments | 描述全部或选定的计算环境 |
UpdateComputeEnvironment | POST /v1/updatecomputeenvironment | 更新计算环境的状态、服务角色与计算资源 |
DeleteComputeEnvironment | POST /v1/deletecomputeenvironment | 删除处于DISABLED且未被任何任务队列引用的计算环境;删除不存在的环境为无操作(no-op) |
CreateJobQueue | POST /v1/createjobqueue | 存储一个挂载到计算环境上的本地任务队列 |
UpdateJobQueue | POST /v1/updatejobqueue | 更新任务队列的状态、优先级与计算环境顺序 |
DeleteJobQueue | POST /v1/deletejobqueue | 删除处于DISABLED状态的任务队列;删除不存在的队列为无操作 |
DescribeJobQueues | POST /v1/describejobqueues | 描述全部或选定的任务队列 |
RegisterJobDefinition | POST /v1/registerjobdefinition | 注册带修订号(revision)的container或multinode任务定义 |
DeregisterJobDefinition | POST /v1/deregisterjobdefinition | 将任务定义的某个修订标记为INACTIVE |
DescribeJobDefinitions | POST /v1/describejobdefinitions | 按名称、ARN、修订号与状态列出任务定义 |
SubmitJob | POST /v1/submitjob | 提交一个本地 Batch 任务 |
DescribeJobs | POST /v1/describejobs | 按任务 ID 描述任务 |
ListJobs | POST /v1/listjobs | 按队列、状态、AWSfilters与分页列出任务 |
控制面的校验与细节
从 BatchService.java 的实现可以看到一系列贴近 AWS 的契约细节:
- 计算环境:
type仅接受MANAGED/UNMANAGED,state仅接受ENABLED/DISABLED;创建后状态固定为VALID。UpdateComputeEnvironment对computeResources采用局部覆盖语义(只覆盖调用方传入的字段,如minvCpus/maxvCpus/desiredvCpus,不重置整个子对象)。删除前会校验状态为DISABLED且未被任何队列的computeEnvironmentOrder引用(BatchService.java)。 - 任务队列:
priority为必填;computeEnvironmentOrder中的每个环境必须是VALID状态;删除ENABLED队列会报ClientException(BatchService.java)。 - 任务定义修订:同名注册自动递增
revision,ARN 形如arn:aws:batch:<region>:<account>:job-definition/<name>:<revision>;DeregisterJobDefinition要求引用必须携带修订号(name:revision或 ARN),仅按名称引用会返回 400;按名称提交时解析为最新 ACTIVE 修订(BatchService.java),BatchIntegrationTest.java 完整验证了修订递增、注销与"名称→最新修订"解析行为。 - 通用校验:标签最多 50 个、键 1-128 字符且不能以
aws:开头、值最多 256 字符;环境变量名不能以AWS_BATCH开头;timeout.attemptDurationSeconds最少 60 秒;retryStrategy.attempts取值范围 1-10;jobName需匹配[A-Za-z0-9][A-Za-z0-9_-]{0,127}。
运行器模式(Runner Modes)
Batch 的运行模式由配置项floci.services.batch.runner-mode(环境变量FLOCI_SERVICES_BATCH_RUNNER_MODE)控制,在 EmulatorConfig.java 中定义,默认值为immediate。
| 值 | 行为 |
|---|---|
immediate | 默认值。SubmitJob持久化任务、记录生命周期时间戳、生成一个成功 attempt,并在任务变为SUCCEEDED后返回。 |
docker | 每个 attempt 从任务定义镜像启动一个 Docker 容器,传入解析后的 command 与环境变量,将MEMORY资源需求应用为容器内存上限,捕获 CloudWatch Logs 日志流名称,并根据容器退出码置为SUCCEEDED或FAILED。超时任务直接失败且不重试,与 AWS Batch 的超时行为一致。 |
process模式未实现。
在immediate模式下,运行循环仍然驱动任务完整经历状态机:PENDING → RUNNABLE → STARTING → RUNNING,随后生成退出码为 0 的 attempt(BatchService.java)。在docker模式下,则调用 BatchDockerRunner.java 执行真实容器。
Docker 运行器的实现细节
BatchDockerRunner.java 是 docker 模式的核心执行引擎,关键行为包括:
- 容器构建:通过
ContainerBuilder构建容器,自动附加host.docker.internal(或 Floci 内置 DNS 后缀)以访问宿主机上的 Floci 端点、嵌入 DNS、日志轮转与资源标签;command 为空时不传入 CMD。 - 内置 AWS 环境:每个容器注入
AWS_REGION、AWS_ACCESS_KEY_ID=test、AWS_SECRET_ACCESS_KEY=test、AWS_SESSION_TOKEN=test、FLOCI_ENDPOINT/AWS_ENDPOINT_URL(指向 Floci 自身端口)、AWS_BATCH_JOB_ID、AWS_BATCH_JOB_ATTEMPT、AWS_BATCH_JQ_NAME、AWS_BATCH_CE_NAME=local(BatchDockerRunner.java)。 - 内存限制:遍历
resourceRequirements,仅将type == "MEMORY"的需求解析为withMemoryMb应用;非法数值会被忽略并告警。 - 超时:
timeout.attemptDurationSeconds转换为容器等待期限,超时返回退出码 137、reason"Job timed out"且标记timedOut=true,从而驱动失败且不重试(BatchDockerRunner.java)。 - 优雅清理:运行中的容器记录在
inFlightContainers映射中,模拟器关闭(SIGTERM)时通过stopManagedContainers()回收,避免中途退出导致孤儿容器。
提交行为(Submit Behavior)
SubmitJob支持的字段如下:
jobNamejobDefinitionjobQueueparameterscontainerOverrides.commandcontainerOverrides.environmentarrayProperties.size(数组任务)nodeOverrides.numNodes、nodeOverrides.nodePropertyOverrides(多节点并行任务)timeout.attemptDurationSecondsretryStrategy.attemptstags
关键合并与解析规则:
- 命令替换:
containerOverrides.command覆盖任务定义中的 command;命令条目中的Ref::inputKey形式会在执行前从合并后的参数映射中解析为实际值(BatchService.java)。 - 环境合并:提交时的环境变量覆盖(overrides)逐键合并到任务定义环境变量之上(definition 为底、overrides 为顶)。
- 不支持
dependsOn:任务依赖未实现,提交时传入dependsOn会直接抛ClientException(BatchService.java)。
任务在本地的状态流转为:
SUBMITTED -> PENDING -> RUNNABLE -> STARTING -> RUNNING -> SUCCEEDED|FAILEDimmediate 模式下每个任务强制经过PENDING是本地为测试做的简化——AWS 在无依赖、无容量等待时可能跳过该状态。集成测试 BatchIntegrationTest.java 验证了Ref::命令解析(payloads/input.json等实际入参被注入 command)、环境覆盖生效、attempt 记录退出码 0 等完整链路。
重试与超时语义
retryStrategy.attempts与timeout.attemptDurationSeconds均可由任务定义提供、SubmitJob 覆盖(treeOrDefault语义)。每次失败 attempt 后,任务回到RUNNABLE并标记"Attempt failed; retrying";只有当 attempt 超时、达到最大尝试次数或退出码为 0 时才终态化(BatchService.java)。多节点任务的重试判定则完全由主节点结果驱动(见下文)。
数组任务(Array Jobs)
通过arrayProperties.size(取值范围2-10,000)提交即产生数组任务:
- 扇出模型:每个索引生成一个子任务(child),共享父任务名称,走与普通任务相同的执行管线;在
docker模式下每个子任务拥有独立容器。 - 实时聚合:父任务的
status与arrayProperties.statusSummary并非增量存储,而是在每次DescribeJobs/ListJobs调用时实时从子任务聚合计算(arrayRollup方法,BatchService.java),因此大量子任务并发完成时不存在状态竞态。 - 聚合规则:任一子任务
FAILED则父任务FAILED;全部子任务SUCCEEDED则父任务SUCCEEDED;否则按是否有子任务已离开SUBMITTED状态在PENDING/SUBMITTED之间选择;startedAt取子任务最小值,stoppedAt取最大值。 - 列表视图:队列级
ListJobs对每个数组任务只显示一条记录(父任务);传入arrayJobId可列出子任务(每个子任务附带arrayProperties.index)。 - 原子写入:父任务与全部子任务在同一把锁内一次性写入,读取方永远不会看到"半成品"数组(BatchService.java)。
由于TerminateJob/CancelJob均未实现,数组任务的级联取消不适用(见 Limitations)。
多节点并行任务(Multi-node Parallel, MNP)
注册"type": "multinode"且带nodeProperties(numNodes、mainNode、nodeRangeProperties)的任务定义即为 MNP 任务定义;对其SubmitJob(可带nodeOverrides)的行为:
- 并发执行:
docker模式下每个节点并发启动一个容器,并注入AWS_BATCH_JOB_NODE_INDEX、AWS_BATCH_JOB_MAIN_NODE_INDEX、AWS_BATCH_JOB_NUM_NODES三个节点环境变量(BatchDockerRunner.java)。 - 主节点裁决:任务的最终状态、重试决策与
startedAt/stoppedAt/statusReason完全由**主节点(main node)**决定——与 AWS 一致,从节点失败不会导致任务失败或重试(BatchService.java)。 - 节点范围解析:
nodeRangeProperties的targetNodes支持"0:3"、":3"(起点 0)、"4:"(到末节点)或裸索引;多个范围覆盖同一索引时按提交顺序最后一个覆盖生效(对齐 AWS 的 "nested ranges override");注册时强制要求所有节点索引被完整覆盖(BatchService.java)。 - numNodes 覆盖:
nodeOverrides.numNodes允许扩缩节点数,但要求任务定义中存在开放区间(如"n:"),且新节点数必须大于mainNode索引。 - 描述视图:
DescribeJobs与队列级ListJobs返回整个任务一条记录(带nodeProperties、无 per-nodecontainer);传入multiNodeJobId调用ListJobs则按节点拆分,每个节点带自己的container与nodeProperties.nodeIndex。 - 约束:MNP 任务定义不支持
containerOverrides与数组任务,请改用nodeOverrides(BatchService.java)。
与 EventBridge 联动
指向 Batch 任务队列的 EventBridge 规则目标可携带:
{ "BatchParameters": { "JobDefinition": "my-job:1", "JobName": "nightly-job", "ArrayProperties": {"Size": 2}, "RetryStrategy": {"Attempts": 2} } }规则触发时,Floci 通过submitFromEventBridge向目标队列提交一个等价 Batch 任务(BatchService.java):
- 若目标负载包含根级
Parameters对象,其键值对会被字符串化并作为 Batch 提交参数传入;扁平负载字段不会被转换为 Batch 参数。 RetryStrategy.Attempts被映射为提交请求的retryStrategy.attempts。ArrayProperties会作为目标元数据被接受与回显(用于本地部署兼容),但 Batch 仍只提交一个本地任务,不会扇出数组子任务——只有直接调用SubmitJob才能触发数组/MNP 展开(见 Limitations)。
CloudFormation 资源供给
Floci 为 Batch 提供以下 CloudFormation 资源类型的本地供给:
AWS::Batch::ComputeEnvironmentAWS::Batch::JobQueueAWS::Batch::JobDefinition
IAM 角色、VPC 字段、Fargate 声明、日志配置、存储与资源需求均作为元数据接受。docker 模式会把MEMORY需求应用为容器内存上限;但本地调度不模拟AWS 的容量、VCPU 分配与 VPC 网络。注意:CloudFormation 的AWS::Batch::JobDefinition供给器只注册container类型任务定义,multinode任务定义只能通过直接调用RegisterJobDefinition供给(见 Limitations)。
配置项(Configuration)
Batch 的完整配置项如下(源码定义于 EmulatorConfig.java):
| 变量 | 默认值 | 描述 |
|---|---|---|
FLOCI_SERVICES_BATCH_ENABLED | true | 启用或禁用 Batch 服务 |
FLOCI_SERVICES_BATCH_RUNNER_MODE | immediate | 运行器模式:immediate或docker |
FLOCI_SERVICES_BATCH_DOCKER_NETWORK | (未设置) | Batch 容器使用的 Docker 网络 |
FLOCI_STORAGE_SERVICES_BATCH_MODE | (继承全局) | 可选的存储模式覆盖 |
FLOCI_STORAGE_SERVICES_BATCH_FLUSH_INTERVAL_MS | 5000 | 持久化存储的刷盘间隔(毫秒) |
对应 YAML 配置方式(application.yml)可写作:
floci: services: batch: enabled: true runner-mode: immediate # 或 docker docker-network: my-batch-net storage: services: batch: mode: file flush-interval-ms: 5000存储实现
Batch 的状态持久化由 BatchService.java 中的StorageFactory完成,共四个存储后端:
batch-job-definitions.json:任务定义(按 ARN 索引)batch-job-queues.json:任务队列batch-compute-environments.json:计算环境batch-jobs.json:任务(按 jobId 索引)
当存储后端为AccountAwareStorageBackend时,任务的读写按账号(accountId)隔离(getForAccount/putForAccount),多账号测试场景下互不串扰。
限制(Limitations)
当前实现的边界(与文档一致,均可在源码中印证):
- 无 IAM 强制:不校验任何 IAM 权限。
- 无 VPC/子网/安全组模拟。
- 无 AWS 忠实容量调度:不模拟容量、VCPU 分配。
- 无任务依赖(
dependsOn):提交即报错。 CancelJob与TerminateJob未实现:普通任务、数组子任务、MNP 节点均无法取消/终止——这是与普通任务相同的既有缺口,并非数组/MNP 引入的新问题。- EventBridge input transformers:走既有 EventBridge 目标输入路径,Batch 专属的 input-transformer 完全对等未实现。
- MNP 结束时机差异:AWS 在主节点退出时立即停止整个任务(含剩余节点);Floci 会等待每个节点容器都退出后才评定该 attempt,因此存活时间超过主节点的从节点会让任务保持
RUNNING更久。关闭这一差异需要BatchDockerRunner具备从节点自身运行循环外部停止其容器的能力(当前不存在)。
数组/MNP 表面的已知后续项(不影响SubmitJob/DescribeJobs/ListJobs的正确性,刻意延后):
submitFromEventBridge不转发规则目标BatchParameters中的arrayProperties/nodeOverrides(虽然ArrayProperties作为目标元数据被接受并回显):EventBridge 触发的 Batch 任务今天无法扇出数组或运行 MNP,只有直接SubmitJob可以。- CloudFormation 的
AWS::Batch::JobDefinition供给器只注册container类型,无法通过 CloudFormation 供给multinode任务定义。 ListJobs使用arrayJobId时忽略filters参数(仅jobStatus对子任务生效)——与 AWS 自身"filters 不适用于子任务"的说明一致,但代价是数组子任务无法按JOB_NAME/JOB_DEFINITION等过滤。ListJobs使用multiNodeJobId时无分页(maxResults/nextToken无效),始终在一次响应中返回全部节点。
实践建议
- 契约测试首选
immediate模式:无需 Docker 环境即可完成"注册定义→提交→描述/列出"全链路,毫秒级返回,BatchIntegrationTest.java 展示了这类测试的典型写法。 - 需要真实执行则切换
docker模式:任务会在独立容器中真实运行,借助注入的AWS_ENDPOINT_URL可访问 Floci 上其他模拟服务,适合端到端验证;记得为 Batch 容器规划 Docker 网络与镜像拉取策略。 - 用数组/MNP 覆盖并行场景:数组任务适合参数化扇出(2-10,000),MNP 适合多节点协同(主节点裁决);注意 EventBridge 触发的任务暂不支持这两者。
- 关注超时与重试:
timeout.attemptDurationSeconds最小 60 秒、retryStrategy.attempts最大 10,docker 模式超时任务以 137 退出且不重试。
参考文件索引
- 服务文档:docs/services/batch.md
- HTTP 端点层:BatchController.java
- 控制面核心逻辑:BatchService.java
- Docker 执行器:BatchDockerRunner.java
- 配置定义:EmulatorConfig.java
- 集成测试:BatchIntegrationTest.java、BatchServiceTest.java、BatchDockerRunnerTest.java
【免费下载链接】flociLight, fluffy, and always free - The AWS Local Emulator alternative项目地址: https://gitcode.com/gh_mirrors/fl/floci
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考