AWSのlambda上でMeCabを実行する (他のバイナリへも応用可)!!

日本語の解析をする場合、とりあえず形態素解析を実施することになると思います。
手っ取り早く動かすのであれば、ローカルで動作させれば良いですが、サーバ側で処理することも多いかと思います。
AWS lambda + APIGateway で動作させることが出来れば、便利なので、その事前準備として、lamdab上で形態素解析を行うと言う話です。

日本語の形態素解析といえば、MeCabが定番なのではないでしょうか。
MeCab: Yet Another Part-of-Speech and Morphological Analyzer

ただ、mecabc/c++で実装されており、lambdaで動作させるためには、lambdaで利用されているCPUが解釈できるバイナリを準備する必要があります。

lambdaが動作する環境は、AMIとしてAWSが公開しています。
Lambda 実行環境と利用できるライブラリ - AWS Lambda
要は、このAMIからEC2インスタンスを一旦起動し、立ち上げたEC2以上でmecabバイナリへコンパイルし、その成果物をlambdaで利用すれば良いことになります。
また、lambdaでプログラムを実行した場合、/var/task配下で実行されることになります。
今回は、/var/task/mecab配下へmecabバイナリや辞書をおくことにします。

以下に具体的な手順を示します。

  1. lambdaのAMIからEC2インスタンスを起動します。
    • AWSコンソールの EC2のタブからAMIの画面を開き、publicイメージから以下を検索します。
      • amzn-ami-hvm-2016.03.3.x86_64-gp2
  2. ログイン後、取り敢えず環境をアップデートしておきます。
    • sudo yum update
  3. Mecabコンパイルに必要なソフトウェアをインストールします。
    • sudo yum install gcc-c++
    • (iconvは最初から入っているので明示的にインストールする必要はありません。)
  4. MeCabのダウンロード (ここはEC2内でも外でも大丈夫です。適宜DLしてEC2インスタンスへ持っていきましょう。)
    • 本体のコードをダウンロードします。
      • 上記の本家のサイトからダウンロードしてください。
      • mecab-0.XXX.tar がDLできると思います。
    • 辞書(IPA dic)をダウンロードします。
      • 同じく本家のサイトからダウンロードしてください。
      • mecab-ipadic-X.X.X-yyyymmdd.tar がダウンロードできると思います。
  5. MeCabをインストールするためのディレクトリを作成します。
    • /varへ移動 ( cd /var)
    • lambdaが実行される時のフォルダを作成 (sudo mkdir task)
    • mecabをインストールするためのフォルダを作成 (sudo mkdir task/mecab)
      • これで /var/task/mecabというフォルダができました。
  6. MeCabのビルド
    • mecab…tarを解凍します。
      • tar xvf [filename]
    • 解凍後のフォルダ内に移動します。
    • configureを実行します。
      • ./configure --prefix=/var/task/mecab --with-charset=utf8
      • prefixで、インストール先を指定します。
      • with-charsetで、エンコードを指定します。今回は、UTF-8にします。
    • ビルドします。 make と打つだけです。
    • インストールします。 make install で /var/task/mecab配下にコピーされるはずです。
  7. IPAdicのビルド
    • IPAdicのビルドもMeCabと同様です。
    • 差分は、cofigureの時のパラメータです。
      • 辞書は、./configure --prefix=/var/task/mecab --with-charset=utf8 --with-mecab-config=/var/task/mecab/bin/mecab-config でconfigureを実行します。
  8. できたバイナリ(/var/task/mecab配下)を、一旦ローカルのコンピュータへコピーしておきます。
    • ここでは、作業用ディレクトリ内に、mecabというフォルダを作成して一式保存することにします。
  9. 確認します。
    • /var/task/mecab/bin/mecab を実行した後
    • 任意の日本語文字列を入力します。
      • 例えば、「あらゆる現実を全て自分の方へねじ曲げたのだ。」と入力すると、以下の出力が得られます。
あらゆる	連体詞,*,*,*,*,*,あらゆる,アラユル,アラユル
現実	名詞,一般,*,*,*,*,現実,ゲンジツ,ゲンジツ
を	助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
全て	名詞,副詞可能,*,*,*,*,全て,スベテ,スベテ
自分	名詞,一般,*,*,*,*,自分,ジブン,ジブン
の	助詞,連体化,*,*,*,*,の,ノ,ノ
方	名詞,非自立,一般,*,*,*,方,ホウ,ホー
へ	助詞,格助詞,一般,*,*,*,へ,ヘ,エ
ねじ曲げ	動詞,自立,*,*,一段,連用形,ねじ曲げる,ネジマゲ,ネジマゲ
た	助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
の	名詞,非自立,一般,*,*,*,の,ノ,ノ
だ	助動詞,*,*,*,特殊・ダ,基本形,だ,ダ,ダ
。	記号,句点,*,*,*,*,。,。,。
EOS

lambda上での実行

  1. AWS lambda functionを作成します。
  2. 作成したlambda functionのコードをローカルへコピーします。
    • コマンドラインから下記を実行すると、ソースコードをダウンロードするための署名付きURLが発行されます。
      • aws lambda get-function --function-name [作成したlambda関数] --region [作成したリージョン] --profile [利用するプロファイル(defaultを使う場合は略)]
    • 発行されたURL (Code.Locationの中身)をブラウザへコピーするなどしてコードを落とします。
      • 恐らく初回であれば、index.jsというファイルが1つ落ちてくると思います。
  3. index.jsを編集します。
    • サンプルを下の方に置いておきます。
  4. ファイルをlambdaへ転送します。
    • 転送用にzipファイルを作成します。
    • AWSへ転送します。
      • 例:aws lambda update-function-code --function-name [lamdba関数名] --zip-file fileb://src.zip --region ap-northeast-1
    • MeCabの辞書は重たいので、zipファイルで転送するよりも、S3からコピーすることをオススメします。
  5. 実行する
    • 今までの手順が正しくできていると、以下の結果がlambdaのログに吐かれていると思います。
      • 「あらゆる 連体詞,*,*,*,*,*,あらゆる,アラユル,アラユル」
  • 以下にindex.jsのサンプルを置いておきます(後述の注意事項も合わせて参照願います)。
'use strict';
const exec = require('child_process').exec;
const fs = require('fs')
exports.handler = (event, context, callback) => {
    /*
      OSコマンドインジェクションは避けたいので、
      以下のように、変動要素(解析文字列)を渡すのは危険
      const cmd = 'echo "あらゆる" | /var/task/mecab/bin/mecab' 
    */

    fs.writeFileSync("/tmp/input.txt","あらゆる");
    const cmd = '/var/task/mecab/bin/mecab /tmp/input.txt';
    /*
      lambda実行時は、/tmp配下以外は書き込めないので注意
     */
    
    exec(cmd , (err, stdout, stderr) => {
	if (err) {
	    console.error(err);
	    callback(null, "error");
	    return;
	}
	
	console.log(stdout); //ここでmecabの実行結果が表示される。
	callback(null, "done");
    });
};
  • 注意事項

execコマンドは、便利ですが、最終的に sh -c "/var/task/mecab/bin/mecab" と言うように入力がシェルに渡ります。
つまり OS コマンド インジェクションに注意しないといけません。
今回は、固定文字列(「あらゆる」)なので問題ないですが、実際は、任意の文字列を渡したいと思います。
その場合、外部からの入力がコマンドラインのパラメータに渡らないように注意してください。
例えば、形態素解析したい文字列は、ファイルへ書き出しておき(input.txt)、execコマンドで実行するのは、「mecab input.txt 」と言うように固定してしまうことです。
もちろん、正攻法で、サニタイズするのもありです。

mecabを実行しようとしてエラーが出た場合

  • lambdaを実行しようとして、libmecab.so.2 がない(no such file or directory)と言って怒られた場合
    • それは、EC2で、make installした時に、しれっとリンク (ln -s -f libmecab.so.2.0.0 libmecab.so.2) が作成されていたのに、lambdaでは作成していないからです。
      • なので、mecab/lib/libmecab.so.2.0.0 を libmecab.so.2 にrenameしてからlambdaへ持って行くか、mecab実行前にlnでリンクを作成するなどしてください。
      • エラー例:error while loading shared libraries: libmecab.so.2: cannot open shared object file: No such file or directory