A Dynamic Web Site using Python and AWS Lambda
Mis a jours le 27 Jan 2021 à 10:40 · 741 mots · Lecture en 4 minutes
Nous allons voir comment utiliser les Lambda d’AWS pour servir un site web dynamique et en mode serverless.
Python
La fonction python que l’on va utiliser pour la lambda doit avoir deux
paramètres : event
et context
.
def my_lambda_function(event, context):
return "Hello world !"
Pour donner une idée de ce que contiennent event
et context
, j’ai créé
cette fonction :
import os
import json
def debug_lambda(event, context):
json_region = os.environ['AWS_REGION']
return {
"statusCode": 200,
"headers": {
"Content-Type": "application/json"
},
"body": json.dumps({
"Region ": json_region,
'event' : str(event),
'context': str(context)
})
}
Où la réponse de la lambda donne :
{
"Region ": "eu-west-3",
"event": "{'resource': '/', 'path': '/', 'httpMethod': 'GET', 'headers': {'Accept': 'text/html', 'Accept-Encoding': 'gzip, deflate, br', 'cache-control': 'max-age=0', 'CloudFront-Forwarded-Proto': 'https', 'CloudFront-Is-Desktop-Viewer': 'true', 'CloudFront-Is-Mobile-Viewer': 'false', 'CloudFront-Is-SmartTV-Viewer': 'false', 'CloudFront-Is-Tablet-Viewer': 'false', 'CloudFront-Viewer-Country': 'FR', 'dnt': '1', 'Host': '[REDACTED]', 'sec-fetch-dest': 'document', 'sec-fetch-mode': 'navigate', 'sec-fetch-site': 'none', 'sec-fetch-user': '?1', 'sec-gpc': '1', 'upgrade-insecure-requests': '1', 'User-Agent': '[REDACTED]', 'Via': '[REDACTED]', 'X-Amz-Cf-Id': '[REDACTED]', 'X-Amzn-Trace-Id': 'Root=1-[REDACTED]', 'X-Forwarded-For': '[REDACTED]', 'X-Forwarded-Port': '443', 'X-Forwarded-Proto': 'https'}, 'multiValueHeaders': {'Accept': ['text/html'], 'Accept-Encoding': ['gzip, deflate, br'], 'Accept-Language': ['fr-FR'], 'cache-control': ['max-age=0'], 'CloudFront-Forwarded-Proto': ['https'], 'CloudFront-Is-Desktop-Viewer': ['true'], 'CloudFront-Is-Mobile-Viewer': ['false'], 'CloudFront-Is-SmartTV-Viewer': ['false'], 'CloudFront-Is-Tablet-Viewer': ['false'], 'CloudFront-Viewer-Country': ['FR'], 'dnt': ['1'], 'Host': ['[REDACTED]'], 'sec-fetch-dest': ['document'], 'sec-fetch-mode': ['navigate'], 'sec-fetch-site': ['none'], 'sec-fetch-user': ['?1'], 'sec-gpc': ['1'], 'upgrade-insecure-requests': ['1'], 'User-Agent': ['[REDACTED]'], 'Via': ['[REDACTED]'], 'X-Amz-Cf-Id': ['[REDACTED]'], 'X-Amzn-Trace-Id': ['Root=1-[REDACTED]'], 'X-Forwarded-For': ['[REDACTED]'], 'X-Forwarded-Port': ['443'], 'X-Forwarded-Proto': ['https']}, 'queryStringParameters': None, 'multiValueQueryStringParameters': None, 'pathParameters': None, 'stageVariables': None, 'requestContext': {'resourceId': '[REDACTED]', 'resourcePath': '/', 'httpMethod': 'GET', 'extendedRequestId': '[REDACTED]', 'requestTime': '[REDACTED]', 'path': '/test', 'accountId': '[REDACTED]', 'protocol': 'HTTP/1.1', 'stage': 'test', 'domainPrefix': '[REDACTED]', 'requestTimeEpoch': 1611741450517, 'requestId': '[REDACTED]', 'identity': {'cognitoIdentityPoolId': None, 'accountId': None, 'cognitoIdentityId': None, 'caller': None, 'sourceIp': '[REDACTED]', 'principalOrgId': None, 'accessKey': None, 'cognitoAuthenticationType': None, 'cognitoAuthenticationProvider': None, 'userArn': None, 'userAgent': '[REDACTED]', 'user': None}, 'domainName': '[REDACTED]', 'apiId': '[REDACTED]'}, 'body': None, 'isBase64Encoded': False}",
"context": "<__main__.LambdaContext object at 0x7f0f14df07c0>"
}
Maintenant qu’on a ça, on peut faire tous ce qu’on veut :-)
Pour pouvoir utiliser le code, il va falloir créer une archive zip.
$ zip python.zip *.py
Terraform
Maintenant qu’on a une fonction python à faire tourner, il faut la déployer ! Dans ce tuto, nous utiliserons Terraform.
Pour déployer sur AWS il faut avoir une clef d’accès avec son ID et son secret. Cette clef peut être récupérée dans la partie IAM de la console AWS.
Setup Terraform
Pour commencer, il faut dire a Terraform quel provider
utiliser:
provider "aws" {
region = "eu-west-3"
}
J’utilise eu-west-3
, mais vous pouvez utiliser n’importe quelle région :)
La Lambda
Pour la resource de la lambda, il faut avoir une resource de type aws_lambda_function
:
resource "aws_lambda_function" "lambda_function" {
role = aws_iam_role.lambda_exec_role.arn
handler = var.handler
runtime = var.runtime # le type de code utilisé (i.e. python3.8)
filename = var.src_zip # le nom du ficher .zip contentant le code
function_name = var.function_name # le nom de la fonction
source_code_hash = filebase64sha256(var.src_zip)
}
Il faut aussi une resource de type aws_iam_role
:
resource "aws_iam_role" "lambda_exec_role" {
name = "lambda_exec"
path = "/"
description = "Allows Lambda Function to call AWS services on your behalf."
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
La gateway
Pour pouvoir accéder au code de la lambda, il faut mettre une gateway API en tant que proxy.
resource "aws_api_gateway_rest_api" "example" {
name = "ServerlessExample"
description = "Terraform Serverless Application Example"
}
resource "aws_api_gateway_resource" "proxy" {
rest_api_id = aws_api_gateway_rest_api.example.id
parent_id = aws_api_gateway_rest_api.example.root_resource_id
path_part = "proxy"
}
resource "aws_api_gateway_method" "proxy" {
rest_api_id = aws_api_gateway_rest_api.example.id
resource_id = aws_api_gateway_resource.proxy.id
http_method = "ANY"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "lambda" {
rest_api_id = aws_api_gateway_rest_api.example.id
resource_id = aws_api_gateway_method.proxy.resource_id
http_method = aws_api_gateway_method.proxy.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.lambda_function.invoke_arn
}
resource "aws_api_gateway_method" "proxy_root" {
rest_api_id = aws_api_gateway_rest_api.example.id
resource_id = aws_api_gateway_rest_api.example.root_resource_id
http_method = "ANY"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "lambda_root" {
rest_api_id = aws_api_gateway_rest_api.example.id
resource_id = aws_api_gateway_method.proxy_root.resource_id
http_method = aws_api_gateway_method.proxy_root.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.lambda_function.invoke_arn
}
resource "aws_api_gateway_deployment" "example" {
depends_on = [
aws_api_gateway_integration.lambda,
aws_api_gateway_integration.lambda_root,
]
rest_api_id = aws_api_gateway_rest_api.example.id
stage_name = "test"
}
resource "aws_lambda_permission" "apigw" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.lambda_function.function_name
principal = "apigateway.amazonaws.com"
# The "/*/*" portion grants access from any method on any resource
# within the API Gateway REST API.
source_arn = "${aws_api_gateway_rest_api.example.execution_arn}/*/*"
}
Et pour enfin avoir l’url sur laquelle tapper pour voir le résultat de la fonction.
output "base_url" {
value = aws_api_gateway_deployment.example.invoke_url
}
Cela va donner:
Outputs:
base_url = https://wncgbhu7te.execute-api.eu-west-3.amazonaws.com/test
La source du blog est disponible sur github. Et la lambda est accessible sur aws.
Aller plus loin
- github action publish
- Utiliser un DNS (R53)
L'auteur: Tom Moulard
Depuis mon enfance, je suis captivé par les articles de science et de technologie. Un jour, j'ai décidé de faire partie de ce monde : j'ai pris ma calculatrice programmable (une TI-82 stat).... La suite, sur mon site
Vous avez vu une erreur ? Quelque chose ne va pas ? Vous pouvez contribuer à cette page sur GitHub ou laisser un commentaire en dessous. Merci d'être passé par là :)