🐳

AWS CDKでLambda and FFmpegレイヤー環境を構築する

2021/09/07に公開

概要

ffmpegをLambdaで動かす機会があった。
手動でFFmpegのLambdaレイヤーを構築する手順については、AWS公式から紹介されている。
https://aws.amazon.com/jp/blogs/media/processing-user-generated-content-using-aws-lambda-and-ffmpeg/

この手順をCDKで整備してみたので、紹介したい。

構築するゴールイメージ

  • 動画格納用のS3バケット
  • 動画のサムネイル作成をするLambda関数
  • FFmpegのLambdaレイヤー

成果物

本記事で説明しているコードは以下にある。
https://github.com/adito0211/lamba-ffmpeg-sample

環境

terminal
$ sw_vers

ProductName:	macOS
ProductVersion:	11.4
BuildVersion:	20F71

$ cdk --version
1.121.0 (build 026cb8f)

CDKプロジェクトの作成

プロジェクト用ディレクトリを作成し、CDKプロジェクトを作成する。

terminal
mkdir lamba-ffmpeg-sample && cd lamba-ffmpeg-sample
cdk init app --language typescript

必要パッケージをインストール

terminal
npm install \
@aws-cdk/aws-s3 \
@aws-cdk/aws-lambda \
@aws-cdk/aws-s3-notifications

FFmpegのバイナリを準備

AWS公式から紹介されている手順と同様に、https://johnvansickle.com/ffmpeg/ からダウンロードする。

terminal
mkdir -p lambda_layer/ffmpeg && cd lambda_layer/ffmpeg

# download 
wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
tar xvf ffmpeg-release-amd64-static.tar.xz

# 必要ファイルだけ取り出す
mkdir bin
cp ffmpeg-*-static/ffmpeg bin/

# 不要ファイル削除
rm -rf ffmpeg-*

動作確認用のLamda Functionを作成

ffmpegの動作を検証する、lambda関数を作成する。

terminal
cd (PROJECT_ROOT)
mkdir lambda_function

上記ディレクトリに、以下ファイルを配置する。

function.py
import json
import os
import subprocess
import shlex
import boto3

SIGNED_URL_TIMEOUT = 60

def lambda_handler(event, context):

    s3_source_bucket = event['Records'][0]['s3']['bucket']['name']
    s3_source_key = event['Records'][0]['s3']['object']['key']

    s3_source_basename = os.path.splitext(os.path.basename(s3_source_key))[0]
    s3_destination_filename = s3_source_basename + "_thumbnail.jpg"

    s3_client = boto3.client('s3')
    s3_source_signed_url = s3_client.generate_presigned_url('get_object',
        Params={'Bucket': s3_source_bucket, 'Key': s3_source_key},
        ExpiresIn=SIGNED_URL_TIMEOUT)

    ffmpeg_cmd = "/opt/bin/ffmpeg -i \"" + s3_source_signed_url + "\" -f image2 -ss 1 -vframes 1 -vf scale=400:400:force_original_aspect_ratio=decrease -y -"
    command1 = shlex.split(ffmpeg_cmd)
    p1 = subprocess.run(command1, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    resp = s3_client.put_object(Body=p1.stdout, Bucket=s3_source_bucket, Key=s3_destination_filename)

    return {
        'statusCode': 200,
        'body': json.dumps('Processing complete successfully')
    }

Stackを作成

lib/lamba-ffmpeg-sample-stack.tsを以下のように修正する。

lib/lamba-ffmpeg-sample-stack.ts
import * as cdk from '@aws-cdk/core';
import * as s3 from '@aws-cdk/aws-s3';
import * as lambda from '@aws-cdk/aws-lambda'
import * as notifications from '@aws-cdk/aws-s3-notifications';

export class LambaFfmpegSampleStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // 動作検証用のS3を作成
    const videoBucket = new s3.Bucket(this, 'video-bucket');

    // FFmpegのLambdaレイヤーを作成
    const ffmegLayer = new lambda.LayerVersion(this, 'ffmpeg-layer', {
      layerVersionName: 'ffmpeg',
      compatibleRuntimes: [lambda.Runtime.PYTHON_3_8],
      code: lambda.AssetCode.fromAsset('lambda_layer/ffmpeg')
    });

    // Lambda関数を作成
    const videoHandler = new lambda.Function(this, 'video-handler', {
      functionName: 'video-handler',
      runtime: lambda.Runtime.PYTHON_3_8,
      handler: 'function.lambda_handler',
      code: lambda.AssetCode.fromAsset('lambda_function'),
      layers: [ffmegLayer],
      timeout: cdk.Duration.seconds(60),
    });

    // Lambda関数にS3の権限を付与
    videoBucket.grantReadWrite(videoHandler);
    
    // S3トリガーでLambdaを起動
    videoBucket.addObjectCreatedNotification(
      new notifications.LambdaDestination(videoHandler),
      { suffix: '.mp4' }
    )
  }
}

Deploy

これで準備が出来たのでデプロイを実行する。

terminal
cdk deploy

動作確認

CDKで作成されたS3のバケットに、適当な動画をアップロードする。
1分程で、サムネイルが作成されることが確認出来た。

まとめ

CDKを使って、Lambda and FFmpegレイヤー環境を構築することが出来た。
注意点としては、FFmpegのバイナリは、73Mbyteと大きめなので、リポジトリに含めるかどうかは別途検討が必要になるかと思う。

Discussion