DEV Community

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

 เล่นกับ 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 ควรจะศึกษาไว้เพื่อให้การทำงานง่ายขึ้น

Discussion (1)

Collapse
chatchaikomrangded profile image
Chatchai Komrangded (Bas)

Nice!