AWS LambdaのカスタムランタイムでC++を動かしてみた #reinvent

はじめに

福岡のyoshihitohです。先日のre:Invent 2018で発表されたAWS Lambdaのカスタムランタイムが発表されました。

【アップデート】 もう言語で悩まない!AWS LambdaでCustom Runtimeが利用できるようになりました! #reinvent

今回はこれを使ってC++を動かしてみます。

このエントリは AWS Lambda Custom Runtimes芸人 Advent Calendar 2018 2日目の記事です。

検証環境

  • macOS: 10.13.6
  • Command Line Tools for Xcode: 10.0.0.0.1.1535735448
  • CMake: 3.9.0
  • Docker: 18.09.0, build 4d60db4

試すこと

まずは動くことを確認するため、ペイロードをエコー表示するだけの簡単なものを試してみます。

環境構築

カスタムランタイムを使う場合はアプリケーションをzipファイルでアップロードします。 aws-lambda-cpp を利用すると、zipファイルにアーカイブするMakefileも自動生成できるのでこれを利用します。

また、Linux環境用のバイナリが必要なのでDockerを使ってビルドします。

参考

Lambdaアプリケーションのプロジェクトを作る

サンプルを参考にアプリケーションのメイン処理を作成します。

#include <aws/lambda-runtime/runtime.h>  
  
using namespace aws::lambda_runtime;  
  
static invocation_response my_handler(invocation_request const& req)  
{  
    return invocation_response::success("Echo: " \+ req.payload,  
                                        "text/plain");  
}  
  
int main()  
{  
    run\_handler(my\_handler);  
    return 0;  
}

次にCMakeプロジェクトを作成します。

cmake_minimum_required(VERSION 3.5)
set(CMAKE_CXX_STANDARD 14)

project(cpp-lambda LANGUAGES CXX)

find_package(aws-lambda-runtime)    # (A)
find_package(AWSSDK COMPONENTS s3)  # (B)

add_executable(${PROJECT_NAME} "main.cpp")
target_link_libraries(${PROJECT_NAME} PRIVATE AWS::aws-lambda-runtime ${AWSSDK_LINK_LIBRARIES})  # (C)
target_compile_features(${PROJECT_NAME} PRIVATE "cxx_std_14")
target_compile_options(${PROJECT_NAME} PRIVATE "-Wall" "-Wextra")

# this line creates a target that packages your binary and zips it up
aws_lambda_package_target(${PROJECT_NAME})  # (D)
  • (A): AWS LambdaのC++用ランタイムパッケージを利用するための指定です
  • (B): AWS SDK for C++パッケージを利用するための指定です
  • (C): C++用ランタイムとAWS SDKをリンクする指定です
  • (D): アップロードするzipファイルを作成するための指定です

Dockerファイル作成

次にC++ビルド環境を準備します。

FROM alpine:latest

# (1) ビルド環境・依存ライブラリをインストール
RUN apk update && apk add cmake make git g++ bash curl-dev zlib-dev openssl-dev zip

# (2) C++用カスタムランタイムをビルド&インストール
WORKDIR /opt/src
RUN mkdir -p external && \
    cd external && \
    git clone https://github.com/awslabs/aws-lambda-cpp-runtime.git && \
    mkdir -p aws-lambda-cpp-runtime/build && \
    cd aws-lambda-cpp-runtime/build && \
    cmake .. -DCMAKE_BUILD_TYPE=Release \
        -DBUILD_SHARED_LIBS=OFF \
        -DCMAKE_INSTALL_PREFIX=/opt/lambda-install && \
    make -j$(nproc) && \
    make install

# (3) アプリケーションをビルド&ZIPファイル化
COPY . /opt/src
RUN mkdir build && \
    cd build && \
    cmake -G "Unix Makefiles" \
        -DCMAKE_BUILD_TYPE=Release \
        -DCMAKE_PREFIX_PATH="/opt/lambda-install;/opt/awssdk-install" \
        .. && \
    make && make aws-lambda-package-cpp-lambda

CMD ["ls", "-lah"]

本番環境で動作させる場合は必ず -DCMAKE_BUILD_TYPE=Release を指定してリリース版でビルドしましょう。 (デバッグ版だとパフォーマンスが著しく低下するため)

ここまででビルド設定は完了です。ビルドできるか試してみます。

$ docker image build -t lambda-cpp-runtime .
Sending build context to Docker daemon 454.3MB
Step 1/9 : FROM alpine:latest
---> 3fd9065eaf02
Step 2/9 : RUN apk update && apk add cmake make git g++ bash curl-dev zlib-dev openssl-dev zip
---> Using cache
---> 394d8401b765
Step 3/9 : WORKDIR /opt/src
---> Using cache
---> d5cfb9056db9
Step 4/9 : RUN mkdir -p external && cd external && git clone https://github.com/awslabs/aws-lambda-cpp-runtime.git && mkdir -p aws-lambda-cpp-runtime/build && cd aws-lambda-cpp-runtime/build && cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=/opt/lambda-install && make -j$(nproc) && make install
---> Using cache
---> 97daa8d06318
Step 5/9 : RUN cd external && git clone https://github.com/aws/aws-sdk-cpp.git && mkdir -p aws-sdk-cpp/build && cd aws-sdk-cpp/build && cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_ONLY="s3" -DCUSTOM_MEMORY_MANAGEMENT=OFF -DBUILD_SHARED_LIBS=OFF -DENABLE_UNITY_BUILD=ON -DCMAKE_INSTALL_PREFIX=/opt/awssdk-install && make -j$(nproc) && make install
---> Using cache
---> 591d2de4cadb
Step 6/9 : RUN cd external && git clone https://github.com/mapbox/gzip-hpp
---> Using cache
---> f7a9929dd8f2
Step 7/9 : COPY . /opt/src
---> caf6aa62d0d8
Step 8/9 : RUN mkdir build && cd build && cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="/opt/lambda-install;/opt/awssdk-install" .. && make && make aws-lambda-package-cpp-lambda
---> Running in 4ee7d3ec7d0b
-- The CXX compiler identification is GNU 6.4.0
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- TARGET_ARCH not specified; inferring host OS to be platform compilation target
-- Building AWS libraries as static objects
-- Generating linux build config
-- Building project version: 1.7.11
-- Found AWS SDK for C++, Version: 1.7.11, Install Root:/opt/awssdk-install, Platform Prefix:, Platform Dependent Libraries: pthread;crypto;ssl;z;curl
-- Components specified for AWSSDK: s3
-- Try finding aws-cpp-sdk-core
-- Found aws-cpp-sdk-core
-- Try finding aws-cpp-sdk-s3
-- Found aws-cpp-sdk-s3
-- Configuring done
-- Generating done
-- Build files have been written to: /opt/src/build
Scanning dependencies of target cpp-lambda
[ 50%] Building CXX object CMakeFiles/cpp-lambda.dir/main.cpp.o
[100%] Linking CXX executable cpp-lambda
/usr/lib/gcc/x86_64-alpine-linux-musl/6.4.0/../../../../x86_64-alpine-linux-musl/bin/ld: warning: libssl.so.44, needed by /usr/lib/gcc/x86_64-alpine-linux-musl/6.4.0/../../../../lib/libcurl.so, may conflict with libssl.so.1.0.0
/usr/lib/libssl.so: warning: warning: EVP_EncryptFinal is often misused, please use EVP_EncryptFinal_ex and EVP_CIPHER_CTX_cleanup
/usr/lib/libssl.so: warning: warning: EVP_DecryptFinal is often misused, please use EVP_DecryptFinal_ex and EVP_CIPHER_CTX_cleanup
/usr/lib/gcc/x86_64-alpine-linux-musl/6.4.0/../../../../lib/libcrypto.so: warning: warning: EVP_CipherFinal is often misused, please use EVP_CipherFinal_ex and EVP_CIPHER_CTX_cleanup
[100%] Built target cpp-lambda
[100%] Built target cpp-lambda
Scanning dependencies of target aws-lambda-package-cpp-lambda
adding: bin/ (stored 0%)
adding: bin/cpp-lambda (deflated 62%)
adding: bootstrap (deflated 24%)
adding: lib/ (stored 0%)
adding: lib/libgcc_s.so.1 (deflated 54%)
adding: lib/ld-musl-x86_64.so.1 (deflated 37%)
adding: lib/libssl.so.44 (deflated 60%)
adding: lib/libssl.so.1.0.0 (deflated 59%)
adding: lib/libssh2.so.1 (deflated 57%)
adding: lib/libz.so.1 (deflated 46%)
adding: lib/libcrypto.so.42 (deflated 58%)
adding: lib/libstdc++.so.6 (deflated 71%)
adding: lib/libcurl.so.4 (deflated 52%)
adding: lib/libcrypto.so.1.0.0 (deflated 55%)
Created /opt/src/build/cpp-lambda.zip
[100%] Built target aws-lambda-package-cpp-lambda
Removing intermediate container 4ee7d3ec7d0b
---> b42cfabaab8a
Step 9/9 : CMD ["ls", "-lah"]
---> Running in 8efa8fcbedf4
Removing intermediate container 8efa8fcbedf4
---> 16f5ded7b5b4
Successfully built 16f5ded7b5b4
Successfully tagged blog/cpp-runtime:latest

ビルドできましたね!

最後にコンテナを起動してAWS Lambdaで使うzipファイルを取得します。

$ docker container run --name lambda-cpp lambda-cpp-runtime
$ docker container cp lambda-cpp:/opt/src/build/cpp-lambda.zip .

ファンクションを作成

作成手順は下記の記事の「Lambdaファンクション作成」と同様です。

AWS LambdaのCustom RuntimeでRustを実行してみた #reinvent

ファンクションを動かす

下記データを流してファンクションを動かしてみます。

{
"key1": "value1",
"key2": "value2",
"key3": "value3"
}

ちゃんと動きましたね!

おわりに

今回はカスタムランタイムを使ってC++のアプリケーションを動かしてみました。カスタムランタイムで好きな言語が使えるようになったのは非常にありがたいですね!

次回はS3やDynamoDBといったAWSのサービスと連動させてみたいと思います!