Несколько недель назад я написал (Мониторинг активности OpenWhisk) о том, как вы можете отслеживать свою активность OpenWhisk. Одна из вещей, на которую я обратил внимание, это то, что было бы неплохо иметь систему предупреждений, в которой я мог бы указать, что если определенное действие начинает выполняться плохо, я могу получить предупреждение. Слово бедный довольно расплывчатое, но сегодня я работал над небольшой демонстрацией, которой хочу поделиться. Он использует сам OpenWhisk для мониторинга ваших действий OpenWhisk, но в целом меня больше беспокоит сбой моего кода или сбой сторонних API, чем сам OpenWhisk.
Я начал с создания базового действия, которое возвращало бы true, если бы действие срабатывало. Позвольте мне поделиться кодом, а затем я объясню, как он работает.
/*
For an action X
Taking all the activations I can get in one pull
Up from optional time Y
If the success rate is below Z% (default 100),
return true
else return false
action: action to check
from: ms since epoch to start from, defaults to forever
rate: numeric value 0-100, defaults to 100
max: total # of activations, defaults to MAX_LIMIT
*/
const openwhisk = require('openwhisk');
// last time i checked, this was 200
const MAX_LIMIT = 200;
function main(args) {
const ow = openwhisk();
if(!args.action) {
return {
error:'Action argument not specified.'
};
}
if(!args.from) {
args.from = 0;
}
if(!args.rate) {
args.rate = 100;
}
if(!args.max) {
args.max = MAX_LIMIT;
}
console.log(`Checking action ${args.action} starting at time ${args.from} and expecting a success rate of ${args.rate}%`);
let status = false;
return new Promise( (resolve, reject) => {
ow.activations.list({
name:args.action,
limit:args.max,
since:args.from,
docs:true
}).then((results) => {
//early out
if(results.length === 0) resolve({status:status, action:args.action});
let total = results.length;
let totalGood = results.reduce( (acc, val) => {
if(val.response.success) return ++acc;
return acc;
},0);
let successRate = Math.floor(totalGood/total*100);
console.log(`${total} activations, ${totalGood} good, for a rate of ${successRate}%`);
if(successRate < args.rate) status = true;
resolve({status:status, action:args.action});
}).catch(err => {
console.log('error', JSON.stringify(err));
reject({error:err});
});
});
}
Итак, действие ожидает следующие аргументы:
- Прежде всего, действия по проверке. Обратите внимание, что код использует активации для отслеживания работоспособности действий, и в последний раз, когда я проверял, была ошибка с получением активаций для действия в пакете. Очевидно, что если это все еще ошибка, она будет исправлена, но имейте это в виду при использовании этого кода.
- Аргумент
from
— это количество миллисекунд от эпохи, используемое в качестве фильтра для выборки данных. - Аргумент
max
— это общее количество предыдущих активаций для проверки. В общем, я ожидаю, что люди будут использовать это поверхfrom
, кроме того, значение, которое вы используете здесь, будет полностью зависеть от вашего использования. Так, например, если у меня есть действие, которое выполняется каждый час, и если я хочу проверять работоспособность каждый день в триггере CRON, я бы установил максимальное значение 24. - Аргумент
rate
представляет собой процентное значение, представляющее здоровое состояние. По умолчанию он равен 100%, что, вероятно, слишком много, но опять же, это зависит от вашего конкретного использования.
Я использую пакет OpenWhisk npm. Обратите внимание, что мне не нужно указывать информацию для аутентификации, так как я создаю ее экземпляр в самой функции main. Это означает, что он подберет значения аутентификации для учетной записи. Затем я получаю активации, используя фильтры, как описано выше. Обратите внимание, что существует недокументированное ограничение в 200 активаций. Вы можете получить больше (я документирую это здесь), но, поскольку вы контролируете, как часто выполняется эта проверка, вы можете указать расписание, которое гарантирует, что вы не столкнетесь с этой проблемой.
Как только у меня есть активация, я перебираю, подсчитываю хорошие, а затем использую немного математики, чтобы получить процент. Если у вас слишком низко, то мы ошибаемся. Я назвал это действие isBad
, и я знаю, что использовать подобное отрицание нехорошо. В этом случае истинный статус этого действия подразумевает, что у нас есть проблема, и это вроде как имеет смысл для меня, но я также мог бы представить это как isGood
и обратить результаты. Часть меня, «Чистый код», расстроилась из-за этого, но часть меня, которая «сделает все», сказала ей закрыть входную дверь.
Обратите внимание, что я возвращаю статус и запрошенное действие. Я мог бы также вернуть общие и общие хорошие значения.
Так что в теории… в основном это так. Человек, использующий это, объединит это с другим действием, чтобы обработать результат и что-то сделать. Итак, для моего первого примера я объединил его с простым действием электронной почты:
/*
requires:
email (will be used for to/from)
action (name)
sgkey (sendgrid key)
I send a hard coded message saying args.action has failed
Note - since we can't do conditional actions in sequences, this
action leaves early if status:false
*/
const helper = require('sendgrid').mail;
function main(args) {
if(!args.status) return {result:0};
let SG_KEY = args.SG_KEY;
let from_email = new helper.Email('[email protected]');
let to_email = new helper.Email(args.email);
let subject = 'Failure Rate Alert: '+args.action;
let mainTemplate = `
The action, ${args.action}, has fallen beneath its desired success rate.
Take action!
`;
let mailContent = new helper.Content('text/html', mainTemplate);
let mail = new helper.Mail(from_email, subject, to_email, mailContent);
let sg = require('sendgrid')(SG_KEY);
var request = sg.emptyRequest({
method: 'POST',
path: '/v3/mail/send',
body: mail.toJSON()
});
return new Promise((resolve, reject) => {
sg.API(request, function(error, response) {
if(error) {
console.log('error in sg', error.response.body);
reject({error:error.message})
} else {
resolve({result:1});
}
});
});
}
В этом действии я просто использую SendGrid для отправки электронной почты. Шаблон невероятно прост. А вот это при выполнении (опечатку с it/it's я исправил позже):
А потом, черт возьми, я написал действие Twilio, чтобы я мог отправить SMS:
/*
as args, needs:
accountSid
authToken
to
from
*/
const twilio = require('twilio');
function main(args) {
var client = new twilio(args.accountSid, args.authToken);
let body = `The action, ${args.action}, has fallen beneath its desired success rate. Take action!`;
return new Promise((resolve, reject) => {
client.messages.create({
body: body,
to: args.to,
from: args.from
})
.then((message) => {
resolve({result:1});
})
.catch(err => {
console.log(err);
reject({error:err});
});
});
}
Для этого требуется информация для аутентификации вашего приложения Twilio и номер для отправки, а также номер для отправки. А вот в действии:
И это в основном все. Чтобы сделать это живым, я бы настроил триггер CRON и выяснил, как я хочу получать уведомления, но, поскольку мой код идеален, мне не нужно об этом беспокоиться. Уот! Вы можете найти исходники всех трех действий здесь: https://github.com/cfjedimaster/Serverless-Examples/tree/master/alert
Первоначально опубликовано на сайте www.raymondcamden.com 27 июня 2017 г.