AWS Lambda with Container Image で MeCab (NEologd) を動かしてみた

はじめに

はじめまして、エンジニアのせーさんです。

2020年12月上旬にAWS LambdaのパッケージフォーマットとしてDockerコンテナイメージが追加されました。
Lambda好きとしては夢が広がるアップデートなので、今回は使用感を確認するためにMeCab + NEologdで構築した形態素処理を行うWeb APIを構築してみました。

※ 本記事では以降 Lambda with Container Image と表記します。
※ 執筆時点で対応しているリージョンは以下になります。
米国東部 (バージニア北部)、米国東部 (オハイオ)、米国西部 (オレゴン)、アジアパシフィック (東京)、アジアパシフィック (シンガポール)、欧州 (アイルランド)、欧州 (フランクフルト)、南米 (サンパウロ)

この記事のまとめ

  • AWS LambdaでDockerコンテナが利用できるようになった
  • 1GBの辞書データを持つ形態素解析処理をLambdaで構築してみた (コードもあるよ)
  • Lambdaの守備範囲が増えサーバレス化の恩恵を受けやすくなった

LambdaでMeCab+NEologdな形態素解析処理を構築

昨年開発したとあるプロダクトにて外部サービスからWebHookで呼び出して連携する機能をLambda+API Gatewayを利用して構築しており、その中でテキスト解析処理のためMeCab+NEologdを利用した形態素解析処理を行っています。

結論としては辞書データを保存したEFSをLambdaにマウントする方式で構築してサービスインしたものの、環境構築やメンテナンスの手間に難があるなどの課題を現在進行系で抱えています。

上記課題をLambda with Container Imageで解決できるのか検証するため、そして何よりブログのネタにするために実際に試してみました。

Lambdaのファイルサイズ制限

外部ライブラリに依存する処理をLambdaで実現しようとした場合に考慮が必要な要素の一つにアップロードファイルのサイズ制限があります。手法とサイズ制限、および、個人的な感想をまとめるとこんな感じかと思います。Lambda with Container Imageにより選択肢が増えました。

手法 サイズ制限 備考/所感 サポート開始時期
コンソールからアップロード 3MB 軽量処理は当然これで
ZIP圧縮してアップロード 50MB
(ZIP圧縮状態)
手軽だがサイズが心もとない
Lambda Layers 250MB
(解凍後)
正直サイズ制限が中途半端
読み込みに時間がかかるのもイマイチ
2018.11~
EFS(Elastic File System)をマウント 実質無制限 EFSをマウントするためVPC Lambdaになるのが面倒
EFSの用意も面倒
2020.6~
Lambda with Container Image 10GB
(イメージサイズ)
new !! 2020.12~

ちなみにLambda with Container Imageによるメリットにはサイズ制限以外もりますが、今回は主にサイズ制限に着目しています。

LambdaでMeCabを利用する場合の課題

LambdaでMaCabを利用しようとした場合にファイルサイズ制限の影響を受けるのが辞書ファイルです。

MeCab本体のサイズはそこまで大きくないのですが、辞書にNEologdを利用するとビルド済みのバイナリデータのサイズが1GBを超えてきます。
ビルド時のオプションで登録単語数を減らしても700MB程度のバイナリデータになります。

1GBのバイナリデータとなるとLambda Layersのサイズ制限である250MBを軽く超えるため、今まではEFSを利用して解決するしかありませんでした。
といってもLambdaにEFSがマウント可能になったのが2020年6月なので「そんな処理はLambdaでやることじゃねぇ、素直にEC2かECSを使えよ。」という感じだった訳です。

でもLambdaを始めとしたサーバレスの便利さ、特にメンテナンス性を一度味わってしまうとEC2などを極力避ける気持ち、分かりますよね?ということで、いままではEFSを利用して構築していました。

さて、2020年6月にサポート開始されたEFSマウントを利用することでLambdaは実質無制限の容量を手にした訳ですが、利用するにあたって大きな制約事項がありました。
「VPC Lambdaでなければいけない。」という制約です。

EFSがVPC内に存在するネットワークファイルストレージ(NAS)であるため、EFSに接続するには接続先のLambdaもそのVPCのプライベートサブネットに接続できる必要があります。
したがってLambdaに対してVPCのネットワーク設定をする必要があり、当然セキュリティ面の考慮などネットワーク設計もVPC Lambdaの利用について検討・反映させる必要があります。

VPCの設定をすることでEFSのファイルシステムをVPC Lambdaにマウントするまでできた訳ですが、次はマウントするファイルシステムに事前にライブラリファイルを保存しておく必要があります。
そのため事前に専用のEC2インスタンスなどからライブラリファイルを保存しておく必要があり、環境構築にも、ライブラリの変更にもどうしても一手間かかってきます。

環境(本番環境用、ステージング環境用、開発環境用など)毎にEFSのファイルシステムを用意するなど運用面を考慮するのもまた一苦労です。

Lambdaのサーバレスとしての恩恵を受けるために支払うコストとしては少し高いのではないかと思います。

Lambda with Container Imageで構築

ようやく本題に入ります。

EFSほどのファイルサイズは扱えませんが、Lambda Layersよりは十分に大きいサイズを扱えるLambda with Container ImageでMeCab+NEologd環境を構築していきいます。

環境と手順

Lambdaを扱う方法はコンソールを始めいくつかの選択肢がありますが個人的に慣れている Serverless Framework を、同じ理由でハンドラーの記述には Python3 を利用していきます。

手順はこんな感じ

  1. Serverless Frameworkプロジェクトの作成
  2. ハンドラー処理の実装
  3. Dockerfile作成とDockerイメージのビルド
  4. ECRのDockerリポジトリ作成とイメージの登録
  5. AWSへのデプロイと動作確認

ちなみにソースコードはGithubにて公開しているのでご自由にご利用ください。
質問や改善ポイントのご指摘などがありましたらブログのコメント欄にいただけると嬉しいです。

環境情報

  • 端末/OS: MacBook Pro (16-inch, 2019) / macOS Big Sur
  • Python: 3.8.5
  • Docker: 20.10.0 (Docker Desktop for Mac 3.0.3)
  • Node.js: v12.16.1 (npm 6.14.8)
  • AWS CLI: 2.1.13
  • Serverless Framework: 2.16.1

また事前にServerless Framework用のIAMユーザーを作成しておきます。

  • 検証用のためポリシーは AdministratorAccess をアタッチ
  • aws configure でローカル端末にプロファイル登録済み

Serverless Frameworkプロジェクトの作成

とりあえずビールの感覚(コロナ禍で忘れ気味)でServerless Frameworkのバージョンを確認します。

テンプレートからプロジェクト作成

sls createコマンドでaws-python3をベースにプロジェクトpymecab-lambda-containerを作成します。

成功して指定ディレクトリにファイルが3つ作成されているのを確認してプロジェクト作成完了です。

プロジェクト設定ファイルの修正

自動生成されたserverless.ymlから有効部分を抜粋するとこんな感じになっていると思います。

Dockerコンテナを利用するための設定変更をしていきます。設定したものがこちらになります。

また環境に依存する設定値を./config/env.ymlに切り出しました。上記serverless.ymlcustom:の部分で読み込んでいます。
今回に限っては「後で記事のマスク処理が面倒」という思いが主な理由のため「stage毎に設定ファイルを切り替える」様な設定は組み込んでいません。

accountIDregionはAWS環境に合わせて設定します。
repositoryはECRのリポジトリ名として後ほど利用します。好みとセンスで命名してください。
digestはECRにDockerイメージをpushしたときに生成されるハッシュ値のため、現時点では未指定になります。

利用するpythonライブラリを設定

requirements.txtにDockerイメージのビルド時にpip installするためのpythonライブラリを設定します。
今回はMaCabとPythonをつなぐ必要があるためmecab-python3のみ設定しておきます。

※ PythonからMeCabを利用する方法としては他にnatto-pyfugashiがありますが、辞書の指定方法がシンプルなため今回はmecab-python3を利用しています。

ハンドラー処理の実装

プロジェクト作成時に出力されたhandler.pyを削除してプロジェクト直下にapp.pyを新規作成します。
ファイル名は筆者の好みです。

簡単な仕様

  1. NEologdを辞書として利用するMeCabのTaggerを分かち書きモードで初期化
  2. リクエストbodyに送られてきた文字列を形態素解析
  3. 解析結果から表示に不要な’BOS’および’EOS’を除外
  4. 解析した結果を配列化して返却

今回はこんな感じに用意しました。
API Gateway経由で利用想定のため、リクエストとレスポンスはそれに合わせて処理しています。

Dockerfile作成とDockerイメージのビルド

Lambdaで利用するDockerのコンテナイメージを作成していきます。
AWSからLambda用のベースイメージが提供されているのでそちらを使っていきます。

@see https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/runtimes-images.html

AWS ECRの接続設定とベースイメージの取得

次のコマンドでAWS ECRのパスワードを取得しつつ、dockerコマンドでECRのリポジトリにログインします。 以後のコマンド、および、コマンド実行結果にある{}表記は./config/env.ymlに指定する値と置き換えてください。

無事ログインに成功するとLogin Succeededと表示されます。

なお、Windows環境だと次のようになるそうです。(未検証)

次に確認も兼ねて今回利用するベースイメージをローカルに取得してみます。

Dockerfileの作成

Dockerfileを作成します。安直に ./Dockerfile に作っていきます。
MeCab、および、辞書のビルドに必要なライブラリと、各種ビルド & インストール、最後にハンドラー実行の設定を書いて完成です。

なお今回は検証目的のため1つのイメージにまとめています。
実際に運用する場合はステップ数を減らし、更にビルド部分と実行部分を分けることでイメージサイズを極力小さくすることをおすすめします。

Dockerfileを保存したらタグ付けしつつビルドしていきます。
各種ファイルのダウンロードとビルドを行うため時間がかかります。筆者の環境では4分弱かかりました。

ECRのDockerリポジトリ作成とイメージの登録

ECRリポジトリの新規作成

次のコマンドでECRに新しいプライベートリポジトリを作成します。
オプションの--image-scanning-configuration scanOnPush=trueでpush成功時に自動でスキャンするよう設定しています。

成功するとJSON形式でリポジトリ情報が返ってきます。

作成したDockerイメージをECRリポジトリに登録

ECRリポジトリ向けにタグ付けをおこない、ECRリポジトリに対してdocker pushします。
ここで実行するdocker pushは合計4GB程度のファイルアップロード処理になるためご注意ください。

成功するとdigestの値が取れるのでコピーしておきます。

取得したdigestの値を設定ファイルに反映

./config/env.ymlの最終行に記述したdigestの値にECRリポジトリ登録時に取得したdigestの値を設定します。
これでLambdaで使うDockerイメージを特定するのに必要な情報が揃いました。

AWSへのデプロイと動作確認

ここまででデプロイの準備が整ったのでsls deployコマンドでデプロイしていきます。
成功するとServerless: Stack update finished...のようなメッセージに合わせてデプロイ結果の情報が表示されます。

動作確認

デプロイ結果の情報にあるendpoints:API Gatewayでアクセス可能なメソッドとURLになります。
今回はPOSTする必要がありブラウザでの確認は難しいのでPostmanを使って確認していきます。

Postmanでの実行結果

Postmanでの実行結果

さすが現代語が反映されているNEologdですね、定番のきゃりーぱみゅぱみゅはもちろんTikTokバズるもそれっぽく解析できています。
指ハートは一般的過ぎる名詞の組み合わせなためか1単語にはなりませんでした。

これで無事にLambda with Container ImageMeCab + NEologdを利用したAPIが構築できました!

おわりに

ここまで読んでいただきありがとうございます。

Lambda with Container ImageによってLambdaの可能性は大きく広がったと感じています。
ファイルサイズが数GB程度までであれば機械学習のモデルなどを利用した解析処理も簡単にLambda化できそうです。

それではまた次のブログで。

参考

https://aws.amazon.com/jp/blogs/news/new-for-aws-lambda-container-image-support/

https://www.serverless.com/blog/container-support-for-lambda

最近の記事

  • 関連記事
  • おすすめ記事
  • 特集記事

アーカイブ

カテゴリー

PAGE TOP