8
6

More than 5 years have passed since last update.

AWS Lambda で C のバイナリを動かす

Last updated at Posted at 2019-02-11

ありふれたネタですが、 Lambda のカスタムランタイムで C を動かしてみました。

ディレクトリ構成

.
├── bin
│   ├── bootstrap
│   └── lambda_function
├── docker-compose.yml
├── Dockerfile
└── src
    ├── lambda_function.c
    └── Makefile

一連のコードは https://github.com/tsubasaogawa/lambda-with-c にあげています。

手順

概要

  1. C のソースを書く
  2. Lambda と同じ実行環境でソースをコンパイルする
  3. bootstrap を修正する
  4. Lambda にアップロードする

詳細

C のソースを書く

普段どおりソースを書きます。今回は「標準入力された内容を出力する」だけの簡単なものを作ってみました。

lambda_function.c
#include <stdio.h>

int main(void) {
  char buf[512];

  while(1) {
    /* Obtain from Stdin https://qiita.com/mpyw/items/aff12a6ff2c7726ed1d8 */
    if(scanf("%511[^\n]%*[^\n]", buf) != 1) {
      break;
    }
    /* Ignore linefeed */
    scanf("%*c");

    printf("%s\n", buf);
  }

  return 0;
}
Makefile
PROGRAM = lambda_function
CC = gcc
CFLAGS = -Wall
OBJS = $(PROGRAM).o

$(PROGRAM): $(OBJS)
    $(CC) $(CFLAGS) -o $(PROGRAM) $(OBJS)

$(OBJS): $(PROGRAM).c
    $(CC) -c $(PROGRAM).c

.PHONY: clean
clean:
    rm -f $(PROGRAM) $(OBJS)

.PHONY: clean_obj
clean_obj:
    rm -f $(OBJS)

Lambda と同じ実行環境でソースをコンパイルする

Lambda で動かすので、Lambda と同じ環境でコンパイルする必要があります。
Lambda 実行環境と利用できるライブラリ によると Amazon Linux を使っている (2019/02 時点) ことがわかるため、Amazon Linux のコンテナ上でコンパイルすることにします。

Dockerfile
FROM amazonlinux:2
MAINTAINER tsubasaogawa

WORKDIR /usr/local/src/lambda-with-c

RUN set -x && yum install -y gcc make

# ADD ./bootstrap .
COPY ./src/lambda_function.c .
COPY ./src/Makefile .

RUN make && make clean_obj

なんとなく Amazon Linux 2 にしてしまいました。docker-compose は以下です。

docker-compose.yml
version: '3'
services:
  lambda-with-c-compiler:
    build: .
    volumes:
      - ./bin:/var/tmp
    command: /bin/bash -c 'cp -r /usr/local/src/lambda-with-c/lambda_function /var/tmp'

コンテナビルド時に make で作られたバイナリを、マウントしたディレクトリにコピーしています。また、コンテナを実行するとホストの bin/ 配下にバイナリが作られます。

$ docker-compose build
$ docker-compose up -d
$ ls -l bin/lambda_function
-rwxr-xr-x 1 root root 8232 Feb 11 12:49 bin/lambda_function

bootstrap を修正する

カスタムランタイムを使用すると、 bootstrap というファイルが実行されます。bootstrap 経由でバイナリを実行するような形になります。
チュートリアル では、シェルスクリプトで bootstrap が書かれています。これを一部 (というかほとんど) 流用します。

bootstrap
#!/bin/sh

set -euo pipefail

# Processing
while true
do
  HEADERS="$(mktemp)"
  # Get an event
  EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
  REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)

  # Execute the handler function from the script
  EXEC="$LAMBDA_TASK_ROOT/$(echo "$_HANDLER" | cut -d. -f1)"
  RESPONSE=$(echo "$EVENT_DATA" | $EXEC)

  # Send the response
  curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response"  -d "$RESPONSE"
done

Lambda にアップロードする

成果物を zip にして Lambda へアップロードします。Role は予め適当なものを用意してください。

Lambdaへアップロード
$ cd bin
$ zip ../lambda_function.zip ./*
$ cd ..
$ ls -l lambda_function.zip 
-rw-rw-r-- 1 vagrant vagrant 3032 Feb 11 12:55 lambda_function.zip

$ aws lambda create-function --function-name lambda-with-c \
    --zip-file fileb://lambda_function.zip --handler lambda_function.handler \
    --runtime provided --role arn:aws:iam::***:role/role_name

試しに実行してみます。

実行
$ aws lambda invoke --function-name lambda-with-c \
    --invocation-type RequestResponse \
    --payload '{ "test1": "value1" }' \
    /tmp/lambda-with-c.log
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
実行結果
$ cat /tmp/lambda-with-c.log 
{
    "test1": "value1"
}

よさそうです。引数が得られたので、あとは自由にパースして使えそうです。

おわりに

  • Lambda のカスタムランタイムを使って、 C で作られたバイナリを動かしてみた
  • Amazon Linux 上でコンパイルを行うと安心
  • カスタムランタイム使用時は bootstrap というファイルがまず実行される
  • bootstrap 経由でバイナリを実行する
  • なお、C++ では便利なライブラリが公開されているのでこれを使うと幸せになれそう
  • 参考記事
8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6