Vấn đề thường gặp
Trong sự nghiệp của mình, tôi thường thấy AWS Lambda được triển khai trực tiếp mà không có mã nguồn được cam kết trên GitHub. Thậm chí tệ hơn, nhiều dự án thiếu một kiến trúc rõ ràng hoặc công cụ cho phép kiểm tra Lambdas cục bộ trước khi triển khai. Ở đây, tôi sẽ hướng dẫn cách giới thiệu một số công cụ cơ bản để khắc phục điều này.
Thiết lập AWS cục bộ
Để chạy và gỡ lỗi Lambdas cục bộ, chúng ta cần một môi trường thực thi mô phỏng các dịch vụ AWS. Để thực hiện điều này, tôi khuyên bạn nên sử dụng LocalStack với Docker:
yaml
services:
localstack:
image: localstack/localstack
ports:
- "4566:4566"
- "4510-4559:4510-4559"
environment:
- SERVICES=lambda,logs
- DEBUG=1
- LAMBDA_EXECUTOR=docker
- DATA_DIR=/tmp/localstack/data
- AWS_DEFAULT_REGION=eu-north-1
- LOCALSTACK_API_KEY=${LOCALSTACK_API_KEY-}
- USE_SSL=false
volumes:
- "./localstack/volume:/var/lib/localstack"
- "/var/run/docker.sock:/var/run/docker.sock"
Có hai điểm bạn cần chú ý:
- Trong biến môi trường
SERVICES, chúng ta cần định nghĩa hai dịch vụ:lambda: chỉ ra rằng chúng ta cần sử dụng localstack cho việc thực thi lambda.logs: nơi bất kỳ bản ghi nào từ lambda sẽ được xuất ra stdout. Điều này tương đương với dịch vụ CloudWatch.
- Chúng ta đã vô hiệu hóa ssl với
USE_SSL. - Container được khởi động cần giao tiếp với docker để chạy lambda. Để thực hiện điều này, chúng ta cần định nghĩa:
LAMBDA_EXECUTORlà docker.- Ánh xạ tệp socket của docker bên trong container thông qua một bind-mount. Điều này đạt được bằng cách thêm
/var/run/docker.sock:/var/run/docker.sockvào phần volumes.
LocalStack có sẵn thông qua http://localhost:4566 như đã định nghĩa ở trên:
yaml
ports:
- "4566:4566"
Tải mã Lambda
Mã Lambda thường được lưu trữ dưới dạng tệp .zip, chứa mã ứng dụng cùng với các phụ thuộc của nó:
Phát hiện điểm vào của Lambda
Lambdas là các script Node.js hoặc Python, thường là một hàm bên trong một tệp cụ thể.
Ví dụ, trong một Lambda Python, điểm vào thường được xác định là file_name.handler_function:
Trong trường hợp này, AWS sẽ bắt đầu thực thi từ phương thức lambda_handler bên trong lambda_function.py:
Việc xác định điểm vào là rất quan trọng vì nó giúp xác định các phụ thuộc và thiết lập.
Cài đặt phiên bản Python hoặc Node.js yêu cầu
AWS Lambdas chạy với các runtime Python (hoặc Node.js) cụ thể (ví dụ: Python 3.9, 3.10).
Bạn có thể kiểm tra runtime đang được sử dụng dưới tab Code trong bảng điều khiển AWS Lambda:
Tổ chức mã và phụ thuộc
Khi bạn đã tải xuống tệp .zip của Lambda, hãy giải nén nó vào một thư mục làm việc. Tôi khuyên bạn nên tạo hai thư mục:
- Original Lambda → Nội dung thô của tệp .zip (ví dụ:
my-lambda-orig) - Clean Repo → Một dự án có cấu trúc tách biệt mã và phụ thuộc (ví dụ:
my-lambda). Cấu trúc sau được khuyến nghị:- src → chứa mã nguồn lambda
- tools → cho các công cụ hữu ích
Tại sao phải tách biệt chúng?
AWS thường đóng gói Lambdas với cả mã nguồn và phụ thuộc. Thay vào đó, bạn sẽ muốn quản lý các phụ thuộc bằng pip (và sau này là requirements.txt/poetry/pipenv).
Dưới đây là quy trình:
- Sao chép tệp Lambda chính (ví dụ:
lambda_function.py) vào./src. - Kiểm tra phần đầu của tệp Lambda (ví dụ:
lambda_function.py). Tại đó, bạn có thể phân biệt các tệp cần thiết và các thư viện bên thứ ba. Bất kỳ thư viện không phải bên thứ ba nào hãy sao chép chúng vào thư mụcsrccủa clean repo. - Sử dụng tên, bạn có thể phát hiện các thư viện bên thứ ba và các thư viện cần thiết. Tốt nhất là giữ một danh sách các thư viện bên thứ ba.
- Sao chép chỉ mã ứng dụng (các tệp .py của bạn) vào clean repo.
Một thư viện phổ biến là thư viện
botođược sử dụng để giao tiếp với AWS.
Sau khi tách biệt mã, hãy khởi tạo git và thực hiện commit đầu tiên:
bash
git init
git commit -m "Cleaned Code"
Tạo một virtualenv
Chúng ta cần có danh sách các phụ thuộc lambda, vì vậy chúng ta cần khởi tạo một môi trường ảo:
bash
python -m venv ./.venv
echo ".env" > .gitignore
git add .gitignore
git commit -m "Ignoring virtualenv"
source .venv/bin/activate
Sau đó, sử dụng pip install, chúng ta sẽ cài đặt các phụ thuộc cần thiết, ví dụ cho boto, chúng ta chạy:
bash
pip install boto3
Khi các phụ thuộc đã được cài đặt, chúng ta cần giữ chúng trong một danh sách, vì vậy chúng ta cần chạy:
bash
pip freeze > ./requirements.txt
git add requirements.txt
git commit -m "Added Dependencies"
Bây giờ mỗi khi chúng ta cần cài đặt chúng, chúng ta có thể làm:
bash
pip install -r requirements.txt
Phát hiện biến môi trường
Lambdas cũng nhận cài đặt từ các biến môi trường bên ngoài. Đối với lambdas Python, hãy quét các chuỗi như os.environ.get(, tham số được truyền vào chứa biến môi trường yêu cầu.
Triển khai trên LocalStack
Bây giờ mà chúng ta đã sắp xếp mã, chúng ta có thể tạo một lambda trong LocalStack. Để làm điều này, chúng ta cần đóng gói lambda thành một zip mới:
bash
mkdir package
echo "package" >> .gitignore
git add .gitignore
git commit -m "Ignoring support folder for packaging"
# Sao chép mã
cp -r ./src package/
# Cài đặt các phụ thuộc vào thư mục package
pip install -r requirements.txt -t package
# Nén gói
cd package
zip -r9 function.zip
cd ../
# Thư mục gói là tạm thời
rm -rf ./package
Các lệnh ở trên thực hiện 2 điều:
- Tạo một thư mục trung gian nơi mã được chuẩn bị, thư mục này được đặt tên là
package. Thư mục này nên được bỏ qua trong git. - Sao chép tất cả mã nguồn lambda vào thư mục
package. Hãy nhớ rằng chúng ta cần sao chép nội dung củasrcvà không phải thư mụcsrcchính nó. - Cài đặt tất cả các phụ thuộc vào thư mục
package. Điều này được thực hiện bằng cách cung cấp tên thư mục vào tham số-t.
Phương pháp mô tả ở trên cũng có thể được sử dụng để đóng gói lambda cho sản xuất. Tệp zip có thể được tải lên, do đó thay thế mã lambda.
Triển khai trên LocalStack
Để làm điều này, chúng ta có thể chạy:
bash
aws --endpoint-url=http://localhost:4566 lambda create-function \
--function-name "my-function" \
--runtime "python3.9" \
--role "arn:aws:iam::000000000000:role/lambda-role" \
--handler "lambda_function.lambda_handler" \
--zip-file "fileb://function.zip" \
--environment Variables="{VAR1=\"Hello\"}"
Lệnh trên tạo chức năng my-function, đây là chức năng lambda mà chúng ta cần tạo. Trong lệnh trên, chúng ta cần chú ý đến một số tham số:
--endpoint-url: Trong trường hợp của chúng ta, chúng ta cần cung cấp nó với thiết lập LocalStack. Nếu không, nó sẽ cố gắng tạo lambda trên AWS thực tế. Trong trường hợp của chúng ta, điều này không mong muốn.--function-name: một tên hàm đặc trưng. Mỗi lambda nên có tên riêng của nó.--role: Giữ giá trịarn:aws:iam::000000000000:role/lambda-rolelà tốt cho mọi lần bạn tạo một lambda mới. LocalStack không kiểm tra các vai trò nhưng chúng ta cần đặt điều này để tránh bất kỳ hành vi không mong muốn nào.--handler: Đặt cái mà lambda thực sự sử dụng trên AWS. Tại đây, chúng ta định nghĩa cái sẽ được sử dụng cho việc thực thi lambda.--zip-file: Tệp mà chúng ta đã tạo chứa mã + phụ thuộc.--environment: Nếu không có biến môi trường nào được sử dụng, hãy bỏ qua nó. Giá trị là một chuỗi được đóng gói trong{}có định dạng này (đừng nhầm lẫn với JSON):
bash
{VARIABLE_NAME=VALUE,VARIABLE_NAME2=VALUE2}
Phát triển và tái triển khai
Trong quá trình phát triển, nếu cần thay đổi, bạn cần phải đóng gói lại lambda. Việc tái tạo là không cần thiết trừ khi container LocalStack bị kết thúc.
Cập nhật mã được thực hiện như sau:
bash
aws --endpoint-url=http://localhost:4566 lambda update-function-code --function-name "my-function" --zip-file "fileb://function.zip"
Trong khi nếu cần thay đổi các biến môi trường, bạn có thể làm như sau:
bash
aws --endpoint-url=http://localhost:4566 lambda update-function-configuration \
--function-name "my-function" \
--environment Variables="{VARIABLE_NAME=VALUE,VARIABLE_NAME2=VALUE2}"
Một số mẹo khác
Nếu cần một phụ thuộc bổ sung, thì tốt nhất là kích hoạt môi trường ảo được tạo ra, sau đó cài đặt nó qua pip. Sau đó, cập nhật mã trên LocalStack như đã đề cập ở trên.
Các thực tiễn tốt nhất
- Kiểm tra cục bộ: Luôn kiểm tra mã của bạn trước khi triển khai lên AWS.
- Sử dụng công cụ CI/CD: Tích hợp các công cụ CI/CD để tự động hóa quy trình triển khai.
Các cạm bẫy thường gặp
- Bỏ qua biến môi trường: Đảm bảo bạn đã định nghĩa các biến môi trường cần thiết trước khi triển khai.
- Không quản lý phụ thuộc: Luôn kiểm tra các phụ thuộc của bạn để không gặp lỗi trong quá trình triển khai.
Mẹo hiệu suất
- Sử dụng bộ nhớ tối ưu: Đảm bảo rằng bạn đã định cấu hình bộ nhớ phù hợp cho Lambda để tối ưu hóa hiệu suất.
- Giảm kích thước mã: Giảm kích thước mã sẽ giúp tăng tốc thời gian khởi động của Lambda.
Câu hỏi thường gặp
- Làm thế nào để gỡ lỗi Lambda cục bộ? Sử dụng LocalStack và các công cụ gỡ lỗi như pdb cho Python hoặc node --inspect cho Node.js.
- Có cần thiết phải sử dụng Docker không? Có, vì Docker giúp mô phỏng môi trường AWS chính xác hơn.