AWS Thai Blog

สิ่งที่ควรเลี่ยงเมื่อคุณออกแบบระบบ Serverless บน AWS Lambda

คุณสามารถหาบทความและคำแนะนำมากมายที่เกี่ยวกับการใช้ AWS Lambda แต่ในบทความนี้เราได้รวบรวมสิ่งที่ควรระวังและควรเลี่ยงเวลาออกแบบระบบ Serverless เพื่อคุณจะได้สร้างระบบที่มีประสิทธิภาพสูงสุดได้

เทคโนโลยีใหม่ๆผ่านมาและจากไปอย่างรวดเร็วมาก หลายโซลูชันดูเหมือนจะเป็นโซลูชันที่ถูก แต่ในทางกลับกันโซลูชันที่เราเรียกกันว่า “Anti-patterns” อาจเป็นอะไรที่ขัดกับรูปแบบและทำให้เราไม่สามารถสร้างโซลูชันที่ดีในแง่ของค่าใช้จ่าย ประสิทธิภาพและความยืดหยุ่น ในโพสนี้เราได้รวบรวมเอา “Anti-patterns” 8 แบบที่เราพบเจอเป็นประจำเพื่อคุณจะสามารถหลีกเลี่ยง เพื่อทำให้ระบบของคุณทำงานได้มีประสิทธิภาพสูงสุด

1. การใช้ Lambda functions ตัวเดียว หรือ “Monolith Lambda” สำหรับงานที่มี code base ขนาดใหญ่

เมื่องานของเรามีความซับซ้อนมากขึ้น code Lambda function ก็จะขยายตัวใหญ่และใหญ่ขึ้นเรื่อยๆ ซึ่งเวลา invocation times ของ Lambda ก็ยาวนานขึ้นและทำให้การดูแลโค้ดมีความซับซ้อนมากยิ่งขึ้น เพื่อหลีกเลี่ยงปัญหานี้เราแนะนำให้

  • แตก code Lambda function ขนาดใหญ่ ออกมาเป็นส่วนๆ เป็น Lambda function ที่เล็กลงพร้อมย่อส่วน business logic ข้างในให้เล็กลงด้วย
  • ใช้ AWS Step Functions ในการจัดการฟังก์ชั่น Lambda ซึ่งจะทำให้เราควบคุม flow การทำงานของ Lambda หลายๆตัว นอกจากนี้เรายังได้ประโยชน์หลากหลายจากฟีเจอร์อย่าง error condition handling, loops, และ input และ output processing
  • ใช้ Amazon EventBridge ในการแลกเปลี่ยนข้อมูลระหว่างส่วนประกอบต่างๆ EventBridge นั้นทำหน้าที่เป็น event bus ที่มีฟังก์ชั่นการทำงานหลากหลายไม่ว่าจะเป็นการ filtering, logging และ forwarding นอกจากนี้ EventBridge ยังรอง event ต้นทางมากกว่า 200 native AWS event source และ partner event source อีกด้วย
Figure 1. Monolithic versus microservice approach

Figure 1. Monolithic versus microservices approach

2. การใช้ Synchronous Architecture

เรามักเห็น pattern การออกแบบ Lambda function ที่ต้องทำงานหลายๆอย่าง ทำงานอย่างนึงแล้วตามด้วยงานอีกอย่างหนึ่ง เช่น Lambda รับไฟล์ที่ผู้ใช้อัปโหลดผ่าน Amazon API Gateway แล้ว Lambda ตรงนี้ก็จะทำงานแปลงไฟล์ แล้ว upload ไฟล์ไปที่ Amazon Simple Storage Service (Amazon S3) bucket และรีเทิร์น HTTP กลับไปที่ผู้ใช้งาน การออกแบบแบบนี้มักทำให้ Lambda ต้องทำงานนานขึ้นนำไปสู่ค่าใช้จ่ายที่มากขึ้น เราแนะนำให้ออกแบบตามแบบนี้

  • ใช้ workflow ที่แบบ asynchronous เช่น เราสามารถอัพโหลดไฟล์ตรงๆไปยัง Amazon S3 จากเว็บหรือแอพพลิเคชันบนสมาร์ทโฟน เราสามารถทำงานหลายตัวพร้อมเช่นการทำ data transformation และเมื่องานจบให้ส่งแจ้งเตือนไปที่ผู้ใช้งานได้
Figure 2. ตัวอย่างการ Re-engineering monolithic API calls

Figure 2. ตัวอย่างการ Re-engineering monolithic API calls

3. การระบบจัดการ Code base ที่ใช้ร่วมกัน

ผมเคยเห็นหลายโปรเจ็คที่ Code บางส่วนของ Lambda function มีส่วนที่ถูกนำไปใช้ซ้ำๆใน Lambda function หลายๆตัว ในการสร้างแอปพลิเคชันการทำแบบนี้ทำให้การ implementation ขาดความสม่ำเสมอและเกิดหนี้ทางด้านเทคโนโลยีเพิ่มมากขึ้นเพราะไม่ว่าช้าหรือเร็วเราอาจจะต้องปรับ code พื้นฐานที่อยู่ในส่วนต่างๆของโปรเจ็ค

เพื่อหลีกเลี่ยงปัญหานี้ให้ลองทำตามขั้นตอนดังต่อไปนี้

  • ใช้ Lambda layers – code พื้นฐาน ที่มีการใช้หลายที่หรือ libraries สามารถบรรจุเข้าไปในฟังก์ชั่น Lambda layer ได้
  • Shared API – ทำ code base ที่มีการใช้ซ้ำๆ ให้เป็น shared microservice API แทน การทำแบบช่วยการจัดการพื้นฐานโค้ดง่ายขึ้นและ service ที่ต้องการใช้ผู้ใช้ไม่จำเป็นต้องรู้รายละอีกการ implement ของ API
Figure 3. การใช้ Lambda layer แทน Code base ที่มีการ share อยู่ในหลาย Lambda function

Figure 3. การใช้ Lambda layer แทน Code base ที่มีการ share อยู่ในหลาย Lambda function

4. การเลือก Memory ของ Lambda Function ผิดขนาด

กำหนดขนาดของฟังก์ชัน Lambda ให้ถูกต้องคล้ายการเลือกขนาดขนาด Amazon Elastic Compute Cloud (Amazon EC2) เราเคยเห็นมาหลายเคสแล้วที่ Lambda function มีการกำหนดค่า Memory มากหรือหลายๆครั้งน้อยเกินไปซึ่งนำไปสู่ค่าใช้จ่ายที่เพิ่มมากขึ้น และปัญหา performance เราแนะนำให้ลองทำตามนี้เพื่อ “right size” Lambda function ของคุณ

  • ทำการเทส Lambda function ให้มีค่า Memory และ timeout value ที่ถูกต้อง
  • ทดสอบและ optimize ให้แน่ใจว่าโค้ดรันได้อย่างมีประสิทธิภาพมากที่สุดเท่าที่เป็นไปได้

ทำการเทส Lambda function ให้มีค่า Memory และ timeout value หรือการ “right size” ให้ที่ถูกต้องนั้นไม่ง่าย ซึ่งปัจจัยที่ส่งผลอย่างมากคือ runtime environment และโค้ด เพื่อให้การทำ right size เป็นเรื่องง่ายขึ้น โดยคุณ Alex Casalboni ได้พัฒนา framework ชื่อ Lambda Power Tuning โดยเค้าได้ทำ state machine ซึ่งเอา Step Function มา optimize Lambda Function เพื่อหาค่า Memory ที่ดีที่สุดทั้งในแง่ค่าใช้จ่ายและประสิทธิภาพด้วยวิถีการทดสอบจริงๆ

นอกจากนี้เรายังมีเครื่องมืออีกตัวชื่อ AWS Compute Optimizer จะช่วยเราระบุการกำหนดค่าฟังก์ชัน Lambda ที่ดีที่สุดสำหรับ workload ซึ่งโพส Optimizing AWS Lambda Cost and Performance using AWS Compute Optimizer จะให้ข้อมูลและช่วยให้คุณตั้งต้นใช้งานตัว AWS Compute Optimizer

Output ของ framework Lambda Power Tuning มีพื้นฐานเป็น JSON ทำให้เราสามรถนำมันไปผสมผสานมันเข้าไปใน CI/CD เพื่อปรับการกำหนดค่า Lambda Memory ก่อน Deploy ได้อย่างอัตโนมัติ

Figure 4. Optimize memory ของ Lambda function แบบอัตโนมัติเพื่อหา Memory setting ที่ดีที่สุดในแง่ของค่าใช้จ่ายและประสิทธิภาพ

Figure 4. Optimize memory ของ Lambda function แบบอัตโนมัติเพื่อหา Memory setting ที่ดีที่สุดในแง่ของค่าใช้จ่ายและประสิทธิภาพ

5. ใช้ Lambda กับ Service ที่ไม่สามารถ scale ได้มาก

Lambda เป็นโซลูชั่นที่สามารถปรับเปลี่ยนขนาด scale ขึ้นลงได้ ซึ่งถ้าเราใช้งานคู่กับ Service ที่ไม่ค่อย scale เราอาจทำให้ service นั้นรับไม่ไหวความจุล้นอย่างรวดเร็ว ถ้า Lambda ต้องเข้าถึง EC2 เครื่องเดียวหรือฐานข้อมูล Amazon Relational Database Service (Amazon RDS) อาจก่อให้เกิดอันตรายได้ เราสามารถป้องกันปัญหาที่อาจเกิดขึ้นได้โดย

  • ลดการใช้งาน Lambda คู่กับ Dependency ที่ไม่ใช่ Serverless services หรือ Decouple Lambda function ออกจาก service ที่ไม่สามารถ Scale ได้ออกจากกัน
  • ใช้ asynchronous processing ในทุกที่ที่สามารถทำได้ ซึ่งการแยกจะช่วย Decouple microservice ออกจากกันและนำ Buffering โซลูชันมาใช้
  • Implement Buffering โซลูชันโดยใช้ Amazon Simple Queue Service (Amazon SQS) message queue ให้อยู่ด้านหน้า service ที่ไม่ใช่ Serverless services หรือ service ที่ไม่สามารถ Scale ได้
  • ใช้ Amazon RDS Proxy หน้า Amazon RDS เพื่อดูแลประสิทธิภาพของฐานข้อมูลที่ต้องการด้วยวิธีการคุมจำนวน Connection การเชื่อมต่อเข้าไปที่ฐานข้อมูล
รูปที่ 5: พัฒนาประสิทธ์ RDS DB และความสามารถในการสเกลแอพพลิเคชั่นด้วย RDS Proxy

รูปที่ 5: พัฒนาประสิทธ์ RDS DB และความสามารถในการสเกลแอพพลิเคชั่นด้วย RDS Proxy

6. การใช้ AWS account เดียวในการ deploy workload

ในแต่ละ AWS Account มีขีดจำกัดหรือ Limit อยู่ ซึ่งหากใช้ AWS Account เดียวนั้นอาจจะทำให้ติด limit ได้อย่างรวดเร็ว ตัวอย่าง limit ที่เรามักเจอคือ จำนวนของ elastic network interface หรือจำนวน AWS CloudFormation stack ที่สามารถมีได้ใน AWS Account

สำหรับโซลูชั่นง่ายๆที่จะช่วยหลีกเลี่ยงปัญหานี้คือ

  • แบ่ง workload ออกจากกันและวาง workload ไว้ในหลาย AWS Account ตัวอย่างก็เช่น AWS Organizations และ AWS Control Tower สามารถจัดการบัญชี AWS ได้หลาย AWS Account โดยลูกค้าที่ไม่ใช่รูปแบบองค์กรก็สามารถใช้ Service ของ AWS Organizations ได้เช่นกัน

7. การใช้ Lambda ในเคสที่ไม่เหมาะสม

Lambda เหมาะกับ workload ที่มีเวลาสั้นและสามารถสเกลออกได้ คุณสามารถเข้าไปดู Workload ที่เหมาะกับ AWS Lambda ได้ที่ The common Lambda application types and use cases อย่างไรก็ตามเราต้องระวังเมื่อพิจารณาที่จะใช้ Lambda กับงานใดก็ตามที่อาจใช้เวลานาน ถ้าไม่ได้ออกแบบดีหรือเขียนโค้ดอย่างเหมาะสมก็จะทำให้ Application ใช้เวลาเกินจนเกิด Lambda timeout ได้

คำแนะนำเพิ่มเติมเล็กน้อย:

8. ไม่มีการ monitoring ค่าใช้จ่าย

เรามีบล็อกและเว็บไซต์จำนวนมากที่พูดถึงการการควบคุมค่าใช้จ่าย อย่างไรก็ตามหลายๆท่านและหลายๆบริษัทมักไม่มีการ monitor ในส่วนนี้เพื่อใช้วัดผลและป้องกัน

คำแนะนำสุดท้ายประจำวันนี้

  • ทำการตั้งค่าค่าใช้จ่ายรายวันใน AWS account ตั้งค่าบัญชีและตั้งการแจ้งเตือนเมื่อใช้จ่ายเกินจุดที่คุณกำหนด การตั้งค่าพวกนี้จะช่วยให้คุณประหยัดเงินและอาจช่วยให้คุณเจอและแก้ปัญหาทางด้านเทคนิคได้ไวขึ้นอีกด้วย

บทสรุป

ในบทความนี้เราได้แสดง 8 anti-pattern ของ Serverless ที่พบมากที่สุด และข้อแนะนำเวลาเราเจอ anti-pattern หล่าวนี้ เราสามารถนำข้อแนะนำหล่าวนี้ไปปรับใช้ได้กับแทบทุก serverless application หรือ workload ที่เรามีได้เลย

ข้อมูลเพิ่มเติม