Вот моя установка:
Лямбда-функция Python 3.6, который я хочу предварительно подогреть на определенном уровне параллелизма (скажем, 10). Инициализация лямбды достаточно болезненна, поэтому я не хочу навязывать эту стоимость посетителям наугад. Я называю эти лямбды "рабочими"
Узловая лямбда-функция, который запускается каждые 5 минут, чтобы попытаться предварительно разогреть 10 экземпляров. Он использует тип вызова Event для 9 из них и RequestResponse для 1. В любой момент времени выполняется только одна или ноль из этой лямбды. Я называю это «теплее».
Я следовал рекомендациям [https://www.jeremydaly.com/lambda-warmer-optimize-aws-lambda-function-cold-starts/]], а именно:
Вот проблема: это отлично работает в течение нескольких минут. Затем, просматривая журналы, я начинаю получать тайм-ауты от лямбда-вызовов моих рабочих. Таймауты быстро принимают на себя все вызовы, которые пытается запустить обогреватель.
Теперь рабочие лямбды больше не прогреваются. Но грелка продолжает попытки в расписании cron событий Cloudwatch, страдая 100% тайм-аутом. Наконец, Lambda вообще перестает пытаться запускать мои рабочие лямбды. Похоже, что какой-то аспект состояния Lambda зашифрован. Единственный способ исправить это - повторно развернуть лямбду. Это дает мне еще один час работы с предварительно подогретыми лямбдами.
Вопросов:
Вот лямбда-код разминки (узел):
// warmer
"use strict";
/** Generated by Serverless WarmUP Plugin at ${new Date().toISOString()} */
const aws = require("aws-sdk");
aws.config.region = "${this.options.region}";
const lambda = new aws.Lambda({httpOptions: {timeout: 60000}});
const functionNames = ${JSON.stringify(functionNames)};
const delay = ms => new Promise(res => setTimeout(res, ms))
const concurrency = 10;
module.exports.warmUp = async (event, context, callback) => {
console.info("Warm Up Start");
const invokes = await Promise.all(functionNames.map(async (functionName) => {
let invocations = [];
try {
for(let i=1;i <= concurrency;i++){
let params = {
FunctionName: functionName,
InvocationType: (i===concurrency)?'RequestResponse': 'Event',
LogType: 'None',
Qualifier: process.env.SERVERLESS_ALIAS || "$LATEST",
Payload: JSON.stringify({
source: 'serverless-plugin-warmup',
'__WARMER_INVOCATION__': i,
'__WARMER_CONCURRENCY__': concurrency,
'__WARMER_REQUESTED__': new Date().toISOString(),
})
};
invocations.push(lambda.invoke(params).promise())
}
return await delay(75).then(Promise.all(invocations.map(p => p.catch(e => e)))
.then(results => console.info('results', results))
.catch(e => {
console.info(e);
return e;
}
))
} catch (e) {
console.info(\`Warm Up Invoke Error: \${functionName}\`, e);
return false;
}
}));
console.info(\`Warm Up Finished\`);
}
А вот и рабочая лямбда (Python):
source = event.get('source')
if source == 'serverless-plugin-warmup':
time.sleep(0.05)
print(event)
return lambda_gateway_response(200, {"status": "lambda warmup"})
В журналах Cloudwatch грелки похоже: "{TimeoutError: Время ожидания соединения истекло через 300000 мсек в ClientRequest. <anonymous> (/var/runtime/node_modules/aws-sdk/lib/http/node.js:83:34)"). Это продолжается до тех пор, пока я повторно не разверну функцию. Потом снова заработает около часа.
Итак, тайм-аут связи с API службы Lambda. Первоначальное впечатление: вы используете функцию Lambda внутри VPC, но две подсети не идентичны ... одна из подсетей, связанных с функцией Lambda, неправильно настроена для использования шлюза NAT в качестве маршрута по умолчанию.
Хорошая догадка, но мы не используем VPC :-) Кроме того, таймауты полностью отсутствуют около часа, затем они начинают возникать. После этого это похоже на болезнь голландского вяза, я должен рассматривать рабочую лямбду как мертвую и повторно развертывать.
Разобрался, спасибо @ Michael-sqlbot!
Это была более теплая (Node) лямбда, выходящая из строя, хотя все журналы указывали на рабочие (Python) лямбды. После установки context.callbackWaitsForEmptyEventLoop = false проблема исчезла.