2018.01.19

【Techの道も一歩から】第6回「API GatewayとAWS Lambda PythonでAPI開発」Vol. 1:API GatewayとAWS Lambdaを知る

こんにちは。DSOC R&Dグループの高橋寛治です。

最近、何かと使っているAWS(Amazon Web Services)で、API GatewayとAWS Lambdaを利用したAPI開発について紹介します。

少しボリュームのある内容になりますので、複数回に分けます。

  1. API GatewayとAWS Lambdaを知る
  2. ローカルでの開発環境構築
  3. エラー処理
  4. デプロイ

サーバーレスでAPIが簡単に作れる

API GatewayとAWS Lambdaを組み合わせることの最大の特徴は、サーバーレスAPIが簡単に作れるということです。

従来、APIを作成するためには、Webサーバーを立てて、さまざまなセットアップをする必要があり、特にちょっとしたAPIを作成する際には手間がかかっていました。また、負荷対策にロードバランサーを配置して台数調整するなど、いろいろと考慮することが多くなりがちでした。

これに対して、API Gatewayではブラウザ上で簡単にAPIを構築し、AWS Lambdaのコードを呼び出すことで、スケーラブルなAPIを簡単に作ることができます。

API Gateway

API Gatewayはたくさんの特徴を持っています。あえて一言で言うならば、「非常に手軽にAPIが作れるサービス」です。

Webコンソール上でAPIの設定ができてしまう便利さはすさまじいです。

例えば、エンドポイントの設定、どのLambda関数を呼び出すか、アクセス権限をどうするかなどといったことは、簡単に設定することができます。

デプロイも多機能です。デプロイするステージを設定することができ、例えばURLを以下のように設定することができます。

  • http://hoge-aws.com/stg/path-to-api
  • http://hoge-aws.com/pro/path-to-api

何と言えばいいのか、恐ろしいほど手軽にエンドポイントが作成できます。

AWS Lambda

AWS Lambdaは、サーバーを構築することなく、アップロードしたコードを実行させることができる従量課金制のサービスです。

ユースケースとしては、AWS上のリソースの読み書きや、データ処理が挙げられます。アプリケーションが常に動作しているのではなく、あるイベントに対して処理をして返すという、箱のイメージです。

AWS内での連携では、トリガーと呼ばれる便利な機能があり、例えばAPI Gatewayで作成したAPIエンドポイントが叩かれると、Lambda関数を実行するといった具合です。

他にも、対象のDynamo DBにデータが書き込まれた際にフックしてLambda関数を動作させる、というようにAWSの各サービスと手軽に連携することができます。

こういった連携処理は、AWSコンソール上でクリックして設定するだけで実現することができます。

Lambdaの環境

対応言語は以下のとおりです。
– Python3.6および2.7
– Node.js v4.3.2および6.10.3
– C# .NET Core 1.0.1
– Java 8

バイナリを実行するときに気になる、Amazon Linux AMIのバージョンは amzn-ami-hvm-2017.03.1.20170812-x86_64-gp2 です。

Lambdaの独特な特徴として、最大実行時間とメモリー容量が設定されていることが挙げられます。

実行時間は5分まで。メモリー容量は、128MBから3008MBまで選択できます。メモリーサイズが大きければ大きいほど、CPUの速度も向上します。

Lambda関数の入出力

基本的には、下記のようなハンドラーメソッドを作成することで、入出力を行います。

def lambda_handler(event, context):
    print("print log.")
    return {
        "message": "Hello World",
    }

event はPythonのdictでパラメータが引数として代入されます。

例えば、API GatewayからPOSTリクエストが下記のようにdictで代入されます。

{
    "key": "Hello World!"
}

context はランタイム情報をハンドラーメソッドに渡します。

print メソッドの出力は、ログに書き出されます。ログは、CloudWatchで確認可能です。

LambdaでPythonモジュールやバイナリを走らせるには?

Lambda上にアップロードしたスクリプトが動作することは分かったと思いますが、Pythonモジュールのインストールやコンパイルして動かすバイナリについて、どうしたら良いのか、分からなくなってしまいがちです。

Pythonモジュールに関しては、プロジェクトのルートディレクトリー以下にインストールをすることで対応できます。

pip コマンドには、インストール先指定オプション -t があります。

ここで、プロジェクトのルートディレクトリーにインストールすると大量にファイルができて、ディレクトリーがごちゃごちゃしてしまいます。

私は、回避策としてプロジェクトのルート以下に python_modules ディレクトリーを作成し、そこに pip -t python_modules install hogehoge とインストールしています。1

Pythonコードから python_modules ディレクトリーをパスに追加することを忘れないよう注意しましょう。

import sys
sys.path.append("./python_modules")

次にコンパイルしたパッケージを走らせたいという場合があると思います。

ここでは、日本語形態素解析器「MeCab」を動作させることを目標に例を示します。

LambdaのAmazon Linux AMIと同じイメージのEC2インスタンスを立て、この中でコンパイルなどの作業を行います。

まず、MeCabと辞書をプロジェクトルート以下の env 2 にインストールします。 $PROJECT_HOME は、プロジェクトのルートの絶対パスが格納されているという設定です。

# MeCab
cd mecab-0.996
./configure --prefix=$PROJECT_HOME/env --with-charset=utf8 --enable-utf8-only
make && make install

# MeCab-IPAdic
cd mecab-ipadic-2.7.0-20070801
./configure --prefix=$PROJECT_HOME/env --with-charset=utf8 --enable-utf8-only
make && make install

次に、MeCabのPythonバインディングである mecab-python3 をインストールします。

mecab-config が必要となり、これは先程 ${PROJECT_ROOT}/env/bin 以下にインストールしたため、PATHを追加します。

PATH=${PROJECT_ROOT}/env/bin:${PATH} pip install mecab-python3

実は、 import MeCab ではまだ動作せず、もう一工夫が必要です。

MeCabをビルドした際に生成された動的リンクライブラリーをロードする必要があります。

import ctypes
import os
# MeCab動作に必要な動的ライブラリを読み込み
libdir = os.path.join(os.getcwd(), 'env', 'lib')
ctypes.cdll.LoadLibrary(os.path.join(libdir, 'libmecab.so'))

import MeCab

# デフォルト辞書を指定
dicdir = os.path.join(os.getcwd(), 'env', 'lib', 'mecab', 'dic', 'ipadic')
rcfile = os.path.join(os.getcwd(), 'env', 'etc', 'mecabrc')

tagger = MeCab.Tagger("-d{} -r{}".format(dicdir, rcfile))

これでMeCabが動作します。

最低限、仕組みを知って実装へ

素早く実装を進めるためには、対象のマシンやサービスがどのような仕組みを持っているかを知る必要があります。

今回は特に、Lambdaの実装面での仕様理解を中心に紹介しました。

次回は、Lambda関数を作る上で何かと厄介な、ローカルでの開発環境の構築について書きたいと思います。

執筆者プロフィール

過去記事

▼第5回
快適なシェル環境の再構築を自動化する

▼第4回
第16回情報科学技術フォーラム(FIT2017)で登壇

▼第3回
第11回テキストアナリティクス・シンポジウム

▼第2回
R&D論文読み会勉強会

▼第1回
言語処理100本ノック勉強会

text:DSOC R&Dグループ 高橋寛治

  1. python_modulesのようにサブディレクトリーを作成した場合は、エディターの設定でPYTHONPATHなどを指定しないと、補完がうまく動きません。ベストプラクティスがあれば、教えてください! 
  2. ビルドしたソフトウェアの通常インストール先の /usr/local/env 以下に指定し、パッケージに含めます。 

Pickup