AWS Application Load Balancer 대상에 Lambda(Python)를 지정해 보았습니다.

12829 단어 람다ALBserverlessAWS
11월 26일부터 개최되고 있습니다 AWS re:Invent 2018에서, Serverless계의 새로운 재료가 적다고 생각했습니다만, 드디어 왔습니다.

애플리케이션 로드 밸런서(ALB) 대상에 대해 AWS Lambda를 선택할 수 있습니다.

지금까지 Lambda를 외부에서 호출하는 경우 API Gateway에서 일반이라고 생각하지만 ALB에서 호출 할 수 있습니다.
API Gateway를 사용하여 구현하는 경우 URL 전환하려는 웹 앱을 구현하기가 어려웠다고 생각합니다. 하지만 ALB를 통해 Lambda를 실행할 수 있게 되면 URL 전환의 앱 구현도 간단해진다고 생각합니다.

이미 사용할 수 있게 되었으므로 시도해 보았습니다.

우선은 파라미터 조사용 Lambda 함수를 준비했습니다



어떤 매개변수가 Lambda 함수에 전달되는지 조사하려면 Cloudwatch logs에서 확인합니다. 블로그에 쓰여진 소스를 참고로 다음과 같이 했습니다.

lambda_function.py
import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

def lambda_handler(event, context):

    logger.debug( event )    

    response = {
        "statusCode": 200,
        "isBase64Encoded": False,
        "headers": {
            "Content-Type": "text/html; charset=utf-8"
        }
    }

    response['body'] = """<html>
    <head>
        <meta charset="UTF-8">
        <title>Hello World!</title>
    </head>
    <body>
    <p>Hello World!</p>
    </body>
    </html>"""

    return response

ALB를 만들었습니다.



Lambda 함수를 만든 후 ALB를 설정했습니다.
대상 그룹 만들기 화면에서 이전에는 없었던 Lambda 함수를 지정할 수 있습니다. 이제 방금 만든 Lambda 함수를 선택합니다.

타겟 그룹 이외는 지금까지의 ALB 작성과 다르지 않았다고 생각합니다.

ALB에 액세스하려고했습니다.



ALB 작성 후, curl http://***.ap-northeast-1.elb.amazonaws.com/lambdatest/?a=b&c=d 로 액세스해, Cloudwatch logs에 출력되고 있는 event 파라미터를 조사해 보았더니, 이하의 출력이 되어 있었습니다 (일부 마스크하고 있습니다).
curl의 출력은 HTML이 출력되었습니다.
{'requestContext': 
    {'elb': {
        'targetGroupArn': 'arn:aws:elasticloadbalancing:ap-northeast-1:***:targetgroup/TestLoggingLambda/***'
        }
    }, 
    'httpMethod': 'GET', 
    'path': '/lambdatest/', 
    'queryStringParameters': {'a': 'b', 'c': 'd'}, 
    'headers': {
            'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 
            'accept-encoding': 'gzip, deflate', 
            'accept-language': 'ja,en-US;q=0.9,en;q=0.8,th;q=0.7', 
            'connection': 'keep-alive', 
            'host': '***.ap-northeast-1.elb.amazonaws.com', 
            'upgrade-insecure-requests': '1', 
            'user-agent': '***', 
            'x-amzn-trace-id': '***'', 
            'x-forwarded-for': '***'', 
            'x-forwarded-port': '80', 
            'x-forwarded-proto': 'http'
    }, 
    'body': '', 
    'isBase64Encoded': False
}

GET 파라미터는, queryStringParameters 에 격납되는 것 같습니다. 요청 헤더도 가져왔습니다.

POST로 액세스하려고했습니다.


curl -F "hoge=fuga" -F "form2=テスト" http://***.ap-northeast-1.elb.amazonaws.com/lambdatest/ 에서 액세스 해 보았을 때, body 부분에 base64encode 된 상태로 데이터가 포함되어있는 것 같습니다.
{'requestContext': 
    {'elb': {
        'targetGroupArn': 'arn:aws:elasticloadbalancing:ap-northeast-1:***:targetgroup/TestLoggingLambda/***'
        }
    }, 
    'httpMethod': 'POST', 
    'path': '/lambdatest/', 
    'queryStringParameters': {}, 
    'headers': {
        'accept': '*/*', 
        'content-length': '246', 
        'content-type': 'multipart/form-data; boundary=------------------------66c2ae32e35a634a', 
        'expect': '100-continue', 
        'host': '***.ap-northeast-1.elb.amazonaws.com', 
        'user-agent': ''***'',', 
        'x-amzn-trace-id': ''***'',', 
        'x-forwarded-for': ''***'',', 
        'x-forwarded-port': '80', 
        'x-forwarded-proto': 'http'
    }, 
    'body':'LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS02NmMyYWUzMmUzNWE2MzRhDQpDb250ZW50LURpc3Bvc2l0aW9uOiBmb3JtLWRhdGE7IG5hbWU9ImhvZ2UiDQoNCmZ1Z2ENCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tNjZjMmFlMzJlMzVhNjM0YQ0KQ29udGVudC1EaXNwb3NpdGlvbjogZm9ybS1kYXRhOyBuYW1lPSJmb3JtMiINCg0K44OG44K544OIDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLTY2YzJhZTMyZTM1YTYzNGEtLQ0K', 
    'isBase64Encoded': True
}

GET 및 POST에서 매개변수를 얻는 샘플



Lambda 함수에 건너오는 파라미터를 알았으므로 GET과 POST의 경우 샘플 프로그램을 작성해 보았습니다. POST 데이터의 디코딩이 번거롭기 때문에 Python의 cgi 모듈을 사용하고 있습니다.

lambda_function.py
import base64
import io
import cgi


def lambda_handler(event, context):

    response = {
        "statusCode": 200,
        "isBase64Encoded": False,
        "headers": {
            "Content-Type": "text/html; charset=utf-8"
        }
    }

    response['body'] = """<html>
    <head>
        <meta charset="UTF-8">
        <title>Hello World!</title>
    </head>
    <body>
    <p>Hello World!</p>"""

    if event['httpMethod'] == "GET":
        for k,v in event['queryStringParameters'].items():
           response['body'] += "<p>" + k + " : " + v + "</p>"

    elif event['httpMethod'] == "POST":
        fp = io.BytesIO(base64.b64decode(event['body']))
        environ = {'REQUEST_METHOD': 'POST'}
        headers = {
            'content-type': event['headers']['content-type'],
            'content-length': event['headers']['content-length']
        }
        form = cgi.FieldStorage(fp=fp, environ=environ, headers=headers)

        for f in form.list:
            response['body'] += "<p>" + f.name + " : " + f.value + "</p>"

    response['body'] += """<p>ALBからLambdaを呼び出しています</p>
    </body>
    </html>"""

    return response

참고로 기사 : [Python] POST 된 multipart / form-data를 FieldStorage로 구문 분석

구성을 생각해 보면



지금까지의 결과, GET이나 POST로 교환하는 페이지는 작성할 수 있을 것 같습니다. 이를 바탕으로 구성을 생각해 보면 정적 페이지는 CloudFront+S3에서 표시를 시켜 동적 처리가 필요한 페이지만 CloudFront+ALB+Lambda+(다음은 필요한 서비스)로 하면 EC2를 이용하지 않아도 대부분의 사이트는 작성할 수 있는 것 같았습니다. 그림으로 보면, 이런 느낌입니다.
EC2가 없어지는 것으로 운용 부하가 격감한다고 생각합니다.



Lambda의 런타임에 Ruby나 PHP도 지정할 수 있게 되었기 때문에, 지금까지 EC2상에서 실행하고 있던 간단한 프로그램은, Lambda로 이행할 수 있을지도 모릅니다.

운용으로 편하게 하고 싶기 때문에, 블로그를 보고, 서둘러 테스트한 결과였습니다.

좋은 웹페이지 즐겨찾기