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
นอกจากการทดสอบระหว่าง API Gateway กับ Lambda แล้ว ในตัวอย่างจะเพิ่มการเชื่อมต่อกับ DynamoDB และ S3 รวมไปถึงการสร้าง DynamoDB Stream และ S3 Event Notification เพื่อเรียก Lambda เพื่อทดสอบดูว่า Trace ไปได้ถึงไหน ในเบื้องต้น
Setup
สร้าง DynamoDB Table ชื่อ xray-demo โดยมี partition key ชื่อ id
สร้าง S3 Bucket ชื่อ xray-demo-20211020 (ถ้าไปทำตามเปลี่ยนชื่อและเปลี่ยนชื่อใน code function xray-demo-s3 ด้วยนะครับ)
สร้าง 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'
}
- สร้าง 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'
}
- สร้าง AWS Lambda Function xray-demo-logging ทำการเขียน Log บน CloudWatch
import json
def lambda_handler(event, context):
print(event)
return {
'Status': 'Done'
}
- สร้าง API Gateway 2 Methods แล้วทำการ Deploy เพื่อใช้ทดสอบ
- GET /dynamodb เพื่อเรียก Lambda Proxy Integrationไปยัง Function xray-demo-dynamo
- GET /s3 เพื่อเรียก Lambda Proxy Integrationไปยัง Function xray-demo-s3
- เปิดใช้งาน DynamoDB Stream แล้วให้ Trigger เป็น Lambda Function xray-demo-loging
- เปิดใช้งาน S3 Event Notification แล้วให้เรียก Lambda Function xray-demo-logging
การเปิดใช้ X-Ray สำหรับ AWS Lambda
สำหรับ AWS Lambda ทั้ง 3 Functions จะต้องทำการเปิดใช้ X-Ray บน AWS Lambda ก่อน โดยสามารถเปิดใช้งานได้ที่หน้า Configuration > Monitoring and operations tools โดยตัว X-Ray จะใช้ชื่อว่า Active Tracing ซึ่งปกติจะขึ้น Not enabled อยู่ และสามารถกด Edit เพื่อเข้าไปเปิดได้
โดยในการเปิด Active tracing ตัว Lambda จะเพิ่ม Permission ให้โดยอัตโนมัติ
การเปิดใช้ X-Ray สำหรับ API Gateway
สำหรับ API Gateway เมื่อทำการ Deploy API แล้วสามารถเปิดใช้งาน AWS X-Ray ได้ที่หน้า Logs/Tracing ของ Stage นั้นๆ
สำหรับ X-Ray Sampling Rule เพื่อให้ง่ายต่อการทดสอบเฉยๆจะใช้ Default ที่มีอยู่แล้ว (จริงๆคือยังไม่ได้ลอง แหะๆ) ถ้าสนใจรายละเอียดอ่านที่เอกสารของ AWS ที่นี่
ทดลองเรียกใช้งานรอบที่ 1
ลองเรียกทั้ง 2 Function ดูแล้วเข้าไปดูที่ AWS X-Ray เพื่อดูผลลัพธ์ดู โดยจะเรียกจาก Postman และดู Header ที่ตอบกลับมา
เรียก API 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 ต่างๆได้ด้วย
เรียก API GET /s3
เพื่อให้เห็นตัวอย่างที่ชัดเจนขึ้นจะทำการแก้ Lambda Function ให้ทำงานผิดพลาด (Lambda Error และ Return 502 จาก API Gateway) 1 รอบ และแก้กลับให้ทำงานปกติ ซึ่งเมื่อดู Trace และ Service Map บน X-Ray จะขึ้นสี เหลือง - Errors (4XX) และ สีแดง - Faults (5XX) ตามลำดับ สำหรับรายการที่ไม่สำเร็จ
การเพิ่มรายละเอียดการ 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 ทางเลือกคือ
- เขียน Code บนเครื่องพร้อม install package แล้ว upload .zip ขึ้นมายัง Lambda
- เพิ่ม 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()
ซึ่งเมื่อแก้ไขแล้วจะได้ 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'
}
- แก้ไข 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'
}
ซึ่งการทำงานของคำสั่ง 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 ที่เรียก
เรียก API GET /s3
เมื่อเรียก API และเปิดดู Service Map จะเห็นว่ามี Node การทำงานของ S3 Bucket เพิ่มขึ้นมา ซึ่งถ้าดูใน Trace จะแสดง API ที่ถูกเรียกด้วย คือ คำสั่ง PutObject และการเรียก xray-demo-logging จะแสดงเป็นการถูกเรียกโดย Lambda function xray-demo-s3 แทน โดยไม่แยกออกจากกันเหมือนกรณี DynamoDB
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 หลังจากนั้นจึงคิดเงินเพิ่มตามรูปด้านล่าง
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 ควรจะศึกษาไว้เพื่อให้การทำงานง่ายขึ้น
Top comments (2)
This is good tutorial
Nice!