Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

เชี่ยวชาญ Ruby บน AWS Lambda: สร้างฟังก์ชันแบบ Event-Driven ที่ขยายได้

AWS Lambda ช่วยให้เราตั้งค่าฟังก์ชันที่ขยายได้พร้อมลดภาระงานลง ต่างจากการเขียน โฮสต์ และดูแลแอป Ruby on Rails ทั้งระบบ เราสามารถใช้ Lambda functions ตอบสนองต่อเหตุการณ์แต่ละอย่างอย่างอิสระ บทความนี้จะพาคุณจากมือใหม่ AWS สู่การเขียน Ruby ใน Lambda functions ของตัวเอง

Lambda ให้รันโค้ดตอบสนองต่อเหตุการณ์โดยไม่ต้องจัดการเซิร์ฟเวอร์ สถาปัตยกรรม event-driven นี้ทำให้คุณจ่ายเงินเฉพาะเมื่อโค้ดทำงาน ไม่ใช่ขณะ idle แม้ Lambda มักใช้ตอบสนองเหตุการณ์ในระบบ AWS เช่น ลบไฟล์ใน S3 Bucket แต่ก็ตั้งค่าให้ทำงานเป็น API ร่วมกับ AWS API Gateway ได้

สามารถใช้ Lambda เพื่อทำงานอัตโนมัติตามเวลา เช่น Cron Jobs หรือตอบสนองเหตุการณ์ ขยายไปสู่การประมวลผลข้อมูล ร่วมกับบริการ AWS อื่นๆ เช่น DynamoDB หรือแม้แต่อย่าง API ที่ขยายได้ ตัวอย่างแผนภาพจากเอกสารของ Amazon แสดงสถาปัตยกรรมง่ายๆ ที่ mobile client ส่ง HTTP request เพื่อ CRUD ข้อมูล API Gateway ส่ง request ไปยัง Lambda function ที่ตรงกัน (คล้าย Rails route HTTP ไป controller) ทำ business logic และติดต่อ AWS DynamoDB เพื่อตอบสนอง request

เชี่ยวชาญ Ruby บน AWS Lambda: สร้างฟังก์ชันแบบ Event-Driven ที่ขยายได้

เนื่องจากโค้ดรันในคอนเทนเนอร์ โครงสร้างพื้นฐานของ Amazon จะวางฟังก์ชันในโหมด sleep เมื่อไม่ได้ใช้งาน เมื่อ request ใหม่เข้ามา คอนเทนเนอร์ต้องเริ่มจาก cold state ซึ่งอาจเพิ่มเวลารันถึง 2000ms ส่งผลต่อคุณภาพบริการด้าน response time และค่าใช้จ่าย การใช้ Lambda สำหรับงานที่ไวต่อเวลา เช่น ส่งอีเมลผู้ใช้ หรือตอบสนอง UI events อาจสร้าง bottleneck สามารถบรรเทาได้โดยจัดตาราง AWS CloudWatch ส่ง request เป็นระยะเพื่อให้ฟังก์ชัน "warm" แต่จะเพิ่มค่าใช้จ่ายโฮสติ้ง

ทำไมต้องใช้ AWS Lambda

Amazon Web Services (AWS) เป็นบริการ cloud computing ยอดนิยมโลก ใช้ง่ายสำหรับนักพัฒนาอิสระ แต่ทรงพลังและขยายได้พอเป็นโครงสร้างพื้นฐานให้บริษัทใหญ่ระดับโลก

AWS Lambda เป็นหนึ่งในบริการของ Amazon ที่ทำหน้าที่เป็น "serverless compute service" ให้รันโค้ดเฉพาะตอบสนองเหตุการณ์ ลดภาระงานและทำให้เราเขียนโปรแกรมตอบสนองสิ่งต่างๆ เช่น API call ได้โดยไม่ต้องโฮสต์เซิร์ฟเวอร์ทั้งเครื่องหรือสร้างแอปทั้งระบบ

สำหรับ use case ที่เหมาะสม สถาปัตยกรรม serverless นี้ประหยัดค่าใช้จ่ายมาก คำนวณราคาจาก cost per request และ cost per duration ของ compute time หลัง request แรก 1 ล้านครั้งต่อเดือน ราคา $0.20 ต่อ 1 ล้าน requests บวก $0.0000166667 ต่อ GB-second ของ compute time ที่ฟังก์ชันใช้ จะเห็นว่า pricing แบบ rate-based นี้ทำให้รันบริการเล็กๆ บน Lambda ราคาถูกมาก พร้อมยังคงขยายได้ไม่มีที่สิ้นสุดในอัตราเดียวกัน

นักพัฒนา Ruby สามารถได้ประโยชน์จาก cloud service นี้โดยใช้เป็น stateless way ตอบสนอง API calls หรือแม้แต่อย่าง AWS events อื่นๆ

ตั้งค่าบัญชี AWS ทดลองใช้

AWS ให้ free tier ค่อนข้างกว้างขวาง รวม 1 ล้าน free requests และ compute time 3.2 ล้านวินาทีต่อเดือนกับ AWS Lambda! หากมีบัญชีแล้วข้ามขั้นตอนนี้ได้ แต่สมัครง่ายมาก

ไปที่ สมัครที่นี่ และกรอกข้อมูลเพื่อเริ่มต้น! เชี่ยวชาญ Ruby บน AWS Lambda: สร้างฟังก์ชันแบบ Event-Driven ที่ขยายได้

จะถูกขอกรอกข้อมูลเพิ่มเติม รวมถึงประเภทบัญชี ข้อมูลติดต่อ และต้องใส่ข้อมูลบัตรเครดิต/เดบิตเพื่อครอบคลุมการใช้งานนอก free tier การใช้ Lambda เรียบง่ายของเราอยู่ใน free tier แต่หากกังวลเกินงบประมาณ สามารถตั้งค่า budget ควบคุมการใช้งานและป้องกันการเรียกเก็บเงินไม่คาดคิด

สร้าง Lambda Function ด้วย Ruby

หลังลงทะเบียนแล้ว วางเมาส์ที่ products แล้ว compute เลือก Lambda

เชี่ยวชาญ Ruby บน AWS Lambda: สร้างฟังก์ชันแบบ Event-Driven ที่ขยายได้

เข้าสู่ Lambda Console (อาจต้องกด 'get started') จะเห็นรายการฟังก์ชันว่าง พร้อมปุ่มสีส้ม "Create Function" กดปุ่มนั้น

เชี่ยวชาญ Ruby บน AWS Lambda: สร้างฟังก์ชันแบบ Event-Driven ที่ขยายได้

เลือก "Author from Scratch" เนื่องจากยังไม่มี template functions สำหรับ Ruby และเริ่มจากศูนย์เป็นวิธีเรียนรู้ที่ดีที่สุด

ตั้งชื่อฟังก์ชันให้เหมาะสม และเลือก Ruby version ที่ต้องการ ผมตั้งชื่อ FizzBuzz ตามโจทย์เขียนโปรแกรมดัง

อาจต้องดับเบิลคลิกไฟล์ lambda_function.rb ใน navigation pane เพื่อให้โค้ดขึ้นมา

ฟังก์ชันมาพร้อมโค้ดชื่อ lambda_handler ตอนนี้เมื่อ invoke จะ return 200 response พร้อมข้อความ "Hello From Lambda!" ตามภาพ:

เชี่ยวชาญ Ruby บน AWS Lambda: สร้างฟังก์ชันแบบ Event-Driven ที่ขยายได้

ใช้ Test Trigger สำหรับ Lambda Function

ตามที่กล่าวมา Lambda functions ถูก trigger หลายวิธี วิธีทั่วไปคือ API call แต่ต้องตั้งค่า AWS API Gateway ซึ่งอยู่นอกขอบเขตบทความ API Gateway ทำงานดีกับ Lambda สร้าง APIs ได้ด้วยการตั้งค่าน้อยและขยายได้ไม่มีที่สิ้นสุด

โชคดีที่เราสามารถจำลอง API call ผ่าน function console ได้ และ test event พร้อมคิวอยู่แล้ว

ใต้ function overview มี toolbar ที่เลือก "Code" อยู่ ข้างๆ มีแท็บ "Test" กดเลือก "Test" จะเห็น Test console แทนที่ source code

เชี่ยวชาญ Ruby บน AWS Lambda: สร้างฟังก์ชันแบบ Event-Driven ที่ขยายได้

Test event ที่ตั้งค่าไว้คือ JSON object ที่ส่งไปเป็น event object ซึ่งฟังก์ชันรับเป็น parameter หากเรียก endpoint ผ่าน API Gateway นี่คือ request body

เนื่องจาก placeholder Lambda function ไม่อ่าน event ไม่ว่าจะส่ง body อะไรก็ได้ เราจึงดำเนินต่อไปโดยกดปุ่มสีส้ม "Invoke"

หากตั้งค่าถูกต้อง จะเห็นกล่องสีเขียว "Execution Succeeded" ขยายเพื่อดู JSON response จากฟังก์ชัน

เขียน FizzBuzz

ตอนนี้ยืนยัน Lambda function ทำงานได้แล้ว สามารถปรับแต่งด้วย Ruby code ของตัวเอง ในตัวอย่างนี้ เราจะให้ Lambda function ตอบสนองโจทย์ FizzBuzz โจทย์คลาสสิกสัมภาษณ์งาน:

เขียนโปรแกรมพิมพ์ตัวเลข 1 ถึง 50 แต่ถ้าเป็นพหุคูณของ 3 ให้พิมพ์ "Fizz" แทนตัวเลข พหุคูณของ 5 ให้ "Buzz" พหุคูณของทั้ง 3 และ 5 ให้ "FizzBuzz"

แทนที่โค้ดใน Lambda function ด้วยโซลูชัน FizzBuzz ของผม:

require 'json'
def lambda_handler(event:, context:)
  max_val = event['max_val']
  textResponse = ''
  1.upto(max_val) do |i|
    if i % 5 == 0 and i % 3 == 0
      textResponse += "FizzBuzz"
    elsif i % 5 == 0
      textResponse += "Buzz"
    elsif i % 3 == 0
      textResponse += "Fizz"
    else
      textResponse += i.to_s
    end
  end
  { statusCode: 200, body: JSON.generate(textResponse) }
end

นี่เป็น spin เล็กน้อยจาก FizzBuzz ธรรมดา แทนที่จะ loop แค่ 50 เรา loop ตามตัวเลขที่ส่งมาพร้อม event object ถูกส่งมาแบบ dictionary ดังนั้น event['max_val'] จะคืนค่าที่ key max_val สุดท้ายแทนที่จะพิมพ์คำตอบ เรา append ไปที่ String และ return เป็น JSON object

ต่อไป อย่าลืมกดปุ่มสีส้ม "Deploy" เพื่ออัปเดต Lambda function ด้วยโค้ดใหม่

หลังเขียนโซลูชันและ deploy แล้ว กลับไปที่ Test console เปลี่ยน test event ให้ส่งแค่ key/value pair max_val ควรเป็นแบบนี้:

เชี่ยวชาญ Ruby บน AWS Lambda: สร้างฟังก์ชันแบบ Event-Driven ที่ขยายได้

JSON แค่:

{ "max_val": 70 }

สุดท้าย กด "Invoke" เพื่อรัน test! ขยายผลลัพธ์จะเห็นว่ฟังก์ชันอ่าน input และคืนคำตอบ FizzBuzz

พัฒนาให้ง่ายขึ้น

แม้ web console สะดวกสำหรับเรียนรู้ แต่ถูกจำกัดอยู่แค่นั้นจะเป็น pesadilla วิธียอดนิยมพัฒนากับ AWS Lambda คือใช้ CLI (Command Line Interface)

สร้าง permissions ผ่าน IAM roles ติดตั้ง CLI บนเครื่อง และ config ด้วย credentials จากนั้นใช้ Lambda CLI ทดสอบฟังก์ชันขณะพัฒนาท้องถิ่น (ใน IDE ที่ชอบ) และ deploy ผ่าน command line

อีกวิธียอดนิยมคือใช้ frameworks เช่น Serverless Serverless จัดการ configuration ที่ซับซ้อนของ AWS รวมถึงตั้งค่า API Gateway หากต้องการ สร้าง logging ดีขึ้น จัดการ Ruby Gems ง่ายขึ้น และ config CI/CD ได้ เพื่อ deploy อัตโนมัติจาก repository ตาม pipelines

โดยไม่ใช้ Serverless หรือ framework คล้ายๆ การ include Ruby Gems ต้องผ่านขั้นตอนซับซ้อน ต้องขยายฟังก์ชันเป็นโฟลเดอร์ สร้างไฟล์ .bundle/config ใน root directory ด้วยโค้ด init เพื่อบอก bundler ว่าไว้ gems ที่ติดตั้งที่ไหน:

---
BUNDLE_PATH: "vendor/bundle"

จากนั้นใช้ CLI bundle add gems ที่ต้องการ และ require โดยตรงในโค้ด

ข้อจำกัดของ AWS Lambda

ยินดีด้วย! คุณเพิ่งเขียน AWS Lambda function แรกด้วย Ruby! ก่อนหน้านี้วิธีเดียวรัน API ด้วย Ruby มักสร้าง Rails app ทั้งระบบ ตอนนี้สร้าง endpoints ทีละอัน ข้าม bloat ทั้งหมดของแอปเต็มรูปแบบ Lambda คิดค่าใช้จ่ายในมิกโรวินาที จึงถูกมากสำหรับโซลูชันเร็วๆ นอกจากนี้ การใช้ประโยชน์จาก cloud offering นี้ช่วยให้เราดำเนินงานได้ในต้นทุนต่ำแต่อย่างไรก็ตามขยายได้อย่างรวดเร็ว AWS เป็นเหตุผลที่ Netflix ทำงานได้ในช่วง peak โดยไม่ต้องจ่าย bandwidth เซิร์ฟเวอร์สูงสุดตลอดเวลา

แม้จะมีข้อดีมากมาย AWS Lambda ไม่ใช่ catch-all solution ไม่ทำงานเป็น API ได้ด้วยตัวเอง และจำกัดด้าน state capabilities ฟังก์ชัน Lambda รันไม่เกิน 15 นาที และ memory จำกัด 6GB การทดสอบ Lambda functions ท้าทาย เพราะมักต้อง duplicate โครงสร้างพื้นฐานทั้งหมดใน environment แยก

หากมีแอปหรือฟีเจอร์ที่ไม่ต้องรัน 24/7 Lambda อาจ เหมาะสม หากฟังก์ชันต้อง maintain state หรืออัปเดตข้อมูลนอก AWS ecosystem Lambda ไม่เหมาะสม Lambda default 1,000 concurrent connections ดังนั้นแอปที่เห็นการใช้งานมากกว่านั้นจะ unreliable บน Lambda โดยสรุป Lambda เหมาะสำหรับ short-lived event-driven automation tasks ที่ไม่ต้องการ persistent state

Lambda functions เป็นเครื่องมือทรงพลัง โดยเฉพาะในมือของนักพัฒนา Ruby อย่างคุณ