Статьи

Внутри Lambda Runtime: заглянуть в безсерверное логово

Вы когда-нибудь задумывались, каково это внутри лямбды? Хватит удивляться. Давай выясним.

С момента своего появления в 2014 году лямбда-функции AWS стали горячей темой, открывающей новые летописи в области безсерверных вычислений . Удовольствия без сохранения состояния, без платы за обслуживание и исполнение буквально меняют — если не искоренивают — самые корни парадигмы облачных вычислений. Пока в игру вступают другие игроки, такие как Google и MS Azure, AWS пока что является абсолютным победителем.

Хорошо, проповедуя в стороне, как это действительно выглядит внутри лямбда-функции?

Согласно AWS, лямбды управляются контейнерными технологиями ; если быть точным, AWS EC2 Container Service (ECS) . Следовательно, в этот момент лямбда — это просто контейнер Docker с ограниченным доступом извне. Однако код функции, который мы запускаем внутри контейнера, имеет практически неограниченный доступ к нему, за исключением привилегий root, включая файловую систему, встроенные и установленные команды и инструменты CLI, системные метаданные и статистику, журналы и многое другое. Не очень полезно для обычного лямбда-автора, но может быть и так, если вы собираетесь идти по колено в вещах уровня ОС.

Очевидно, что самый простой способ изучить все эти предложения на уровне ОС — это иметь доступ к командной оболочке CLI (оболочки). К сожалению, в настоящее время это невозможно; тем не менее, комбинируя безумно простой синтаксис, предоставляемый средой выполнения NodeJS, и тем фактом, что лямбды имеют время поддержки нескольких минут , мы можем легко написать десятистрочную лямбду, которая может эмулировать оболочку. Хотя настоящая «сессия» не может быть установлена ​​таким образом (например, вы не можете запустить top для представления обновления в реальном времени), вы можете многократно запускать серию команд, как будто вы взаимодействуете с пользовательской консолью.

01
02
03
04
05
06
07
08
09
10
let {exec} = require('child_process');
 
exports.handle = (event, context, callback) => {
  console.log(event);
  exec(event.cmd, (err, stdout, stderr) => {
    console.log(stdout);
    if (err) console.log(stderr);
    callback(undefined, {statusCode: 200});
  });
}

К счастью для нас, поскольку код представляет собой простой набор из десяти строк с нулевыми внешними зависимостями, мы можем развернуть всю лямбду — включая код, конфигурации и роль выполнения — через один шаблон CloudFormation :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  shell:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: shell
      Handler: index.handle
      Runtime: nodejs6.10
      Code:
        ZipFile: >
          let {exec} = require('child_process');
 
          exports.handle = (event, context, callback) => {
            console.log(event);
            exec(event.cmd, (err, stdout, stderr) => {
              console.log(stdout);
              if (err) console.log(stderr);
              callback(undefined, {statusCode: 200});
            });
          }
      Timeout: 60
      Role:
        Fn::GetAtt:
        - role
        - Arn
  role:
    Type: AWS::IAM::Role
    Properties:
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
        - Action: sts:AssumeRole
          Effect: Allow
          Principal:
            Service: lambda.amazonaws.com

Развернуть все это так же просто, как:

1
aws cloudformation deploy --stack-name shell --template-file /path/to/template.yaml --capabilities CAPABILITY_IAM

или выберите и загрузите шаблон на панель мониторинга CloudFormation , если у вас нет интерфейса командной строки AWS, чтобы сделать это (см. выше).

После развертывания достаточно просто вызвать лямбду с полезной нагрузкой, содержащей желаемую команду оболочки:

1
{"cmd":"the command to be executed"}

Если у вас есть интерфейс командной строки AWS, все это становится намного более сексуальным, когда вызывается с помощью следующего фрагмента оболочки:

1
2
3
4
5
6
7
8
9
echo -n "> "
read cmd
while [ "$cmd" != "exit" ]; do
  echo
  aws lambda invoke --function-name shell --payload "{\"cmd\":\"$cmd\"}" --log-type Tail /tmp/shell.log --query LogResult --output text | base64 -d
  echo
  echo -n "> "
  read cmd
done

С этим сценарием все, что вам нужно, это вызвать сценарий; вам будет выдана фальшивая «оболочка», в которой вы сможете выполнить долгожданную команду, и лямбда выполнит ее и сразу же вернет вывод обратно на консоль, вернув вас обратно в приглашение «shell»:

01
02
03
04
05
06
07
08
09
10
11
12
13
> free
 
START RequestId: c143847d-12b8-11e8-bae7-1d25ba5302bd Version: $LATEST
2018-02-16T01:28:56.051Z    c143847d-12b8-11e8-bae7-1d25ba5302bd    { cmd: 'free' }
2018-02-16T01:28:56.057Z    c143847d-12b8-11e8-bae7-1d25ba5302bd                 total       used       free     shared    buffers     cached
Mem:       3855608     554604    3301004        200      44864     263008
-/+ buffers/cache:     246732    3608876
Swap:            0          0          0
 
END RequestId: c143847d-12b8-11e8-bae7-1d25ba5302bd
REPORT RequestId: c143847d-12b8-11e8-bae7-1d25ba5302bd  Duration: 6.91 ms   Billed Duration: 100 ms     Memory Size: 128 MB Max Memory Used: 82 MB
 
>

С помощью этой хитрости вы можете узнать немного больше о среде обитания и образе жизни лямбда-функции. Для начала я узнал, что среда выполнения контейнеров включает в себя экземпляры Amazon Linux с около 4 ГБ (возможно, совместно используемой) памяти Memeey и несколькими (неиспользуемыми) дисковыми хранилищами значительного размера (в дополнение к «рекомендуемым для использования» 500 МБ). смонтировать на /tmp ):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
> df
 
START RequestId: bb0034fa-12ba-11e8-8390-cb81e1cfae92 Version: $LATEST
2018-02-16T01:43:04.559Z    bb0034fa-12ba-11e8-8390-cb81e1cfae92    { cmd: 'df' }
2018-02-16T01:43:04.778Z    bb0034fa-12ba-11e8-8390-cb81e1cfae92    Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/xvda1      30830568 3228824  27501496  11% /
/dev/loop8        538424     440    526148   1% /tmp
/dev/loop9           128     128         0 100% /var/task
 
END RequestId: bb0034fa-12ba-11e8-8390-cb81e1cfae92
REPORT RequestId: bb0034fa-12ba-11e8-8390-cb81e1cfae92  Duration: 235.44 ms Billed Duration: 300 ms     Memory Size: 128 MB Max Memory Used: 22 MB
 
> cat /etc/*-release
 
START RequestId: 6112efb9-12bd-11e8-9d14-d5c0177bc74f Version: $LATEST
2018-02-16T02:02:02.190Z    6112efb9-12bd-11e8-9d14-d5c0177bc74f    { cmd: 'cat /etc/*-release' }
2018-02-16T02:02:02.400Z    6112efb9-12bd-11e8-9d14-d5c0177bc74f    NAME="Amazon Linux AMI"
VERSION="2017.03"
ID="amzn"
ID_LIKE="rhel fedora"
VERSION_ID="2017.03"
PRETTY_NAME="Amazon Linux AMI 2017.03"
ANSI_COLOR="0;33"
CPE_NAME="cpe:/o:amazon:linux:2017.03:ga"
Amazon Linux AMI release 2017.03
 
END RequestId: 6112efb9-12bd-11e8-9d14-d5c0177bc74f
REPORT RequestId: 6112efb9-12bd-11e8-9d14-d5c0177bc74f  Duration: 209.82 ms Billed Duration: 300 ms     Memory Size: 128 MB Max Memory Used: 22 MB
 
>

Правда, формат вывода (который в основном является необработанным из журналов CloudWatch ) может быть значительно улучшен, в дополнение к десяткам других возможных улучшителей. Итак, давайте обсудим, под комментариями!

Смотреть оригинальную статью здесь: Внутри Lambda Runtime: заглянуть в безсерверное логово

Мнения, высказанные участниками Java Code Geeks, являются их собственными.