Thiết lập bộ PDF generator trên AWS Lambda sử dụng python3 và thư viện wkhtmltopdf
I. GIỚI THIỆU:
Trong thời gian gần đây việc tạo một API với thuộc tính scale trở nên dễ dàng hơn trước với việc sử dụng serverless trên nên tảng hệ thống scale tự động sẵn có.
Trong bài viết này tôi sẽ set up một hệ thống API với tính năng scale có khả năng handle 1000 requests trong cùng một lúc.
Đồng thời tôi sẽ show cho bạn cách thiết lập một API với tính năng scale một cách dễ dàng đến mức nào và sau khi kêt thúc bài viết bạn có thể tạo một HTTP endpoint và bạn có thể POST lên một object JSON và nhận một liên kết link của file PDF trên AWS S3.
File JSON như sau:
{
"filename": "sample.pdf",
"html": "<html><head></head><body><h1>It works! This is the default PDF.</h1></body></html>"
}
II. SET UP:
1. Các yêu cầu cần thiết khi thiết lập:
- Python3
- Aws CLI được cài đặt và cấu hình
- Serverless được cài đặt.
2. Serverless
Điều đầu tiên bạn phải khởi động một project dùng serverless. Serverless là một bộ công cụ đơn giản cho việc sử dụng trên môi trường cloud như AWS, Google cloud và Azure, .. những dịch vụ mà khi bạn làm việc mà không phải lo lắng về quản trị Sever thế nào.
Nếu bạn muốn tìm hiểu SERVERLESS bạn truy cập : https://serverless.com
Để khởi tạo một serverless chạy lệnh:
sls create --template aws-python3
WKHTMLTOPDF Binary:
Là bộ nhị phân dùng để turning HTML thành PDF
Bạn download tại đây : https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.4/wkhtmltox-0.12.4_linux-generic-amd64.tar.xz
Sau khi giải nén bạn copy file wkhtmltopdf vào thư mục làm việc của project được tạo phía trên .
./binary/wkhtmltopdfPYTHON3:
Bây giờ bạn cần python3 để chạy và thư viện pdfkit. Trên serverless bạn không cần pip install pdfkit mà bạn chỉ cần chạy :
sls plugin install -n serverless-python-requirements
Trong file serverless.yml bạn cấu hình:
custom:Tạo file: Requirements.txt
pythonRequirements:
dockerizePip: true
pdfkitIII. Giai đoạn DEVELOP
Hai file chính khi ta làm việc trong bài viết này là :
serverless.yml và handle.yml
Cơ bản về syntax của file YML bạn có thể tham khảo tại : https://learn.getgrav.org/16/advanced/yaml
Một phần của file serverless.yml bạn tạo file config.yml có nội dung như sau:
BucketName: 'your-s3-bucket-name'Về serverless.yml high-level design sẽ như sau :
service: pdf-services # name or reference to our project
provider: # It is possible to use Azure, GCloud, or AWS
functions: # Array of functions to deploy as Lambdas
resources: # S3 buckets, DynamoDB tables, and other possible resources to create
plugins: # Plugins for Serverless
custom: # Custom variables used by you or plugins during setup and deployment
Khi deploy chúng ta cần tạo các bước sau trong file serverless.
1. Tạo S3 bucket để lưu file PDF có tên : pdf-service-bucket
2. Tạo một function để tạo PDF
3. Cấp quyền cho function để access vào S3 bucket
4. Thiết lập một API endpoint cho function.
POST https://xxxx.execute-api.xxxx.amazonaws.com/dev/new-pdf
Nội dung của file serverless.yml :
service: pdf-serviceHandle.py là file python mà ta cần, nó chứa đựng một function để generate ra PDF và lưu nó vào lambda.
provider:
name: aws
runtime: python3.7
# Set environment variable for the S3 bucket environment:
S3_BUCKET_NAME: ${file(./config.yml):BucketName}
# Gives our functions full read and write access to the S3 Bucket iamRoleStatements:
- Effect: "Allow"
Action:
- "s3:*"
Resource:
- arn:aws:s3:::${file(./config.yml):BucketName}
- arn:aws:s3:::${file(./config.yml):BucketName}/*
functions:
generate_pdf:
handler: handler.generate_pdf
events:
- http:
path: new-pdf
method: post
cors: true
resources:
# Creates an S3 bucket in our AWS account Resources:
NewResource:
Type: AWS::S3::Bucket
Properties:
BucketName: ${file(./config.yml):BucketName}
custom:
pythonRequirements:
dockerizePip: true
plugins:
- serverless-python-requirements
Trong file này bạn chú ý đến 2 argument : event và context.
context chứa đựng các biến môi trường và các thông tin về hệ thống.
event chứa đựng các dữ liệu yêu cầu, dữ liệu này được gửi đến lambda.
Nội dung file này là:
import json
import pdfkit
import boto3
import os
client = boto3.client('s3')
# Get the bucket name environment variables to use in our code
S3_BUCKET_NAME = os.environ.get('S3_BUCKET_NAME')
def generate_pdf(event, context):
# Defaults key = 'deafult-filename.pdf'
html = "<html><head></head><body><h1>It works! This is the default PDF.</h1></body></html>"
# Decode json and set values for our pdf
if 'body' in event:
data = json.loads(event['body'])
key = data['filename']
html = data['html']
# Set file path to save pdf on lambda first (temporary storage)
filepath = '/tmp/{key}'.format(key=key)
# Create PDF
config = pdfkit.configuration(wkhtmltopdf="binary/wkhtmltopdf")
pdfkit.from_string(html, filepath, configuration=config, options={})
# Upload to S3 Bucket
r = client.put_object(
ACL='public-read',
Body=open(filepath, 'rb'),
ContentType='application/pdf',
Bucket=S3_BUCKET_NAME,
Key=key
)
# Format the PDF URI
object_url = "https://{0}.s3.amazonaws.com/{1}".format(S3_BUCKET_NAME, key)
# Response with result
response = {
"headers": {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": True,
},
"statusCode": 200,
"body": object_url
}
return response
IV: DEPLOY
Khi deploy chạy câu lệnh sau :
sls deploy
# or
serverless deploy
Sau khi deploy xong ta có thông tin về HTTP endpoint :
https://xxxxxx.execute-api.us-east-1.amazonaws.com/dev/new-pdf
Cuối cùng bạn có thể dùng curl để test function chúng ta viết ở trên :
curl -d '{"filename":"my-sample-filename.pdf", "html":"<html><head></head><body><h1>Custom HTML -> Posted From CURL as {JSON}</h1></body></html>"}' -H "Content-Type: application/json" -X POST https://xxxxxx.execute-api.us-east-1.amazonaws.com/dev/new-pdf
Kết quả trả về bạn có thể check được là function đó work hay không ?
V. CONCLUSION:
Bạn thấy đó thật sự dễ dạng cho việc tạo một API trên môi trường serverless, môi trường mà bạn không cần phải lo lắng về server như thế nào về maintain server, ....
Reference: https://github.com/wkhtmltopdf/wkhtmltopdf
Nhận xét
Đăng nhận xét