1年ぶりにこんにちは。エンジニアの永田智章です。
みなさん、AWS Lambda触ってますかー?
自分は最近どっぷり浸かりすぎて勝手に社内エバンジェリストみたいになってます・・・。
今回はそんなLambdaのお話。
LambdaでPhantomJSでスクリーンショット撮ろうーと思って
いざやってみるとこんな感じ。日本語フォントが出ない・・・。
いい方法ないかなーと探し回ってみたものの、どこにも見つからない!
AWSのDiscussion Forumsに直接聞いていた方(おそらく中国の方)もいましたが、
なかのひとから「やりたきゃ自前でがんばって」と無慈悲な返答がきています・・・。
で、仕方なく自前でがんばってみた結果、それなりにいい具合に対応できたので手順を公開しますー。
自分はVirtualBox + VagrantでCentOS6.4のVMを用意しましたー。
https://github.com/2creatives/vagrant-centos/releases/tag/v6.4.2
こいつにepel-releaseとかを入れてやるとだいたい幸せになれます。
sudo yum install epel-release
いろいろ調べたところ、PhantomJSのフォント管理はfontconfig依存のようです。
なので、普通のサーバーであれば「fc-cache」とか使って解決できそうなのですが、
もちろんLambdaは許してくれません(sudoもないしホームディレクトリもない)。
書き込みができるのは/tmpの中だけです。
じゃあ、/tmpの中にfontconfigを入れてやって
LD_LIBRARY_PATH指定して実行すればいいんじゃね?
というわけで、まずは手元の環境で/tmp下だけで動くようなfontconfigをつくります。
# 依存ライブラリをインストール(何度も試した結果これらが必要)
sudo yum install gperf freetype-devel libxml2-devel python-lxml
# ソースを落としてディレクトリ移動
git clone http://anongit.freedesktop.org/git/fontconfig
cd fontconfig
# configure
# fontconfigのソースのディレクトリにある「INSTALL」というドキュメントによると
# ./autogen.sh --sysconfdir=/etc --prefix=/usr --mandir=/usr/share/man
# とあるので、以下のようになるようにオプションを変更
# /etc -> /tmp/fontconfig/etc
# /usr -> /tmp/fontconfig/usr
# /usr/share/man -> /tmp/fontconfig/usr/share/man
# あと--enable-libxml2のオプションもないと怒られる
./autogen.sh --sysconfdir=/tmp/fontconfig/etc --prefix=/tmp/fontconfig/usr --mandir=/tmp/fontconfig/usr/share/man --enable-libxml2
# make & install
make
make install
これで想定通り/tmp/fontconfigにetcやらusrやらができましたー。わーい。
さっそく/tmp/fontconfig/etc/fonts/fonts.confの値を書き換えます。
が、fonts.confを開くと「DO NOT EDIT THIS FILE」と書いてあるので、ここは素直に従いましょう・・・。
/tmp/fontconfig/etc/fonts/local.confというファイルを以下の内容で作成します(補足1)。
<?xml version=“1.0”?>
<!DOCTYPE fontconfig SYSTEM “fonts.dtd”>
<fontconfig>
<dir>/tmp/fontconfig/usr/share/fonts</dir>
</fontconfig>
続いて、/tmp/fontconfig/usr/share/fontsというディレクトリを作成し、
IPAフォントとかを突っ込んでおきます。
mkdir /tmp/fontconfig/usr/share/fonts/
curl http://dl.ipafont.ipa.go.jp/IPAexfont/ipaexg00301.zip > ipaexg00301.zip
unzip ipaexg00301.zip
cp ipaexg00301/ipaexg.ttf /tmp/fontconfig/usr/share/fonts/
nodejsでさくっとLambda関数をつくります。
#nodejsとnpmのインストール
sudo yum install nodejs npm
#プロジェクトディレクトリの作成
mkdir phantomjs_japanese
cd phantomjs_japanese
#スクリーンショットを撮るだけなので「webshot」というnpmモジュールを使用
npm install webshot
#さきほど作成したfontconfigをまるっとコピー(実行時に/tmp下にコピーする用)
cp -r /tmp/fontconfig .
そして、以下の内容でindex.jsとwebshot.jsを作成します。エラー処理とかは省略(補足2)。
2つに分けているのは、LD_LIBRARY_PATHを反映させるために別プロセスで動かす必要があるためです。
var exec = require(‘child_process’).exec;
exports.handler = function(event, context) {
// 同梱のfontconfigを/tmp下にコピーしてからフォントキャッシュを作成
exec(‘cp -r fontconfig /tmp; /tmp/fontconfig/usr/bin/fc-cache -fs’, function(error) {
// LD_LIBRARY_PATHを指定してwebshot.jsを別プロセス起動(URLを引数に渡す)
exec(‘LD_LIBRARY_PATH=/tmp/fontconfig/usr/lib/ node webshot.js ‘ + event.url, function(error) {
context.done(error, ‘done’);
})
});
};
(accessKeyId, secretAccessKey, Bucketは環境に合わせて書き換えてください)
var webshot = require(‘webshot’);
var fs = require(‘fs’);
var aws = require(‘aws-sdk’);
var tmp_file_path = ‘/tmp/screen.png’;
// コマンドライン引数のURLのスクリーンショットを/tmp/screen.pngに保存
webshot(process.argv2, tmp_file_path, function(error) {
aws.config.update({accessKeyId: ‘*****’, secretAccessKey: ‘**’});
var params = {Bucket: ‘*****‘, Key: ‘screen.png’, Body: fs.createReadStream(tmp_file_path)}
// /tmp/screen.pngをS3にアップロード
new aws.S3().upload(params, function(error, data) {
process.exit();
});
});
あとはこいつをzipに固めてLambdaに登録して実行するだけです。
最初は余裕を見てメモリは256MB、タイムアウトは20秒ぐらいに設定しておきます。
あとロールにS3へのアクセス権限をつけるのも忘れずにー。
Inputの引数をこんな感じで与えてやって
{“url”: “http://www.rco.recruit.co.jp/blog/"}
テスト実行してみます。
できました!
ちなみに、webshotを使わずに素のPhantomJS(バージョン2系)でも動きました。
以上、日本語フォント対応の方法でしたー。
同様の方法でImageMagick、GraphicsMagickとかの日本語対応とかもいけるやもしれません。
他にもこんな感じでいろんなライブラリを読み込めそうですね(あとは気力と体力の問題)。
fonts.confを直接編集してやると、指定した日本語フォントのみでフォントキャッシュが作成できます。
英字フォントが指定されているページでも、すべて日本語フォントによる表示になってしまいますが、
local.confを追加するのと比較して2500msぐらい処理が速くなりました。
(local.confを追加するとfonts.conf + local.confの内容となるので遅くなる)
/tmp下のファイル名は乱数とかをつけてやらないと、たまにファイル名重複エラーになることがあるそうです。
が、さすがにfontconfigのディレクトリ名をランダムにするのは厳しいので、
ディレクトリ存在チェックでもしてエラーにするしかなさそうかなー。