2022-10-26 07:22:15 -07:00

226 lines
8.5 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Build a Video Thumbnailer on AWS
h1: "Build a Video Thumbnailer with Pulumi using Lambdas, Containers, and Infrastructure on AWS"
date: "2018-06-21"
meta_desc: "Extracting a thumbnail from a video using a combination of Lambdas, containers, and connected data services and infrastructure."
meta_image: "video-thumbnail-diagram.png"
authors: ["donna-malayeri"]
tags: ["JavaScript","Serverless","AWS","containers"]
---
Pulumi makes it easy to build cloud applications that use a combination
of containers, lambdas, and connected data services and infrastructure:
Colada apps.
An example of a Colada app is extracting a thumbnail from a video. A
serverless function can only run for 5 minutes, so we'll run a
container in AWS Fargate to do the video processing.
In this app, a Lambda function is triggered whenever a new video is
uploaded to S3. This function launches a task in Fargate that
uses [FFmpeg](https://www.ffmpeg.org/) to extract a video thumbnail. A
second Lambda function is triggered when a new thumbnail has been
created.
<!--more-->
This post was inspired by a blog post from Serverless, Inc that shows
how to combine [AWS Fargate and Lambda for a long-running
process](https://serverless.com/blog/serverless-application-for-long-running-process-fargate-lambda/).
Let's see how a similar app would be implemented in Pulumi entirely in
code.
![video-thumbnail-diagram](./video-thumbnail-diagram.png)
## Setup
If this is your first time using Pulumi, go
to [https://app.pulumi.com](https://app.pulumi.com/) and sign in with
GitHub.
Then, run the following command to install the Pulumi CLI:
$ curl -fsSL https://get.pulumi.com/ | sh
If you're on Windows, run this:
@"%SystemRoot%System32WindowsPowerShell1.0powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://get.pulumi.com/install.ps1'))"
SET "PATH=%PATH%;%USERPROFILE%.pulumiin"
You'll deploy this app to your own AWS account, so follow the steps
to [configure your AWS account](/registry/packages/aws/installation-configuration/).
Make sure you have [Node.js](https://nodejs.org/en/download/) installed,
with a version of 6.10.x or later.
Finally, make sure [Docker](https://docs.docker.com/install/) is
installed and running.
## Create the App
We'll create a Pulumi project, define the infrastructure and app code
in JavaScript, and create a Dockerfile for the Fargate task.
First, to create a new Pulumi project, run the following commands:
mkdir hello-colada && cd hello-colada
pulumi new aws-javascript
This creates a new project in the `hello-colada` directory.
Second, replace the contents of `index.js` with the following:
```javascript
const cloud = require("@pulumi/cloud-aws");
// A bucket to store videos and thumbnails.
const bucket = new cloud.Bucket("bucket");
const bucketName = bucket.bucket.id;
// A task which runs a containerized FFMPEG job to extract a thumbnail image.
const ffmpegThumbnailTask = new cloud.Task("ffmpegThumbTask", {
build: "./", // folder containing the Dockerfile
memoryReservation: 128,
});
// When a new video is uploaded, run the FFMPEG task on the video file.
// Use the time index specified in the filename (e.g. cat_00-01.mp4 uses timestamp 00:01)
bucket.onPut("onNewVideo", async (bucketArgs) => {
console.log(`*** New video: file ${bucketArgs.key} was uploaded at ${bucketArgs.eventTime}.`);
const file = bucketArgs.key;
const thumbnailFile = file.substring(0, file.indexOf('_')) + '.jpg';
const framePos = file.substring(file.indexOf('_')+1, file.indexOf('.')).replace('-',':');
await ffmpegThumbnailTask.run({
environment: {
"S3_BUCKET": bucketName.get(),
"INPUT_VIDEO": file,
"TIME_OFFSET": framePos,
"OUTPUT_FILE": thumbnailFile,
},
});
console.log(`Running thumbnailer task.`);
}, { keySuffix: ".mp4" });
// When a new thumbnail is created, log a message.
bucket.onPut("onNewThumbnail", async (bucketArgs) => {
console.log(`*** New thumbnail: file ${bucketArgs.key} was saved at ${bucketArgs.eventTime}.`);
}, { keySuffix: ".jpg" });
// Export the bucket name.
exports.bucketName = bucketName;
```
This code uses `cloud.Task`, a high-level, convenient component for
working with containers. The component automatically provisions a
container registry instance in ECR, runs a Docker build, and saves the
Docker image to the provisioned ECR instance. It also defines an ECS
task and configures it to use the built image. All this in just 4 lines
of code!
Next, in the same directory, create a `Dockerfile` with the following
contents. We'll use an existing FFmpeg container and install the AWS
CLI. When the container is started, it copies the video file from S3,
runs `ffmpeg`, and copies the output back to S3.
FROM jrottenberg/ffmpeg
RUN apt-get update &&
apt-get install python-dev python-pip -y &&
apt-get clean
RUN pip install awscli
WORKDIR /tmp/workdir
ENTRYPOINT
echo "Starting ffmpeg task..." &&
echo "Copying video from S3" &&
aws s3 cp s3://${S3_BUCKET}/${INPUT_VIDEO} ./${INPUT_VIDEO} &&
ffmpeg -v error -i ./${INPUT_VIDEO} -ss ${TIME_OFFSET} -vframes 1 -f image2 -an -y ${OUTPUT_FILE} &&
echo "Copying thumbnail to S3" &&
aws s3 cp ./${OUTPUT_FILE} s3://${S3_BUCKET}/${OUTPUT_FILE}
Install the `@pulumi/cloud-aws` NPM package:
$ npm install --save @pulumi/cloud-aws @pulumi/cloud
Finally, configure Pulumi to use AWS Fargate. Note that, currently,
Fargate is available only in `us-east-1`, `us-east-2`, `us-west-2`,
and `eu-west-1`.
$ pulumi config set cloud-aws:useFargate true
That's it! Now, let's deploy the app.
## Deploy the App
To deploy both the infrastructure and app code, we'll
run `pulumi update`. This command first shows a preview of all the
resources that will be created and prompts for confirmation. During the
preview phase, Pulumi invokes `docker build`.
Choose the "yes" option to deploy to AWS. This will take about 5
minutes. Pulumi automatically builds and provisions an AWS container
repository in ECR, builds the Docker container, and places the image in
the repository. This all happens automatically and does not require
manual configuration on your part.
![pulumi-update-output](./pulumi-update-output.png)
At the end of the update, you'll see a link to the Pulumi Service that
shows the details of the deployment.
![pulumi-update-complete-1](./pulumi-update-complete-1.png)
Go to the **Resources** tab and filter to S3 resources. Then, on the S3
bucket, click the link to go to the AWS Console.
![console-s3-filtered](./console-s3-filtered.png)
## Upload a Video
Upload an .mp4 video to your S3 bucket, making sure to encode the
desired time index in the filename. For instance,
use `myvideo_00-02.mp4` to extract the frame at 0 minutes and 2 seconds.
You can [use this video of my cat](https://github.com/pulumi/examples/blob/master/cloud-js-thumbnailer/sample/cat.mp4),
which you should save as `cat_00-01.mp4`.
Upload this video in the S3 console. Or, use the AWS CLI along with
the `pulumi stack output` command:
$ aws s3 cp cat_00-01.mp4 s3://$(pulumi stack output bucketName)
upload: cat_00-01.mp4 to s3://bucket-3a4f226/cat_00-01.mp4
To see what the app is doing, use the `pulumi logs` command, with
the `--follow` or `-f` parameter. This command aggregates all the logs
for your compute, so the logs for the Lambda function **and** the
Fargate task are all in one place!
![terminal-logs](./terminal-logs.png)
Once the thumbnail has been generated, either view it in the S3 console,
or download it with the AWS CLI:
$ aws s3 cp s3://$(pulumi stack output bucketName)/cat.jpg .
download: s3://bucket-0c91106/cat.jpg to ./cat.jpg
## Clean Up
To clean up the resources we've provisioned, run `pulumi destroy`.
### Next Steps
In this post, we saw how easy it is to use containers and serverless
functions in one application. With Pulumi, you get the best of both
worlds, and don't have to choose one or the other.
The [sample code for this post](https://github.com/pulumi/examples/tree/master/cloud-js-thumbnailer) is
available in the [Pulumi examples repo on
GitHub](https://github.com/pulumi/examples).
For a version of this sample that includes AWS Rekognition, see
the [Video Thumbnailer with Machine Learning](https://github.com/pulumi/examples/tree/master/cloud-js-thumbnailer-machine-learning) JavaScript
example.