[P7] Xây Dựng Ứng Dụng Serverless với AWS CDK: DynamoDB và Lambda Function

Sun, November 17, 2024 - 8 min read View Count
AWS CDK Deployment Process

Xây Dựng Ứng Dụng Serverless với AWS CDK: DynamoDB và Lambda Function

Giới thiệu

Trong bài hướng dẫn này, chúng ta sẽ tìm hiểu cách sử dụng AWS CDK để xây dựng một ứng dụng serverless đơn giản. Ứng dụng này bao gồm một DynamoDB table để lưu trữ thông tin sản phẩm và một Lambda function để truy xuất dữ liệu thông qua Function URL.

Kiến thức cần có

  • Hiểu biết cơ bản về Python
  • Đã cài đặt AWS CDK CLI
  • Có tài khoản AWS

1. Tổng quan về Kiến trúc

Ứng dụng của chúng ta sẽ có các thành phần sau:

  • DynamoDB table lưu trữ thông tin sản phẩm
  • Lambda function để scan và trả về danh sách sản phẩm
  • Function URL để truy cập Lambda function từ internet

1732087900575

2. Khởi tạo Project

mkdir serverless_app
cd serverless_app
cdk init app --language python
python3 -m pip install --upgrade pip
pip install -r requirements.txt

3. Tạo DynamoDB Table

3.1. Import các thư viện cần thiết

from aws_cdk import (
    aws_dynamodb as dynamodb,
    RemovalPolicy
)

3.2. Định nghĩa DynamoDB Table

products_table = dynamodb.Table(
    self, 'ProductsTable',
    partition_key=dynamodb.Attribute(
        name='id',
        type=dynamodb.AttributeType.STRING
    ),
    billing_mode=dynamodb.BillingMode.PAY_PER_REQUEST,
    removal_policy=RemovalPolicy.DESTROY
)

Giải thích các thành phần:

  • partition_key: Định nghĩa khóa chính của table là trường ‘id’ kiểu STRING
  • billing_mode: Sử dụng PAY_PER_REQUEST để chỉ trả phí khi có request
  • removal_policy: Cấu hình xóa table khi destroy stack

4. Tạo Lambda Function

4.1. Cấu trúc thư mục

serverless_app/
├── lambda_src/
│   └── product_list_function.py
└── serverless_app/
    └── serverless_app_stack.py

4.2. Code Lambda Function

# lambda_src/product_list_function.py
import json
import os
import logging
import boto3 # AWS Python SDK
 
# Configure logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
 
# Initialize the DynamoDB client
dynamodb_client = boto3.client('dynamodb')
 
def lambda_handler(event, context):
    '''
    Returns all products from the DynamoDB table provided.
 
    Environment variables:
        - TABLE_NAME: The name of the DynamoDB table scanned.
    '''
 
    logger.info(f"Received event: {json.dumps(event, indent=2)}")
 
    # Scan the DynamoDB table to get all products
    products = dynamodb_client.scan(
        TableName=os.environ['TABLE_NAME']
    )
 
    return {
        "statusCode": 200,
        "body": json.dumps(products['Items'])
    }
 

4.3. Định nghĩa Lambda Function trong Stack

product_list_function = lambda_.Function(
    self, 'ProductListFunction',
    code=lambda_.Code.from_asset('lambda_src'),
    handler='product_list_function.lambda_handler',
    runtime=lambda_.Runtime.PYTHON_3_10,
    environment={
        'TABLE_NAME': products_table.table_name
    }
)
 
# Thêm Function URL
product_list_url = product_list_function.add_function_url(
    auth_type=lambda_.FunctionUrlAuthType.NONE
)

Giải thích các tham số:

  • code: Trỏ đến thư mục chứa code Lambda
  • handler: Định nghĩa entry point của function
  • runtime: Chọn môi trường runtime Python 3.10
  • environment: Truyền tên table qua biến môi trường
  • auth_type: Cấu hình Function URL không yêu cầu xác thực

5. Deploy và Test

cdk deploy

Sau khi deploy, bạn có thể:

  1. Truy cập DynamoDB table để thêm dữ liệu test
  2. Sử dụng Function URL để gọi Lambda function
  3. Kiểm tra CloudWatch Logs nếu có lỗi (chắc chắn có lỗi LOL :))))

5.1. Test Function URL

Truy cập Lambda Url trả về lỗi:

1732087166008

Vì chưa cấp quyền cho lambda đọc dữ liệu từ dynamoDB (xem ở cloudwwatch):

1732087079962

6. Granting Permissions trên L2 Constructs

6.1. Tổng quan về Grant Methods

L2 Constructs trong AWS CDK cung cấp các methods bắt đầu bằng “grant” để đơn giản hóa việc cấp quyền. Đây là cách hiệu quả để quản lý IAM permissions mà không cần phải định nghĩa chi tiết các IAM roles và policies.

6.2. Các loại Grant Methods phổ biến

  1. grant(): Method tổng quát cho phép tùy chỉnh các actions cụ thể
# Ví dụ cấp quyền tùy chỉnh
table.grant(function.role, ['dynamodb:PutItem', 'dynamodb:GetItem'])
  1. grant_read_data(): Chỉ cấp quyền đọc dữ liệu
# Cấp quyền đọc (scan, query, getItem)
table.grant_read_data(function.role)
  1. grant_read_write_data(): Cấp quyền đọc và ghi dữ liệu
# Cấp quyền đọc và ghi
table.grant_read_write_data(function.role)
  1. grant_full_access(): Cấp toàn bộ quyền trên resource
# Cấp toàn bộ quyền (thận trọng khi sử dụng)
table.grant_full_access(function.role)

6.3. Áp dụng cho ứng dụng của chúng ta

Để cho phép Lambda function scan DynamoDB table, chúng ta chỉ cần thêm một dòng code:

# Cấp quyền đọc data từ DynamoDB cho Lambda function
products_table.grant_read_data(product_list_function.role)

Giải thích:

  • products_table: DynamoDB table construct
  • grant_read_data(): Method cấp quyền đọc dữ liệu
  • product_list_function.role: IAM role của Lambda function

6.4. Re-Deploy

Lambda Url Đã truy cập được

1732087408946

6.5. Ưu điểm của Grant Methods

  1. Đơn giản hóa: Không cần định nghĩa chi tiết IAM policies
  2. Best Practices: Tự động áp dụng principle of least privilege
  3. Tự động hóa: Tự động xử lý các resource liên quan (ví dụ: KMS keys nếu được cấu hình)
  4. Maintainable: Code dễ đọc và bảo trì hơn

6.6. Best Practices khi sử dụng Grant Methods

  1. Luôn sử dụng method cấp quyền ít nhất có thể (least privilege)
  2. Sử dụng các specific grant methods thay vì grant_full_access()
  3. Document lại lý do cấp quyền bằng comments
  4. Kiểm tra logs sau khi deploy để đảm bảo permissions hoạt động đúng

7. Triển khai Stack Outputs

Stack Outputs là một tính năng của CloudFormation cho phép bạn:

  • Export các thông tin quan trọng dưới dạng cặp key-value
  • Truy cập dễ dàng thông qua CloudFormation Console hoặc AWS CLI
  • Thuận tiện cho việc tổ chức và quản lý nhiều stacks (cross-stack references)

7.1 Thêm Stack Output trong CDK

7.1.1. Import CfnOutput

from aws_cdk import CfnOutput

7.1.2. Khởi tạo CfnOutput

CfnOutput(self,# scope: stack hiện tại
"ProductListUrl",# id: tên của output
          value=product_list_url.url  # giá trị của output
)

Các Parameters Quan Trọng của CfnOutput

  • scope: Stack mà output thuộc về
  • id: Định danh của output (cũng là tên hiển thị trên CloudFormation)
  • value: Giá trị của output
  • description: Mô tả về output (tùy chọn)
  • export_name: Tên export cho cross-stack references (tùy chọn)
  • condition: Điều kiện để tạo output (khuyến nghị dùng if statement thay thế)

Ví Dụ

# Thêm Stack Output cho Function URL
        CfnOutput(self, 
                 "ProductListUrl",
                 value=product_list_url.url,
                 description="URL endpoint for the product list function"
        )

Cách Truy Cập Stack Output

Sau khi deploy, bạn có thể truy cập output theo nhiều cách:

  1. Qua Terminal : Output sẽ hiển thị ngay sau khi deploy hoàn tất 1732087950176
  2. CloudFormation Console :
  • Truy cập stack của bạn
  • Chuyển đến tab “Outputs”
  • Tìm output theo tên đã định nghĩa

1732087972308

8. Triển khai CloudWatch Metrics

Trong quá trình phát triển serverless application, việc monitoring các Lambda function là một phần không thể thiếu để đảm bảo ứng dụng hoạt động ổn định. Phần này sẽ hướng dẫn bạn cách sử dụng AWS CDK để setup monitoring cho Lambda function thông qua CloudWatch metrics và alarms.

8.1. Các Loại Metrics Cơ Bản

AWS CDK cung cấp nhiều methods để truy cập CloudWatch metrics của Lambda function:

  • metric(): Method tổng quát để truy cập bất kỳ metric nào
  • metric_duration(): Đo thời gian thực thi của function
  • metric_errors(): Đếm số lần function thực thi thất bại
  • metric_invocations(): Đếm số lần function được gọi
  • metric_throttles(): Đếm số lần function bị throttle

8.2. Cách Sử Dụng Metrics

from aws_cdk import aws_cloudwatch as cloudwatch
from aws_cdk import Duration
 
# Lấy metric về errors của Lambda function
errors_metric = lambda_function.metric_errors(
    label="ProductListFunction Errors",
    period=Duration.minutes(5),
    statistic=cloudwatch.Stats.SUM
)

8.3. Tạo CloudWatch Alarm

8.3.1. Cấu Trúc Cơ Bản của Alarm

errors_metric.create_alarm(
    self,
    "ProductListErrorsAlarm",
    evaluation_periods=1,
    threshold=1,
    comparison_operator=cloudwatch.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
    treat_missing_data=cloudwatch.TreatMissingData.IGNORE
)

8.3.2. Các Thông Số Quan Trọng

  • evaluation_periods: Số khoảng thời gian để đánh giá điều kiện alarm
  • threshold: Ngưỡng để kích hoạt alarm
  • comparison_operator: Cách so sánh giá trị metric với threshold
  • treat_missing_data: Cách xử lý khi không có dữ liệu

8.4. Best Practices

8.3.1. Thiết Lập Monitoring

  • Luôn setup monitoring cho các Lambda function quan trọng
  • Chọn period phù hợp với tần suất thực thi của function
  • Đặt threshold dựa trên business requirements

8.4.2. Error Handling

  • Sử dụng treat_missing_data=cloudwatch.TreatMissingData.IGNORE nếu function không được gọi thường xuyên
  • Kết hợp với SNS để nhận notification khi có lỗi

Ví Dụ

from aws_cdk import (
    Stack,
    aws_dynamodb as dynamodb,
    aws_lambda as lambda_,
    CfnOutput,
    RemovalPolicy,
    Duration,
    aws_cloudwatch as cloudwatch,
)
from constructs import Construct
 
 
class ServerlessAppStack(Stack):
 
        #...
 
        # Configure metrics to monitor the lambda function
        errors_metric = product_list_function.metric_errors(
            label="ProductListFunctionErrors",
            period=Duration.minutes(5),
            statistic=cloudwatch.Stats.SUM,
        )
 
        # Create an alarm to monitor the errors metric
        errors_metric.create_alarm(
            self,
            "ProductListErrorsAlarm",
            evaluation_periods=1,
            threshold=1,
            comparison_operator=cloudwatch.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
            treat_missing_data=cloudwatch.TreatMissingData.IGNORE,
        )
 
  1. Metric Period : Mặc định là 5 phút, có thể điều chỉnh theo nhu cầu
  2. Statistic : Đối với errors, nên sử dụng SUM thay vì AVERAGE
  3. Permissions : Đảm bảo Lambda function có đủ quyền truy cập các resource cần thiết
  4. Testing : Nên test alarm bằng cách tạo lỗi có chủ ý để verify setup

8.5. Lưu ý

  1. Chỉ sử dụng Stack Outputs cho các thông tin cần truy cập sau khi deploy
  2. Đặt tên output có ý nghĩa và dễ nhận biết
  3. Nên thêm description cho output để người khác dễ hiểu
  4. Với cross-stack references, CDK sẽ tự động xử lý phần lớn các trường hợp

Next Steps

  • Tìm hiểu cách truy cập Function URL từ stack
  • Phát triển thêm các endpoints để thêm/sửa/xóa sản phẩm
  • Tích hợp với API Gateway để có nhiều tính năng hơn
  • Thêm authentication và authorization

Lưu ý quan trọng

  1. Property Types trong CDK:

    • Sử dụng các class có sẵn để cấu hình (như Attribute, BillingMode)
    • Enum types đảm bảo giá trị cấu hình hợp lệ
    • Có thể sử dụng dictionary hoặc class instance để cấu hình
  2. Instance Methods:

    • L2 Constructs cung cấp các method tiện ích (như add_function_url())
    • Giúp đơn giản hóa việc thêm các resource liên quan
  3. Permissions:

    • Lambda function cần quyền để truy cập DynamoDB
    • CDK tự động tạo IAM Role nhưng cần cấu hình thêm permissions

Kết luận

AWS CDK giúp đơn giản hóa việc tạo và quản lý infrastructure bằng code. Thông qua ví dụ này, chúng ta đã học được:

  • Cách sử dụng L2 Constructs và Property Types
  • Tạo và cấu hình DynamoDB Table
  • Triển khai Lambda Function với Function URL
  • Best practices trong việc tổ chức code và quản lý dependencies