3.3 Thumbnailer Container

Now that you have a project configured to use AWS, we can create the thumbnailer. We’ll use the AWS Lambda Containers for this.

Step 1 — Create a Docker Image

Create a new directory within your Pulumi project called app

mkdir app

Next, create a Dockerfile within the app directory and use it to build ffmpeg into the image:

FROM amazon/aws-lambda-nodejs:12
ARG FUNCTION_DIR="/var/task"

# Install tar and xz
RUN yum install tar xz unzip -y

# Install awscli
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" -s
RUN unzip -q awscliv2.zip
RUN ./aws/install

# Install ffmpeg
RUN curl https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o ffmpeg.tar.xz -s
RUN tar -xf ffmpeg.tar.xz
RUN mv ffmpeg-4.3.1-amd64-static/ffmpeg /usr/bin

# Create function directory
RUN mkdir -p ${FUNCTION_DIR}

# Copy handler function and package.json
COPY index.js ${FUNCTION_DIR}

# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "index.handler" ]

You’ll notice you’re specifying an index.handler as the command, but we haven’t defined it yet.

Step 2 — Define your lambda script

Inside the app/ directory, let’s create index.js which becomes your Lambda entrypoint.

'use strict';
const { execSync } = require('child_process');

function run(command) {
    console.log(command);
    const result = execSync(command, {stdio: 'inherit'});
    if (result != null) {
        console.log(result.toString());
    }
}

exports.handler = async (event) => {
    console.log("Video handler called");

    if (!event.Records) {
        return;
    }

    for (const record of event.Records) {
        const fileName = record.s3.object.key;
        const bucketName = record.s3.bucket.name;
        const thumbnailFile = fileName.substring(0, fileName.indexOf("_")) + ".jpg";
        const framePos = fileName.substring(fileName.indexOf("_")+1, fileName.indexOf(".")).replace("-", ":");
        
        run(`aws s3 cp s3://${bucketName}/${fileName} /tmp/${fileName}`);
        run(`ffmpeg -v error -i /tmp/${fileName} -ss ${framePos} -vframes 1 -f image2 -an -y /tmp/${thumbnailFile}`);
        run(`aws s3 cp /tmp/${thumbnailFile} s3://${bucketName}/${thumbnailFile}`);

        console.log(`*** New thumbnail: file ${fileName} was saved at ${record.eventTime}.`);
    }    
};

The app directory should now look like this:

.
├── Dockerfile
└── index.js

Step 3 — Build Docker Image & Push to ECR

Back inside your Pulumi program (index.ts in the directory above the app directory) we now need to build this Docker image and create a place to push it.

We’ll use Pulumi Crosswalk to build the image and push it to the ECR repository. Add the following to index.ts:

const image = awsx.ecr.buildAndPushImage("thumbnailer", {
    context: "./app",
});

The index.ts file should now look like this:

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";

const image = awsx.ecr.buildAndPushImage("thumbnailer", {
    context: "./app",
});

This Pulumi component takes care of creating an ECR repository and building the local container image and pushing it.

Step 4 — Run the Pulumi Program

Run your Pulumi program and inspect the resources it’ll create:

Previewing update (dev)

View Live: https://app.pulumi.com/jaxxstorm/thumbnailer/dev/previews/4a0d6e8e-1b71-4c97-8aae-5bfb5c10d419

     Type                           Name             Plan
 +   pulumi:pulumi:Stack            thumbnailer-dev  create
 +   └─ awsx:ecr:Repository         thumbnailer      create
 +      ├─ aws:ecr:Repository       thumbnailer      create
 +      └─ aws:ecr:LifecyclePolicy  thumbnailer      create

Resources:
    + 4 to create

Hit yes when you’re ready, you should see some output while the image builds in the background this may take a few minutes:

Updating (dev)

View Live: https://app.pulumi.com/jaxxstorm/thumbnailer/dev/updates/1

     Type                           Name             Status       Info
 +   pulumi:pulumi:Stack            thumbnailer-dev  creating
 +   └─ awsx:ecr:Repository         thumbnailer      created      Executing ' docker build ./app -t 12fda807-container'
 +      ├─ aws:ecr:Repository       thumbnailer      created
 +      └─ aws:ecr:LifecyclePolicy  thumbnailer      created