|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "Deploy WhisperAI, FFMpeg and OpenAI in AWS Lambda using Docker Container" |
| 4 | +date: 2023-05-22 |
| 5 | +desc: "Deploy WhisperAI, FFMpeg and OpenAI into AWS Lambda Containers" |
| 6 | +keywords: "ai, code, aws, docker, lambda, containers, ffmpeg" |
| 7 | +categories: [ai, code, aws, docker, lambda, containers, ffmpeg] |
| 8 | +tags: [ai, code, aws, docker, lambda, containers, ffmpeg] |
| 9 | +icon: icon-antenna |
| 10 | +--- |
| 11 | + |
| 12 | +Let's talk about working with AI, Docker and AWS Lambda. Sounds exciting, right? Maybe? |
| 13 | + |
| 14 | +But let me slide in a quick disclaimer: AWS Lambda might not be your first choice for AI tasks. |
| 15 | + |
| 16 | +These AI tasks are resource-intensive and sometimes prefer their tasks to be run on a GPU. But hey, who doesn't like a bit of challenge and fun? |
| 17 | + |
| 18 | +As we start with this journey, let's keep few points in our mind: |
| 19 | + |
| 20 | +1. Lambda functions can run for a 15-minute run, MAX. Default is 3 seconds. You might want to configure that. |
| 21 | + |
| 22 | +2. When it comes to using docker containers as lambda functions, they have a "10GB max" size limit. No more. I tried ! :/ |
| 23 | + |
| 24 | +3. As of this date, Lambda doesn't support GPU, so remember this when you're tinkering around. |
| 25 | + |
| 26 | + |
| 27 | +Alright, gear up, let's dive in. |
| 28 | +<br><br> |
| 29 | + |
| 30 | +First off, ensure you have docker running on your trusty laptop/PC. |
| 31 | + |
| 32 | +You'll need three major files to get this show going: |
| 33 | + |
| 34 | +A **handler.py** file - *our command center* - looking something like this: |
| 35 | + |
| 36 | +```python |
| 37 | +import os |
| 38 | +import time |
| 39 | +import boto3 |
| 40 | +import openai |
| 41 | +import whisper |
| 42 | + |
| 43 | +start = time.time() |
| 44 | +# Load the model outside for a warm lambda executions... Seriously !! This helps !! |
| 45 | +model = whisper.load_model("base", download_root='/tmp/') |
| 46 | +end = time.time() |
| 47 | +print("Time it took Whisper Model to load: ", end - start) |
| 48 | + |
| 49 | +session = boto3.session.Session() |
| 50 | +s3_client = boto3.client('s3') |
| 51 | +# Security 101: Always keep your secrets ... You know .. SECRETS !! ... |
| 52 | +client = session.client(service_name='secretsmanager', region_name='<YOUR_AWS_REGION>') |
| 53 | +openai.api_key = client.get_secret_value( |
| 54 | + SecretId='<SECRETIDHERE_SHHH!!>')['SecretString'] |
| 55 | + |
| 56 | + |
| 57 | +def handler(event, context): |
| 58 | + # To run your lambda on a batch, consider iterating the records... |
| 59 | + # My lambda executes when a file is uploaded to a S3 bucket... Tweak this according to your needs ... |
| 60 | + bucket = event['Records'][0]['s3']['bucket']['name'] |
| 61 | + key = event['Records'][0]['s3']['object']['key'] |
| 62 | + |
| 63 | + print("Commencing download: ", bucket, key) |
| 64 | + download_path = os.path.join('/tmp', os.path.basename(key)) |
| 65 | + s3_client.download_file(bucket, key, download_path) |
| 66 | + print("Done downloading. Wow internet!") |
| 67 | + start = time.time() |
| 68 | + result = model.transcribe(download_path) |
| 69 | + end = time.time() |
| 70 | + print("Transcription time: ", end - start) |
| 71 | + return { |
| 72 | + "statusCode": 200, |
| 73 | + "result": result, |
| 74 | + } |
| 75 | +``` |
| 76 | +<br> |
| 77 | + |
| 78 | +The **requirements.txt** file - tending to the dependencies: |
| 79 | +```text |
| 80 | +boto3==1.26.137 |
| 81 | +openai==0.27.7 |
| 82 | +openai-whisper==20230314 |
| 83 | +``` |
| 84 | +<br> |
| 85 | + |
| 86 | +And last but not least, the **Dockerfile** - our docker container creator, soon-to-be Lambda function core: |
| 87 | +```Dockerfile |
| 88 | +FROM public.ecr.aws/lambda/python:3.10-arm64 |
| 89 | + |
| 90 | +RUN yum -y update && \ |
| 91 | + yum -y install tar gzip xz && \ |
| 92 | + yum clean all && \ |
| 93 | + rm -rf /var/cache/yum |
| 94 | + |
| 95 | +RUN curl https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-arm64-static.tar.xz -o ffmpeg.tar.xz && \ |
| 96 | + tar -xf ffmpeg.tar.xz && \ |
| 97 | + mv ffmpeg-*-static/ffmpeg /usr/local/bin/ && \ |
| 98 | + rm -r ffmpeg.tar.xz ffmpeg-*-static |
| 99 | + |
| 100 | +COPY requirements.txt . |
| 101 | +RUN pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}" |
| 102 | + |
| 103 | +# Copy function code |
| 104 | +COPY handler.py ${LAMBDA_TASK_ROOT} |
| 105 | + |
| 106 | +# Set the CMD to your handler |
| 107 | +CMD [ "handler.handler" ] |
| 108 | +``` |
| 109 | +<br> |
| 110 | +Customize these files to meet your requirements, and voila! You're ready to launch your code into the AWS Lambda. |
| 111 | +<br><br> |
| 112 | + |
| 113 | +## Unleash the Deployment !! |
| 114 | + |
| 115 | +Kick things off by building the docker image: |
| 116 | +```shell |
| 117 | +docker build -t image-name . |
| 118 | +``` |
| 119 | + |
| 120 | +To check if your creation is functioning as expected, put it through a local test run: |
| 121 | +```shell |
| 122 | +docker run -p 9000:8080 image-name |
| 123 | +``` |
| 124 | + |
| 125 | +And hope it works: |
| 126 | +```shell |
| 127 | +curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}' |
| 128 | +``` |
| 129 | + |
| 130 | +At this point, your terminal should return you with an output. |
| 131 | + |
| 132 | +Your docker image, now ready and waiting, needs to be uploaded to ECR (Elastic Container Registry) for its Lambda adventure. |
| 133 | + |
| 134 | +To do this, you'll need an ECR repository. If you have one, use it by all means. If not, create an ECR repo with this command: |
| 135 | +```shell |
| 136 | +aws ecr create-repository --repository-name docker-image-ecr-repository --region <YOUR_AWS_REGION> --image-scanning-configuration scanOnPush=true --image-tag-mutability MUTABLE |
| 137 | +``` |
| 138 | + |
| 139 | +Your terminal will reward you with a response like this. COPY that `repositoryUri`. We'll need it to upload our docker image to the repository: |
| 140 | +```json |
| 141 | +{ |
| 142 | + "repository": { |
| 143 | + "repositoryArn": "arn:aws:ecr:<YOUR_AWS_REGION>:XXXXXXXXXX:repository/docker-image", |
| 144 | + "registryId": "XXXXXXXXXX", |
| 145 | + "repositoryName": "docker-image", |
| 146 | + "repositoryUri": "XXXXXXXXXX.dkr.ecr.<YOUR_AWS_REGION>.amazonaws.com/docker-image", |
| 147 | + "createdAt": "2023-03-09T10:39:01+00:00", |
| 148 | + "imageTagMutability": "MUTABLE", |
| 149 | + "imageScanningConfiguration": { |
| 150 | + "scanOnPush": true |
| 151 | + }, |
| 152 | + "encryptionConfiguration": { |
| 153 | + "encryptionType": "AES256" |
| 154 | + } |
| 155 | + } |
| 156 | +} |
| 157 | +``` |
| 158 | + |
| 159 | +But wait, there's more! Before you can upload the images to the repository, you need to prove your identity. Authenticate by executing this: |
| 160 | +```shell |
| 161 | +aws ecr get-login-password --region <YOUR_AWS_REGION> | docker login --username AWS --password-stdin <YOUR_AWS_ACCOUNT_ID>.dkr.ecr.<YOUR_AWS_REGION>.amazonaws.com |
| 162 | +``` |
| 163 | + |
| 164 | +Just one more teeny step. To guide it to the right ECR repository, you need to tag your docker image with the repository name, like so: |
| 165 | +```shell |
| 166 | +docker tag docker-image:latest <YOUR_AWS_ACCOUNT_ID>.dkr.ecr.<YOUR_AWS_REGION>.amazonaws.com/docker-image-ecr-repository:latest |
| 167 | +``` |
| 168 | + |
| 169 | +Trust me !! this is the final step! Now it's time to push the image. |
| 170 | +```shell |
| 171 | +docker push <YOUR_AWS_ACCOUNT_ID>.dkr.ecr.<YOUR_AWS_REGION>.amazonaws.com/docker-image-ecr-repository:latest |
| 172 | +``` |
| 173 | + |
| 174 | +With your image now in ECR you are ready to deploy it to AWS Lambda. Here, you have a lots of choices. You can create lambda functions from the console or from tools like AWS SAM, CDK, Terraform, and more. |
| 175 | + |
| 176 | +As always, if you hit any roadblocks or need some guidance, don't hesitate to give me a shout. |
| 177 | + |
| 178 | +**Happy Coding!** |
| 179 | + |
| 180 | + |
| 181 | + |
| 182 | + |
| 183 | + |
| 184 | + |
| 185 | + |
| 186 | + |
0 commit comments