LambdaをRで動かすlambdr

# はじめに
AWS LambdaをRで動かせるようにした`lambdr`なるものを作った方がいらっしゃいました。

https://github.com/mdneuzerling/lambdr

今回はこれを動かしてみました。


# 概要

- LambdをRで動かせます
    - 非公式なので、利用は自己責任で

参考に、PHPでLambdaを動かす事例が公式で記事になっています。これと同様のものをRで作っています。

https://aws.amazon.com/jp/builders-flash/202106/new-lambda-container-development-3/?awsf.filter-name=*all


# やってみた

## 環境
いつものCloud9です。t2.microでやりました。ストレージがギリギリです。他のものが入っているとエラーになりますので容量にはご注意ください。

## コンテナ作成とPUSH
まずはコンテナをbuildします。
```bash
# プロジェクトディレクトリ作成
mkdir test-lambdr && cd test-lambdr

# 対象をPULL
docker pull public.ecr.aws/lambda/provided

# サンプル用のファイルを作成。コードは以下に
# https://github.com/mdneuzerling/lambdr#example
touch Dockerfile
touch runtime.R

# よく使う文字列を環境変数にセット
REGION="ap-northeast-1"
ACCOUNTID=$(aws sts get-caller-identity --output text --query Account)
IMAGENAME="test-lambdr"

# build 3分強。Cloud9デフォルト10GBだとカツカツ
docker build -t ${IMAGENAME} .
```

次にECRを作ります。
```bash
# ECRリポジトリ作成CFn
touch createECRRepository.yaml
```
ECRを作るCFnは以下になります
```yaml:createECRRepository.yaml
AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  RepositoryName:
    Type: String

Resources:
  TestEcrPoc:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: !Ref RepositoryName

Outputs:    
  RepositoryUri:
    Value: !GetAtt TestEcrPoc.RepositoryUri
```
ECRを作って、先ほどのコンテナをPUSHします。
```bash
# ECRにレポジトリ作成
STACKNAME="create-ecrrepo-lambdr"
REPOSITORYNAME="test-lambdr-ecs"
aws cloudformation create-stack --stack-name ${STACKNAME} \
  --template-body file://createECRRepository.yaml \
  --region ${REGION} \
  --parameters \
    ParameterKey=RepositoryName,ParameterValue=${REPOSITORYNAME}

# イメージにタグ付与
TAGNAME=`aws cloudformation describe-stacks --stack-name ${STACKNAME} --query "Stacks[].Outputs[?OutputKey=='RepositoryUri'].[OutputValue]"  --output text`:latest
docker tag ${IMAGENAME}:latest ${TAGNAME}

# 認証
aws ecr get-login-password --region ${REGION} | docker login --username AWS --password-stdin ${ACCOUNTID}.dkr.ecr.${REGION}.amazonaws.com

# 作ったイメージをPUSH
docker push ${TAGNAME}
```

## Lambdaを作る
昔の自分の記事を参考に作ってきます。

https://qiita.com/a_b_/items/d9398fd6efe0f66b6c6f#lambada%E4%BD%9C%E6%88%90

後で消しやすいように、LambdaもCFnで作ります。
```bash
# Lambda作るCFn
touch createLambda.yaml
```
```yaml:createLambda.yaml
AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  LambdaFunctionName:
    Type: String
  ImageUri:
    Type: String
    
Resources:
  ########################################################
  ### Log Group
  ########################################################
  FunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/aws/lambda/${LambdaFunctionName}"
      RetentionInDays: 3653

  ########################################################
  ### IAM Role
  ########################################################
  FunctionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "for-lambdafunction-${LambdaFunctionName}"
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Path: '/service-role/'
      Policies:
        # CloudWatch
        - PolicyName: write-cloudwatchlogs
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action: 
                  - 'logs:CreateLogStream'
                  - 'logs:PutLogEvents'
                Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${LambdaFunctionName}:*"    

  ########################################################
  ### Lambda Function
  ########################################################
  TargetFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Ref LambdaFunctionName
      Role: !GetAtt FunctionRole.Arn
      PackageType: Image
      Code:
        ImageUri: !Ref ImageUri
```

必要な文字列は変数に格納して、Lambdaを作ります。
```bash
DIGEST=$(aws ecr list-images --repository-name ${REPOSITORYNAME} --region ${REGION} --out text --query 'imageIds[?imageTag==`latest`].imageDigest')
IMAGEURI=`aws cloudformation describe-stacks --stack-name ${STACKNAME} --query "Stacks[].Outputs[?OutputKey=='RepositoryUri'].[OutputValue]"  --output text`@${DIGEST}
FUNCTIONNAME="func-lambdr"
STACKNAMEFUNCTION="create-func-lambdr"

aws cloudformation create-stack --stack-name ${STACKNAMEFUNCTION} \
  --template-body file://createLambda.yaml \
  --region ${REGION}  \
  --parameters \
    ParameterKey=LambdaFunctionName,ParameterValue=${FUNCTIONNAME} \
    ParameterKey=ImageUri,ParameterValue=${IMAGEURI} \
  --capabilities CAPABILITY_NAMED_IAM
```

## 実行
以下のようにして実行できます。
```bash
aws lambda invoke --function-name ${FUNCTIONNAME} \
  --cli-binary-format raw-in-base64-out --region ${REGION} \
  --payload '{"number": 11}' output ; cat output
```

# おわりに
今回はlambdrを紹介しました。
Lambdaの動作については以下のページがわかりやすいです。

https://aws.amazon.com/jp/builders-flash/202104/new-lambda-container-development-2/?awsf.filter-name=*all

これをRで実装したのですから、作成者には頭が下がります…