用Terraform创建Lambda实现AWS中SDWAN实例的路由自动切换
背景
为了实现在AWS的一个VPC内部署两个SDWAN实例,并通过lambda实现双机热备和路由的自动切换,我之前写过如下博客。
为了能够通过“基础设施即代码”的思想,完成相关服务的快速部署和维护,本文尝试通过terraform对相关资源进行创建和管理。
操作步骤
准备步骤
确保当前环境已安装terraform,并创建指定文件夹并进入,然后创建main.tf文件并准备写入配置文件。
mkdir terraform-experiment4
cd terraform-experiment4
vim main.tf
注:下文中提及写入main.tf的内容,请逐一贴入文件内后,最后统一执行 terraform apply的指令。
创建SDWAN实例
实际生产环境中,我们会提供具有路由功能的SDWAN实例。
本测试环境中,我们通过terraform创建两个t3.micro的ubuntu实例用做测试,因为实例的AMI和类型,不影响lambda的路由倒换功能。
在main.tf中,通过如下代码创建两个SDWAN实例。
provider "aws" {
access_key = "AKIA**********NU5"
secret_key = "leVIw***************************wx/a"
region = "ap-east-1"
}
# 创建名为"EC2-SDWAN-A"的EC2
resource "aws_instance" "EC2-SDWAN-A" {
ami = "ami-03490b1b7425e5fe3"
instance_type = "t3.micro"
availability_zone = "ap-east-1a"
tags = {
Name = "SDWAN-A"
}
}
# 创建名为"EC2-SDWAN-B"的EC2
resource "aws_instance" "EC2-SDWAN-B" {
ami = "ami-03490b1b7425e5fe3"
instance_type = "t3.micro"
availability_zone = "ap-east-1b"
tags = {
Name = "SDWAN-B"
}
}
创建lambda
在main.tf中,通过如下代码创建lambda
# 创建名为"terraform-managed-lambda_RouteTableFailover"的lambda
resource "aws_lambda_function" "terraform_lambda_RouteTableFailover" {
function_name = "terraform-managed-lambda_RouteTableFailover"
filename = "RouteTableFailover.zip"
handler = "RouteTableFailover.lambda_handler"
role = "${aws_iam_role.iam_lambda_RouteTableFailover.arn}"
runtime = "python3.9"
timeout ="15"
environment {
variables = {
region = "ap-east-1"
vpcid = "vpc-0479a92f0dd5add79"
host1eni = aws_instance.EC2-SDWAN-A.primary_network_interface_id
host2eni = aws_instance.EC2-SDWAN-B.primary_network_interface_id
}
}
}
这里RouteTableFailover.zip是把包含了路由倒换python代码的RouteTableFailover.py压缩为zip文件,并上传至当前terraform运行服务器的terraform-experiment4文件夹下。
环境变量中的vpcid 是已经创建好的VPC的ID;host1eni是SDWAN-A实例的网络接口ENI的ID,可通过terraform自动获取变量;host2eni与host1eni同理。
handler = “RouteTableFailover.lambda_handler” 代表RouteTableFailover这个文件下的lambda_handler为该lambda函数的入口点,它告诉 Lambda 在哪里开始执行代码。
创建IAM执行角色
在main.tf中,通过如下代码创建名为”iam_lambda_RouteTableFailover”的IAM执行角色,并创建信任关系
#创建名为"iam_lambda_RouteTableFailover"的IAM执行角色,并创建信任关系
resource "aws_iam_role" "iam_lambda_RouteTableFailover" {
name = "iam_lambda_RouteTableFailover"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
上述指令生成的内容,在AWS控制台如下所示。
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-31.png)
创建IAM Policy策略
在main.tf中,通过如下代码创建两个policy:
- policy1,添加路由表相关操作的必要的权限,并绑定到当前IAM iam_lambda_RouteTableFailover 上;
- policy2,添加lambda执行的基本权限,并绑定到当前IAM iam_lambda_RouteTableFailover 上;
# 创建policy1,添加路由表相关操作的必要的权限,并绑定到当前IAM iam_lambda_RouteTableFailover 上;
data "aws_iam_policy_document" "lambda_policy_1" {
statement {
effect = "Allow"
actions = [
"ec2:DescribeInstances",
"ec2:DescribeTags",
"ec2:DescribeVpcs",
"ec2:DescribeRouteTables",
"ec2:CreateRoute",
"ec2:DeleteRoute",
"ec2:ReplaceRoute"
]
resources = [
"*"
]
}
}
resource "aws_iam_policy" "lambda_policy_1" {
name = "Policy-RouteTableFailover"
description = "Policy for Lambda execution"
policy = data.aws_iam_policy_document.lambda_policy_1.json
}
resource "aws_iam_role_policy_attachment" "Policy-RouteTableFailover" {
role = aws_iam_role.iam_lambda_RouteTableFailover.name
policy_arn = aws_iam_policy.lambda_policy_1.arn
}
# 创建policy2,添加lambda执行的基本权限,并绑定到当前IAM iam_lambda_RouteTableFailover 上;
data "aws_iam_policy_document" "lambda_policy_2" {
statement {
effect = "Allow"
actions = [
"logs:CreateLogGroup"
]
resources = [
"arn:aws:logs:*"
]
}
statement {
effect = "Allow"
actions = [
"logs:CreateLogStream",
"logs:PutLogEvents"
]
resources = [
"arn:aws:logs:*"
]
}
}
resource "aws_iam_policy" "lambda_policy_2" {
name = "Policy-AWSLambdaBasicExecutionRole"
description = "Policy for Lambda execution"
policy = data.aws_iam_policy_document.lambda_policy_2.json
}
resource "aws_iam_role_policy_attachment" "Policy-AWSLambdaBasicExecutionRole" {
role = aws_iam_role.iam_lambda_RouteTableFailover.name
policy_arn = aws_iam_policy.lambda_policy_2.arn
}
创建后,AWS控制台可见配置如下图。
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-32-1024x684.png)
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-33.png)
创建lambda触发器
在main.tf中,通过如下代码创建触发lambda的触发器,用Cloudwatch的EventBridge触发lambda运行:当EC2-SDWAN-A或EC2-SDWAN-B的状态变为非运行状态时,触发EventBridge。
这部分代码还配置了Cloudwatch Events运行Lambda所必需的权限。
#创建触发lambda的触发器,用Cloudwatch的EventBridge触发lambda运行
#当EC2-SDWAN-A或EC2-SDWAN-B的状态变为非运行状态时,触发EventBridge
resource"aws_cloudwatch_event_rule""EventBridge_Rule_DetectSdwanState" {
name ="CloudWatch-Rule-Axesdn-DetectvAerState-ChangeRouteTableAuto"
description ="For triggering lambda AxesdnRouterTableAutoFailoverForSDWAN-vAER, when one of the vAERs‘ state is NOT running, to change route table automatically. Please do NOT delete it."
event_pattern = jsonencode({
"source": [
"aws.ec2"
],
"detail-type": [
"EC2 Instance State-change Notification"
],
"detail": {
"state": [
"pending",
"shutting-down",
"stopped",
"stopping",
"terminated"
],
"instance-id": [
"${aws_instance.EC2-SDWAN-A.id}",
"${aws_instance.EC2-SDWAN-B.id}"
]
}
})
}
resource"aws_cloudwatch_event_target""set_target" {
rule ="${aws_cloudwatch_event_rule.EventBridge_Rule_DetectSdwanState.name}"
arn ="${aws_lambda_function.terraform_lambda_RouteTableFailover.arn}"
}
data"aws_caller_identity""current" {
}
resource"aws_lambda_permission""allow_cloudwatch" {
statement_id ="AllowExecutionFromCloudWatch"
action ="lambda:InvokeFunction"
function_name ="${aws_lambda_function.terraform_lambda_RouteTableFailover.function_name}"
principal ="events.amazonaws.com"
source_account ="${data.aws_caller_identity.current.account_id}"
source_arn ="${aws_cloudwatch_event_rule.EventBridge_Rule_DetectSdwanState.arn}"
}
用terraform创建资源
上述所有代码都写入main.tf文件中后,就可以运行
terraform init
terraform apply
来创建必要的资源了。
运行printout截图如下。
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-34.png)
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-35.png)
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-36.png)
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-37.png)
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-38.png)
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-39.png)
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-40.png)
功能验证
完成配置后,可登陆AWS控制台,查看两个SDWAN实例已创建,如下图。
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-41.png)
我们在该VPC默认路由表里手动创建一条静态路由:
当目的地址为10.0.0.0/24时,下一跳网关设置为实例SDWAN-A,如下图,
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-42.png)
完成配置后,可见路由如下图,
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-43.png)
其中,“目标”(也就是下一跳网关)显示为实例SDWAN-A的网络接口ID(ENI)。
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-44.png)
根据我们设计的功能,当SDWAN-A实例状态不为running时,CloudWatch的EventBridge会触发lambda,查找路由表中下一跳网关为SDWAN-A的相关路由,并把这些下一跳网关改为SDWAN-B。
下面我们开始验证。
把SDWAN-A实例状态改为停止。
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-46.png)
此时我们刷新路由表,可见相关路由表的“目标”改变了,如下图,
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-47.png)
变更后的“目标”,即为SDWAN-B的网络接口ID,
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-48.png)
还可到lambda页面的监控页中,点击“查看CloudWatch Logs”,查看相关log。
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-49-1024x605.png)
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-50-1024x571.png)
![](https://yushuoxin.online/wp-content/uploads/2023/11/image-51-1024x469.png)
通过上述验证,可见自动倒换路由表的功能,可以正常实现。
此时若恢复启动SDWAN-A,路由表不变;
然后再把SDWAN-B实例状态变为停止,则相关路由自动倒换至SDWAN-A。这里不再提供截图。
总结
通过上面的配置,我们成功实现了,通过terraform的声明式的方式,创建了lambda函数,并创建了必要的IAM role和policy,以及lambda的trigger,从而实现:
当某个作为路由表下一跳网关的SDWAN实例的状态不正常时,CloudWatch的EventBridge触发lambda,运行python脚本,把相关路由倒换到另一个正常的SDWAN实例,从而实现SDWAN实例的冗余和自动倒换。
发表回复