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/wkhtmltopdf
PYTHON3:

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:
     pythonRequirements:  
          dockerizePip: true
 Tạo file: Requirements.txt

pdfkit
III. 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-service
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
Handle.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.

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

Bài đăng phổ biến từ blog này

Trang web medium.com chết, vì sao ?

Cách sử dụng sys.argv trong python.

Thiết kế một RESTful API bằng python và flask.