【免费下载链接】floci
Light, fluffy, and always free - The AWS Local Emulator alternative
本文以 Floci 开源项目(AWS Local Emulator alternative)的 SSM 服务文档 为核心,系统讲解其在本地完整模拟 AWS Systems Manager(SSM)的方式:从 JSON 1.1 协议接入、Parameter Store 全部受支持操作、公共 AMI 参数、Run Command 的两种执行路径(容器内直接执行与 SSM Agent 轮询),到存储与开关配置。读完本文,你将掌握如何在本地用 AWS CLI 与 SDK 无缝使用 SSM 参数管理与命令下发能力,并理解其与 EC2、IMDS 的联动边界。
协议接入:JSON 1.1 与统一端点
与 Floci 模拟的其他 AWS 服务一致,SSM 服务走 JSON 1.1 协议,所有请求都是POST到同一端点,通过X-Amz-Target头区分操作:
| 协议面 | X-Amz-Target 前缀 | 说明 |
|---|---|---|
| 公开 API(Parameter Store / Run Command 等) | AmazonSSM.* | 面向 AWS CLI 与 SDK 的标准操作 |
| Agent 内部协议(ec2messages) | AmazonSSMMessageDeliveryService.* | 供amazon-ssm-agent轮询与回传结果使用 |
- Endpoint:
POST http://localhost:4566/
从源码看,请求分发集中在 SsmJsonHandler.java 的handle(String action, JsonNode request, String region)方法——一个覆盖数十个操作的switch表达式;而 ec2messages 侧则由 Ec2MessagesJsonHandler.java 处理GetMessages、AcknowledgeMessage、SendReply、FailMessage、DeleteMessage与GetEndpoint。
Parameter Store:完整的参数生命周期
支持的 Actions
| Action | Description |
|---|---|
PutParameter | Create or update a parameter |
GetParameter | Get a single parameter by name |
GetParameters | Get multiple parameters by name |
GetParametersByPath | Get all parameters under a path prefix |
DeleteParameter | Delete a parameter |
DeleteParameters | Delete multiple parameters |
GetParameterHistory | List all versions of a parameter |
DescribeParameters | List parameters with optional filters |
LabelParameterVersion | Attach a label to a specific version |
AddTagsToResource | Tag a parameter |
ListTagsForResource | List tags on a parameter |
RemoveTagsFromResource | Remove tags from a parameter |
DescribePatchBaselines | List AWS-owned predefined patch baselines (filter byOWNER,OPERATING_SYSTEM,NAME_PREFIX) |
GetDefaultPatchBaseline | Get the default patch baseline id for an operating system |
版本化写入与保留命名空间(PutParameter)
putParameter的核心行为(SsmService.java)包括:
- 每次写入返回递增的
Version(已有参数version + 1,新参数从1开始),并为该参数追加一条历史记录; - 不加
Overwrite时重复写入同名参数返回ParameterAlreadyExists(400),与 AWS 行为一致; Overwrite与Tags不能同时使用,否则返回ValidationException;- 参数 ARN 通过
regionResolver.buildArn("ssm", region, "parameter" + name)生成,并记录LastModifiedDate; - 保留命名空间校验:
rejectReservedName(SsmService.java)拒绝以aws或ssm开头的参数名(大小写不敏感、有无前导/均拒绝),保证账户参数永远无法覆盖 AWS 公共参数;校验基于首个路径段,因此ssm-auto-stack-Param-ABC这类 CloudFormation 自动生成的名字仍可正常写入。
版本历史通过addHistory维护,超过maxParameterHistory上限时丢弃最旧的记录(SsmService.java),历史条数上限由配置FLOCI_SERVICES_SSM_MAX_PARAMETER_HISTORY控制(默认 5)。
查询与批量删除(Get/Delete)
GetParameter:找不到返回ParameterNotFound;查找时会先查账户存储,再回落到公共参数(见下文公共 AMI 参数一节);GetParameters:按名字批量读取,仅返回存在的参数,并把不存在的名字放入响应中的InvalidParameters数组(对应处理见 SsmJsonHandler.java);GetParametersByPath:路径查询会把path规范化为以/结尾;Recursive=true匹配路径前缀下的全部参数,非递归时仅匹配直接子级(路径段内不含/),实现见underPath(SsmService.java);DeleteParameter/DeleteParameters:删除参数同时清除其历史记录;批量删除响应同样携带DeletedParameters与InvalidParameters两个数组。
过滤、分页与标签
- DescribeParameters 过滤语义:多个过滤器之间按 AND 组合、单个过滤器内部按 OR 组合;支持的 Key 为
Name、Type、KeyId、Path、Tier、DataType以及tag:<key>前缀;Path的 Option 支持Recursive/OneLevel,Name额外支持Contains,其余 Key 支持Equals/BeginsWith。非法 Key 返回InvalidFilterKey、非法 Option 返回InvalidFilterOption、非法值返回InvalidFilterValue,而不是静默放宽结果(校验逻辑见 SsmService.java)。由于 Floci 不存储 Tier 与客户 KMS Key,所有参数按StandardTier、SecureString 按默认alias/aws/ssmKey 报告; - 分页:
DescribeParameters支持MaxResults(1–50)与NextToken,超出范围返回ValidationException(SsmJsonHandler.java);同时兼容已废弃的Filters请求形态(仅接受Name/Type/KeyId); - 标签:
AddTagsToResource/ListTagsForResource/RemoveTagsFromResource支持以参数名或其 ARN 作为ResourceId(normalizeResourceId会解析 ARN 中的parameter/资源段),标签在Overwrite更新时会被保留(SsmService.java); - 版本标签:
LabelParameterVersion把标签挂到指定版本的历史记录上,版本不存在返回ParameterVersionNotFound。
Patch Baselines(预定义只读数据)
DescribePatchBaselines与GetDefaultPatchBaseline返回的是 AWS 拥有的预定义补丁基线静态数据(非客户状态,仅驻留内存),覆盖 WINDOWS、AMAZON_LINUX、AMAZON_LINUX_2/2022/2023、UBUNTU、RHEL、SUSE、CENTOS、ORACLE_LINUX、DEBIAN、MACOS、RASPBIAN、ROCKY_LINUX、ALMA_LINUX 等 15 个操作系统,基线 ID 由名称确定性生成(pb-<17位十六进制>),见buildPredefinedBaselines(SsmService.java)。OWNER=Self不返回任何结果(因为不存在客户自有基线)。
公共 AMI 参数:开箱即用的/aws/service/ami-amazon-linux-latest/
AWS 在每个账户下都发布只读的 AMI ID 查询参数(/aws/service/ami-amazon-linux-latest/),Terraform 模块无需任何配置即可读取。Floci 从自己的 EC2 镜像目录(Ec2ImageCatalog.java,findByPublicParameterName)应答文档化的 Amazon Linux 2 与 Amazon Linux 2023 参数名,因此GetParameter、GetParameters、GetParametersByPath都能解析出目录中的 AMI ID。
与 AWS 行为一致,这些公共参数:
- 只读:不归属任何账户,因此不会出现在
DescribeParameters与GetParameterHistory中; - 只对目录中登记过的名字应答,
/aws/service/前缀下的其他名字仍返回ParameterNotFound; - 对祖先路径(如
/aws)做递归GetParametersByPath时同样会列出。
实现位于publicParameter(SsmService.java),其 ARN 形如arn:aws:ssm:{region}::parameter{name},LastModifiedDate取镜像目录中的创建时间。
aws ssm get-parameter --name /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64Parameter Types
所有 AWS 参数类型均被接受:String、StringList、SecureString。
注意:
SecureString参数在 Floci 中按原样存储,不做真实的 KMS 加密。类型会被保留并正确返回,但值在静态存储时并未加密。这一取舍可参考 SsmService.java 中参数模型仅保存明文值、未引入任何 KMS 交互的实现。
Run Command:两种执行路径
支持的 Actions
| Action | Description |
|---|---|
UpdateInstanceInformation | Register or update an SSM agent record for an instance. Does not create an EC2 instance or register the container with IMDS (see Run Command Execution) |
DescribeInstanceInformation | List registered SSM managed instances |
SendCommand | Create command invocations for target instances |
GetCommandInvocation | Return a command invocation result |
ListCommands | List command records |
ListCommandInvocations | List command invocation records |
CancelCommand | Cancel pending or in-progress command invocations |
路径一:对 Floci EC2 实例的直接执行
SendCommand支持AWS-RunShellScript文档。当目标是Floci 通过 EC2RunInstances启动的容器(即容器由 Floci 管理、且正在运行)时,Floci 会:
- 创建命令(
Command)与逐实例的调用记录(CommandInvocation),立即返回命令响应; - 通过守护线程池(
floci-ssm-direct-execution,见 SsmCommandService.java)异步在目标容器内执行脚本; - 调用方通过
GetCommandInvocation观察完成结果。
直接执行由 SsmDirectCommandExecutor.java 实现:
supports()判定两条条件:文档名为AWS-RunShellScript且实例是运行中的 Floci EC2 容器(SsmDirectCommandExecutor.java);- 执行方式为 Docker
exec:sh -c运行用户脚本,支持commands(多行合并)与workingDirectory参数,stdout/stderr 分别捕获(SsmDirectCommandExecutor.java); - 若容器内有
timeout命令,脚本会被timeout --kill-after=1s包裹,从而在容器内限制超过TimeoutSeconds的命令(见timeoutWrappedScript);宿主侧等待时间为TimeoutSeconds + 2秒; - 退出码
124/137/143视为超时,调用状态标记为TimedOut,StatusDetails为Execution Timed Out;非零退出码标记为Failed。
输出遵循 AWS 内联输出限制:stdout 保留前 24,000 字符、stderr 保留前 8,000 字符(常量MAX_STDOUT_CHARS/MAX_STDERR_CHARS,见 SsmCommandService.java)。stdout、stderr、响应码、开始时间、结束时间都会记录在调用记录上。
命令级状态由updateCommandStatus汇总:全部成功为Success;全部超时为TimedOut;全部失败为Failed;部分失败按 AWS 语义报告为Success(错误计数与超时计数仍单独记录)。由于Command对象在存储层是共享引用,状态写入使用synchronized(command)与CancelCommand互斥,避免并发完成时状态与状态详情错位(SsmCommandService.java)。
路径二:SSM Agent 轮询(回退流程)
如果目标不是Floci EC2 容器,或文档不支持直接执行,Floci 回退到 SSM Agent 轮询流程:
SendCommand把 ec2messages 负载入队(ConcurrentLinkedQueue,按instanceId分桶);- 调用在 Agent 调用
SendReply后才完成。
命令负载由buildCommandPayload构建并 Base64 编码,携带DocumentName、CommandId、Parameters以及按 schemaVersion 2.2 生成的AWS-RunShellScript(或AWS-RunPowerShellScript)文档内容,供 Agent 执行(SsmCommandService.java)。
Managed instance 注册独立于 EC2 与 IMDS
用
UpdateInstanceInformation注册容器 SSM Agent 只创建 managed instance 记录。它不会创建 EC2 实例、不会在容器内安装 link-local169.254.169.254代理、也不会把容器注册到 EC2 IMDS 服务器。因此只注册过 SSM 的容器没有可用的 IMDS 端点、也没有实例配置文件凭据,针对它的SendCommand走的是 Agent 轮询流程而非直接执行。要让一个容器既应答 IMDS 又能直接执行
SendCommand,应先通过 EC2RunInstances启动它,再注册同一个容器的 SSM Agent。当 Agent 从未被 Floci EC2 实例托管的容器注册时,Floci 会记录一条警告日志(SsmCommandService.java)。
命令取消与实例信息
CancelCommand:把 Pending/InProgress 的调用标记为Cancelled,并移除该实例尚未轮询的消息;命令级状态同步置为Cancelled(SsmCommandService.java);DescribeInstanceInformation:列出当前区域已注册的 managed instance(PingStatus=Online)。老版本 Agent 不发送InstanceId时,Floci 会生成mi-前缀的 ID;SendCommand校验:缺少DocumentName返回InvalidDocument,无InstanceIds返回InvalidInstanceId,TimeoutSeconds默认 3600 且不得小于 30(返回ValidationException)。
ec2messages Agent 协议
| Action | Description |
|---|---|
GetMessages | Agent polls for pending command messages |
AcknowledgeMessage | Agent acknowledges receipt of a command message |
SendReply | Agent reports command output and status |
协议流程(Ec2MessagesJsonHandler.java + SsmCommandService.java):
GetMessages:Agent 以Destination(instanceId)轮询待处理消息,消息被取出并登记到messageIndex用于回关联;VisibilityTimeoutInSeconds默认 30 秒;AcknowledgeMessage:把对应调用从Pending推进到InProgress并记录执行开始时间;SendReply:Agent 回传 Base64 编码的负载,Floci 解析runtimeStatus/pluginResults中的status、returnCode、standardOutput、standardError,按 24,000/8,000 字符上限截断后写入调用记录,并触发命令级状态汇总;未知messageId会记录警告;- 另有
FailMessage(Agent 报告处理失败)与DeleteMessage(丢弃消息)两个扩展操作;GetEndpoint返回config.effectiveBaseUrl()作为服务端点。
Configuration 配置项
| Variable | Default | Description |
|---|---|---|
FLOCI_SERVICES_SSM_ENABLED | true | Enable or disable the service |
FLOCI_SERVICES_SSM_MAX_PARAMETER_HISTORY | 5 | Number of parameter versions retained per parameter |
FLOCI_STORAGE_SERVICES_SSM_MODE | (global default) | Storage mode override for SSM (memory,persistent,hybrid,wal) |
FLOCI_STORAGE_SERVICES_SSM_FLUSH_INTERVAL_MS | 5000 | Flush interval forhybrid/walstorage modes (milliseconds) |
配置定义可追溯至 EmulatorConfig.java:
- 服务开关与历史条数:
SsmServiceConfig.enabled(@WithDefault("true"))与SsmServiceConfig.maxParameterHistory(@WithDefault("5")),位于 EmulatorConfig.java; - 存储覆盖:
SsmStorageConfig.mode(可选,继承全局)与flushIntervalMs(默认 5000),位于 EmulatorConfig.java。
关于存储模式,参考 storage.md 中的四种后端:memory(最快、重启即失)、persistent(每次变更同步写盘)、hybrid(内存读 + 异步刷盘,适合日常本地开发)、wal(追加式预写日志 + 压缩,适合高写入)。注意:代码中@WithDefault的全局存储模式是hybrid,但发布的 Docker 镜像在application.yml中固定为memory,因此直接运行官方镜像时默认是memory,除非显式设置FLOCI_STORAGE_MODE。SSM 的持久化文件包括ssm-parameters.json、ssm-history.json、ssm-documents.json、ssm-instances.json、ssm-commands.json、ssm-invocations.json等(由SsmService/SsmCommandService通过StorageFactory创建)。
快速上手示例
以下示例假定 Floci 已在localhost:4566运行,AWS_ENDPOINT_URL指向该端点:
export AWS_ENDPOINT_URL=http://localhost:4566 # Store parameters aws ssm put-parameter --endpoint-url $AWS_ENDPOINT_URL \ --name /app/db/host --value "localhost" --type String aws ssm put-parameter --endpoint-url $AWS_ENDPOINT_URL \ --name /app/db/password --value "secret" --type SecureString # Retrieve aws ssm get-parameter --endpoint-url $AWS_ENDPOINT_URL \ --name /app/db/host aws ssm get-parameters-by-path --endpoint-url $AWS_ENDPOINT_URL \ --path /app/ --recursive # Delete aws ssm delete-parameter --endpoint-url $AWS_ENDPOINT_URL \ --name /app/db/host # Run a shell command on a Floci EC2 instance aws ssm send-command --endpoint-url $AWS_ENDPOINT_URL \ --instance-ids i-0123456789abcdef0 \ --document-name AWS-RunShellScript \ --parameters commands='["echo hello"]'需要留意的是:直接执行要求--instance-ids指向由 FlociRunInstances启动的实例(其实例记录关联了运行中的 Docker 容器);对仅注册了 SSM Agent 的容器,命令会进入 Agent 轮询流程,需要容器内运行amazon-ssm-agent并轮询 ec2messages 才能完成。
验证与测试依据
- 单元/集成测试集中在 src/test/java/io/github/hectorvent/floci/services/ssm/:
SsmIntegrationTest覆盖参数增删改查、保留前缀拒绝(putParameterRejectsReservedPrefixes)、公共 AMI 参数解析(publicAmiParametersResolveWithoutSetup)、过滤与分页(describeParametersAppliesParameterFilters、describeParametersPagesWithMaxResultsAndNextToken)、文档共享与关联等;SsmSendCommandIntegrationTest、SsmDirectCommandExecutorTest、SsmCommandServiceDirectExecutionTest覆盖直接执行路径与超时/失败语义; - AWS CLI 兼容性测试见 compatibility-tests/sdk-test-awscli/test/ssm.bats,覆盖 put/get/get-parameters-by-path/标签/覆盖写/删除等场景;
- SSM 与 IMDS 的联动边界说明见 ec2.md。
Floci 的 SSM 实现覆盖了 Parameter Store 全生命周期、公共 AMI 参数、Run Command 的双路径执行与 ec2messages Agent 协议,足以支撑本地开发、CI 测试以及 Terraform/CloudFormation 等 IaC 工具链对 SSM 的常规依赖;唯一需要开发者知悉的语义差异是SecureString不加密与命令执行必须经由 Floci EC2 容器(或真实 Agent 轮询)这两个本地模拟边界。
【免费下载链接】floci
Light, fluffy, and always free - The AWS Local Emulator alternative
相关推荐
Floci CloudWatch 服务全解析:CloudWatch Logs 与 Metrics 的本地模拟实战指南
Floci CloudWatch 服务全解析:CloudWatch Logs 与 Metrics 的本地模拟实战指南 Floci 是开源的 AWS 本地模拟器(
RustDesk 跨平台部署实战:三平台装好调通一篇搞定
RustDesk 跨平台部署实战:三平台装好调通一篇搞定 RustDesk 是一款可以自建的开源远程桌面工具。按本文走一遍,你可在 Windows、macOS
External Secrets Operator 集成 AWS SSM Parameter Store 完整实战指南
External Secrets Operator 集成 AWS SSM Parameter Store 完整实战指南 本指南围绕 External Secre
云原生运维
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考