モチベーション
SVGから別のラスタライズ形式(ここではPNGとする)への変換をAWS Lambdaで行いたい。 ただし、この手の変換ではフォントを指定しないと日本語が文字化けすることが多々ある。
そのための方法についてを調査して、自分なりにこれで行く、という方針を定めたのでここにメモする。
AWS Lambda Layer とは
今回は環境設定に AWS Lambda Layer を利用しているので最初にこれの説明を行う。
AWS Lambda Layer は一言でいえば複数のLambdaで共用可能なリソースセット。 LambdaごとにどのLayerを利用するか最大で5個まで指定できる。
より具体的に言うと、事前に S3 にアップロードしたコンテンツを Lambda の実行時に /opt
以下に展開してくれるサービス。 1 Lambda の容量制限は 50MB だが、Layer + Lambda の場合合計で 250MB まで可能。
一般的なユースケースは、特定プログラミング言語のライブラリであったり、事前にLambda用の環境で make したバイナリファイルなどをアップロードしておき、メインの関数が肥大化しないような設計を可能とする。
今回の検討段階で上がったものは、実行可能バイナリ( ImageMagick の convert
など)を作成している。
調査の変遷
- AWS Lambda で ImageMagick を利用する
-
そのために用意されたAWS Lambda Layer を利用して、Lambda上で
convert -f [fontファイルパス] [SVGファイル] [PNGファイル]
を実行する - Python の場合なら
subprocess.check_call(['convert', '-f', ...])
のようにする - 上記 Layer を使った場合、
/opt/bin/convert
と実行ファイルが作られ、/opt/bin
以下に自動的にパスが通るようになる - ただ、上記のGithubを見れば分かるが、上記のLayerのデフォルトセットではSVGファイルを扱えないので、別途ビルドする必要がある
-
そのために用意されたAWS Lambda Layer を利用して、Lambda上で
- rsvg-convert を利用する
-
そのために用意されたAWS Lambda Layer を利用して、Lambda上で
rsvg-convert -o [PNGファイル] [SVGファイル]
を実行する - 残念ながら、このコマンドではフォントパスを指定するところがない
- fontconfig を利用しているので、適切に fontconfig で利用するフォントのパスを指定できれば日本語でのラスタライズが可能
-
そのために用意されたAWS Lambda Layer を利用して、Lambda上で
これらの方法を検討し、
- Githubにコミットされたままの make スクリプトが利用可能
- Fontセットはファイルサイズが大きいので、Lambda内に入れるよりは、Layerの中に入れてしまったほうが良いと考えた
ため、後者のAWS Lambda Layerをカスタマイズすることにした。
Layerのビルド方法
前提として、make, git, docker がローカルにインストールされていて、docker サービスが動いていること。
Lambda が動作している OS の Docker Image を元にビルドすることで、AWS Lambda でも動作するバイナリをビルドすることができる。
git clone git@github.com:serverlesspub/rsvg-convert-aws-lambda-binary.git
cd rsvg-convert-aws-lambda-binary
# ビルド開始, 割と時間がかかる
make all
FTPで取得しているところが curl でダウンロードできなかったので https のURLに書き換えたところがあったが、基本はこれそのままでビルド可能です。
ただ、結構バージョンが古いので、新しいバージョンにしたい場合は Makefile_Rsvg の先頭にバージョン定義があるので、ここを書き換えると良いです。
make が完了した後、 result
以下が成果物です。 このディレクトリがAWS Lambda の /opt
以下にマウントされるようになります。 補足として、Docker内でビルドされているため owner は root になっている点に注意してください。
FontConfig の設定と Font のコピー
このレイヤーで利用するフォントと fonts.conf
を result/fonts
以下に設置します。
今回は以下のように配置しました。
検証用として /usr/share/fonts/opentype
以下からサブセットをコピーしましたが、自分の環境で行うときは、自分の利用するフォントを配置してください。
また、AWS Lambda Layerを利用する場合、Lambda と合わせても合計容量は 250MB までですので、必要なフォントを厳選するようにしてください。
fonts
├── fonts.conf
└── opentype
└── noto
├── NotoSansCJK-Bold.ttc
├── NotoSansCJK-Medium.ttc
├── NotoSansCJK-Regular.ttc
└── NotoSansCJK-Thin.ttc
fonts.conf
は フォーラムの記事 を参考に、このディレクトリをフォントディレクトリとして読み込むように設定します。
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<dir>/opt/fonts/</dir>
<cachedir>/tmp/fonts-cache/</cachedir>
<config></config>
</fontconfig>
AWS Lambda Layer のデプロイ
事前準備
以下の通り make コマンドでデプロイしますが、以下の事前準備が必要です。
- awscli のインストールが必要です。
- 該当AWSアカウントに default profile でアクセス可能であること
- もし profile を使いたい場合、 Makefile の該当コマンドに
--profile [自分のプロファイル名]
を足してください
- もし profile を使いたい場合、 Makefile の該当コマンドに
- Layerとして利用する zip ファイルを配置する S3 Bucket を作成しておくこと
また、デフォルトのテンプレート設定では node10.x でしか利用できないようになっているので、自分の利用するランタイムに合わせた設定を template.yaml
に書き加えてください。 例えば、 Python3.8 (※3.7以前は Base が Amazon Linux であり、このレイヤーは利用不可)の場合は以下のように CompatibleRuntimes
に python3.8
を書き加えます。
Resources:
RsvgConvertLayer:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: rsvg-convert
Description: Static build of rsvg-convert (librsvg) for Amazon Linux 2
ContentUri: result/
CompatibleRuntimes:
- nodejs10.x
- nodejs12.x
- python3.8
LicenseInfo: https://gitlab.gnome.org/GNOME/librsvg/blob/master/COPYING
RetentionPolicy: Retain
AWS へのデプロイ
準備ができたら、以下のコマンドを実行します。
make deploy DEPLOYMENT_BUCKET=[準備したS3 Bucket]
これで現在の設定から必要なCloudFormation用テンプレートを作成して、これを使って新しいスタックをデプロイし、新しいLayerを作成することができます。
なお、make の性質から result
や build
のファイルの中身を変えただけでは更新してくれないことがあります。
その場合、sudo rm -f build/output.yaml
として、ファイルの状態を変化させてから再度デプロイしましょう。
AWS Lambdaの設定
rsvg-convert
を利用する Lambda 関数で以下の設定を行います。
- 関数に今回デプロイしたレイヤー (
rsvg-convert
) を設定する- AWSCLI:
aws lambda update-function-configuration --function-name my-function --layers arn:aws:lambda:us-east-2:123456789012:layer:my-layer:3
のようにして設定 - Management Console で関数を開き、デザイナーの関数の下に Layer というところがあるので、ここをクリックした後に画面乗に現れる「追加」ボタンから設定
- AWSCLI:
- 関数の環境変数に
FONTCONFIG_PATH=/opt/fonts
を設定する- これがないとアップロードしたフォントは読み込まれない
実行
以上の設定を行った AWS Lambda 上のコードでコマンドを実行すれば大丈夫です。
例えば、Python 3.8の場合だと、以下のような利用方法が考えられます。
import subprocess
def handler_name(event, context):
# 何らかの方法で svg を /tmp 以下に持ってくる
svgpath = '/tmp/input.svg'
pngpath = '/tmp/output.png'
subprocess.check_call(['rsvg-convert', svgpath, '-o', pngpath])
# 生成した PNG ファイルを S3にアップロードするなどして保存
return
まとめ
最初からフルビルドの ImageMagick インストール済のインスタンス用意したほうが早かったような気はします、が、Lambdaの安定性と未使用時のインフラコストは捨てがたかったのでそれなりに調査しました。
ただ、この機会に AWS Lambda Layer を便利に使う方法について知ることができたので良かったです。