实现目标
创建一个 Lambda 接收调用时传入的数据, 写入 RDS 数据库 Post
表存储文章信息. 表结构如下:
id | title | content | create_date |
---|---|---|---|
1 | 我是标题 | 我是正文内容 | 2023-10-21 15:20:00 |
AWS 资源准备
- RDS 控制台创建 MySQL 实例, 不允许 Public access (后面 Lambda 需要通过 VPC 访问)
- Secrets Manager 控制台创建
Credentials for Amazon RDS database
类型密码, 关联上面创建好的 RDS 实例, 起名为mydb-secret
- 从 VPC 内的跳板机手动连接 RDS 创建数据库
mysql -h [RDS 实例 Endpoint] -u admin -p
MySQL [(none)]> create database lambdaDB;
MySQL [(none)]> use lambdaDB;
MySQL [(lambdaDB)]> CREATE TABLE post (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255),
content TEXT,
create_date DATETIME
);
- Lambda 控制台创建函数
rds-test
, Runtime 使用Python 3.10
和开发环境保持一致, 使用默认选项Create a new role with basic Lambda permissions
自动为新建函数创建对应的 IAM Role, 展开 Advanced settings, 勾选Enable VPC
, 选择和 RDS 相同的 VPC, 勾选两个不同 AZ 的 Private Subnet (因为是通过内网访问), 选择能访问 RDS 的安全组.
撸码环节
开发环境为 WSL2 + VSCode
# 创建项目文件夹
mkdir lambda
cd lambda
# 创建虚拟环境
virtual env
source ./env/bin/active
# 安装依赖
pip install pymysql peewee boto3
# 启动 VSCode
code .
VSCode 中创建 rds_lambda.py
from peewee import *
import json
from datetime import datetime
import boto3
from botocore.exceptions import ClientError
def get_secret():
"""从 Secrets Manager 获取 RDS 密码"""
secret_name = "mydb-secret"
region_name = "cn-northwest-1"
# Create a Secrets Manager client
session = boto3.session.Session()
client = session.client(service_name="secretsmanager", region_name=region_name)
try:
get_secret_value_response = client.get_secret_value(SecretId=secret_name)
except ClientError as e:
raise e
# Decrypts secret using the associated KMS key.
secret = get_secret_value_response["SecretString"]
return json.loads(secret)
secret = get_secret()
# 定义 RDS 连接
db = MySQLDatabase(
database="lambdaDB",
host=secret["host"],
user=secret["username"],
password=secret["password"],
port=secret["port"],
)
# 定义基础模型
class BaseModel(Model):
class Meta:
database = db
# 定义数据模型
class Post(BaseModel):
title = CharField()
content = TextField()
create_date = DateTimeField(default=datetime.now)
# Lambda 入口函数
def lambda_handler(event, context):
"""
Lambda 入口函数, 调用时传入 event 应当符合以下 JSON 格式:
{
"title": "我是标题",
"content": "我是正文内容"
}
"""
# 解析调用函数时传入的参数
try:
Post.create(**event)
except:
raise {"code": 1, "message": "添加数据失败"}
return {"code": 0, "message": "添加数据成功"}
由于函数中用到了额外的依赖, 所以我们可以连同依赖和函数代码一块打包成 ZIP 文件, 通过 AWSCLI 进行上传部署. 官方操作文档
# 确认当前 Virtualenv 环境下安装的依赖位置
(venv) lpwm@Beijing:~/lambda$ pip show peewee
Name: peewee
Version: 3.17.0
Summary: a little orm
Home-page: https://github.com/coleifer/peewee/
Author: Charles Leifer
Author-email: coleifer@gmail.com
License: MIT License
Location: /home/lpwm/lambda/venv/lib/python3.11/site-packages
Requires:
Required-by:
# 退出 Virtualenv 并将依赖的所有包进行打包
lpwm@Beijing:~/lambda$ deactivate
lpwm@Beijing:~/lambda$ cd venv/lib/python3.11/site-packages/
lpwm@Beijing:~/lambda/venv/lib/python3.11/site-packages$ zip -r ../../../../deployment_package.zip .
# 返回项目主文件夹中, 检查依赖的包打好了
lpwm@Beijing:~/lambda/venv/lib/python3.11/site-packages$ cd ../../../../
lpwm@Beijing:~/lambda$ ls
deployment_package.zip rds_lambda.py venv
# 将 rds_lambda.py 再添加到 ZIP 包里面
lpwm@Beijing:~/lambda$ zip deployment_package.zip rds_lambda.py
adding: rds_lambda.py (deflated 49%)
# 检查确认 rds_lambda.py 添加成功
lpwm@Beijing:~/lambda$ unzip -l deployment_package.zip | tail
1759 2023-10-21 14:57 s3transfer-0.7.0.dist-info/METADATA
4 2023-10-21 14:57 s3transfer-0.7.0.dist-info/INSTALLER
83 2023-10-21 14:57 s3transfer-0.7.0.dist-info/NOTICE.txt
92 2023-10-21 14:57 s3transfer-0.7.0.dist-info/WHEEL
11 2023-10-21 14:57 s3transfer-0.7.0.dist-info/top_level.txt
2679 2023-10-21 14:57 s3transfer-0.7.0.dist-info/RECORD
11358 2023-10-21 14:57 s3transfer-0.7.0.dist-info/LICENSE.txt
1596 2023-10-21 15:17 rds_lambda.py
--------- -------
102887926 4266 files
# 上传 ZIP 包到 Lambda
lpwm@Beijing:~/lambda$ aws lambda update-function-code --function-name rds-test --zip-file fileb://deployment_package.zip
上传命令执行后会返回 Lambda 函数的信息, 显示上传正在进行
期间可以检查上传状态和结果
aws lambda get-function --function-name rds-test
上传成功
回到 Lambda 控制台, 通过 ZIP 部署的函数将不再能从浏览器直接编辑代码, 另外留意 Handler
这里用的还是之前创建时默认的入口函数, 需要修改成和我们代码一致的 rds_lambda.lambda_handler
创建测试
测试结果报错:
这是由于当前 Lambda 的 Execution Role 并没有访问 Secrets Manager 的权限. 到 Configuration - Permissions 中找到当前使用的 Role, 跳转到 IAM 控制台
Add permissions - Create inline policy
参考 文档 编写权限 JSON, 允许对特定 Secret 只读的权限
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetResourcePolicy",
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret",
"secretsmanager:ListSecretVersionIds"
],
"Resource": [
"arn:arn:aws-cn:secretsmanager:cn-northwest-1:888888888888:secret:mydb-secret-lNuRmj"
]
},
{
"Effect": "Allow",
"Action": "secretsmanager:ListSecrets",
"Resource": "*"
}
]
}
再次测试 Lambda 调用成功.
回到跳板机中检查数据库可以看到进来的数据.
MariaDB [lambdaDB]> select * from post;
+----+-----------+---------+---------------------+
| id | title | content | create_date |
+----+-----------+---------+---------------------+
| 1 | I'm title | Hello | 2023-10-21 08:30:25 |
+----+-----------+---------+---------------------+
1 row in set (0.001 sec)