AWS ECS部署Rails应用:环境变量合并引发文件路径过长错误
问题描述
在AWS ECS上部署Ruby on Rails应用,用AWS Secrets Manager管理环境变量时出现异常:调用ENV["RAILS_ENV"]返回的不是预期的production字符串,而是包含所有Secret变量的JSON哈希(格式如{"RAILS_ENV": "production", "JWT_SECRET":"xxx", ...}),启动应用时触发以下错误:
File name too long @ rb_sysopen - /app/.env.
{
"RAILS_ENV": "production",
"JWT_SECRET":"xxx",
...
}
.local (Errno::ENAMETOOLONG)
推测是某环境变量被用于配置文件访问时,因内容过长导致路径解析错误。核心疑问:为何环境变量会被合并成一个哈希而非单独设置?
原因分析
这是因为在ECS配置Secrets Manager时,错误地将整个Secret的JSON字符串赋值给了单个环境变量(此处为RAILS_ENV),而非将Secret中的每个键值对映射为独立的环境变量。ECS默认不会自动解析Secret的JSON内容,若配置时选择了「Set as single value」模式,而非「Secrets」模式下的逐个键映射,就会出现所有Secret内容被塞进一个环境变量的情况。
现有解决方案
在config/boot.rb中,ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)代码之前添加以下代码,手动解析JSON哈希并拆分到对应环境变量:
require 'aws-sdk-secretsmanager' if ENV['RAILS_ENV'] && ENV['RAILS_ENV'] != "development" secret_hash = JSON.parse(ENV['RAILS_ENV']) ENV['JWT_SECRET'] = secret_hash['JWT_SECRET'] ENV['RAILS_ENV'] = secret_hash['RAILS_ENV'] # 按需添加其他需要的环境变量 end
该方案可临时解决问题,但需要手动维护变量映射,扩展性较差。
更优方案推荐
方案1:ECS任务定义中直接映射Secret键到环境变量
在ECS任务定义的容器配置里,选择「Secrets」而非普通的「Environment variables」模式,将Secret中的每个键(如RAILS_ENV、JWT_SECRET)分别映射为独立的环境变量:
- 对每个需要的变量,设置
Name为环境变量名,ValueFrom为Secret的ARN加上键路径,格式为arn:aws:secretsmanager:<region>:<account-id>:secret:<secret-name>:<key>:: - 示例:
Name: RAILS_ENV,ValueFrom: arn:aws:secretsmanager:us-east-1:1234567890:secret:my-rails-secrets:RAILS_ENV::
这种方式下ECS会自动将Secret中的每个键值对解析为单独的环境变量,无需修改应用代码。
方案2:使用ECS启动脚本自动注入环境变量
在ECS任务的容器启动命令中添加预处理脚本,拉取Secret并自动解析为环境变量:
- 创建shell脚本(如
load-secrets.sh):
#!/bin/bash # 拉取Secret内容 SECRET=$(aws secretsmanager get-secret-value --secret-id my-rails-secrets --query SecretString --output text) # 将JSON键值对转为export命令并执行 echo "$SECRET" | jq -r 'to_entries|map("export \(.key)=\(.value|tostring)")|.[]' > /tmp/secrets.sh source /tmp/secrets.sh # 启动Rails应用 exec "$@"
- 在ECS任务定义中,将容器启动命令设置为:
["/bin/bash", "/app/load-secrets.sh", "rails", "server"] - 确保容器拥有Secrets Manager的
secretsmanager:GetSecretValue权限
这种方式无需修改Rails代码,且能自动同步Secret中的所有变量,适合变量较多的场景。
内容的提问来源于stack exchange,提问作者Louis Daisomont

