如何在AWS Lambda函数内部进行外部API调用

20

我遇到了一个问题,跟Lambda函数有关,它因某种原因超时了。

我尝试添加一些console.log来理解发生了什么事情,直到外部http请求之前一切都运行正常。

我使用npm模块请求(request)。

我可以在请求之前看到日志,但是回调函数里面的那个从未出现过,就好像端点没有响应一样。

我错过了什么吗?谢谢提前!

var mysql = require('mysql'); 
var request = require('request');
var config = require('./config.json'); 

var pool = mysql.createPool({
  host: config.dbhost,
  user: config.dbuser,
  password: config.dbpassword,
  database: config.dbname
});

var openWeatherOptions = (device) => {
    let domain = 'https://api.openweathermap.org/data/2.5/';
    let apiEndpoint = 'weather?';
    let params = `lat=${device.lat}&lon=${device.lng}&APPID=${process.env.WEATHER_APP_ID}&units=metric`;
    let url = [domain, apiEndpoint, params].join('').toString();
    return {
      url: url,
      method: 'GET',
      headers: { 'Content-Type': 'application/json'},
    };
};

exports.handler = (event, context, callback) => {
    context.callbackWaitsForEmptyEventLoop = false;
    // Connect to DB
    console.log('fallback connecting to DB')
    pool.getConnection( (errOnConnection, connection) => {
        if(errOnConnection) {
            callback({"err": errOnConnection });
        }
        console.log('fallback connected to DB')
        // Retrieve device position
        let queryOne = 'SELECT lat, lng FROM T03_DevicesPosition WHERE deviceCode = (?);';
        console.log('fallback retrieving device position')
        connection.query( queryOne, [event.device], (errOnQ1, results, fields) => { 
            if(errOnQ1) {
                connection.release();
                callback({"err": errOnQ1 });
            }
            console.log('fallback device position retrieved')

            // Call openweather 
            let device = results[0];
            let options = openWeatherOptions(device);
            console.log('fallback calling openWeather with following data: ', device, options);

            request( options, (errOpenWeather, response, body) => {
                console.log('fallback openweather response received');
                if(errOpenWeather || (response.statusCode !== 200 && response.statusCode !== 201) ) {
                    connection.release();
                    callback({"err": errOpenWeather });
                }
                let meteo = JSON.parse(body).main;
                meteo.date = new Date(event.time*1000);
                meteo.pressure = Math.floor( meteo.pressure );
                console.log('fallback storing data', meteo);
                let query = `INSERT INTO T02_DevicesTransmissions (deviceCode, transmissionDate, temperature, humidity, pressure, unixDate, rawData) VALUES ( ?, ?, ?, ?, ?, ?, ?);`;
                let queryValues = [ event.device, meteo.date, meteo.temp, meteo.humidity, meteo.pressure, event.time, 'fallback']; 
                connection.query( query, queryValues, (errInsert, results, fields) => { 
                    connection.release();
                    console.log('fallback completed with', errInsert ? '' : 'out');
                    if (errInsert) callback({"err": errInsert });
                    else callback();
                });
            });

        });
    });
}

1
这是有关此事的AWS说明:https://aws.amazon.com/premiumsupport/knowledge-center/internet-access-lambda-function/ - Michael Hays
4个回答

40

这是一个老问题,但我花了几个小时才想明白如何正确设置,希望这能帮助其他人节省时间 :)

如果你的Lambda函数在VPC中,那么它们需要通过NAT网关才能访问外部服务。

一种实现方法是:

  • 配置你的Lambda函数使用一个(或多个)特定子网(Lambda控制台会建议你将至少2个位于不同可用区的子网与你的Lambda函数关联起来以确保可用性)
  • 不同的子网中创建一个NAT网关
  • 将与Lambda函数子网关联的路由表发送所有出站流量(0.0.0.0/0)到你创建的NAT网关上(你需要在target字段中选择NAT)
  • 将NAT子网中的路由表发送所有出站流量到Internet网关

这将通过NAT和Internet网关正确地路由Lambda函数产生的流量,从而使它们可以访问外部服务。

请注意,如果您的Lambda函数同时使用RDS,并且您想要从外部连接到RDS(例如从本地计算机管理数据库),则不能将RDS实例放在Lambda函数的子网中,否则您将无法从外部连接到它(NAT仅支持出站)。在这种情况下,您应该确保您的RDS实例关联到一个可访问的子网,例如NAT所在的子网(即向Internet Gateway发送出站流量的那个子网)。

1
是的,那就是正确的答案。我不记得我是怎么理解它的,但我不会忘记阅读了许多文档的艰辛。此外,我记得向 Lambda 配置提供一个或多个子网很重要(无法记住它们是否需要是私有的、公共的或没有区别)。 - Robdll
1
是的,AWS网络需要一些时间来适应...您确实需要在其VPC配置中将子网与lambda关联起来(AWS建议至少在不同可用区域中使用2个,并且每次实例化lambda时选择一个)。重要的是这些子网的路由表需要将出站流量发送到NAT(NAT本身需要具有Internet访问权限)。 - Jules Olléon
1
非常感谢!我已经花了6个多小时在谷歌搜索和尝试每一种可能的nodejs http请求代码的变化,此时大家可以想象我的抓狂程度。但是这样做却成功了并拯救了我。感谢您的记录。 - phonicx
@LukasLiesis 我同意AWS在这方面非常好。但我特别想知道如何通过VPC peering设置外部服务。 - Patryk Szylin
@pejno 请查看 https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html,其中有一个包含所有信息的PDF:https://docs.aws.amazon.com/vpc/latest/peering/vpc-pg.pdf,所有VPC文档都在这里:https://docs.aws.amazon.com/vpc/index.html。如果您刚开始学习,请建议从YouTube上观看关于网络的re:invent视频以获得概述。 - Lukas Liesis
显示剩余3条评论

4

您没有设置VPC,对吗?如果有,请检查是否已将NAT网关附加到私有子网中。


我有一个VPC,并且在上面运行了几个Lambda函数。然而,这是唯一与外部通信的函数。 - Robdll
1
我认为你的答案指引了我正确的方向,我正在阅读这篇文章:https://aws.amazon.com/premiumsupport/knowledge-center/internet-access-lambda-function/。 - Robdll

2

0
Jules Olléon说得很好。记得将Lambda VPC的子网设置为私有,否则它将无法在公共子网中工作。
您可以在AWS支持此处中详细了解更多信息。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接