in Technology

วิธีง่ายๆ สำหรับทำ AWS Lambda Layers ที่เป็น C# .Net Core

ใน Best Practices ของการทำ AWS Lambda Function มีอยู่ข้อหนึ่งที่บอกว่า “Control the dependencies in your function’s deployment package.” คือ แนะนำให้เราควบคุมและจัดการกับ Library ที่ Funtion ของเราต้องใช้งาน

เพราะเมื่อระบบเราใหญ่ขึ้น สมมติว่ามี 100 Functions และต้องเรียกใช้ Library คล้ายกัน ปัญหาคือ

  1. เราจะควบคุม Version ของ Library ให้เหมือนกันทุก Function ได้ไหม
  2. Filesize ของ Deployment Package เราจะใหญ่ เพราะ ทุก Function ต้องแพ็ค Library ที่เกี่ยวข้องติดไปด้วยทุกตัว
  3. เมื่อ Deployment Package เราใหญ่ จะทำให้ช่วงเวลา Cold Start ของ Lambda เรา ช้าขึ้นไปอีก

Amazon เลยมีฟีเจอร์หนึ่งให้ใช้คือ Lambda Layers เพื่อใช้เก็บ Libraries, Custom Runtime หรือพวก Dependencies ต่างๆ แยกออกจาก Function ของเรา เพื่อให้ Function เราเล็กลง และสามารถเรียกใช้ Layers ต่างๆที่ Function อื่นได้ด้วย

คำแนะนำ: บทความนี้จะอ่านเข้าใจ ถ้าคุณใช้ AWS Lambda เบื้องต้นเป็นแล้ว

ปัญหา Lambda Layers กับ C# .Net Core

ในขณะที่ผมกำลังเขียนบล็อกนี้ (7 Feb 2019) ตัว AWS Lambda เอง รองรับการทำ Layers แค่ Node.js, Python, และ Ruby functions เท่านั้น

ส่วนภาษาอื่นๆ อาจจะลำบากหน่อย เพราะ Dependency อยู่ในโฟลเดอร์ข้างนอก project เช่น C# ของ MacOS จะอยู่ใน /usr/local/share/dotnet/ ซึ่งเราจะต้องสร้าง Runtime package store ของ project นั้น แล้วไปคัดเลือก Dependency เอง จากนั้น zip file ออกมานำไปใช้ใน Lambda Layer ส่วนโค้ดของเรา เมื่อทำคำสั่ง publish แล้ว เราต้อง zip เฉพาะโค้ดเราเท่านั้น และนำไปขึ้น AWS เอง และทำการลิงค์กัน… ฟังดูยุ่งยากเนอะ

แต่อย่าเพิ่งตกใจไป ที่เขียนบล็อกนี้เพราะ Amazon กำลังปรับปรุงเครื่องมือ AWS CLI ของตัวเอง เพื่อให้ชาว C# .Net Core ทำทำ Layers ได้ง่ายๆแล้ว ซึ่งขณะนี้อยู่ในช่วงของ Beta Version เพิ่งปล่อย Docs มาให้ได้อ่านกันเพียง 20 วันนี้เอง

Amazon Lambda Tools V.3.2.0-beta1 ที่มาพร้อมกับ คำสั่ง “dotnet lambda publish-layers”

ถ้าอ่านถึงตรงนี้ ชาว C# น่าจะอยากใช้งาน Layer กันบ้างแล้ว แต่เราจะไม่สามารถใช้กับ AWS Lambda Tools เวอร์ชั่นปัจจุบันได้ (ตอนนี้คือ V.3.1.2) ดังนั้นต้องเอาตัว Beta มาใช้ ด้วยวิธีการ Build Package มันขึ้นมาเองเอง

สำหรับคนที่ไม่เคยใช้ AWS CLI จำเป็นต้องทำสองขั้นตอนนี้ก่อน คือ

  1. ติดตั้ง AWS CLI
  2. สร้าง Configuration เพื่อให้ AWS User เรา ใช้ได้กับ AWS CLI

แต่ถ้าเรามี AWS CLI พร้อมใช้อยู่แล้ว ให้เริ่มติดตั้ง Amazon Lambda Tools กันเลยได้ให้ทำการ clone repo https://github.com/aws/aws-extensions-for-dotnet-cli.git

1.ให้ทำการ clone repo https://github.com/aws/aws-extensions-for-dotnet-cli.git

git clone https://github.com/aws/aws-extensions-for-dotnet-cli.git

2. จากนั้นทำการ checkout branchไปที่ “lambda-layers”

git checkout lambda-layers

3.สร้าง NuGet Package ใหม่ของ amazon.lambda.tools

dotnet msbuild buildtools/build.proj /t:build-nuget-packages

4.ทำการ uninstall amazon.lambda.tools ตัวเก่าออก (หากใครยังไม่เคยใช้มาก่อน ก็ไม่ต้องทำบรรทัดนี้)

dotnet tool uninstall -g amazon.lambda.tools

5.ติดตั้ง package amazon.lambda.tools ใหม่

dotnet tool install -g --add-source ./Deployment/nuget-packages/ Amazon.Lambda.Tools --version 3.2.0-beta1

เมื่อติดตั้งเสร็จ ให้ลองพิมพ์ “dotnet lambda ดูครับ จะต้องได้เวอร์ชั่น 3.2.0-beta1 และมีคำสั่ง publish-layer เพิ่มเข้ามา (นอกจากมี publish-layer แล้ว ยังทำให้คำสั่ง deploy-function และ package รองรับการใช้งาน layers ด้วย)

Amazon Lambda Tools Version 3.2.0-beta1

มาลองสร้าง Lambda Layers ของภาษา C# .Net Core กัน

ก่อนอื่นเลย เราต้องมี S3 Bucket ก่อน เพราะว่าเมื่อทำคำสั่งนี้ มันจะเก็บ Package ต่างๆ ไว้ที่ S3 ซึ่งในที่นี้ผมตั้งชื่อว่า lambda-profile-dependency

S3 ที่สร้างไว้ชื่อ lambda-profile-dependency

จากนั้นเข้าไปที่โฟลเดอร์ C# Project ของเรา และเราจะใช้คำสั่ง dotnet lambda publish-layer ซึ่งมีรูปแบบการใช้งานดังนี้

dotnet lambda publish-layer layer-name --layer-type runtime-package-store --s3-bucket bucket-name

โดย

  • layer-name คือ ชื่อของ Layer ที่เราต้องการ
  • bucket-name คือ ชื่อของ S3 Bucket ที่เราจะใช้เก็บไฟล์
  • runtime-package-store คือ type ของ layer ซึ่งคำสั่งนี้ถูกทำเผื่อไว้ในอนาคต ว่าจะมีประเภทของ package ให้เลือกได้ ตอนนี้มีให้ใช้อย่างเดียวคือ runtime package

ในที่นี้ ถ้าผมต้องการเก็บลง S3 Bucket ชื่อ lambda-profile-dependency และตั้งชื่อ Layer ว่า profile-lib จะได้คำสั่งประมาณนี้

dotnet lambda publish-layer profile-lib --layer-type runtime-package-store --s3-bucket lambda-profile-dependency

เมื่อรันคำสั่ง มันก็จะทำการแพ็คไฟล์นั่นนู่นนี่ไปเรื่อยๆ จนสุดท้าย ออกมาจะต้องได้ ARN ของ AWS Lambda Layer ที่เราจะใช้งาน ตามภาพด้านล่าง (บรรทัดสุดท้าย)

ให้เราคัดลอก ARN ไปใช้ในคำสั่ง deploy lambda ด้วยรูปแบบดังนี้

dotnet lambda deploy-function function-name --function-layers layer-arn

โดย

  • function-name คือ ชื่อ Lambda Function ของเรา
  • layer-arn คือ ARN ของ Lambda Layer ที่เราจะลิงค์ถึง (รูปแบบจะประมาณนี้ arn:aws:lambda:[region-name]:[aws-user-id]:layer:[layer-name]:[layer-version])

ดังนั้น ถ้าผมต้องการ Function ชื่อ list_profile และ ใช้ layer ที่เพิ่งสร้างขึ้นเมื่อสักครู่ จะใช้คำสั่งดังนี้

dotnet lambda deploy-function list_profile –-function-role MyRole --function-layers arn:aws:lambda:ap-southeast-1:819684245502:layer:profile-lib:1

เมื่อรันคำสั่ง มันก็จะทำการแพ็คไฟล์ เพื่อเตรียม deploy โดยมันจะแยกเอาเฉพาะไฟล์ Function ของเราเท่านั้น ไม่เอาพวก dependency library เข้าไปด้วย (ขั้นตอนนี้ ทำมือเองได้ แต่ใช้คำสั่งเถอะ เร็วกว่า)

เป็นอันว่าเสร็จเรียบร้อย Lambda Function ของเรา กับ Lambda Layer ถูกนำขึ้นไปไว้บน AWS และผูกมิตรกันให้ด้วย

สำรวจผลลัพธ์ Lambda Function และ Lambda Layer หลัง Deployment

ลองมาดูกันว่าผลลัพธ์หลังจากการใช้งาน Lambda Layer เป็นอย่างไร

แต่เดิม list_profile ของผมมีขนาด 2.6 MB เหมือนกับตัวอื่นๆ ตอนนี้เหลือเพียง 15.9kB ลดลงไป 93.88%
ลองใช้ Lambda Layer กับทุก Function ที่จะใช้งาน ไฟล์เล็กลงไปเยอะ เหลือไม่เกินตัวละ 15kB
อันนี้ลองทดสอบแบบกากๆ (กดมือเอง) แบบที่มี layer และไม่มี layer ผลลัพธ์ที่ได้ ดูเหมือนจะไม่ต่างมาก
เพราะฟังก์ชันที่ทดสอบ โค้ดมันเล็กมากๆ (get data ด้วย id จาก MySQL บน RDS)

เพิ่มเติม: อย่าลืมว่า การคิดเงินของ AWS Lambda มันคิดที่หน่วย 100ms นะ
อย่าง cold start จากที่โดนคิดเงิน 16000ms จะเหลือแค่ 15500ms หายไป 500ms

มีชั้น Layers เพิ่มมา 1 ตัว
Layer ที่เพิ่มเข้ามา ก็คือ layer ที่เราทำการสร้างใหม่ด้วยคำสั่ง publish-layer ซึ่งจะเป็น version 1 ถ้าเรา publish-layer ซ้ำไปอีกครั้งจะเป็น version 2
มี Environment Variable เพิ่มมาตัวหนึ่ง เพื่อระบุ PATH ว่า Library เราเอาไปไว้ที่ไหน

คำเตือน: ถ้าเราเคยมี Environment Variable เดิมอยู่ มันจะหายไป!!!

ลองดูไฟล์ใน Function จะเหลือแค่โค้ดของเราเท่านั้น ไม่มีพวก DLL อื่นๆ ติดมาด้วย
มาดูในหน้า Lambda Layer ก็จะเจอ Dependency ที่เราเพิ่งทำการนำขึ้นไป
ลองไปดูใน S3 Bucket ที่มี Lambda Layer พบโฟลเดอร์และเก็บไฟล์ packages.zip, artifact.xml ไว้
ลอง unzip packages.zip ออกมา พบโฟลเดอร์และ ไฟล์ DLL หน้าตาเหมือนกับใน runtime package store นั่นเอง

สรุป

ถ้าชาว C# .Net Core อยากใช้ Lambda Layer ก็ลองทำตามในบล็อกนี้ได้เลย หรือไม่ก็รอ AWS CLI เวอร์ชั่น 3.2.0 ออกตัวเต็ม ก็ได้ครับ แต่เท่าที่ลองใช้ ไม่มีปัญหาอะไรนะ สะดวกดี

และข้อจำกัดตอนนี้ คือ Lambda Layer มีให้ใช้กันที่ 5 Layers ดังนั้น ต้องมาคิดต่อว่าจะจัดสรร Dependency กันอย่างไร เพื่อให้ใช้ประโยชน์ร่วมกันเยอะที่สุด

อ้างอิง

  • https://github.com/aws/aws-extensions-for-dotnet-cli/blob/lambda-layers/docs/Layers.md
  • https://github.com/aws/aws-extensions-for-dotnet-cli/tree/lambda-layers
  • https://github.com/aws/aws-extensions-for-dotnet-cli/issues/58