MeCab(+IPADIC) + Python3.8 を AWS Lambda 上で利用する

2020年10月13日 Python3.8 に対応しました:tada:

# はじめに
形態素解析を行うライブラリ [MeCab](https://taku910.github.io/mecab/) を用いたアプリケーションをサーバーレスな環境に乗せる機会があり、今後も増えてきそうなので自分用のメモの意味も含めその手順を記載しました。

MeCabをLambda上で実行するためには、Lambdaの実行環境と全く同じ環境でMeCabをビルドし、関数パッケージに含めてデプロイする必要があります。

今回は、DockerとLambdaビルド用のイメージを使って、簡単にMeCabをビルドする方法を紹介します。
※下記のリポジトリよりクローンし、READMEの手順を実行すれば簡単に利用できます。

[[GitHub] noyoikw/mecab-python3-lambda](https://github.com/noyoikw/mecab-python3-lambda)

# 各ファイルの解説
手順の前に、最終的なディレクトリの構成と各ファイルの役割について説明します。

## ディレクトリ構成
このサンプルでは、最終的に以下のようなディレクトリ構成になり、 `function` 配下を Lambda にデプロイすることを目指します。

```
mecab-python3-lambda
├── docker-compose.yaml
├── function
│   └── lambda_function.py
└── requirements.txt
```
## docker-compose.yaml
DockerComposeを用い、`function` 配下にMeCabをビルドします。
Lambdaにパッケージをデプロイすると、内部的には `/var/task` 配下にファイルが置かれるので、それにあわせてビルド先を設定しています。
また、Python3 で MeCab を利用するためのパッケージ `mecab-python3` のインストールも含めます。

```yaml:docker-compose.yaml
version: '3'
services:
  mecab-python3-lambda:
    image: lambci/lambda:build-python3.8
    environment:
      - AWS_DEFAULT_REGION=ap-northeast-1
      - LAMBDA_PACKAGE_DIR=/var/task
      - MECAB_SOURCE_URL=https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7cENtOXlicTFaRUE
      - IPADIC_SOURCE_URL=https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7MWVlSDBCSXZMTXM
      - MECAB_VERSION=0.996
      - IPADIC_VERSION=2.7.0-20070801
      - LIB_MECAB_DIR_NAME=.mecab
    volumes:
      - ./function:/var/task
      - ./requirements.txt:/requirements.txt
    command: >
      /bin/sh -c '
      cd ~ &&
      curl -L $${MECAB_SOURCE_URL} -o mecab.tar.gz &&
      curl -L $${IPADIC_SOURCE_URL} -o mecab-ipadic.tar.gz &&
      tar -zxvf mecab.tar.gz && tar -zxvf mecab-ipadic.tar.gz &&
      cd ~/mecab-$${MECAB_VERSION} && 
      ./configure --prefix=$${LAMBDA_PACKAGE_DIR}/$${LIB_MECAB_DIR_NAME} --with-charset=utf8 &&
      make && make install &&
      cd ~/mecab-ipadic-$${IPADIC_VERSION} && 
      ./configure --prefix=$${LAMBDA_PACKAGE_DIR}/$${LIB_MECAB_DIR_NAME} --with-charset=utf8 --with-mecab-config=$${LAMBDA_PACKAGE_DIR}/$${LIB_MECAB_DIR_NAME}/bin/mecab-config &&
      make && make install &&
      cd ~ && pip3 install -r /requirements.txt -t $${LAMBDA_PACKAGE_DIR}'
```
### image
[dockerhub](https://hub.docker.com)で、[lambci](https://hub.docker.com/u/lambci)が提供する、 `lambci/lambda:build-python3.8` を使用します。
これは、実際のLambda環境を再現したサンドボックス環境のDockerイメージです。
（ランタイムはpython3.8のビルド用のイメージ）

### environment
ディレクトリの設定やバージョンを記載します。
`MECAB_SOURCE_URL` や `IPADIC_SOURCE_URL` はMeCab作者のサイトから削除・変更等があった場合は適宜変更してください。

### volumes
`function` ディレクトリ配下をパッケージとしてデプロイするので、 `/var/task` にマウントします。
`requirements.txt` は、コンテナ側で実行するコマンドで使用するためマウントします。

### command
やっていることは、 `MeCab` と `ipadic` をダウンロード、インストールしているだけです。
最後に `mecab-python3` をインストールします。

## requirements.txt
今回利用するパッケージは `mecab-python3` のみですが、Pythonのコード内で他のパッケージを利用する場合は追記してください。

```txt:requirements.txt
mecab-python3
```

## lambda_function.py
Lambdaにデプロイするファンクションの本体です。
前半部分では、ビルドしたMeCabをライブラリとして使用できるようにロードしています。
`import MeCab` は `mecab-python3` のパッケージをインポートしています。

今回はサンプルとして、ボディにテキストを付与してリクエストすると、形態素解析の結果をレスポンスとして返する関数を作成しました。

```python:lambda_function.py
import json
import os
import ctypes

mecabdir = os.path.join(os.getcwd(), '/var/task/.mecab')
libmecab = ctypes.cdll.LoadLibrary(os.path.join(mecabdir, 'lib/libmecab.so'))

import MeCab

output_format_type = 'chasen'
dicdir = os.path.join(mecabdir, 'lib/mecab/dic/ipadic')
rcfile = os.path.join(mecabdir, 'etc/mecabrc')

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

def lambda_handler(event, context):
    req_body = json.loads(event['body'])
    res_body = {
      'morphemes': tagger.parse(req_body['text'])
    }
    return {
        'statusCode': 200,
        'headers': {
            'Content-Type': 'application/json',
            'Access-Control-Allow-Origin': '*'
        },
        'body': json.dumps(res_body)
    }
```

# ビルドとデプロイ手順
実際に行う手順を解説していきます。

## 1. ビルド
作業フォルダのルートに移動して、下記コマンドを実行します。

```bash
$ docker-compose up
```
指定したDockerイメージがビルドされ、 `docker-compose.yaml` 内に記載したコマンドが実行されます。
しばらくすると、 `function` 配下にMeCabのライブラリとpipでインストールしたパッケージが追加されます。

```
mecab-python3-lambda
├── docker-compose.yaml
├── function
│   ├── .mecab/
│   ├── bin/
│   ├── MeCab/
│   ├── mecab_python3-1.0.1.dist-info/
│   └── lambda_function.py
└── requirements.txt
```

## 2. function配下をzipに圧縮
デプロイ用のパッケージとして、Lambdaにアップロードできる形式（.zip）にします。
下記コマンドを実行すると、 `function` 配下を圧縮した `deploy_package.zip` が生成されます。

```bash
$ cd function && zip -r ../deploy_package.zip * .* 
```

## 3. AWSのコンソールからLambdaにデプロイ
あらかじめLambdaのリソースを作成し、 `関数コード` の項目から、

- コード エントリ タイプ → `.zip ファイルをアップロード`
- ランタイム → `Python 3.8`
- ハンドラ → `lambda_function.lambda_handler`
- 関数パッケージ → `アップロード` をクリックして `deploy_package.zip` を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356162/db26ec95-80ff-3310-3b37-330b200108b2.png)

最後に、最上右部の `保存` をクリックして、デプロイ完了です。

## 4. 動作確認
`3.` の Lambda を API Gateway とつなぎ、APIをコールしてみます。
今回は、ステージ名を `dev` 、リソース名を `mecab` としています。
※ 日本語で確認したいため、 [jq](https://stedolan.github.io/jq/) を利用しています。

```bash
curl -X POST -H "Content-Type: application/json" -d '{"text":"この文章は形態素解析のテストです。"}' https://xxxxxx.execute-api.us-west-2.amazonaws.com/dev/mecab  | jq .
```
以下のようなレスポンスが確認できれば成功です！

```bash
{
  "morphemes": "この\tコノ\tこの\t連体詞\t\t\n文章\tブンショウ\t文章\t名詞-一般\t\t\nは\tハ\tは\t助詞-係助詞\t\t\n形態素\tケイタイソ\t形態素\t名詞-一般\t\t\n解析\tカイセキ\t解析\t名詞-サ変接続\t\t\nの\tノ\tの\t助詞-連体化\t\t\nテスト\tテスト\tテスト\t名詞-サ変接続\t\t\nです\tデス\tです\t助動詞\t特殊・デス\t基本形\n。\t。\t。\t記号-句点\t\t\nEOS\n"
}
```

# 最後に
いかがでしたでしょうか。Lambdaの環境を再現しれくれる便利なDockerイメージのおかげで、簡単にMeCabをビルドできました。
今回の環境構築はあくまでMeCabのビルドを行うためのものなので、MeCabを利用したAPIを開発・運用して行く際には、今回ビルドしたMeCabを開発用環境のリポジトリに置いてしまうのが良いかと思います。

# 参考文献
[MeCab](https://taku910.github.io/mecab/)
[Docker Hub - lambci](https://hub.docker.com/u/lambci)
[AWS Lambda でMeCabを動かす (2018年9月時点）]
(https://qiita.com/rtaguchi/items/e6cb594196fa8186e9a2)
[AWS Lambda x Pythonでのローカル開発環境の再現について](https://qiita.com/i35_267/items/15b981acae8cf7c763ae)