Thực tiễn tốt nhất để sử dụng lại AWS Lambda Container

Tối ưu hóa khởi động ấm khi kết nối AWS Lambda với các dịch vụ khác

AWS Lambda cung cấp khả năng mở rộng cao do không có máy chủ và không trạng thái, cho phép nhiều bản sao của chức năng lambda được sinh ra ngay lập tức (như được mô tả ở đây). Tuy nhiên, khi viết mã ứng dụng, bạn có thể muốn truy cập vào một số dữ liệu trạng thái. Điều này có nghĩa là kết nối với kho dữ liệu, chẳng hạn như RDS hoặc S3. Tuy nhiên, việc kết nối với các dịch vụ khác từ AWS Lambda sẽ thêm thời gian vào mã chức năng của bạn. Cũng có thể có các tác dụng phụ từ khả năng mở rộng cao, chẳng hạn như đạt được số lượng kết nối được phép tối đa đến một phiên bản RDS. Một tùy chọn để chống lại điều này là sử dụng tái sử dụng container trong AWS Lambda để duy trì kết nối và giảm thời gian chạy lambda.

Có một số sơ đồ hữu ích ở đây để giải thích vòng đời của yêu cầu lambda.

Điều sau đây xảy ra khi bắt đầu lạnh, khi chức năng của bạn được gọi lần đầu tiên hoặc sau một thời gian không hoạt động:

  • Mã và phụ thuộc được tải xuống.
  • Một container mới được bắt đầu.
  • Thời gian chạy được bootstrapping.

Hành động cuối cùng là bắt đầu mã của bạn, điều này xảy ra mỗi khi hàm lambda được gọi. Nếu vùng chứa được sử dụng lại cho lần gọi tiếp theo của hàm lambda, chúng ta có thể bỏ qua trước để bắt đầu mã. Đây được gọi là khởi đầu ấm áp và đây là bước chúng ta có thể tối ưu hóa khi kết nối với các dịch vụ khác bằng cách xác định kết nối bên ngoài phạm vi của phương thức xử lý.

Kết nối với các dịch vụ AWS khác từ Lambda

Ví dụ: Kết nối với ví dụ RDS, các biểu tượng AWS có nguồn gốc từ đây

Chúng tôi có một ví dụ cơ bản và phổ biến để chạy qua - chúng tôi muốn kết nối với tài nguyên container để lấy dữ liệu làm giàu. Trong ví dụ này, một tải trọng JSON đi kèm với một ID và Hàm Lambda kết nối với một cá thể RDS đang chạy PostgreQuery để tìm tên tương ứng của ID để chúng ta có thể trả về tải trọng được làm giàu. Vì chức năng lambda đang kết nối với RDS, sống trong VPC, nên giờ chức năng lambda cũng cần phải sống trong một mạng con riêng tư. Điều này thêm một vài bước để bắt đầu lạnh - giao diện mạng đàn hồi VPC (ENI) cần được đính kèm (như đã đề cập trong blog Jeremy Daly tựa, điều này thêm thời gian cho khởi động lạnh của bạn).

Lưu ý: chúng tôi có thể tránh sử dụng VPC nếu chúng tôi sử dụng bộ lưu trữ khóa / giá trị với DynamoDB thay vì RDS.

Tôi sẽ xem xét hai giải pháp cho nhiệm vụ này, giải pháp đầu tiên là giải pháp của tôi, trong khi giải pháp thứ hai tối ưu hóa cho thời gian bắt đầu ấm áp bằng cách sử dụng lại kết nối cho các lần gọi tiếp theo. Sau đó, chúng tôi sẽ so sánh hiệu suất của từng giải pháp.

Tùy chọn 1 - Kết nối với RDS trong Trình xử lý

Ví dụ mã này cho thấy cách tôi có thể tiếp cận nhiệm vụ này một cách ngây thơ - kết nối cơ sở dữ liệu nằm trong phương thức xử lý. Có một truy vấn chọn đơn giản để tìm nạp tên của ID trước khi trả lại tải trọng, hiện bao gồm tên.

Hãy để xem cách tùy chọn này thực hiện trong một thử nghiệm nhỏ với 2000 lần gọi với số lần truy cập là 20. Thời lượng tối thiểu là 18 ms với trung bình 51ms và tối đa chỉ hơn 1 giây (thời gian bắt đầu lạnh).

Thời lượng Lambda

Biểu đồ dưới đây cho thấy có số lượng tối đa tám kết nối đến cơ sở dữ liệu.

Số kết nối đến cơ sở dữ liệu RDS trong cửa sổ 5 phút.

Tùy chọn 2 - Sử dụng kết nối toàn cầu

Tùy chọn thứ hai là xác định kết nối là toàn cục bên ngoài phương thức xử lý. Sau đó, bên trong trình xử lý, chúng tôi thêm một kiểm tra để xem liệu kết nối có tồn tại không và chỉ kết nối nếu nó không có. Điều này có nghĩa là kết nối chỉ được thực hiện một lần trên mỗi container. Đặt kết nối theo cách này với điều kiện tại chỗ có nghĩa là chúng ta không cần tạo kết nối nếu logic logic không yêu cầu.

Chúng tôi không còn đóng kết nối với cơ sở dữ liệu, vì vậy kết nối vẫn còn cho một lần gọi hàm tiếp theo. Việc sử dụng lại kết nối làm giảm đáng kể thời lượng khởi động ấm - thời lượng trung bình nhanh hơn khoảng 3 lần và tối thiểu là 1 ms thay vì 18 ms.

Thời lượng Lambda

Kết nối với một cá thể RDS là một nhiệm vụ tốn thời gian và không phải kết nối cho mỗi lần gọi là có lợi cho hiệu suất. Khi kết nối với cơ sở dữ liệu cho một truy vấn cơ sở dữ liệu đơn giản, chúng tôi đạt được số lượng kết nối cơ sở dữ liệu tối đa là 20, phù hợp với mức độ đồng thời (chúng tôi đã thực hiện 20 lần gọi đồng thời x 100 lần). Khi sự bùng nổ của các yêu cầu dừng lại, các kết nối dần dần đóng lại.

Bây giờ AWS đã tăng thời gian cho phép lambda lên 15 phút, điều này có nghĩa là các kết nối cơ sở dữ liệu có thể tồn tại lâu hơn và bạn có thể gặp nguy hiểm khi đạt đến số kết nối tối đa RDS. Các kết nối tối đa mặc định có thể được ghi đè trong cài đặt nhóm tham số RDS, mặc dù việc tăng số lượng kết nối tối đa có thể dẫn đến các vấn đề về cấp phát bộ nhớ. Các phiên bản nhỏ hơn có thể có giá trị max_connections mặc định dưới 100. Hãy chú ý đến các giới hạn này và chỉ thêm logic ứng dụng để kết nối với cơ sở dữ liệu khi cần.

Sử dụng kết nối toàn cầu cho các nhiệm vụ khác

Lambda Kết nối với S3

Một nhiệm vụ chung chúng ta có thể cần thực hiện với Lambda là truy cập dữ liệu trạng thái từ S3. Đoạn mã dưới đây là bản thiết kế chức năng Python Lambda do AWS cung cấp - mà bạn có thể điều hướng bằng cách đăng nhập vào bảng điều khiển AWS và nhấp vào đây. Bạn có thể thấy trong mã rằng máy khách S3 được xác định đầy đủ bên ngoài trình xử lý khi vùng chứa được khởi tạo, trong khi đối với ví dụ RDS, kết nối toàn cục được đặt bên trong trình xử lý. Cả hai cách tiếp cận sẽ đặt các biến toàn cục cho phép chúng có sẵn cho các lần gọi tiếp theo.

s3-get-object đoạn mã chi tiết lambda https://console.aws.amazon.com/lambda/home?region=us-east-1#/create/new?bp=s3-get-object-python

Giải mã biến môi trường

Bảng điều khiển lambda cung cấp cho bạn tùy chọn mã hóa các biến môi trường của bạn để bảo mật hơn. Đoạn mã sau đây là một ví dụ Java do AWS cung cấp về tập lệnh trợ giúp để giải mã các biến môi trường từ hàm Lambda. Bạn có thể điều hướng đến đoạn mã bằng cách làm theo hướng dẫn này (cụ thể là bước 6). Vì DECRYPTED_KEY được định nghĩa là một lớp toàn cầu, nên hàm và logic decryptKey () chỉ được gọi một lần cho mỗi container lambda. Do đó, chúng ta sẽ thấy một sự cải thiện đáng kể trong thời gian bắt đầu ấm áp.

https://console.aws.amazon.com/lambda/home?region=us-east-1#/fifts và https://docs.aws.amazon.com/lambda/latest/dg/tutorial-env_console.html

Sử dụng biến toàn cầu trong các giải pháp FaaS khác

Cách tiếp cận này không được phân lập cho AWS Lambda. Phương pháp sử dụng kết nối toàn cầu cũng có thể được áp dụng cho các nhà cung cấp dịch vụ đám mây khác. Trang mẹo và thủ thuật của Google Cloud Function đưa ra lời giải thích tốt cho các biến không lười biếng (khi biến luôn được khởi tạo bên ngoài phương thức xử lý) so với biến lười biếng (biến toàn cục chỉ được đặt khi cần) biến toàn cục.

Thực tiễn tốt nhất khác

Dưới đây là một số thực hành tốt nhất khác cần ghi nhớ.

Kiểm tra

Sử dụng FaaS tạo điều kiện có kiến ​​trúc microservice. Và có các phần nhỏ, rời rạc của chức năng đi đôi với thử nghiệm đơn vị hiệu quả. Để hỗ trợ kiểm tra đơn vị của bạn:

  • Hãy nhớ loại trừ phụ thuộc kiểm tra khỏi gói lambda.
  • Tách logic ra khỏi phương thức xử lý, như bạn làm với một phương thức chính của chương trình.

Phụ thuộc và kích thước gói

Giảm kích thước của gói triển khai có nghĩa là việc tải xuống mã sẽ nhanh hơn khi khởi tạo và do đó sẽ cải thiện thời gian bắt đầu lạnh của bạn. Xóa các thư viện không sử dụng và mã chết để giảm kích thước tệp ZIP triển khai. AWS SDK được cung cấp cho thời gian chạy Python và JavaScript, do đó không cần đưa chúng vào gói triển khai của bạn.

Nếu Node.js là thời gian chạy Lambda ưa thích của bạn, bạn có thể áp dụng thu nhỏ và làm mờ để giảm kích thước mã chức năng và giảm thiểu kích thước của gói triển khai. Một số nhưng không phải tất cả các khía cạnh của giảm thiểu và xấu xí có thể được áp dụng cho các thời gian chạy khác, ví dụ. bạn không thể xóa khoảng trắng khỏi mã python nhưng bạn có thể xóa bình luận và rút ngắn tên biến.

Đặt bộ nhớ

Thử nghiệm để tìm lượng bộ nhớ tối ưu cho Chức năng Lambda. Bạn trả tiền cho việc cấp phát bộ nhớ, do đó nhân đôi bộ nhớ có nghĩa là bạn phải trả gấp đôi mỗi mili giây; nhưng khả năng tính toán tăng theo bộ nhớ được phân bổ để nó có khả năng giảm thời gian chạy xuống dưới một nửa so với dung lượng. Đã có một số công cụ hữu ích để chọn cài đặt bộ nhớ tối ưu cho bạn, chẳng hạn như công cụ này.

Để kết luận

Một điều cần xem xét là liệu áp dụng phương pháp tái sử dụng kết nối có cần thiết hay không. Nếu chức năng lambda của bạn chỉ được gọi không thường xuyên, chẳng hạn như một lần một ngày, thì bạn sẽ không được hưởng lợi từ việc tối ưu hóa cho khởi đầu ấm áp. Thường có một sự đánh đổi giữa việc tối ưu hóa hiệu năng so với khả năng đọc mã của bạn - thuật ngữ này có nghĩa là xấu xí. Ngoài ra, việc thêm các biến toàn cục vào mã của bạn để sử dụng lại các kết nối với các dịch vụ khác có thể có khả năng khiến mã của bạn khó theo dõi hơn. Hai câu hỏi xuất hiện trong đầu:

  • Một thành viên nhóm mới sẽ hiểu mã của bạn?
  • Bạn và nhóm của bạn sẽ có thể gỡ lỗi mã trong tương lai?

Nhưng rất có thể bạn đã chọn Lambda cho quy mô của nó và muốn hiệu suất cao và chi phí thấp, vì vậy hãy tìm sự cân bằng phù hợp với nhu cầu của nhóm bạn.

Những ý kiến ​​này là của tác giả. Trừ khi có ghi chú khác trong bài đăng này, Capital One không liên kết với, và cũng không được chứng thực bởi bất kỳ công ty nào được đề cập. Tất cả các nhãn hiệu và tài sản trí tuệ khác được sử dụng hoặc hiển thị là quyền sở hữu của chủ sở hữu tương ứng của họ. Bài viết này là © 2019 Capital One.