あきろぐ

いろいろめもするよ🐈🐈🐈

AWS LambdaでExifToolを使う Ruby編

何をしようとしたか

Lambda上で動画や画像のメタデータ取得するためにExifToolのRubyラッパーツールを導入しました。 しかし、ExifToolをLambda上で実行するには一筋縄ではいかなかったので、その解決方法をまとめてみます。

環境

  • AWS Lambda
  • Ruby3.2
  • serverless framework

ExifToolをRubyで扱うために以下のgemを使用しました。

github.com

発生した問題:Exiftoolの実行ファイルが存在しないと怒られる

READMEを参考にGemfile内にexiftool_vendoredを追加した後、

gem 'exiftool_vendored'

以下のコマンドで必要なgemを指定パスにインストールしました。

bundle install --path

Exiftoolを使用して動画や画像のメタデータを取得するコードを記述します。

# sample code
require 'exiftool_vendored`

exiftool = Exiftool.new("tmp/test.jpg").to_hash
# 画像の縦横の長さを取得
width = exiftool[:width]
height = exiftool[:height]

serverless frameworkを用いてLambdaにデプロイし、実際にLambdaを実行すると、以下のようなExiftool::ExiftoolNotInstalledエラーが発生します。

Exiftool::ExiftoolNotInstalled
"/var/task/vendor/bundle/ruby/3.2.0/gems/exiftool-1.2.4/lib/exiftool.rb:60:in `initialize'"
...
...
...

該当するソースコードを参照するとcmdの中身が空である場合ExiftoolNotInstalledエラーになっているようです。

    # I'd like to use -dateformat, but it doesn't support timezone offsets properly,
    # nor sub-second timestamps.
    cmd = "#{self.class.command} #{exiftool_opts} -j -coordFormat \"%.8f\" #{escaped_filenames} 2> /dev/null"
    json = `#{cmd}`.chomp
    raise ExiftoolNotInstalled if json == ''

https://github.com/exiftool-rb/exiftool.rb/blob/011d12b4f51e0e86a0d73c43ba2efe5138b6d698/lib/exiftool.rb#L60

Exiftool.commandでExiftoolの実行ファイルパスを出力してみましたが、以下のように実行ファイルパスが返ってきますし、実行ファイルはLambdaにアップロードしたファイル群にも含まれています。

"/var/task/vendor/bundle/ruby/3.2.0/gems/exiftool_vendored-12.68.0/bin/exiftool"

しかし、Exiftool.exiftool_installed?を実行するとfalseが返ってくる謎の状態となっていました。

原因

ExifToolは、Perlライブラリであり実行環境にPerlがインストールされていないといけません。すなわち、Lambdaの実行環境(ランタイムがRubyの場合)にはデフォルトでPerlが入っていかなったため上記のエラーが発生していました。

ExifTool is a platform-independent Perl library plus a command-line application for reading, writing and editing meta information in a wide variety of files.

exiftool.org

そのため、ExifToolを実行するにはLambda上にPerlをインストールする必要があります。

解決方法

今回は、Lambda Layerを使ってPerlとExiftoolのレイヤーを作成しました。レイヤーの作成方法は以下のブログを参考にしました。

dev.to

上記のブログと異なるのは、デプロイするリージョンはTokyoリージョンなので、Tokyoリージョンのパブリックレイヤー(arn:aws:lambda:ap-northeast-1:445285296882:layer:perl-5-38-runtime-al2-x86_64:2)を使用しました。

shogo82148.github.io

serveless.ymlには対象のhandlerにlayersを追加すればOKです。

    handler: handler.main
    layers:
      - arn:aws:lambda:ap-northeast-1:445285296882:layer:perl-5-38-runtime-al2-x86_64:2 # public layer
      - arn:aws:lambda:ap-northeast-1:<account_id>:layer:exiftool:1 # custom layer

そして、ExifToolを実行するために参照するパスをExiftool.commandで指定しておきます。

# sample code
require 'exiftool_vendored`

#exiftoolはperlが必要なためlambda layer上のexiftoolを指定
Exiftool.command = '/opt/bin/perl /opt/bin/exiftool' 

exiftool = Exiftool.new("tmp/test.jpg").to_hash
# 画像の縦横の長さを取得
width = exiftool[:width]
height = exiftool[:height]

この状態でLambdaをデプロイ&実行すれば、問題なく画像のメタデータを取得することができました。

参考文献