Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

AWS LambdaでSVGの日本語を文字化けさせずにラスタライズ変換する

モチベーション

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ファイルを扱えないので、別途ビルドする必要がある
  • rsvg-convert を利用する
    • そのために用意されたAWS Lambda Layer を利用して、Lambda上で rsvg-convert -o [PNGファイル] [SVGファイル] を実行する
    • 残念ながら、このコマンドではフォントパスを指定するところがない
    • fontconfig を利用しているので、適切に fontconfig で利用するフォントのパスを指定できれば日本語でのラスタライズが可能

これらの方法を検討し、

  • 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.confresult/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フォーラムの記事 を参考に、このディレクトリをフォントディレクトリとして読み込むように設定します。

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 [自分のプロファイル名] を足してください
  • Layerとして利用する zip ファイルを配置する S3 Bucket を作成しておくこと

また、デフォルトのテンプレート設定では node10.x でしか利用できないようになっているので、自分の利用するランタイムに合わせた設定を template.yaml に書き加えてください。 例えば、 Python3.8 (※3.7以前は Base が Amazon Linux であり、このレイヤーは利用不可)の場合は以下のように CompatibleRuntimespython3.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 の性質から resultbuild のファイルの中身を変えただけでは更新してくれないことがあります。
その場合、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 というところがあるので、ここをクリックした後に画面乗に現れる「追加」ボタンから設定
  • 関数の環境変数に 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 を便利に使う方法について知ることができたので良かったです。

t-kigi
仕事上で引っかかった技術的事項のメモ帳として使います。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away