如何在AWS ECS Fargate的CloudFormation中禁用Spring Boot Data MongoDB重试写入
问题背景
通过CloudFormation将Docker化的Spring Boot应用部署到AWS ECS Fargate,使用Spring Data MongoDB连接AWS DocumentDB,所有资源均通过模板自动化部署。最初的容器定义通过环境变量注入数据库端点、端口,从Secrets Manager获取用户名和密码:
Environment: - Name: SPRING_DATA_MONGODB_HOST Value: !GetAtt DbCluster.Endpoint - Name: SPRING_DATA_MONGODB_PORT Value: !GetAtt DbCluster.Port Secrets: - Name: SPRING_DATA_MONGODB_USERNAME ValueFrom: !Sub "${DbCredentials}:username::" - Name: SPRING_DATA_MONGODB_PASSWORD ValueFrom: !Sub "${DbCredentials}:password::"
应用启动时出现以下错误:
com.mongodb.MongoCommandException: Command failed with error 301: 'Retryable writes are not supported' on server my-db-cluster.cluster-xxxxxxxxxxxx.us-east-1.docdb.amazonaws.com:27017. The full response is {"ok": 0.0, "code": 301, "errmsg": "Retryable writes are not supported", "operationTime": {"$timestamp": {"t": xxxxxxxxxx, "i": 1}}}
原因是DocumentDB不支持重试写入,官方要求通过完整的SPRING_DATA_MONGODB_URI连接字符串设置retryWrites=False,格式如下:
mongodb://<username>:<password>@my-db-cluster.cluster-xxxxxxxxxxxx.us-east-1.docdb.amazonaws.com:27017/database?retryWrites=False
但直接在CloudFormation中定义URI会覆盖原有的用户名/密码环境变量,因此需要解决如何将Secrets Manager中的凭证注入到URI,或通过其他方式禁用重试写入。
解决方案
方案1:直接通过Secrets Manager注入完整连接URI
将包含用户名、密码、retryWrites=False的完整连接字符串存储到Secrets Manager中,然后在容器定义中直接引用该Secret作为SPRING_DATA_MONGODB_URI:
Secrets: - Name: SPRING_DATA_MONGODB_URI ValueFrom: !Sub "${DbUriSecret}::"
注意:Secret中的URI需严格遵循格式:mongodb://<username>:<password>@<cluster-endpoint>:<port>/<database>?retryWrites=False
方案2:容器启动脚本动态拼接URI
由于CloudFormation无法直接将Secrets Manager的单个字段嵌入到环境变量字符串中,可通过容器启动命令动态拼接URI:
- 保留原有环境变量和Secrets注入配置:
Environment: - Name: SPRING_DATA_MONGODB_HOST Value: !GetAtt DbCluster.Endpoint - Name: SPRING_DATA_MONGODB_PORT Value: !GetAtt DbCluster.Port Secrets: - Name: SPRING_DATA_MONGODB_USERNAME ValueFrom: !Sub "${DbCredentials}:username::" - Name: SPRING_DATA_MONGODB_PASSWORD ValueFrom: !Sub "${DbCredentials}:password::"
- 修改容器
Command字段,用Shell脚本拼接URI并启动应用:
Command: - "/bin/sh" - "-c" - > export SPRING_DATA_MONGODB_URI="mongodb://${SPRING_DATA_MONGODB_USERNAME}:${SPRING_DATA_MONGODB_PASSWORD}@${SPRING_DATA_MONGODB_HOST}:${SPRING_DATA_MONGODB_PORT}/?retryWrites=False" && java -jar your-app.jar
启动时会自动将Secrets注入的凭证与端点信息拼接成符合要求的URI。
方案3:通过Spring Boot配置直接禁用重试写入(推荐)
无需修改连接方式,直接通过Spring Boot配置禁用重试写入,保留原有凭证注入方式:
- 方式一:在应用的
application.properties或application.yml中添加配置:
spring.data.mongodb.retry-writes=false
- 方式二:通过环境变量注入该配置(无需修改应用代码):
Environment: - Name: SPRING_DATA_MONGODB_HOST Value: !GetAtt DbCluster.Endpoint - Name: SPRING_DATA_MONGODB_PORT Value: !GetAtt DbCluster.Port - Name: SPRING_DATA_MONGODB_RETRY_WRITES Value: "false" Secrets: - Name: SPRING_DATA_MONGODB_USERNAME ValueFrom: !Sub "${DbCredentials}:username::" - Name: SPRING_DATA_MONGODB_PASSWORD ValueFrom: !Sub "${DbCredentials}:password::"
这种方式最简洁,完全兼容原有部署模式,无需调整URI或启动脚本。
内容的提问来源于stack exchange,提问作者Garret Wilson

