bref icon indicating copy to clipboard operation
bref copied to clipboard

Add documentation regarding Lambda @ edge

Open tlfbrito opened this issue 5 years ago • 4 comments

Add documentation regarding Lambda with the following points (suggestions):

  • Is it possible when using layers/php ?
  • Does it need to be deployed in a special region?
  • Basic configuration example

tlfbrito avatar Mar 10 '20 08:03 tlfbrito

I just tried to deploy a Lambda@Edge with Bref and it seems that AWS currently only allows a limited Runtimes to be deployed as Lambda@Edge:

An error occurred: CloudFrontDistribution - The function has a runtime that is not supported by Lambda@Edge: provided Function: arn:aws:lambda:us-east-1:750336674511:function:bref-demo-symfony-dev-website:3 Supported runtimes: [nodejs12.x, nodejs8.10, nodejs10.x, nodejs6.10, python3.7, python3.8] (Service: AmazonCloudFront; Status Code: 400; Error Code: InvalidLambdaFunctionAssociation; Request ID: 5a26f8f9-d75d-40e4-90d3-675cd4a56199).

For now, it seems that Layers can't be deployed as Lambda@Edge.

tlfbrito avatar Mar 12 '20 08:03 tlfbrito

It's possible to run it on Edge but with some up and downsides.

This is my scenario: I have 3 apps running in production with this stack. Laravel 5, 6 and 7 lifted by bref.sh on Lambda@Edge w/o the need of an API Gateway.

These are my stats: I'm serving roughly 250k requests every month on the API layer with an error rate of 0,00004% per 1 mil requests. It's not app error codes (they are a sadly higher) it's more about lambda invocation errors because of the proxy.

I am using an alias lambda based on python. It's only purpose is to run on Lambda@Edge and forward the event down my php-based lambda and responding with it's response. The alias-lambda needs to be deployed to us-east-1 but it will automatically deploy copies to each region. I'm running mainly in europe so I have deployed my lambdas across multiple eu-regions. In case some request come in from outsde the EU I'm falling back to my master region. I've added my alias-lambda code. Spoiler: I'm not an python expert but it does it's work 🙏

Here is some insights from running this:

  • High-Level: CloudFront -> Python Proxy Lambda -> PHP Lambda by bref
  • The proxy adds around ~10ms on top to the exection time
  • In order to have this cost-efficient the PHP Layer should respond in less than 30ms because Lambda@Edge charges per 50ms and not like Lambda which charges every 100ms
  • Running on a "keepalive" layer is a must have to leverage opcache (https://github.com/brefphp/bref/issues/439) - Attention: I didn't reviewed/tested the new keep-alive layers yet.
  • Depending on your code/framework - you'll might need to adopt your framework because there is a chance it will not be able to handle multiple requests in a permanent running application correctly (Recheck your singleton bindings in your container). The worst was: Shared sessions between mutliple clients. The second worst where some shared caches between requests.

After figuring out all these little things I'm happy with this configuration.

Now to the perhaps heavy lifting besides bref.sh. I have built an cloudfront@edge event-transformer which will transform the edge events into laravel/symfony-request objects. Back then I was not able to find any libary for this. It does it's job for now but it definitely needs a rewrite.

https://github.com/christoph-kluge/cloudfront-edge-php-adapter

Additionally this is my python-code for the alias lambda:

import boto3
import json

edge = boto3.client('lambda')
master = boto3.client('lambda', region_name='eu-central-1')

def lambda_handler(event, context):

    lambda_request_payload = json.dumps(event)
    
    try:
        lambda_response = edge.invoke(
            FunctionName='api-production-http',
            InvocationType='RequestResponse',
            Payload=lambda_request_payload
        )
        
    except:
        lambda_response = master.invoke(
            FunctionName='api-production-http',
            InvocationType='RequestResponse',
            Payload=lambda_request_payload
        )

    pass
    
    return json.loads(lambda_response.get('Payload').read())

christoph-kluge avatar Apr 13 '20 15:04 christoph-kluge

Hi @christoph-kluge, thank you for sharing all those details!

I have a doubt: from what I understand, Python runs in Lambda@Edge and the PHP app runs in a standard Lambda. Python forwards the CloudFront request to PHP.

Is that correct?

What are the benefits for doing this? It seems to me you would be paying both Lambda@Edge + Lambda for each request, because the Python script would be waiting for the PHP response. Why not use API Gateway instead?

mnapoli avatar Apr 13 '20 16:04 mnapoli

Hi @mnapoli,

yes this is correct. The benefit as of now? Not much besides exploring and learning.

In case AWS would add custom runtime support for Lambda@Edge then the learnings and unittest from the event transformer might be good.

christoph-kluge avatar Apr 13 '20 18:04 christoph-kluge

Closing this issue because Lambda@Edge does not support custom runtimes. Let's reopen when it does!

mnapoli avatar Dec 01 '22 17:12 mnapoli