Introduction
2023年5月6日追記
mecabのインストール部分のDockerfileが急に動かなくなったので、追記しました。
詳細は最下部の追記
から確認ください。
きっかけ
mecabで言語解析して、Wordcloudへ出力したかった「だけ」ですが、この「だけ」に大幅に苦労させられたという話です。
早い話が、こちらの記事の内容をAWS Lambda上で再現したいといったイメージです。1
自分のPC上で出来ることをLambdaの上でやると、大変に苦労することになりました。
なお、今回はやり方を完全に紹介するというわけでなく、落とし穴部分をかいつまんで解説2していきます。ソースコードをもとに解説するといったこともほとんどしません。今回の記事自体わりとニーズは少ないと思っているからです。
しかし自分がつまづいた時に、日本語フォントをLambda上で扱う記事があまり見当たらなかったので、備忘録として残しておこうとの意図です。
そのため、参考記事は多めに載せました。似たようなことで悩む方へ届けば良いなと思っております。
結論
「Lambdaへのファイルアクセス」と「s3とVPCの関係性」の2つに気がついていれば、ここまで苦労しませんでした。
しかし、後になってみればなんとでも言えるというやつですね。
この二つのキーワードで大体のことが想像できる方には、この記事は不要だと思います。大変に羨ましいです。
AWS関連の知識を持っていれば、もっと早く気づけたかもしれませんが、自分はDVAしか持ってないのでね。
本日のお品書き
・mecabをAWS Lambdaで動かす方法
・wordcloudの日本語フォント問題
・Dockerfileでアクセス制限を取っ払ってみた
・s3へアクセス出来てない?
対象者
・Lambdaの初心者の方
・1を聞いて10を理解できるエンジニア
・Lambda上で、日本語フォントを使いたい方
・Macユーザーの人
・ある程度AWSに抵抗がない方
非対象者
・Lambdaが超お得意な人
・説明下手な筆者を攻撃しようとするエンジニア
・ローカル上で、日本語フォントを使いたい方
・Windowsユーザーの人
・AWS初心者の方
自己紹介
環境情報
macOS Monterey ver:12.6.4
aws-cli/2.2.34 Python/3.8.8 Darwin/21.6.0 exe/x86_64 prompt/off
node v18.7.0
npm 9.2.0
Python 3.9.13
最近はPipenvを使っています。
Pipenvに関する詳細は、こちらの記事がわかりやすかったので、リンクを貼っておきます。(古い記事ですのでご注意ください)
要約すると仮想環境でいい感じにライブラリを管理してくれるバージョン管理システムです。
Python2系とかPython3系の意味が分からない人へ!
Pythonには2系と3系があって、最近始めた人ならほとんどPython3系だと思います。一応バージョンを確認する方法を記載しておきます。 MacOSに入っているターミナル(Terminal)でPythonのインタラクティブ(対話)モードを起動すればバージョン情報が表示されます。$ Python
> Python 3.9.13 (main, Aug 17 2022, 11:46:02)
[Clang 13.1.6 (clang-1316.0.21.2.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
インタラクティブモードを辞めるには*exit()*を入力しましょう!
Let's Start
今回の記事は、VSCode上での実装です。
mecabをAWS Lambdaで動かす方法
mecabのインストールなど環境構築部分に関しては、概ね下記の記事に従って作成しました。当初はEC2でなんとか出来るかと思ったりもしましたが、LambdaからのEC2起動実行関連3が大変そうに感じたので、おとなしく巨人の肩に乗りました。
EC2でなんとか出来るかと思ったりもしていた当初↓
元々の記事では、requirement.txtを用いていますが、自分はpipenvで仮想環境を管理することが多い4ので、そちらの方法にDockerfileを修正しました。Pythonも3.8ではなく、3.9に更新しました。
- FROM public.ecr.aws/lambda/python:3.8
+ FROM public.ecr.aws/lambda/python:3.9
# install build libs
RUN yum groupinstall -y "Development Tools" \
&& yum install -y which openssl
# install mecab, ipadic, ipadic-neologd
WORKDIR /tmp
- RUN curl -L "https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7cENtOXlicTFaRUE" -o mecab-0.996.tar.gz \
+ RUN curl -L "https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7cENtOXlicTFaRUE&confirm=t" -o mecab-0.996.tar.gz \
&& tar xzf mecab-0.996.tar.gz \
&& cd mecab-0.996 \
&& ./configure \
&& make \
&& make check \
&& make install \
&& cd .. \
&& rm -rf mecab-0.996*
WORKDIR /tmp
RUN curl -L "https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7MWVlSDBCSXZMTXM" -o mecab-ipadic-2.7.0-20070801.tar.gz \
&& tar -zxvf mecab-ipadic-2.7.0-20070801.tar.gz \
&& cd mecab-ipadic-2.7.0-20070801 \
&& ./configure --with-charset=utf8 \
&& make \
&& make install \
&& cd .. \
&& rm -rf mecab-ipadic-2.7.0-20070801
WORKDIR /tmp
RUN git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git \
&& cd mecab-ipadic-neologd \
&& ./bin/install-mecab-ipadic-neologd -n -a -y \
&& rm -rf mecab-ipadic-neologd
# setup python
- COPY ./requirements.txt /opt/
- RUN pip install --upgrade pip && pip install -r /opt/requirements.txt
+ COPY Pipfile /
+ COPY Pipfile.lock /
+ RUN pip install --upgrade pip && pip install pipenv && pipenv install --system --deploy
# set function code
WORKDIR /var/task
COPY app.py .
CMD ["app.lambda_handler"]
wordcloudの日本語フォント問題
WordCloudで日本語を扱うためには、日本語フォントが必要です。5
Lambda上でも同様です。
いくつかやり方があるようですが、せっかくECRを使っているので、日本語フォントをそのままインストールして、Lambdaから読み込む方法にしましょう。
日本語フォントは、IPAexフォントを使うことにします。
FROM public.ecr.aws/lambda/python:3.9
# install build libs
RUN yum groupinstall -y "Development Tools" \
&& yum install -y which openssl
# install mecab, ipadic, ipadic-neologd
WORKDIR /tmp
RUN curl -L "https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7cENtOXlicTFaRUE&confirm=t" -o mecab-0.996.tar.gz \
&& tar xzf mecab-0.996.tar.gz \
&& cd mecab-0.996 \
&& ./configure \
&& make \
&& make check \
&& make install \
&& cd .. \
&& rm -rf mecab-0.996*
WORKDIR /tmp
RUN curl -L "https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7MWVlSDBCSXZMTXM" -o mecab-ipadic-2.7.0-20070801.tar.gz \
&& tar -zxvf mecab-ipadic-2.7.0-20070801.tar.gz \
&& cd mecab-ipadic-2.7.0-20070801 \
&& ./configure --with-charset=utf8 \
&& make \
&& make install \
&& cd .. \
&& rm -rf mecab-ipadic-2.7.0-20070801
WORKDIR /tmp
RUN git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git \
&& cd mecab-ipadic-neologd \
&& ./bin/install-mecab-ipadic-neologd -n -a -y \
&& rm -rf mecab-ipadic-neologd
+ # 日本語フォントの追加
+ WORKDIR /tmp
+ RUN curl -LO "https://moji.or.jp/wp- content/ipafont/IPAexfont/IPAexfont00401.zip" \
+ && unzip IPAexfont00401.zip \
+ && mkdir -p /usr/share/fonts/ipa \
+ && cp IPAexfont00401/*.ttf /usr/share/fonts/ipa/ \
+ && fc-cache -fv \
+ && rm -rf IPAexfont00401.zip IPAexfont00401/
# setup python
COPY Pipfile /
COPY Pipfile.lock /
RUN pip install --upgrade pip && pip install pipenv && pipenv install --system --deploy
# set function code
WORKDIR /var/task
COPY app.py .
CMD ["app.lambda_handler"]
Lambdaから日本語フォントを使おうとすると、OS Errorが出力されました。
これが辛かったですね。
下記のエラーですね。もはや何度見たことか。。
"errorMessage": "cannot open resource",
"errorType": "OSError",
<以下省略>
要するに、Pythonの実行環境であるLambdaから、fontファイルを開くことができないとのことでした。
そこで、ひとまずOSErrorを出力されたので、そもそもOS内にフォントファイルがあるかを確認しました。
os.walk()
という関数を初めて使ったので、ここに記しておきますね。
for root, dirs, files in os.walk('/usr/share/fonts/ipa'):
for file in files:
print(os.path.join(root, file))
# -> /usr/share/fonts/ipa/.uuid
# -> /usr/share/fonts/ipa/ipaexg.ttf
# -> /usr/share/fonts/ipa/ipaexm.ttf
Dockerfileでアクセス制限を取っ払ってみた
フォントファイル自体はあることがわかったので、どうにか該当のディレクトリのアクセス権限を付与してみることにしました。
FROM public.ecr.aws/lambda/python:3.9
# install build libs
RUN yum groupinstall -y "Development Tools" \
&& yum install -y which openssl
# install mecab, ipadic, ipadic-neologd
WORKDIR /tmp
RUN curl -L "https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7cENtOXlicTFaRUE&confirm=t" -o mecab-0.996.tar.gz \
&& tar xzf mecab-0.996.tar.gz \
&& cd mecab-0.996 \
&& ./configure \
&& make \
&& make check \
&& make install \
&& cd .. \
&& rm -rf mecab-0.996*
WORKDIR /tmp
RUN curl -L "https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7MWVlSDBCSXZMTXM" -o mecab-ipadic-2.7.0-20070801.tar.gz \
&& tar -zxvf mecab-ipadic-2.7.0-20070801.tar.gz \
&& cd mecab-ipadic-2.7.0-20070801 \
&& ./configure --with-charset=utf8 \
&& make \
&& make install \
&& cd .. \
&& rm -rf mecab-ipadic-2.7.0-20070801
WORKDIR /tmp
RUN git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git \
&& cd mecab-ipadic-neologd \
&& ./bin/install-mecab-ipadic-neologd -n -a -y \
&& rm -rf mecab-ipadic-neologd
# 日本語フォントの追加
WORKDIR /tmp
RUN curl -LO "https://moji.or.jp/wp-content/ipafont/IPAexfont/IPAexfont00401.zip" \
&& unzip IPAexfont00401.zip \
&& mkdir -p /usr/share/fonts/ipa \
&& cp IPAexfont00401/*.ttf /usr/share/fonts/ipa/ \
+ && chmod -R a+rX /usr/share/fonts/ipa/ \
&& fc-cache -fv \
&& rm -rf IPAexfont00401.zip IPAexfont00401/
# setup python
COPY Pipfile /
COPY Pipfile.lock /
RUN pip install --upgrade pip && pip install pipenv && pipenv install --system --deploy
# set function code
WORKDIR /var/task
COPY app.py .
CMD ["app.lambda_handler"]
s3へアクセス出来てない?
OS Errorを解決した後、色々と試していた結果、どうやらそもそもs3へアクセス出来ていないことが判明しました。というのも、Dockerfileでアクセス制限を取っ払ってみたあとは、Lambdaがtimeoutで落ちるようになったのです。
逆にいえば、エラーが吐かれなくなってしまいエラーログの情報がなくなったのです。
さらに、IAM関連は700億回くらい見直したので、まさかs3へアクセス出来ないことに起因する理由とは思いづらかったわけです。Lambdaからs3へのアクセスとVPC内のLambdaからs3へのアクセスによって挙動が変わることを失念していましたが、なかなか。。
少しでも思いあたれば、chatGPTに聞くこともできるわけで、今回は思い当たるまでに時間がかかりました。
VPC内のLambdaからs3へのアクセス方法に関しては、いくつも解決法があります。
同様の事例で苦しんでいる皆様の解決方法の一つとなったら、幸いです。
VPCから外に出せば?
と言う声が聞こえてきそうですが、今回の仕様はDBから文言を取得したものを言語処理して、Wordcloudに出力する必要がありました。
LAmbdaからDBへアクセスするには、LambdaをVPCの中に配置する必要があるため、VPCの外に出すという選択肢は取れませんでした。
To be Continued
再会のゆびきり
今後もQiitaでデータの可視化など技術情報を発信しています。
noteでは、備忘録を記録しています。
新着情報はTwitterで配信いたします。フォローをお願いいたします。
Sempleの自由帳
Sempleのアイデア帳
Sempleのツイッター
困っていること
ECRの使い方がわかりません。
記事の内容から概ねわかるとは思いますが、筆者はECRの使用経験がありません。
コンテナも雰囲気で理解している程度です。
Lambdaをserverless Frameworkで利用する際には、AWS Lambdaのマネジメントコンソール上で、Pythonコードをいじることができるんですが、ECRのコンテナイメージとしてデプロイしている場合は、Lambdaのマネジメントコンソール上で、操作できないものなんですかね。
せめてapp.pyからECRのライブラリをimportする形にできればありがたいんですけどね。
何か助言いただけるとありがたいです。
現状は、Pythonコードを変更するたびに、参考記事の下記コマンドを打っています。
docker build -t pymecab-lambda-container-dev ./
docker tag pymecab-lambda-container-dev:latest {accountID}.dkr.ecr.{region}.amazonaws.com/{repository}:latest
docker push {accountID}.dkr.ecr.{region}.amazonaws.com/{repository}:latest
取得したdigestの値をserverless.ymlにコピペしてsls deploy
でデプロイしています。
ただ、この作業は、もっと簡単にならないものでしょうか?
想像ですが、
- Dockerfileからapp.pyを分離する。
- ECR内のmecabを利用したり、その他ライブラリ等を読み込むPythonファイルをServerless.ymlでデプロイする
といった仕様へ変えたいと考えています。
しかしやり方がわからず、毎回ECRコンテナをpushするという苦行をこなしています。コンテナの更新に5分程度かかるのとdigestの値を手動で変更するのが悲しいところです。
詳しい諸兄の皆様、教えてください。
解決につながらなかったが、試してみたこと
日本語フォントで詰まった方は、wordcloud以外にmatplotlibのフォントで悩んでいる方も多かったので、探される時には検索ワードに追加してみてください。
本文では、wordcloudの日本語フォント問題 -> Dockerfileでアクセス制限を取っ払ってみた
この部分をすぐに気づけたように感じるかもしれませんが、そんなことは断じてありません
というのを記しておきます。苦闘の跡に興味のある方は、ぜひご覧ください💕
Lambdaは全てのディレクトリがアクセス出来ないのか?(OSError)
-> 出来ない。全て同じ結果。
usr/local
がダメなら、/opt
配下ならどうか?
/opt
配下がダメなら、/var/task
配下ならどうか?
Dockerfileの日本語フォント部分のインストール箇所を色々といじってみたりしました。
変わらずOSErrorでしたね。悲しいことです。
Lambdaのフォント設定は、追加/上書き出来ないものか。(OSError)
-> 出来ない。
いくつか検索してみましたが、解決の糸口にはなり得ませんでした。
FONTCONFIG_PATH: 'usr/share/fonts/ipa'
構成イメージは下記のようなものです。(font.confを配置してみたり、)
mecab_wordcloud
├─Pipfile
├─Pipfile.lock
├─function/
├─resource
│ ├─font.conf
│ ├─ipaexg.ttf
│ └─ipaexm.ttf
├─serverless.yml
└─Dockerfile
[奥の手]Lambda LayersへZipファイルで日本語フォントを配置する。
日本語フォントを使う場合は、Lambda Layersへzip形式でuploadする方法が良いらしいです。
-> Lambda with Container Imageでは、Lambda layersを追加できませんでした。
日本語フォントのサイトへアクセスして、最新版の日本語フォントをローカルへダウンロードします。
ZipファイルのままLambdaへアップロードします。意気揚々とARNをコピーして、該当のLambdaを開きましたが、Lambda layersを追加する画面が見当たりませんでした。奥の手が通用しなくて膝から崩れ落ちました。
[奥の手]s3にあげた日本語フォントを読み取る。
-> 本文で解説済みですが、そもそもs3へアクセス出来ていなかったので、無理ですね。
とは言いつつ、VPC Lambdaからs3へのアクセス完了した後の状態ならどうなるのかを検証してみました。
-> 出来ます。
手動でs3へfontsをアップロードして、Pythonから該当バケットへ日本語フォントファイルを取得します。
参考コードを下記に貼り付けておきます。
def make_wordcloud(text):
s3_client = boto3.client('s3')
bucket_name = 'your-bucket'
font_path = 'fonts'
font_file = 'ipaexg.ttf'
# s3上のファイルをLambdaの/tmpフォルダにダウンロードする
s3_client.download_file(bucket_name, font_path +
'/' + font_file, '/tmp/' + font_file)
fpath = '/tmp/' + font_file}
# Wordcloudの作成
wordcloud = WordCloud(background_color="white",
font_path=fpath,width=800, height=600).generate(text)
本文記載のDockerfileの方が自分はコード管理できるのでオススメです。しかしこちらはs3にアップロードするだけなので、手軽さはあるかもしれないと思い、一応紹介しておきました。おすすめはしませんが、出来ないわけではないです。6
追記
2023年5月6日時点
(本文では、修正済みです。)
冒頭に記載の通り、Dockerfileが急に動作しなくなって、原因を調べたところ、mecabのインストール部分が怪しそうでした。
executor failed running [/bin/sh -c curl -L "https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7cENtOXlicTFaRUE" -o mecab-0.996.tar.gz && tar xzf mecab-0.996.tar.gz && cd mecab-0.996 && ./configure && make && make check && make install && cd .. && rm -rf mecab-0.996*]: exit code: 2
Dockerfileのダウンロード部分は、
https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7cENtOXlicTFaRUE
となっていますが、そこからダウンロードしたものがgzip形式でないよ。というエラーのようです。
そこで、URLを叩いて調べたところ、下記のような画面が表示されていました。
本来なら、URLを叩けば直接ダウンロードされるはずですが、確認画面が挟まってしまったことが原因のようです。
そこで、
https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7cENtOXlicTFaRUE&confirm=t
と修正しました。
なお、ChatGPTに相談したところ、下記の通りです。confirm=tの追加は自己責任でお願いいたします。
confirm=t パラメータは、Google Drive でファイルをダウンロードする際に、確認プロンプトをスキップするために使用されることがあります。Google Drive は、特定の条件下で(たとえば、ファイルがウイルススキャンを回避するほど大きい場合)、ファイルのダウンロード前にユーザーに確認を求めます。confirm=t を追加することで、この確認プロンプトを回避してダウンロードを開始できる場合があります。
ただし、このパラメータは公式にはサポートされていないため、今後の変更で動作しなくなる可能性があります。公式なリポジトリやダウンロードページからファイルをダウンロードすることをお勧めします。
また、他に対策等がある場合は、教えてください。
参考記事
-
誰でも
URL入力すれば、歌詞の可視化が出来るWebサービスを作る
といった構想はありません。この記事なら、成果物のイメージを端的に共有しやすかったのです。ちなみに、こちらの記事は作成から3年も経っているのに、今でもTwitterなどで参考にしました!との声をいただけて感涙ものです。この場を借りて、御礼申し上げます。ありがとうございます。 ↩ -
YAMLとDockerfileとPythonをそれぞれ解説なんてしんどいわけで、、、 ↩
-
EC2を呼び出して、解析対象を受け渡して、結果を受け取る。そしてEC2を停止させる。みたいなことをLambdaで全て制御することを考えたら目眩がしたので、やめました。勘違いしてたら申し訳ないですが、僕は富豪ではありません。EC2はこまめに停止するタイプです。少年時代から、使っていない部屋の電気もこまめに消すタイプでした。 ↩
-
requirements.txtでのやり方がわからないとも言う。 ↩
-
豆腐になってしまいますからね。 ↩
-
s3へのIAMの設定等は本文に書いたので、割愛しています。 ↩