DEV Community

Cover image for  เล่นกับ AWS X-Ray เบื้องต้น
6 1

 เล่นกับ AWS X-Ray เบื้องต้น

Level: Overview - Basic

งานสำคัญอย่างนึงในการพัฒนาระบบคือการตรวจสอบและติดตามหา Root cause ของ error หรือ bug ในโปรแกรม ซึ่งโดยปกติจะมีการนั่งดู log ของ service ต่างๆที่เกี่ยวข้อง แต่สำหรับกรณีที่เราใช้ Serverless หรือ Service ใน AWS นอกจากที่จะเข้าไปตรวจสอบ Log ใน Cloudwatch หรือจุดอื่นๆแล้ว AWS มี Service อีกตัวที่หลายๆคนอาจจะไม่เคยได้ลองเล่น คือ AWS X-Ray ที่ช่วยให้เราสามารถติดตามการทำงานของระบบได้สะดวกมาก และมีประโยชน์ในการวิเคราะห์ว่าการทำงานมีส่วนไปนที่ทำงานผิดพลาดหรือมีคอขวดตรงไหนหรือไม่

โดยในตัวอย่างที่ยกมาจะลองเล่นกับ AWS X-Ray เบื้องต้นซึ่งจะมี Service ต่างๆที่เกี่ยวข้องดังนี้

  • API Gateway
  • Lambda Function
  • S3
  • DynamoDB

Architecture

Architecture

นอกจากการทดสอบระหว่าง API Gateway กับ Lambda แล้ว ในตัวอย่างจะเพิ่มการเชื่อมต่อกับ DynamoDB และ S3 รวมไปถึงการสร้าง DynamoDB Stream และ S3 Event Notification เพื่อเรียก Lambda เพื่อทดสอบดูว่า Trace ไปได้ถึงไหน ในเบื้องต้น

Setup

  • สร้าง DynamoDB Table ชื่อ xray-demo โดยมี partition key ชื่อ id
    DynamoDB xray-demo

  • สร้าง S3 Bucket ชื่อ xray-demo-20211020 (ถ้าไปทำตามเปลี่ยนชื่อและเปลี่ยนชื่อใน code function xray-demo-s3 ด้วยนะครับ)
    S3 xray-demo-20211020

  • สร้าง AWS Lambda Function xray-demo-dynamo ทำการเขียนข้อมูลลงบน dynamoDB (อย่าลืมเพิ่ม DynamoDB Policy ให้ LambdaExecution Role)

import json
import boto3
import uuid

def lambda_handler(event, context):
    client = boto3.resource('dynamodb')
    table = client.Table('xray-demo')
    table.put_item(
        Item={ 
            'id': str(uuid.uuid4()), 
            'data': event
        }
    )

    return {
        'statusCode': 200,
        'body': 'Wrote to DynamoDB'
    }

Enter fullscreen mode Exit fullscreen mode
  • สร้าง AWS Lambda Function xray-demo-s3 ทำการเขียนข้อมูลลงบน s3 (อย่าลืมเพิ่ม S3 Policy ให้ LambdaExecution Role)
import json
import boto3
import uuid

def lambda_handler(event, context):
    filename = str(uuid.uuid4())
    s3 = boto3.resource('s3')
    s3.Object('xray-demo-20211020', 
        f'{filename}.json').put(
            Body=bytes(json.dumps(event).encode('UTF-8'))
            )
    return {
        'statusCode': 200,
        'body': 'Wrote to S3'
    }
Enter fullscreen mode Exit fullscreen mode
  • สร้าง AWS Lambda Function xray-demo-logging ทำการเขียน Log บน CloudWatch
import json

def lambda_handler(event, context):
    print(event)
    return {
        'Status': 'Done'
    }

Enter fullscreen mode Exit fullscreen mode
  • สร้าง API Gateway 2 Methods แล้วทำการ Deploy เพื่อใช้ทดสอบ
    • GET /dynamodb เพื่อเรียก Lambda Proxy Integrationไปยัง Function xray-demo-dynamo
    • GET /s3 เพื่อเรียก Lambda Proxy Integrationไปยัง Function xray-demo-s3

API Gateway Setup

  • เปิดใช้งาน DynamoDB Stream แล้วให้ Trigger เป็น Lambda Function xray-demo-loging

Enable DynamoDB Stream

  • เปิดใช้งาน S3 Event Notification แล้วให้เรียก Lambda Function xray-demo-logging

Enable S3 Event Notification

การเปิดใช้ X-Ray สำหรับ AWS Lambda

สำหรับ AWS Lambda ทั้ง 3 Functions จะต้องทำการเปิดใช้ X-Ray บน AWS Lambda ก่อน โดยสามารถเปิดใช้งานได้ที่หน้า Configuration > Monitoring and operations tools โดยตัว X-Ray จะใช้ชื่อว่า Active Tracing ซึ่งปกติจะขึ้น Not enabled อยู่ และสามารถกด Edit เพื่อเข้าไปเปิดได้
Lambda Config

โดยในการเปิด Active tracing ตัว Lambda จะเพิ่ม Permission ให้โดยอัตโนมัติ
Enable Active tracing

การเปิดใช้ X-Ray สำหรับ API Gateway

สำหรับ API Gateway เมื่อทำการ Deploy API แล้วสามารถเปิดใช้งาน AWS X-Ray ได้ที่หน้า Logs/Tracing ของ Stage นั้นๆ
Enable API Gateway X-Ray

สำหรับ X-Ray Sampling Rule เพื่อให้ง่ายต่อการทดสอบเฉยๆจะใช้ Default ที่มีอยู่แล้ว (จริงๆคือยังไม่ได้ลอง แหะๆ) ถ้าสนใจรายละเอียดอ่านที่เอกสารของ AWS ที่นี่
X-Ray Sampling

ทดลองเรียกใช้งานรอบที่ 1

ลองเรียกทั้ง 2 Function ดูแล้วเข้าไปดูที่ AWS X-Ray เพื่อดูผลลัพธ์ดู โดยจะเรียกจาก Postman และดู Header ที่ตอบกลับมา

  • เรียก API GET /dynamodb
    Postman GET /dynamodb
    จากรูปข้างบนจะเห็นว่าจะมี Header X-Amzn-Trace-Id กลับมาด้วย ซึ่งตัวข้อมูลนี้คือค่าที่ X-Ray ใช้ Trace ข้อมูล Request/Response ต่างๆในระบบ ซึ่งเมื่อมีการส่งต่อเลขนี้ไปด้วย
    เมื่อเข้าไปดูที่หน้าจอ X-Ray จะสามารถดูรายการ Trace ที่เกิดขึ้นในช่วงระยะเวลาที่เราเลือกได้ (ขวาบน) ซึ่งจะสังเกตได้ว่าเลขอ้างอิง หรือ ID ที่แสดงใน Trace List จะเป็นเลขเดียวกับค่าของ X-Amzn-Trace-Id โดยสามารถเข้าไปดูรายละเอียดของ Request นั้นๆและ Service Map ที่แสดงภาพรวมระยะเวลาในการทำงานของ Node ต่างๆได้ด้วย
    Dynamo Trace
    Dynamo Trace Detail
    Dynamo Service Map

  • เรียก API GET /s3
    เพื่อให้เห็นตัวอย่างที่ชัดเจนขึ้นจะทำการแก้ Lambda Function ให้ทำงานผิดพลาด (Lambda Error และ Return 502 จาก API Gateway) 1 รอบ และแก้กลับให้ทำงานปกติ ซึ่งเมื่อดู Trace และ Service Map บน X-Ray จะขึ้นสี เหลือง - Errors (4XX) และ สีแดง - Faults (5XX) ตามลำดับ สำหรับรายการที่ไม่สำเร็จ
    Trace S3
    Service Map S3

การเพิ่มรายละเอียดการ Trace ด้วย Code

จากตัวอย่างการเรียกทั้ง 2 แบบด้านบน จะเห็นว่าข้อมูลการ Trace จบที่ AWS Lambda ไม่แสดงรายละเอียดการเรียกไปยัง DynamoDB และ S3 รวมไปถึงไม่สามารถแสดงกรณีที่ S3 Event หรือ DynamoDB Stream เป็นผู้ Trigger Lambda xray-demo-logging ได้ ซึ่งการจะแสดงผลให้ละเอียดขึ้นจะต้องทำการเรียกใช้ aws-xray-sdk เพื่อให้ทำการเพิ่ม Trace ID ลงบน Header ของการเรียกใช้งาน Service อื่นๆด้วย
ในตัวอย่างจะแสดงการเรียกใช้แบบง่าย ซึ่งถ้าต้องการรายละเอียดเชิงลึกเพิ่มเติมสามารถดูได้ที่เอกสารของ AWS ที่นี่ ส่วนถ้าใช้ภาษาอื่นๆนอกเหนือจาก Python สามารถหาข้อมูลต่อได้จาก Link ข้างต้นเหมือนกัน

ในการเรียก aws-xray-sdk บน Lambda เนื่องจาก AWS ไม่ได้ include library ตัวนี้ไว้ใน default runtime ดังนั้นในเบื้องต้นจะมี 2 ทางเลือกคือ

  1. เขียน Code บนเครื่องพร้อม install package แล้ว upload .zip ขึ้นมายัง Lambda
  2. เพิ่ม Lambda Layer ที่ติดตั้ง aws-xray-sdk ไว้ ซึ่งในตัวอย่างจะใช้การทำงานแบบที่ 2 ซึ่งวิธีการสร้าง Lambda Layer ง่ายๆอ่านได้ ที่นี่

เมื่อทำการสร้าง Lambda Layer แล้ว ให้ทำการ Attach Layer และแก้ Lambda function สำหรับ dynamodb และ s3 โดยเพิ่ม 3 บรรทัดด้านบนดังนี้

from aws_xray_sdk.core import xray_recorder
from aws_xray_sdk.core import patch_all
patch_all()
Enter fullscreen mode Exit fullscreen mode

ซึ่งเมื่อแก้ไขแล้วจะได้ Code ของ. Function ที่แก้ไขแล้ว

  • แก้ไข AWS Lambda Function xray-demo-dynamo
import json
import boto3
import uuid

from aws_xray_sdk.core import xray_recorder
from aws_xray_sdk.core import patch_all
patch_all()

def lambda_handler(event, context):
    client = boto3.resource('dynamodb')
    table = client.Table('xray-demo')
    table.put_item(
        Item={ 
            'id': str(uuid.uuid4()), 
            'data': event
        }
    )

    return {
        'statusCode': 200,
        'body': 'Wrote to DynamoDB'
    }

Enter fullscreen mode Exit fullscreen mode
  • แก้ไข Function xray-demo-s3
import json
import boto3
import uuid

from aws_xray_sdk.core import xray_recorder
from aws_xray_sdk.core import patch_all
patch_all()

def lambda_handler(event, context):
    filename = str(uuid.uuid4())
    s3 = boto3.resource('s3')
    s3.Object('xray-demo-20211020', 
        f'{filename}.json').put(
            Body=bytes(json.dumps(event).encode('UTF-8'))
            )
    return {
        'statusCode': 200,
        'body': 'Wrote to S3'
    }
Enter fullscreen mode Exit fullscreen mode

ซึ่งการทำงานของคำสั่ง patch_all() จะไปทำการเพิ่ม Trace Id ใน Header ของทุกคำสั่งที่ X-Ray รองรับ เพื่อให้สามารถ Trace การทำงานได้

ทดลองเรียกใช้งานรอบที่ 2

  • เรียก API GET /dynamodb
    เมื่อเรียก API และเปิดดู​ Service Map จะเห็นว่ามี Node การทำงานของ DynamoDB Table เพิ่มขึ้นมา ซึ่งถ้าดูใน Trace จะแสดง API ที่ถูกเรียกด้วย คือ คำสั่ง PutItem แต่การแสดงข้อมูลการเรียก xray-demo-logging จะถูกแยกออกจาก Trace ของ API ที่เรียก
    X-Ray with Dynamo Table
    X-Ray Dynamo PutItem

  • เรียก API GET /s3
    เมื่อเรียก API และเปิดดู Service Map จะเห็นว่ามี Node การทำงานของ S3 Bucket เพิ่มขึ้นมา ซึ่งถ้าดูใน Trace จะแสดง API ที่ถูกเรียกด้วย คือ คำสั่ง PutObject และการเรียก xray-demo-logging จะแสดงเป็นการถูกเรียกโดย Lambda function xray-demo-s3 แทน โดยไม่แยกออกจากกันเหมือนกรณี DynamoDB
    X-Ray With S3 Bucket
    X-Ray S3 PutObject

Limitation

อย่างไรก็ตาม จากตัวอย่างข้างบน 2 รูปแบบ จะเห็นว่าการเรียก xray-demo-logging ซึ่งถูกเรียกโดย DynamoDB Stream จะขึ้น Trace Id แยกออกจากการคำสั่งชุดแรกที่ผ่าน API แต่การเรียกผ่าน S3 Event Notification จะมีการส่งต่อ TraceID เพื่อให้เห็นว่าที่มาของการเกิด Trigger เกิดจากที่ใด
ซึ่งรูปแบบที่แตกต่างกันนี้เกิดขึ้นเนื่องจาก X-Ray ยังไม่รองรับการส่ง Trace Id ผ่าน Dynamo DB แต่สำหรับ S3 มีการรองรับการส่งต่อ Trace ID ไปยังคำสั่งต่อไปแล้ว เพียงแต่จะไม่เห็นว่าต้นทางที่เรียกมาจาก S3 Event Notification โดยรายละเอียดการรองรับการทำงานร่วมกับ X-Ray สามารถอ่านเพิ่มเติมได้ที่เอกสารของ AWS ที่นี่

Cost

AWS X-Ray เปิดให้ใช้ฟรีสำหรับการเก็บข้อมูลเดือนละ 100,000 trace และฟรีสำหรับการเรียกข้อมูลหรือสแกนข้อมูลที่บันทึกไว้ 1,000,000 trace หลังจากนั้นจึงคิดเงินเพิ่มตามรูปด้านล่าง
X-Ray Pricing

Conclusion

Software developers spend 35-50 percent of their time validating and debugging software. The cost of debugging, testing, and verification is estimated to account for 50-75 percent of the total budget of software development projects
Devon H. O'Dell ACMQueue

งาน Monitor & Debugging เป็นงานที่สำคัญและกินเวลาของ Developer ค่อนข้างมาก ซึ่งการเลือกเครื่องมือต่างๆมาช่วยให้สามารถตรวจสอบหรือติดตามผลการทำงานของ Software ที่พัฒนาขึ้นมาได้อย่างเหมาะสมจะช่วยลดเวลาและค่าใช้จ่ายในการพัฒนาระบบได้ด้วย ดังนั้น AWS X-Ray จึงเป็นหนึ่งในตัวเลือกที่นักพัฒนาบน AWS ควรจะศึกษาไว้เพื่อให้การทำงานง่ายขึ้น

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more →

Top comments (2)

Collapse
 
doctorit04 profile image
DoctorIT

This is good tutorial

Collapse
 
chatchaikomrangded profile image
Chatchai Komrangded (Bas)

Nice!

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more