inngest / inngest-py

Python SDK for Inngest: Durable functions and workflows in Python, hosted anywhere
https://www.inngest.com/docs/reference/python
Apache License 2.0
83 stars 9 forks source link

Using AWS Lambda #204

Open josephp27 opened 2 months ago

josephp27 commented 2 months ago

How do I serve aws lambda in pyhon? I see there is one in TS but need it in python. Thanks!

amh4r commented 2 months ago

We have some users combining Mangum with FastAPI: https://pypi.org/project/mangum

If you do that, make sure you set serve_path="/" in your Inngest serve function

josephp27 commented 2 months ago

Hey! thank you for the help here. Ive been wracking my brain with this for awhile and not sure what I'm missing here. I cant get inngest to discover this function locally. I've gotten it to work outside of an aws handler. But when it's in this, I can't get discovery to work at all :

Dockerfile (docker run -p 8000:8080 ):

FROM public.ecr.aws/lambda/python:3.11

RUN yum update -y
RUN yum update -y python3 curl libcom_err ncurses expat libblkid libuuid libmount
RUN yum install ffmpeg libsm6 libxext6 python3-pip git -y

RUN pip3 install fastapi --target "${LAMBDA_TASK_ROOT}"
RUN pip3 install mangum --target "${LAMBDA_TASK_ROOT}"
RUN pip3 install inngest --target "${LAMBDA_TASK_ROOT}"

# Copy function code
COPY app.py ${LAMBDA_TASK_ROOT}

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

App.py

import logging
import os
from fastapi import FastAPI
from inngest.fast_api import serve
import inngest
from mangum import Mangum

os.environ["INNGEST_DEV"] = "1"

inngest_client = inngest.Inngest(
    app_id="fast_api_example",
    api_base_url="host.docker.internal:8288",
    logger=logging.getLogger("uvicorn"),
)
app = FastAPI()

@app.get("/")
def read_root():
    return {"Welcome": "Welcome to the FastAPI on Lambda"}

@inngest_client.create_function(
    fn_id="my_function",
    trigger=inngest.TriggerEvent(event="app/my_function"),
)
async def my_function(ctx: inngest.Context, step: inngest.Step) -> str:
    ctx.logger.info(ctx.event)
    return "done"

serve(app, inngest_client, [my_function], serve_path="/")
handler = Mangum(app=app)

Querying:

curl -XPOST "http://localhost:8000/2015-03-31/functions/function/invocations" -d '{"resource": "/", "path": "/", "httpMethod": "GET", "requestContext": {}, "multiValueQueryStringParameters": null}'
{
  "statusCode": 200,
  "headers": {
    "content-length": "46",
    "content-type": "application/json"
  },
  "multiValueHeaders": {},
  "body": "{\"Welcome\":\"Welcome to the FastAPI on Lambda\"}",
  "isBase64Encoded": false
}
amh4r commented 2 months ago

If you're running an app in a Docker container then you'll need to tell our SDK how to reach our Dev Server. By default, our SDK will try to reach the Dev Server at http://localhost:8288 but that won't work in a Docker container. So instead, try setting the INNGEST_DEV=http://host.docker.internal:8288 env var.

The INNGEST_DEV env var can be a URL if you want to use the Dev Server and you want to explicitly set the Dev Server URL 😄

josephp27 commented 2 months ago

Hmm, I don't think thats the problem. I was actually able to solve that a different way off the AWS server just using Mangum and FastAPI in a docker container with

inngest_client = inngest.Inngest(
    app_id="fast_api_example",
    api_base_url="host.docker.internal:8288", // <-- this line
    logger=logging.getLogger("uvicorn"),
)

The issue now is inngest dev finding the fastapi server to sync with. I'm not sure where or if it is even being exposed.

Ive tried a few urls like:

curl -XPOST "http://localhost:8000/2015-03-31/functions/function/invocations" -d '{"resource": "/api/inngest", "path": "/api/inngest", "httpMethod": "GET", "requestContext": {}, "multiValueQueryStringParameters": null}'
curl http://localhost:8000/2015-03-31/functions/function/invocations/api/inngest
curl http://localhost:8000/2015-03-31/functions/function/invocations

with no luck

amh4r commented 2 months ago

Our Dev Server isn't able to find your app? Could you share the command you use to start our Dev Server?

amh4r commented 2 months ago

I got it working locally by overriding entrypoint and command:

services:
  app:
    build:
      context: .
    environment:
      INNGEST_DEV: "http://inngest:8288"
    ports:
      - "9000:8080"

    # Override entrypoint and command so that we don't get the weird AWS Lambda
    # response. In production, we also avoid the weird AWS Lambda response if we
    # use a "function URL".
    entrypoint: ""
    command:
      [
        "fastapi",
        "dev",
        "lambda_function.py",
        "--host",
        "0.0.0.0",
        "--port",
        "8080",
      ]

  inngest:
    image: "inngest/inngest:latest"
    command: "inngest dev -u http://app:8080/"
    ports:
      - "8288:8288"

If you don't override them, you'll need to send the weird AWS Lambda request shape:

curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"resource": "/", "path": "/", "httpMethod": "GET", "requestContext": {}, "multiValueQueryStringParameters": null}'

And you'll get the weird AWS Lambda response shape:

{"statusCode": 200, "headers": {"content-type": "application/json", "x-inngest-sdk": "inngest-py:v0.4.19", "x-inngest-req-version": "1", "user-agent": "inngest-py:v0.4.19", "x-inngest-framework": "fast_api", "x-inngest-expected-server-kind": "dev", "content-length": "187"}, "multiValueHeaders": {}, "body": "{\"schema_version\": \"2024-05-24\", \"authentication_succeeded\": null, \"function_count\": 1, \"has_event_key\": false, \"has_signing_key\": false, \"has_signing_key_fallback\": false, \"mode\": \"dev\"}", "isBase64Encoded": false}

In production, using a "Function URL" for your AWS Lambda should avoid the weird requests and responses