You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 16:27:01