Category: AWS

  • [AWS] Serverless và SAM, bạn chọn dùng cái nào?

    [AWS] Serverless và SAM, bạn chọn dùng cái nào?

    Mình đã viết khá nhiều bài sử dụng Serverless, tại sao mình lại viết bài này. Thực ra mình cũng mới bắt đầu làm AWS Lambda được một thời gian ngắn. Dự án đầu tiên mình làm Lambda thì đã các bạn đi được đã chọn Serverless để phát triển. Dự án thứ hai mình làm với AWS Lambda thì khách hàng đưa cho mình bộ mã nguồn đã được cấu hình sử dụng Serverless. Mọi thứ đều có vẻ ổn cho đến một ngày mình quyết định thử debug Lambda bằng Visual Studio Code. Mọi thứ trở nên phức tạp và mình tìm thấy SAM, dường như nó đã giải quyết vấn đề của mình nên mình quyết định viết bài này để cho các bạn nếu mới đến với thế giới AWS thì có thể dễ dàng lựa chọn thứ mình cần.

    Chọn Serverless hay SAM?

    Dự án đầu tiên mình dùng Serverless và viết bằng JavaScript, mọi thứ đều ổn vì mình chỉ dùng Serverless có kết hợp với Serverless Offline để chạy các hàm Lambda API trên máy tính cá nhân được. Việc debug cũng không gặp trở ngại gì do Serverless Offline có hỗ trợ debug. Thế nhưng đến dự án tiếp theo, ngôn ngữ được chọn làm môi trường thực thi là Python và mình thực sự gặp khó khăn. Mình vẫn có thể chạy được các hàm Lambda trên máy tính cá nhân nhưng không thể debug đươc. Và thế là mình bắt đầu tìm hiểu để giải quyết vấn đề này. Rồi mình tìm thấy SAM và mọi thứ dường như được giải quyết.

    Ngôn ngữ nào được hỗ trợ?

    • Serverless Offline hỗ trợ những ngôn ngữ sau:
      • Python
      • Ruby
      • Node
    • SAM hỗ trợ nhưng ngôn ngữ sau:
      • Python
      • Ruby
      • Node
      • Java
      • .NET Core

    Được hỗ trợ như thế nào?

    • Serverless Offline là plugin được cá nhân phát triển. Nó không phải gói được hỗ trợ chính thức từ AWS.

    • SAM được hỗ trợ chính thức từ AWS.

    Hỗ trợ debug như thế nào?

    • Serverless Offline chỉ hỗ trợ debug với Node.

    • SAM thì có vẻ như đã hỗ trợ tất cả các trình thực thi ở trên. Mình đã thử debug với Java thì thấy vẫn OK.

    Môi trường thực thi

    • Serverless chạy trực triếp trên máy host.
    • SAM thì sử dụng container trong docker để thực thi.

    Các bạn có thể tham khảo hướng dẫn sử dụng Serverless ở các tài liệu sau nhé:

    Kết luận

    SAM dường như có những lợi thế hơn hẳn so với Serverless. Nếu bạn quyết định phát triển bằng Node thì bạn sẽ không gặp nhiều khó khăn khi dùng Serverless hay SAM. Nếu bạn chọn một môi trường thực thi khác như Python hay Ruby hay bất kỳ môi trường nào khác thì lựa chọn SAM sẽ là quyết định sáng suốt hơn đấy. Mình sẽ hướng dẫn các bạn sử dụng SAM trong loạt bài viết về SAM sau nhé.

  • [AWS] Sử dụng API Gateway Lambda Authorizers với JWT như thế nào?

    [AWS] Sử dụng API Gateway Lambda Authorizers với JWT như thế nào?

    Một trọng những vấn đề quan trọng trong các dự án đó là điều khiển quyền truy cập. Với các ứng dụng xây dựng trên nền tảng AWS việc điều khiển truy cập cũng phức tạp hơn. Trong bài viết này tôi sẽ hướng dẫn các bạn cách tôi đã làm để điểu khiển truy cập với các API sử dụng API Gateway Lambda Authorizers.

    Luồng xác thực Lambda Authorizer

    Luồng xác thực của Lambda Authorizer được minh hoạ trong hình sau:

    auhorizer

    Các bược xác thực như sau:

    1. Máy khách gửi yêu cầu lên API Gateway API có kèm theo Bearer Token.
    2. API Gateway kiểm tra cấu hình authorizer đã được cấu hình tương ứng với hàm Lambda. Nếu nó tồn tại thì Lambda Authoirizer sẽ được gọi.
    3. Lambda Authorizer sẽ thực hiện xác thực bằng Bearer Token đã được gửi lên.
    4. Nếu việc gọi Lambda Authrorizer thực hiện thành công, hàm Lambda sẽ trả về thông tin chứa chính sách IAM và thông tin người dùng.
    5. API Gateway sử dụng thông tin trả về từ Lambda Authorizer để kiểm tra quyền truy cập:
    • Trường hợp nhận được thông tin từ chối truy cập thì API Gateway sẽ trả về mã 403 và từ chối truy cập tới API từ máy khách.
    • Trường hợp nhận được thôn tin cho phép truy cập thì phương thức sẽ được thực thi.

    Định nghĩa Lambda Authorizer

    • Khai báo authorizer trong serverless.yml:
    functions:
      authorizer:
        handler: src.api.authorizer.lambda_handler
        cors: true
    
    • Định nghĩa hàm Lambda Authorizer:
    import jwt
    
    
    def lambda_handler(event, context):
        try:
            token = event.get("authorizationToken").split(" ")[1] # lấy thông tin token trong Authorization header
            claims = jwt.decode(token, "secret", algorithms=["HS256"]) # decode xem token có hợp lệ không
            return {
                "principalId": claims["uid"], # lấy thông tin user đề gán vào IAM
                "policyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Action": "execute-api:Invoke",
                            "Effect": "Allow", # cho phép nếu token hợp lệ
                            "Resource": event["methodArn"],
                        }
                    ],
                },
            }
        except:
            return {
                "principalId": "denied",
                "policyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Action": "execute-api:Invoke",
                            "Effect": "Deny", # từ chối nếu token không hợp lệ
                            "Resource": event["methodArn"],
                        }
                    ],
                },
            }
    

    Định nghĩa hàm Lambda cần điều khiển quyề truy cập

    • Khai báo hàm Lambda trong serverless.yml
    functions:
      test:
        handler: src.api.test.lambda_handler
        events:
          - http:
              method: get
              path: api/test
              cors: true
              authorizer: authorizer # khai báo Lambda Authorizer
    
    • Định nghĩa hàm Lambda:
    import json
    
    
    def lambda_handler(event, context):
        headers = {"Access-Control-Allow-Origin": "*", "Accept": "application/json"}
        body = {"status": "success"}
        return {
            "statusCode": 200,
            "headers": headers,
            "body": json.dumps(body),
        }
    

    Test thử Lambda với Authorizer

    • Trường hợp không truyền token trên Authorizer Header, API Gateway sẽ trả về 403

    No Auth

    • Trường hợp có truyền token trên Authorization Header, API Gateway sẽ cho phép phương thức được thực thi

    Token được tạo như sau

    (zpn) hieunv@HieuNV lambda % python
    Python 3.7.7 (default, Mar 10 2020, 15:43:33)
    [Clang 11.0.0 (clang-1100.0.33.17)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import jwt
    >>> jwt.encode({'uid': 'hieunv'}, "secret", algorithm='HS256')
    b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiJoaWV1bnYifQ.xuZSlS_3lw6NvGvw_fQ2qXGBWiv2HpXTFtYtO85lQac'
    

    Truyền token lên Authorizer Header

    Bearer Auth

    Tài liệu tham khảo:

    Cám ơn các bạn đã theo dõi bài viết. Hy vọng bài viết có thể giúp các bạn cài đặt việc điều khiển truy cập dễ dàng hơn với các ứng dụng trên nền tảng AWS.

  • [AWS] Lambda và DynamoDB Streams không còn khó nữa!

    [AWS] Lambda và DynamoDB Streams không còn khó nữa!

    Với các ứng dụng hiện nay, việc giao tiếp giữa client và server phổ biến đang sử dụng Rest API. Trong một số trường hợp việc phải để client đợi xử lý là điều không thể chấp nhận được. Do đó để giải quyết vấn đề này thì phần lớn cách giải quyết là sử dụng batch process, nghĩa là tại thời điểm đó chúng ta sẽ trả lại dữ liệu cho client ở trạng thái đang xử lý(tránh xảy ra tình trạng timeout). Tuy nhiên ngay tại thời điểm đó một batch process sẽ được khởi động để thực thi tiếp các công việc còn lại. Trong bài viết này tôi sẽ hướng dẫn các bạn viết batch process bằng AWS Lambda bằng cách sử dụng DynamoDB Streams.

    DynamoDB Streams

    DynamoDB Streams là một tính năng trong DynamoDB cho phép bạn lắng nghe thay đổi trên một bảng dữ liệu nào đó và thực hiện các tác vụ đáp ứng yêu cầu nghiệp vụ trong ứng dụng của bạn. Mỗi khi có sự thay đổi DynamoDB sẽ ghi các bản ghi gần như ngay lập tức là dòng dữ liệu mà các ứng dụng đang lắng nghe.

    Với DynamoDB Streams để giải quyết vấn đề timeout của API chúng ta chỉ gần ghi dữ liệu vào bảng trong DynamoDB sau đó dữ liệu được ghi lên dòng dữ liệu mà batch process của chúng ta đang lắng nghe rồi tiếp tục thực hiện nhiệm vụ còn lại.

    Các bạn tham khảo link sau để cài đặt DynamoDB ở local nhé.

    Định nghĩa bảng trong DynamoDB

    Các bạn có thẻ sử dụng NoSQL Workbench for Amazon DynamoDB để tạo bảng hoặc viết code để chia sẻ với các member khác như sau:

    # -*- coding: utf-8 -*-
    import os
    from datetime import datetime
    import boto3
    
    dynamodb = boto3.client(
        "dynamodb",
        endpoint_url="http://localhost:8000",
        region_name="us-east-1",
        aws_access_key_id="test",
        aws_secret_access_key="test",
    )
    
    
    def create_orders():
        try:
            dynamodb.delete_table(TableName="dev_orders")
        except Exception as exp:
            print(exp)
    
        response = dynamodb.create_table(
            TableName="dev_orders",
            AttributeDefinitions=[
                {"AttributeName": "id", "AttributeType": "S"},
                {"AttributeName": "status", "AttributeType": "S"},
            ],
            KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}],
            ProvisionedThroughput={"ReadCapacityUnits": 1, "WriteCapacityUnits": 1},
            GlobalSecondaryIndexes=[
                {
                    "IndexName": "statusGSIndex",
                    "KeySchema": [{"AttributeName": "status", "KeyType": "HASH"}],
                    "Projection": {"ProjectionType": "ALL"},
                    "ProvisionedThroughput": {
                        "ReadCapacityUnits": 1,
                        "WriteCapacityUnits": 1,
                    },
                },
            ],
            # bắt buộc phải có khai báo này để sử dụng DynamoDB Streams cho bảng này
            StreamSpecification={
                "StreamEnabled": True,
                "StreamViewType": "NEW_AND_OLD_IMAGES",
            },
        )
        print(response)
    
    
    create_orders()
    

    Kiểm tra bảng được tạo bằng NoSQL Workbench for Amazon DynamoDB

    dev_orders

    Các bạn chú ý giá trị bôi vàng nhé. Đây là dòng dữ liệu sẽ được DynamoDB ghi lên đó. Khi ứng dụng của bạn lắng nghe dòng dữ liệu này thì bất kỳ hành động nào xảy ra trên bảng sẽ được ghi lên dòng dữ liệu và ứng dụng của chúng ta sẽ phát hiện được điều đó.

    Định nghĩa Lambda lắng nghe dòng dữ liệu từ DynamoDB

    Để lắng nghe dòng dữ liệu từ DynamoDB Streams bạn cần thêm serverless-offline-dynamodb-streams và cấu hìn serverless.yml như sau:

    custom:
      # ...
      serverless-offline-dynamodb-streams:
        endpoint: http://dynamodb:8000
        accessKeyId: root
        secretAccessKey: root
    # ...
    plugins:
      - serverless-offline
      - serverless-python-requirements
      - serverless-offline-dynamodb-streams
    

    Các bạn tham khảo bài viết Mô phỏng AWS Lambda & API Gateway bằng Serverless Offline để biết các viết API bằng Lambda nhé.

    Trong bài viết này, dể thực hiện lắng nghe dòng dữ liệu, bạn định nghĩa hàm Lambda trong Serverless như sau:

    jobs_order:
      handler: src.jobs.order.lambda_handler
      events:
        - stream:
            enabled: true
            type: dynamodb
            # đây là giá trị màu vàng tôi có đề cập ở trên
            arn: arn:aws:dynamodb:ddblocal:000000000000:table/dev_orders/stream/2020-03-15T07:59:46.532
            batchSize: 1
    

    Thử viết Rest API ghi dữ liệu vào bảng và kiểm tra DynamoDB Streams

    Các bạn định nghĩa một API như sau:

    functions:
      post_orders:
        handler: src.api.post_orders.lambda_handler
        events:
          - http:
              method: post
              path: api/orders
              cors: true
    

    src/api/post_orders.py

    import json
    import logging
    from datetime import datetime
    from uuid import uuid4
    import boto3
    
    LOGGER = logging.getLogger()
    LOGGER.setLevel(logging.INFO)
    
    
    def lambda_handler(event, context):
        headers = {"Access-Control-Allow-Origin": "*", "Accept": "application/json"}
        body = json.loads(event["body"])
        dynamodb = boto3.client(
            "dynamodb",
            endpoint_url="http://localhost:8000",
            region_name="us-east-1",
            aws_access_key_id="test",
            aws_secret_access_key="test",
        )
        now = int(datetime.utcnow().timestamp())
        body = dynamodb.put_item(
            TableName="dev_orders",
            Item={
                "id": {"S": str(uuid4())},
                "name": {"S": body["name"]},
                "status": {"S": " "},
                "created_at": {"N": str(now)},
                "updated_at": {"N": str(now)},
            },
        )
        return {
            "statusCode": 200,
            "headers": headers,
            "body": json.dumps(body),
        }
    

    Các bạn thử post dữ liệu bằng Postman nhé post_orders

    Các bạn để ý Terminal sau khi post dữ liệu nhé

    terminal

    Cám ơn các bạn đã theo dõi bài viết. Hy vọng bài viết đã giúp các bạn có thể sử dùng Lambda và DynamoDB tốt hơn.

  • [MacOS] Hướng dẫn cài đặt Oracle JDK

    [MacOS] Hướng dẫn cài đặt Oracle JDK

    Mặc định thì Oracle JDK sẽ được chọn cài đặt trên MacOS. Do đó nếu muốn sử dụng Oracle JDK thì bạn cần phải cài đặt lại. Trong bài viết này tôi sẽ hướng dẫn các bạn cài đặt Oracle JDK.

    Homebrew

    • Nếu bạn chưa cài đặt brew thì có thể sử dụng lệnh sau để tiến hành cài đặt
    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
    
    • Nếu đã cài đặt rồi thì tiến hành update lastest brew như sau:
    hieunv@HieuNV ~ % brew update && brew upgrade
    Updated 1 tap (homebrew/core).
    ==> New Formulae
    swift-sh
    ==> Updated Formulae
    apache-spark               jetty                      xcodegen
    docker-slim                vim                        zsh-syntax-highlighting
    Updating Homebrew...
    

    Kiểm tra caskjava

    brew cask info java
    

    Nếu homebrew/cask chưa được cài đặt thì nó sẽ tự động cài đặt luôn

    hieunv@HieuNV ~ % brew cask info java
    ==> Tapping homebrew/cask
    Cloning into '/usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask'...
    remote: Enumerating objects: 3655, done.
    remote: Counting objects: 100% (3655/3655), done.
    remote: Compressing objects: 100% (3648/3648), done.
    remote: Total 3655 (delta 26), reused 510 (delta 5), pack-reused 0
    Receiving objects: 100% (3655/3655), 1.23 MiB | 215.00 KiB/s, done.
    Resolving deltas: 100% (26/26), done.
    Tapped 1 command and 3543 casks (3,660 files, 4.0MB).
    java: 13.0.2,8:d4173c853231432d94f001e99d882ca7
    https://openjdk.java.net/
    Not installed
    From: https://github.com/Homebrew/homebrew-cask/blob/master/Casks/java.rb
    ==> Name
    OpenJDK Java Development Kit
    ==> Artifacts
    jdk-13.0.2.jdk -> /Library/Java/JavaVirtualMachines/openjdk-13.0.2.jdk (Generic Artifact)
    
    • Nếu đã cài đặt rồi bạn sẽ nhận được thông tin về phiên bản java đã được cài đặt
    hieunv@HieuNV ~ % brew cask info java
    java: 13.0.2,8:d4173c853231432d94f001e99d882ca7
    https://openjdk.java.net/
    Not installed
    From: https://github.com/Homebrew/homebrew-cask/blob/master/Casks/java.rb
    ==> Name
    OpenJDK Java Development Kit
    ==> Artifacts
    jdk-13.0.2.jdk -> /Library/Java/JavaVirtualMachines/openjdk-13.0.2.jdk (Generic Artifact)
    

    Tiến hành cài đặt Oracle JDK sử dụng brew cask

    hieunv@HieuNV ~ % brew cask install oracle-jdk
    ==> Caveats
    Installing oracle-jdk means you have AGREED to the license at:
      https://www.oracle.com/technetwork/java/javase/terms/license/javase-license.html
    
    ==> Downloading https://download.oracle.com/otn-pub/java/jdk/13.0.2+8/d4173c8532
    ==> Downloading from https://download.oracle.com/otn-pub/java/jdk/13.0.2+8/d4173
    ######################################################################## 100.0%
    ==> Verifying SHA-256 checksum for Cask 'oracle-jdk'.
    ==> Installing Cask oracle-jdk
    ==> Running installer for oracle-jdk; your password may be necessary.
    ==> Package installers may write to any location; options such as --appdir are i
    Password:
    installer: Package name is JDK 13.0.2
    installer: Installing at base path /
    installer: The install was successful.
    ?  oracle-jdk was successfully installed!
    

    Kiểm tra phiên bản java sau khi cài đặt

    hieunv@HieuNV ~ % java --version
    java 13.0.2 2020-01-14
    Java(TM) SE Runtime Environment (build 13.0.2+8)
    Java HotSpot(TM) 64-Bit Server VM (build 13.0.2+8, mixed mode, sharing)
    
    hieunv@HieuNV ~ % javac --version
    javac 13.0.2
    

    setting JAVA_HOME

    Thêm export JAVA_HOME=$(/usr/libexec/java_home) vào ~/.zshrc

    echo 'export JAVA_HOME=$(/usr/libexec/java_home)' >> ~/.zshrc
    

    Kiểm tra biến JAVA_HOME

    Đóng Termial sau đó bật lại và kiểm tra biến JAVA_HOME

    hieunv@HieuNV libexec % echo $JAVA_HOME
    /Library/Java/JavaVirtualMachines/jdk-13.0.2.jdk/Contents/Home
    

    Như vậy là bạn đã tiến hành cài đặt thành công Oracle Java rồi.
    Tài liệu tham khảo
    https://emcorrales.com/blog/install-oracle-jdk-macos-homebrew

  • Mô phỏng AWS Lambda & API Gateway bằng Serverless Offline

    Mô phỏng AWS Lambda & API Gateway bằng Serverless Offline

    Khi phát triển ứng dùng bằng AWS Lambda không phải lúc nào chúng ta cũng có thể phát triển trực tiếp trên AWS được. Do đó việc giả lập môi trường AWS để có thể chạy được Lambda và API Gateway là cần thiết. Nó không chỉ giúp chúng ta có thể học mà còn giúp cho quá trình phát triển nhanh hơn. Trong bài viết này tôi sẽ hướng dẫn các bạn giả lập AWS Lambda và API Gateway bằng Serverless Offline

    Các công cụ cần thiết

    Trước tiên bạn cần cài đặt các tool cần thiết, bạn có thể tham khảo hướng dẫn cài đặt trong các bài viết sau:

    Bạn có thể dùng lệnh sau để cài serverless

    hieunv@HieuNV lambda % yarn global add serverless
    yarn global v1.22.0
    [1/4] ?  Resolving packages...
    [2/4] ?  Fetching packages...
    [3/4] ?  Linking dependencies...
    [4/4] ?  Building fresh packages...
    success Installed "[email protected]" with binaries:
          - serverless
          - slss
          - sls
    ✨  Done in 14.23s.
    

    Tạo một project mới

    • Tạo project với yarn
    hieunv@HieuNV hieunv % mkdir lambda
    hieunv@HieuNV hieunv % cd lambda
    hieunv@HieuNV lambda % yarn init
    yarn init v1.22.0
    question name (lambda):
    question version (1.0.0):
    question description:
    question entry point (index.js):
    question repository url:
    question author:
    question license (MIT):
    question private:
    success Saved package.json
    ✨  Done in 3.53s.
    
    • Cài đặt serverless-offline
    hieunv@HieuNV lambda % yarn add serverless-offline -D
    
    • Cài đặt serverless-python-requirements để viết lambda handler bằng python
    hieunv@HieuNV lambda % yarn add serverless-python-requirements -D
    

    Cấu hình serverless.yml

    serverless.yml

    service: lambda
    
    frameworkVersion: '>=1.1.0 <2.0.0'
    
    provider:
      name: aws
      runtime: python3.7
    custom:
      serverless-offline:
        port: 4000
    plugins:
      - serverless-offline
      - serverless-python-requirements
    

    Cấu hình lambda handler đầu tiên trong serverless.yml

    Chúng ta tạo một Rest API sử dụng lambda bằng cách thêm đoạn sau vào file serverless.yml

    functions:
      test:
        handler: src.api.test.lambda_handler
        events:
          - http:
              method: get
              path: api/test
              cors: true
    

    Ở đây chúng ta tạo ra một Rest API với phướng thức GET và path /api/test. Các bạn nhìn thấy handler: src.api.test.lambda_handler đúng không. Đây là cấu hình hàm lamda sẽ được gọi bởi API Gateway

    Viết code cho lambda handler

    src/api/test.py

    import json
    
    
    def lambda_handler(event, context):
        headers = {"Access-Control-Allow-Origin": "*", "Accept": "application/json"}
        return {
            "statusCode": 200,
            "headers": headers,
            "body": json.dumps({"status": "success", "data": {}}),
        }
    

    Tạo script để run server

    Thêm đoạn sau vào package.json

        "scripts": {
            "start": "sls offline start"
        },
    

    Giờ thì chạy thôi nào các thanh niên

    hieunv@HieuNV lambda % yarn start
    yarn run v1.22.0
    $ sls offline start
    Serverless: Starting Offline: dev/us-east-1.
    
    Serverless: Routes for test:
    Serverless: GET /api/test
    Serverless: POST /{apiVersion}/functions/lambda-dev-test/invocations
    
    Serverless: Offline [HTTP] listening on http://localhost:4000
    Serverless: Enter "rp" to replay the last request
    

    Dùng Postman để call api vừa tạo nhé:

    Cám ơn các bạn đã theo dõi bài viết. Hy vọng bài viết có thể giúp các bạn tiếp tục học và làm việc cùng với AWS Lambda và API Gateway trong các dự án của mình.

  • Hướng dẫn cài đặt DynamoDB với Docker

    Hướng dẫn cài đặt DynamoDB với Docker

    Cài đặt docker

    • Cập nhập brew
    hieunv@HieuNV ~ % brew update && brew upgrade
    Updated 1 tap (homebrew/core).
    No changes to formulae.
    
    • Cài đặt docker
    brew install docker
    
    • Kiểm tra docker sau khi cài đặt
    hieunv@HieuNV ~ % docker -v
    Docker version 19.03.1, build 74b1e89
    
    • Khởi động Docker
    Kiểm tra trạng thái docker

    Tạo DynamoDB container

    • Tạo docker-compose.yml để up DynamoDB

    docker-compose.yml

    version: '3'
    services:
      dynamodb:
        image: amazon/dynamodb-local
        container_name: dynamodb
        ports:
          - '8000:8000'
        volumes:
          - ./dynamodb/data:/home/dynamodblocal/data
        entrypoint: java
        command: '-jar DynamoDBLocal.jar -sharedDb -dbPath /home/dynamodblocal/data'
    
    • Up DynamoDB container sau khi tạo docker-compose.yml
    docker-compose up -d
    
    Kiểm tra trạng thái docker sau khi up DynamoDB container

    Bạn có thể truy cập vào docker shell bằng link sau:

    http://localhost:8000/shell
    

    Như vậy là bạn đã tạo xong container cho DynamoDB rồi.

  • Quản lý python packages như thế nào cho đúng

    Quản lý python packages như thế nào cho đúng

    Maven dùng pom để quản lý packages, Node thì có packages.json. Anh em python thì quản lý python packages bằng pip (Package installer for Python). Tuy nhiên khi sử dụng pip sẽ gặp phải tình huống các dự án khác nhau sử dụng dánh sách packages khác nhau. Vấn đề lớn hơn nữa có thể xảy ra tình huống 2 dự án nào đó sử dụng cùng package ở hai phiên bản khách nhau. Trong bài viết này tôi sẽ hướng dẫn các bạn cách quản lý python packages cho các dự án khác nhau mà không bị phụ thuộc vào nhau. Chúng ta cùng bắt đầu nhé.

    Kiểm tra tình trạng hoạt động của brew nào:

    hieunv@HieuNV ~ % brew update && brew upgrade
    Updated 1 tap (homebrew/core).
    ==> Updated Formulae
    abcm2ps             consul              grails              pjproject
    allure              dps8m               graphicsmagick      rancid
    appstream-glib      erlang              i2p                 ratfor
    byteman             flyway              jruby               stress-ng
    camlp5              folly               libgphoto2          zydis
    cargo-instruments   fonttools           libjwt
    cfengine            gmsh                mill
    clblast             gptfdisk            mongoose
    

    Cài đặt python 3

    brew install python
    

    Mặc định MacOS sử dụng python 2

    hieunv@HieuNV ~ % python -V
    Python 2.7.16
    hieunv@HieuNV ~ % python3 -V
    Python 3.7.0
    

    Các bạn cần thêm đoạn sau vào ~/.zshrc.

    export PATH="/usr/local/opt/python/libexec/bin:/usr/local/sbin:$PATH"
    

    Nếu đang mở Terminal bạn cần đóng Terminal lại rồi kiểm tra lại python version

    hieunv@HieuNV ~ % python -V
    Python 3.7.6
    hieunv@HieuNV ~ % pip -V
    pip 19.3.1 from /usr/local/lib/python3.7/site-packages/pip (python 3.7)
    

    Cài đặt virtualenvvirtualenvwrapper

    Với python các packages không được cài đặt cục bộ giống như node. Do đó chúng ta cần tạo ra các môi trường khác nhau với các packages khác nhau để sử dụng cho các dự án khác nhau.

    Cài đặt virtualenv

    pip install virtualenv
    

    Cài đặt virtualenvwrapper

    pip install virtualenvwrapper
    

    Activate virtualenv mỗi khi bật khởi động Terminal

    Các bạn thêm đoạn sau vào ~/.zshrc để virutalenv có thể dượcd khởi động mỗi khi bạn bật Terminal

    export WORKON_HOME=$HOME/.virtualenvs
    source /usr/local/bin/virtualenvwrapper.sh
    

    Tiến hành tạo môi trưởng ảo và cài đặt packages mong muốn

    • Tạo môi trường ảo
    hieunv@HieuNV ~ % mkvirtualenv a
    created virtual environment CPython3.7.6.final.0-64 in 515ms
      creator CPython3Posix(dest=/Users/hieunv/.virtualenvs/a, clear=False, global=False)
      seeder FromAppData(download=False, pip=latest, setuptools=latest, wheel=latest, via=copy, app_data_dir=/Users/hieunv/Library/Application Support/virtualenv/seed-app-data/v1)
      activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator,XonshActivator
    virtualenvwrapper.user_scripts creating /Users/hieunv/.virtualenvs/a/bin/predeactivate
    virtualenvwrapper.user_scripts creating /Users/hieunv/.virtualenvs/a/bin/postdeactivate
    virtualenvwrapper.user_scripts creating /Users/hieunv/.virtualenvs/a/bin/preactivate
    virtualenvwrapper.user_scripts creating /Users/hieunv/.virtualenvs/a/bin/postactivate
    virtualenvwrapper.user_scripts creating /Users/hieunv/.virtualenvs/a/bin/get_env_details
    (a) hieunv@HieuNV ~ %
    

    Các bạn để ý dòng cuối cùng (a). Sau khi tạo xong thì zsh đã activate vào môi trường ảo.

    • Active vào môi trường ảo nếu môi trường chưa được active thì có thể làm như sau:
    hieunv@HieuNV ~ % workon a
    (a) hieunv@HieuNV ~ %
    
    • Khi muốn thoát khỏi môi trường ảo thì có thể làm như sau:
    (a) hieunv@HieuNV ~ % deactivate
    hieunv@HieuNV ~ %
    
    • Để cài đặt packages thì tiến hành cài đặt bằng pip như bình thường
    pip install boto3
    

    Sử dụng môi trường ảo với python 2

    • Cài đặt python 2
    brew install python2
    
    • Tạo môi trường ảo sử dụng python bằng tham số -p
    mkvirtualenv py2 -p python2
    

    Export package list để install trên máy khác

    (a) hieunv@HieuNV ~ % pip freeze > requirements.txt
    (a) hieunv@HieuNV ~ % cat requirements.txt
    boto3==1.12.11
    botocore==1.15.11
    docutils==0.15.2
    jmespath==0.9.5
    python-dateutil==2.8.1
    s3transfer==0.3.3
    six==1.14.0
    urllib3==1.25.8
    

    Cài đặt packages sử dụng requirements.txt

    pip install -r requirements.txt
    

    Tài liệu tham khảo:

    • https://swapps.com/blog/how-to-configure-virtualenvwrapper-with-python3-in-osx-mojave/
  • Optimize Lambda function with Nodejs.

    Optimize Lambda function with Nodejs.

    Gần đây có nghiên cứu lại mấy vấn đề của Lambda function và mày mò vào Node Summit, bài viết này thực ra trình bày lại topic này của Matt Lavin

    Về cơ bản với Lambda, AWS đã làm gần hết mọi thứ về management, scale function, kết nối đến các service như DynamoDB, SQS,… Gần như chúng ta chỉ cần chú ý đến việc coding là chính. Tuy nhiên để mọi thứ tốt hơn cho người dùng thì cần giảm latency, response nhanh hơn và dễ debug hơn trong những trường hợp cần thiết và chính trong source code Lambda function tức là:

    • Cải tiện latency
    • Tìm ra bug performance
    • Debug.

    Bài này sẽ nói về các cách optimze coding là chính, những phần khác thì hãy xem kỹ topic nhé.

    Đầu tiên bao giờ cũng cần tìm hiểu xem Lambda hoạt động như nào nhưng trước tiên mình sẽ đưa một ví dụ điển hình về lambda function:

    const dynamodb = require('aws-sdk/clients/dynamodb');
    const docClient = new dynamodb.DocumentClient();
    const tableName = process.env.SAMPLE_TABLE;
    exports.getByIdHandler = async (event) => {
        const { httpMethod, path, pathParameters } = event;
        if (httpMethod !== 'GET') {
            throw new Error(`Unsupported method`);
        }
        console.log('received:', JSON.stringify(event));
        const { id } = pathParameters;
        const params = {
            TableName: tableName,
            Key: { id },
        };
        const { Item } = await docClient.get(params).promise();
        const response = {
            statusCode: 200,
            body: JSON.stringify(Item),
        };
        return response;
    };
    

    Khá là điển hình với việc: Khởi tạo SDK, handle request, query database và đưa ra kết quả, tất nhiên trước đó sẽ là download source code và khởi chạy lambda function. Và hãy ghép nó vào mô hình lifecycle của lambda function như ở bên dưới.

    Như hình bên trên toàn bộ Lifecycle của AWS Lambda bao gồm Cold StartWarm Start. Warm start: bao gồm phần thời gian code chạy Cold start: thời gian chuẩn bị.

    Như vậy có thể thấy rằng phần warm start là phần coding đơn thuần và optimze như chúng ta optimze source code khi sử dụng các framework hay runtime khác. Mặt khác, mọi người thường nghĩ rằng Lambda Function sẽ thực hiện toàn bộ các bước trên mỗi lần execute nhưng không Lambda sẽ không khởi chạy lại Cold Start, miễn là bạn không update source code nhưng chỉ trong 15 phút thôi nhưng vậy là quá đủ. Reduce latency sẽ bắt đầu từ đây.

    Như hình trên cứ sau một khoảng thời gian nhất định Lambda function lại thực hiện Cold Start, những chỗ thời gian execute cao bất thường ấy, nhìn chung hãy để function Lambda luôn sẵn sàng để execute.

    Một cách chính thống hơn thì có thể tìm hiểu ở đây, AWS đề cập đến Lambda execution context (Môi trường để running Lambda code), context này sẽ bị đóng băng sau khi sử dụng xong function và được giã đông khi chạy lần tiếp và AWS cũng đề xuất một vài thủ thuật để optimize Lambda function:

    • Đầu tiên bắt đầu với handler method, Các object được khai báo bên ngoài handler vẫn được khởi tạo, cung cấp tối ưu hóa bổ sung khi handler được gọi lại. Ví dụ: nếu Lambda connect đến database (RDS, DynamoDB), thay vì kết nối đi kết nối lại, kết nối được tái sử dụng qua các lần invoke khác nhau trong một lambda instance. Một cách đơn giản có thể lazy load connection, như bây giờ AWS đã cải tiến SDK để dùng keep alive hoặc đơn giản là chuyễn những thứ nặng nề ra khỏi handler, cache lại AWS SDK Client
        const AWS = require('aws-sdk')
        // http or https
        const https = require('https');
        const agent = new https.Agent({
          keepAlive: true
        });
      
        const dynamodb = new AWS.DynamoDB({
          httpOptions: {
            agent
          }
        });
    
        fuction fuckingHeavyFunction() {
        }
    
        const outsideHeavyResult = fuckingHeavyFunction(); // run on every Lambda init instance.
    
        exports.handler = async (event) => {
          const heavyResult = fuckingHeavyFunction(); // run on every lambda request
          return response;
        };
    
    • Mỗi Lambda function có 512Mb lưu trữ ở /tmp, bạn có thể lưu trữ bất kỳ thứ gì. Vùng lưu trữ này sẽ được đóng băng cùng với Execution context, như vậy bạn có thể lưu trữ nhiều thứ ở trong này, ví dụ những tính toán, variable ít thay đổi có thể lưu trữ lại và dùng lại cho lần tiếp theo.
    • Nếu sử dụng background process trong Lambda function, hãy chắc chắn nó được hoàn toàn hoàn thành khi Lambda function kết thúc, vì có thể nó được sử dụng lại và tiếp tục chạy. Dẫn đến những bug không như ý. Nhưng nói chung cũng không nên nghĩ rằng Lambda sẽ sử dụng lại các tài nguyên khi chạy lại Lambda function, hãy chuẩn bị lại các tài nguyên hoặc kiểm tra việc sử dụng cho chắc chắn.