EC2上使用Sendgrid API v3出现ETIMEDOUT错误求助
NestJS + SendGrid 部署ECS后ETIMEDOUT问题排查
问题描述
在NestJS应用中使用@sendgrid/mail发送邮件,本地测试完全正常,但部署到ECS管理的EC2实例(带负载均衡)后,抛出ETIMEDOUT连接超时错误。
邮件服务代码
export class EmailService { constructor() { SendGrid.setApiKey(process.env.SENDGRID_API_KEY); } async sendMail(mail: SendGrid.MailDataRequired) { try { const transport = await SendGrid.send(mail); return transport; } catch (err) { console.error(JSON.stringify(err)); } } }
EC2上的错误日志
{ "message":"connect ETIMEDOUT 54.248.129.79:443", "name":"Error", "stack":"Error: connect ETIMEDOUT 54.248.129.79:443\n at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1494:16)", "config": { "transitional":{ "silentJSONParsing":true, "forcedJSONParsing":true, "clarifyTimeoutError":false }, "transformRequest":[null], "transformResponse":[null], "timeout":0, "xsrfCookieName":"XSRF-TOKEN", "xsrfHeaderName":"X-XSRF-TOKEN", "maxContentLength":null, "maxBodyLength":null, "headers":{ "Accept":"application/json", "Content-Type":"application/json", "User-Agent":"sendgrid/7.7.0;nodejs", "Authorization":"Bearer <token>", "Content-Length":22019 }, "url":"/v3/mail/send", "method":"post", "data": { ...data } } }
ECS任务定义(部分值已移除)
{ "taskDefinitionArn": "", "containerDefinitions": [ { "name": "", "image": "", "cpu": 0, "portMappings": [ { "name": "portfolio-backend-3000-tcp", "containerPort": 3000, "hostPort": 3000, "protocol": "tcp", "appProtocol": "http" } ], "essential": true, "environment": [ { "name": "AWS_REGION", "value": "ap-southeast-1" }, { "name": "JWT_EXPIRATION_TIME", "value": "86400" }, { "name": "PORT", "value": "3000" }, { "name": "AWS_BUCKET_NAME", "value": "" }, { "name": "AWS_SECRET_ACCESS_KEY", "value": "" }, { "name": "SENDGRID_API_KEY", "value": "" }, { "name": "DATABASE_URL", "value": "" }, { "name": "JWT_SECRET", "value": "" }, { "name": "AWS_ACCESS_KEY_ID", "value": "" } ], "environmentFiles": [], "mountPoints": [], "volumesFrom": [], "ulimits": [] } ], "family": "", "executionRoleArn": "", "networkMode": "awsvpc", "revision": 6, "volumes": [], "status": "ACTIVE", "requiresAttributes": [ { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18" }, { "name": "ecs.capability.task-eni" } ], "placementConstraints": [], "compatibilities": [ "EC2" ], "requiresCompatibilities": [ "EC2" ], "cpu": "512", "memory": "307", "runtimePlatform": { "cpuArchitecture": "X86_64", "operatingSystemFamily": "LINUX" }, "registeredAt": "", "registeredBy": "", "tags": [] }
已执行的排查操作
- 已配置负载均衡和EC2实例接受所有入站流量及IPv4出站流量
- 尝试改用axios替代
@sendgrid/mail,仍出现相同超时问题 - 在EC2实例内执行cURL请求SendGrid API可正常工作,
telnet 54.248.129.79 443也能正常连接
提问
请问是否缺少某些配置以在EC2上正常运行?
问题分析与解决方案
1. 重点排查ECS任务的网络权限(awsvpc模式)
你的任务使用awsvpc网络模式,这种模式下任务拥有独立的弹性网络接口(ENI),其出站流量受任务安全组和子网网络ACL控制,而非EC2实例本身的安全组:
- 检查任务关联的安全组,添加出站规则:允许到
0.0.0.0/0的443端口流量 - 检查任务所在子网的网络ACL,确认出站规则允许443端口的流量通过
2. 修复Node.js DNS解析问题
ECS容器内的DNS解析可能和本地环境存在差异,可强制指定公共DNS服务器:
// 在初始化SendGrid前添加 const dns = require('dns'); dns.setServers(['8.8.8.8', '8.8.4.4']); // 使用Google公共DNS
3. 检查容器内部网络连通性
虽然EC2实例能连通,但容器内部可能存在限制:
- 进入运行中的ECS容器,执行
telnet 54.248.129.79 443测试连通性 - 排查容器是否有自定义iptables规则或网络策略限制出站流量
4. 配置SendGrid客户端超时
当前代码中SendGrid客户端超时设为0(无限制),可设置合理超时时间避免无限等待:
async sendMail(mail: SendGrid.MailDataRequired) { try { // 设置30秒超时 const transport = await SendGrid.send(mail, { timeout: 30000 }); return transport; } catch (err) { console.error(JSON.stringify(err)); } }
5. 指定亚太区域SendGrid API端点
你的EC2位于ap-southeast-1区域,使用新加坡区域的API端点可减少网络延迟:
SendGrid.setApiKey(process.env.SENDGRID_API_KEY); SendGrid.setDefaultConfig({ baseUrl: 'https://api.sendgrid.sg/v3/' });
内容的提问来源于stack exchange,提问作者ahmadafiquddin
相关产品推荐
相关产品推荐

