Có một câu nói mà tôi rất tâm đắc, không nhớ của ai nhưng nó rất đúng: Quyết định kiến trúc hệ thống quan trọng nhất luôn là quyết định liên quan đến cái ví của bạn. Thật vậy, mọi quyết định về hạ tầng đều nên xoay quanh câu chuyện chi phí. Sau một thời gian làm việc với serverless, tôi có đúc kết được vài kinh nghiệm để tối ưu chi phí cho Lambda, và trong bài viết này tôi muốn chia sẻ lại với bạn.
Một trong những điều đầu tiên tôi nhận ra khá là ngược đời: tăng RAM cho Lambda đôi khi lại giúp giảm chi phí. Nghe có vẻ vô lý, nhưng khi có nhiều RAM hơn, hàm sẽ chạy nhanh hơn, thời gian thực thi ngắn lại và kết quả là tổng chi phí có thể thấp hơn. Dĩ nhiên không phải lúc nào cũng đúng, đến một ngưỡng nào đó thì việc tăng RAM sẽ tốn kém hơn.

Kinh nghiệm của tôi là với hầu hết các tác vụ, khoảng RAM tối ưu thường nằm đâu đó giữa 500MB và 1GB. bạn có thể bắt đầu với mức 500MB mặc định, sau đó theo dõi qua CloudWatch để điều chỉnh cho phù hợp với từng hàm cụ thể.
Nói về hiệu năng, không thể không nhắc đến cold start. Để giải quyết vấn đề này, có một tính năng là Provisioned Concurrency, cho phép bạn đặt trước một số lượng hàm luôn trong trạng thái sẵn sàng. Cách này giúp loại bỏ gần như hoàn toàn cold start, nhưng bạn phải cẩn thận vì sẽ phải trả tiền cho phần tài nguyên cấp sẵn đó ngay cả khi không dùng đến. Nó phù hợp nhất cho các tác vụ cần phản hồi ngay lập tức hoặc các tác vụ xử lý lô lớn.
Và một cách khác để giảm cold start chính là dọn dẹp các dependency không cần thiết. Gói cài đặt càng nhẹ thì hàm khởi động càng nhanh. Quy tắc của tôi là nếu một đoạn code có thể tự viết được thì không cần dùng thư viện. Và khi so sánh các thư viện có cùng chức năng, tôi luôn ưu tiên gói nào nhẹ hơn.
Một sai lầm tôi thấy nhiều người mắc phải là đặt timeout cho hàm quá dài cho chắc. Nhưng vì Lambda tính tiền theo gb/giây, việc này có thể rất tốn kém. Thay vì đặt một con số tuỳ ý, bạn nên theo dõi thời gian thực thi trung bình và tối đa của hàm qua CloudWatch.
Cách làm khá đơn giản: trong tab Monitor của hàm Lambda, bạn vào xem log trên CloudWatch, mở một log stream bất kỳ và tìm đến dòng Report requestId
.
Ở đó bạn sẽ thấy Duration
và Billed duration
. Sau khi xem xét trên một lượng lớn các lần chạy, bạn sẽ có được thời gian tối đa mà hàm thường cần, từ đó đặt ra một con số timeout hợp lý hơn.
Gần đây tôi có một phản xạ là mỗi khi tạo hàm Lambda mới, tôi luôn chọn kiến trúc ARM trong phần cài đặt.
Kiến trúc ARM thường có hiệu năng giá thành tốt hơn khoảng 34% so với x86. Nó mạnh hơn, rẻ hơn, nhưng nhược điểm là có thể không tương thích với một số thư viện. bạn chỉ cần lưu ý kiểm tra các dependency của mình trước khi chọn.
Nhân tiện nói về thời gian chạy, có những tác vụ quá dài như xử lý file hay ghi dữ liệu lớn. Với những việc này, tôi thường không để Lambda chạy quá lâu mà sẽ đẩy nó sang cho các dịch vụ khác như SQS hay Step Functions để xử lý bất đồng bộ. Đừng bao giờ lạm dụng 15 phút timeout tối đa của Lambda, chi phí sẽ tăng vọt rất nhanh.
Và cuối cùng, điều quan trọng nhất mà tôi học được thực ra không phải là một mẹo kỹ thuật, mà là một thay đổi trong tư duy: luôn thiết kế hệ thống với chi phí trong đầu.
Khi đứng trước hai lựa chọn, đừng mặc định chọn cái mạnh hơn, nhanh hơn hay mở rộng tốt hơn. Thay vào đó, hãy cân nhắc xem việc đánh đổi một chút hiệu năng để có một giải pháp chi phí hợp lý hơn có xứng đáng hay không.