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

Node.js Serverless API本地正常,部署到AWS Lambda后无法启停EC2实例

问题:AWS Lambda部署后未执行EC2实例启停操作(本地运行正常)

场景描述

我用Node.js编写了一个Serverless API,通过POST接口传入以下JSON来启停EC2实例:

{"action" : "stop","instance_id" : "i-xxxxxxxxxxxxxxxx"}

本地使用sls offline运行时,能正常完成EC2实例的启停操作,但部署到AWS Lambda后,函数返回200成功响应,却实际没有执行EC2的启停动作,也没有任何报错日志。

问题代码

"use strict";

require("dotenv").config();

// load the SDK for JavaScript
var AWS = require("aws-sdk");
// Set the region
AWS.config.update({ region: "us-west-2" });

// Create EC2 service object
var ec2 = new AWS.EC2({ apiVersion: "2016-11-15" });

module.exports.trigger = async (event) => {
  const body = JSON.parse(event.body);

  let id = body.instance_id;
  let action = body.action;

  var params = {
    InstanceIds: [id],
    DryRun: true,
  };

  if (action.toUpperCase() === "START") {
    // Call EC2 to start the selected instances
    ec2.startInstances(params, function (err, data) {
      if (err && err.code === "DryRunOperation") {
        console.log("inside start");
        params.DryRun = false;
        ec2.startInstances(params, function (err, data) {
          if (err) {
            console.log("Error", err);
            // issue = err;
          } else if (data) {
            console.log("Success", data.StartingInstances);
            // out = data.StartingInstances;
          }
        });
      } else {
        console.log("You don't have permission to start instances.");
      }
    });
  } else if (action.toUpperCase() === "STOP") {
    // Call EC2 to stop the selected instances
    ec2.stopInstances(params, function (err, data) {
      if (err && err.code === "DryRunOperation") {
        params.DryRun = false;
        ec2.stopInstances(params, function (err, data) {
          if (err) {
            console.log("Error", err);
            // issue = err;
          } else if (data) {
            console.log("Success", data.StoppingInstances);
            // out = data.StoppingInstances;
          }
        });
      } else {
        console.log("You don't have permission to stop instances", err);
      }
    });
  }

  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        message: "Go Serverless v3.0! Your function executed successfully!",
        input: event,
      },
      null,
      2
    ),
  };
};

问题原因

核心矛盾在于Lambda的执行模型与回调式异步代码的兼容性:

  • 函数标记为async,但内部使用了AWS SDK的回调式API(ec2.startInstances(params, callback))
  • Lambda会在执行到return语句后立即终止函数进程,不会等待回调函数中的代码执行
  • 本地运行时,Node.js进程不会自动退出,所以回调代码能正常执行;但Lambda是事件驱动的无服务器环境,函数返回后会立刻销毁执行上下文,导致回调里的EC2操作根本没机会运行

解决方案

改用AWS SDK的Promise版本配合async/await,确保Lambda等待所有异步操作完成后再返回:

修改后的代码

"use strict";

require("dotenv").config();

const AWS = require("aws-sdk");
AWS.config.update({ region: "us-west-2" });

const ec2 = new AWS.EC2({ apiVersion: "2016-11-15" });

module.exports.trigger = async (event) => {
  try {
    const body = JSON.parse(event.body);
    const { instance_id: id, action } = body;
    const upperAction = action.toUpperCase();

    // 校验action合法性
    if (!["START", "STOP"].includes(upperAction)) {
      return {
        statusCode: 400,
        body: JSON.stringify({ message: "无效操作,仅支持START/STOP" })
      };
    }

    // 先执行DryRun验证权限
    const dryRunParams = { InstanceIds: [id], DryRun: true };
    try {
      if (upperAction === "START") {
        await ec2.startInstances(dryRunParams).promise();
      } else {
        await ec2.stopInstances(dryRunParams).promise();
      }
    } catch (dryRunErr) {
      // DryRunOperation错误表示权限验证通过,执行实际操作
      if (dryRunErr.code === "DryRunOperation") {
        const actualParams = { InstanceIds: [id], DryRun: false };
        let result;

        if (upperAction === "START") {
          result = await ec2.startInstances(actualParams).promise();
          console.log(`实例${id}启动成功`, result.StartingInstances);
        } else {
          result = await ec2.stopInstances(actualParams).promise();
          console.log(`实例${id}停止成功`, result.StoppingInstances);
        }

        return {
          statusCode: 200,
          body: JSON.stringify({
            message: `EC2实例${upperAction}操作执行成功`,
            result: result,
            input: event
          }, null, 2)
        };
      }
      // 捕获其他权限或操作错误
      else if (dryRunErr.code === "UnauthorizedOperation") {
        console.error(`权限不足:无法${upperAction}实例${id}`, dryRunErr);
        return {
          statusCode: 403,
          body: JSON.stringify({ message: "无权限执行该操作", error: dryRunErr.message })
        };
      } else {
        throw dryRunErr;
      }
    }

    // 如果DryRun没有抛出错误(理论上DryRun应该会抛出DryRunOperation),说明异常情况
    return {
      statusCode: 500,
      body: JSON.stringify({ message: "DryRun验证异常,请检查配置" })
    };

  } catch (err) {
    console.error("操作失败:", err);
    return {
      statusCode: 500,
      body: JSON.stringify({ message: "服务器内部错误", error: err.message })
    };
  }
};

额外检查项

  1. Lambda IAM角色权限:确保Lambda的执行角色拥有ec2:StartInstances和ec2:StopInstances权限,且资源范围包含目标EC2实例(例如添加权限策略:
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": ["ec2:StartInstances", "ec2:StopInstances"],
          "Resource": "arn:aws:ec2:us-west-2:你的账户ID:instance/i-xxxxxxxxxxxxxxxx"
        }
      ]
    }
    
  2. 日志排查:部署后查看CloudWatch Logs,确认是否有隐藏的错误日志(之前的回调代码可能因为进程终止没来得及输出日志)

内容的提问来源于stack exchange,提问作者coderByChance

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 14:45:32