Miserlou/Zappa

Add Docker Container Image Support

ian-whitestone opened this issue ยท 7 comments

Earlier this month, AWS announced container image support for AWS Lambda. This means you can now package and deploy lambda functions as container images, instead of using zip files. The container image based approach will solve a lot of headaches caused by the zip file approach, particularly with file sizes (container images can be up to 10GB) and the dependency issues we all know & love.

In an ideal end state, you should be able to call zappa deploy / zappa update / zappa package (etc.) and specify whether you want to use the traditional zip-based approach or new Docker container based approach. If choosing the latter, Zappa would automatically:

  • Build the new docker image for you
    • Not 100% sure how this would work yet. There is a Python library for docker that could be used. Would need to detect the dependencies a user has in their virtual env. and then install them all in the Docker image creation flow.
    • A simpler alternative could involve a user having a Dockerfile that they point Zappa to, and Zappa just executes the build for that.
  • Pushes the docker image to Amazon's Container Registry solution
    • Automatically creates new repository if one does not exist
  • Creates the lambda function with the new Docker image

For a MVP, we should take a BYOI (bring your own image) approach and just get zappa deploy and zappa update to deploy a lambda function using an existing Docker Image that complies with these guidelines.

Initial Exploration

For an MVP, we should take a BYOI (bring your own image) approach and just get zappa deploy and zappa update to deploy a lambda function using an existing Docker Image that complies with these guidelines.

I was able to get a little POC doing this โ˜๏ธ :

image

This is the app.py:

import time

from flask import Flask, jsonify

app = Flask(__name__)

@app.route("/")
def serve():
    return jsonify(success=True)

@app.route("/time")
def get_current_time():
    return {"time": round(time.time())}

Steps

  1. I prebuilt a Docker image
FROM amazon/aws-lambda-python:3.8
COPY ./ /var/task/

# Install dependencies with poetry
RUN pip install poetry
RUN POETRY_VIRTUALENVS_CREATE=false poetry install --no-root

CMD [ "handler.lambda_handler" ]

The only hack here is you need to have the handler.py (from zappa library) and zappa_settings.py files baked into your docker image. The zappa_settings.py is read in by the handler.py here, and gets generated here in the zip file creation flow.

To get around this, I'm thinking the following may be a simple solution:

  • Refactor handler.py so it looks for zappa_settings.py file, and if that is not there, it looks for all the settings as environment variables.
  • In zappa flows involving docker, zappa will automatically provide all the contents of zappa_settings.py as environment variables that get provided to the Docker image at runtime. That way we don't need to generate any zappa_settings.py during the Docker build step
  • All that's missing now is adding in handler.py in the docker image. I'm thinking this could be solved by having a snippet users can paste in their Dockerfile which either downloads a copy of handler.py to their image, or grabs it from their Python libraries folder (since they will have to have installed zappa as a dependency in their image setup)
  1. I modified the Zappa code to accept -d / --docker-image-uri as a parameter. If this is provided, it skips all the zip compiling steps, and uses the new lambda create_function API to provide the Docker Image URI.

This project looks a little dead.

As the workflow is pretty different with docker, maybe worth forking the relevant bits perhaps?

Scratch that.. for the use-case I'm describing https://github.com/adamchainz/apig-wsgi seems to suffice.

(I'm thinking more conventional docker build + terraform kinda setup)

Keep in mind that Lambdas which are created from ECR have very long cold starts: for my project it's 4.5 seconds compared with 1.2 seconds before
Looks like we need a feature like "concurrency" for warmups

@ian-whitestone using the -d / --docker-image-uri flag, is there a way to pass in multiple --build-arg flags for the image?

Let's move the conversation over to zappa/Zappa#922 (the new repo). I will reply there @alexanderdamiani @ArtikUA !