关于AWS Step Function & CDK & SAM Local & 杂项的注意事项
AWS Step Function、CDK、SAM Local以及杂项主题。
引言
这是一篇关于 AWS Step Function & CDK & SAM Local & 杂项主题的笔记。AWS Step Functions 是一个“serverless”函数编排器,可以轻松地将 AWS Lambda 函数和多个 AWS 服务编排成业务关键型应用程序。这篇笔记将介绍如何使用 CDK 创建 AWS Step Functions,以及如何在本地计算机上运行 Step Functions 以进行测试和调试。
环境
在这篇笔记中,我们的环境需要以下项目。
- AWS 账户
- Node/NPM
- https://node.org.cn/en/
- https://codeproject.org.cn/Articles/878189/Node-js-Get-Started-and-Miscellaneous
- 您只需解压下载的文件,并将 bin 目录添加到
PATH
环境变量中。
- VSC
- Docker
- https://www.docker.com/
- https://docs.dockerd.com.cn/install/linux/docker-ce/ubuntu/#install-docker-ce-1
- https://docs.dockerd.com.cn/install/linux/linux-postinstall/
- https://codeproject.org.cn/Articles/1264033/A-Note-on-Docker
- 在某些 Linux 发行版中,您可能需要将存储库更改为
xenial
才能通过apt-get
安装 Docker。
- GIT
- AWS CLI V2
- https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html
- https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html
- AWS CLI 版本 2 不需要依赖其他软件包。它包含一个自包含的、嵌入式的安装程序,其中包含所有依赖项。您不再需要安装和维护 Python 来使用 AWS CLI。
- 在 Linux 上,要升级您的 AWS CLI 版本 2,请执行与安装时相同的步骤,但这次在安装命令中包含
--update
或-u
选项。
- SAM CLI
- https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html
- https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install-linux.html
- 在 Linux 上,安装 SAM CLI 的推荐方法是使用 Homebrew。安装 SAM CLI 时,Homebrew 还会将 Python 副本安装在 /home/linuxbrew/.linuxbrew/bin/python3 目录中。
- 在 Linux 上,如果您想升级 SAM CLI,可以发出命令
brew upgrade aws-sam-cli
。它将同时升级 Homebrew 和 SAM CLI。 - 如果您不熟悉 Homebrew,我的早期笔记可能会有所帮助。
- AWS CDK
如果您在安装某些软件包时遇到任何问题,可以参考我之前的笔记。
CDK & Step Function & Lambdas
创建 AWS CloudFormation 模板最便捷的方法是使用 CDK。在 AWS 中,一组 Step Functions 也称为 状态机。为保持这篇笔记的简洁,以下状态机实现了一个简单的算术计算。
const cdk = require('@aws-cdk/core');
const iam = require('@aws-cdk/aws-iam');
const lambda = require('@aws-cdk/aws-lambda');
const sfn = require('@aws-cdk/aws-stepfunctions');
const tasks = require('@aws-cdk/aws-stepfunctions-tasks');
class StepFunctionExampleCdkStack extends cdk.Stack {
constructor(scope, id, props) {
super(scope, id, props);
const PREFIX = 'STEP_FUNCTION_EXAMPLE';
const create_lambda_role = () => {
const role_name = `${PREFIX}_LAMBDA_ROLE`;
const role = new iam.Role(this, role_name, {
roleName: role_name,
description: role_name,
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
});
role.addToPolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
resources: ['*'],
actions: [
'logs:CreateLogGroup',
'logs:CreateLogStream',
'logs:PutLogEvents'
]
}));
return role;
};
const lambda_role = create_lambda_role();
const create_lambda = (name, path) => {
const lambda_name = `${PREFIX}_${name}`;
return new lambda.Function(this, lambda_name, {
runtime: lambda.Runtime.PYTHON_3_8,
functionName: lambda_name,
description: lambda_name,
timeout: cdk.Duration.seconds(15),
role: lambda_role,
code: lambda.Code.asset(path),
memorySize: 256,
handler: 'app.lambda_handler'
});
};
const sum_lambda = create_lambda('SUM_LAMBDA',
'./lambdas/sum-lambda/');
const square_lambda = create_lambda('SQUARE_LAMBDA',
'./lambdas/square-lambda/');
const STEP_1_NAME = `${PREFIX}_STEP_1_SUM`;
const step_1 = new tasks.LambdaInvoke(this, STEP_1_NAME, {
lambdaFunction: sum_lambda, inputPath: '$',
outputPath: '$.Payload'
});
const STEP_2_NAME = `${PREFIX}_STEP_1_SQUARE`;
const step_2 = new tasks.LambdaInvoke(this, STEP_2_NAME, {
lambdaFunction: square_lambda, inputPath: '$.Payload',
outputPath: '$.Payload'
});
const STEP_WAIT_NAME = `${PREFIX}_STEP_WAIT`;
const waitX = new sfn.Wait(this, STEP_WAIT_NAME, {
time: sfn.WaitTime.duration(cdk.Duration.seconds(3))
});
const definition = step_1.next(waitX).next(step_2);
const STATE_MACHINE_NAME = `${PREFIX}_STEP_FUNCTION`;
new sfn.StateMachine(this, STATE_MACHINE_NAME, {
stateMachineName: STATE_MACHINE_NAME,
definition: definition,
timeout: cdk.Duration.minutes(5)
});
}
}
module.exports = { StepFunctionExampleCdkStack }
状态机通过以下两个 Lambda 函数执行计算。
def lambda_handler(event, context):
x = event['x']
y = event['y']
s = x + y
return {
'Payload': {
'sum': s
}
}
和
def lambda_handler(event, context):
sum = event['sum']
return {
'Payload': {
'result': sum * sum
}
}
部署到 AWS & 执行
为了将堆栈部署到 AWS,我们需要准备好权限文件。要设置权限,我们可以在用户主文件夹中创建一个 .aws 文件夹。我们可以在该文件夹中的 credentials 文件中添加权限。
[default]
aws_access_key_id = Your_aws_access_key_id
aws_secret_access_key = Your_aws_secret_access_key
您也可以在 config 文件中添加额外信息。
[default]
region = us-east-1
output = json
准备好权限后,我们就可以首次引导 (bootstrap) CDK 了。
cdk bootstrap
要部署 CDK 堆栈(包括 Lambda 函数和状态机),您可以在找到 cdk.json 文件的文件夹中执行以下命令。在运行此命令之前,您需要安装 Node 包。
cdk deploy
成功部署后,我们可以使用以下输入来测试状态机:
{
"x": 2,
"y": 3
}
我们可以看到结果计算正确,如下所示:
{
"Payload": {
"result": 25
}
}
在本地运行状态机
虽然我们可以在 AWS 上部署和执行状态机,但如果能在本地运行它仍然是很有吸引力的。这使得我们更容易调试和修改状态机和 Lambda 函数。要将在本地执行状态机,我们需要执行以下步骤。
在本地托管 Lambda 函数
作为第一步,我们需要在本地托管 Lambda 函数,以便它们可以从状态机调用。为了托管 Lambda 函数,我们需要生成 template.yaml 文件。
cdk synth --no-staging > template.yaml
有了 template.yaml 文件,我们就可以通过以下命令在本地机器上托管 Lambda 函数:
sam local start-lambda -t template.yaml -p 3001
上述命令将在 localhost 上打开 3001 端口,因此可以通过 URL "http://127.0.0.1:3001" 调用 template.yaml 中定义的 Lambda 函数。例如,我们可以通过以下命令调用 STEP_FUNCTION_EXAMPLE_SUM_LAMBDA
:
aws lambda invoke --function-name STEP_FUNCTION_EXAMPLE_SUM_LAMBDA \
--cli-binary-format raw-in-base64-out \
--payload '{ "x": 3, "y": 2 }' \
--endpoint-url http://127.0.0.1:3001 \
--no-verify-ssl sam-local-response-out.txt && \
cat sam-local-response-out.txt && echo && \
rm sam-local-response-out.txt
我们可以看到 Lambda 函数返回了两个数字的正确 SUM
。
{"Payload":{"sum":5}}
在本地托管状态机
为了能在本地托管状态机,我们需要启动托管环境。
docker run -p 8083:8083 -d \
--rm \
--network host \
--env-file ./local-run/aws-stepfunctions-local-credentials.txt \
amazon/aws-stepfunctions-local
上述命令启动了一个 Docker 容器来托管状态机。Docker 容器的权限定义在 aws-stepfunctions-local-credentials.txt 文件中。您可以在 aws-stepfunctions-local-credentials.txt 中指定复杂的配置。对于我的示例,我只指定了运行本地状态机所需的最低信息。
AWS_DEFAULT_REGION=us-east-1
LAMBDA_ENDPOINT=http://127.0.0.1:3001
在 Docker 容器运行的情况下,我们可以通过端点 "https://:8083" 向容器定义状态机。
aws stepfunctions --endpoint https://:8083 create-state-machine --definition "{\
\"StartAt\": \"STEP_FUNCTION_EXAMPLE_STEP_1_SUM\",\
\"States\": {\
\"STEP_FUNCTION_EXAMPLE_STEP_1_SUM\": {\
\"Next\": \"STEP_FUNCTION_EXAMPLE_STEP_WAIT\",\
\"Type\": \"Task\",\
\"InputPath\": \"$\",\
\"OutputPath\": \"$.Payload\",\
\"Resource\": \"arn:aws:states:::lambda:invoke\",\
\"Parameters\": {\
\"FunctionName\": \"arn:aws:lambda:us-east-1:123456789012:function:STEP_FUNCTION_EXAMPLE_SUM_LAMBDA\",\
\"Payload.$\": \"$\"\
}\
},\
\"STEP_FUNCTION_EXAMPLE_STEP_WAIT\": {\
\"Type\": \"Wait\",\
\"Seconds\": 3,\
\"Next\": \"STEP_FUNCTION_EXAMPLE_STEP_1_SQUARE\"\
},\
\"STEP_FUNCTION_EXAMPLE_STEP_1_SQUARE\": {\
\"End\": true,\
\"Type\": \"Task\",\
\"InputPath\": \"$.Payload\",\
\"OutputPath\": \"$.Payload\",\
\"Resource\": \"arn:aws:states:::lambda:invoke\",\
\"Parameters\": {\
\"FunctionName\": \"arn:aws:lambda:us-east-1:123456789012:function:STEP_FUNCTION_EXAMPLE_SQUARE_LAMBDA\",\
\"Payload.$\": \"$\"\
}\
}\
},\
\"TimeoutSeconds\": 300\
}" --name "STEP_TEST_STATE_MACHINE" --role-arn "arn:aws:iam::123456789012:role/DummyRole"
在本地调用状态机
在 Docker 容器运行且状态机已定义的情况下,我们可以通过以下命令调用本地托管的状态机:
aws stepfunctions --endpoint https://:8083 start-execution \
--state-machine arn:aws:states:us-east-1:123456789012:stateMachine:STEP_TEST_STATE_MACHINE \
--input '{"x": 3, "y": 5}' \
--name test
我们可以通过以下命令检查执行状态:
aws stepfunctions --endpoint https://:8083 describe-execution \
--execution-arn arn:aws:states:us-east-1:123456789012:execution:STEP_TEST_STATE_MACHINE:test
如果一切顺利,我们可以看到如下结果:
{
"executionArn": "arn:aws:states:us-east-1:123456789012:execution:STEP_TEST_STATE_MACHINE:test",
"stateMachineArn": "arn:aws:states:us-east-1:123456789012:stateMachine:STEP_TEST_STATE_MACHINE",
"name": "test",
"status": "SUCCEEDED",
"startDate": "2020-08-18T17:43:07.072000-04:00",
"stopDate": "2020-08-18T17:43:12.829000-04:00",
"input": "{\"x\": 3, \"y\": 5}",
"output": "{\"Payload\":{\"result\":64}}"
}
进一步讨论
我们可以在本地运行状态机的一个优点是,我们可以以调试模式执行 Lambda 函数。如果您有兴趣,可以参考我之前的笔记。如果您想清除本笔记中使用的 Docker 镜像并停止所有容器,以下命令可能会有所帮助。
docker system prune -a
docker container stop $(docker container ls -a -q)
docker container rm -f $(docker container ls -a -q)
关注点
- 这是一篇关于 AWS Step Function & CDK & SAM Local & 杂项主题的笔记。
- 希望您喜欢我的帖子,也希望这篇笔记能以某种方式帮助到您。
历史
- 2020 年 8 月 13 日:首次修订