使用 SAM 框架(Amazon Linux 2 Runtime)构建 Go 无服务器 REST API 并部署到 AWS
为什么还要再讲一个 Go 教程
AWS 近期一直在弃用多项服务和运行时环境。正如我们所见,备受喜爱的 CodeCommit 和其他一些关键服务已经停止服务,AWS Lambda 函数也不再支持 Go 1.x 版本。
如果您尝试部署大多数过时的教程,可能会遇到如下错误:
Resource creation Initiated
CREATE_FAILED AWS::Lambda::Function DemoFunction
Resource handler returned message:
"The runtime parameter of go1.x is no longer supported for
creating or updating AWS Lambda functions. We recommend you
use a supported runtime while creating or updating functions.
(Service: Lambda, Status Code: 400, Request ID:
81f1f708-0a7a-40d0-8442-b9c16510d01f)"
ROLLBACK_IN_PROGRESS AWS::CloudFormation::Stack lambda-go-gorilla
The following resource(s) failed to create:
[DemoFunction]. Rollback requested by user.
关键在于,软件领域唯一不变的就是变化。然而,有一些永恒不变的原则我们应该始终牢记:
为了解决这个问题,我决定创建一个包含部署 Go 应用程序所需所有基础架构的最新代码库。有两种方案可供选择:
- 使用 Docker 容器部署 Fargate。
- 在 AWS 上使用 SAM 框架进行部署。
你可以在这里找到GitHub仓库。
软件开发中的永恒原则
- 基础设施即代码至关重要。
- 软件中良好的命名规范至关重要。
- 永远要检验你的逻辑。
- 可用性和可扩展性
- 部署管道是一种实现软件交付流程自动化的机制。
- 可观测性是强制性的。
- 在云原生应用中,安全性是重中之重。
- Go 是构建 API 的绝佳选择。
基础设施即代码至关重要
不可变基础设施允许我们在更高层面上声明我们的需求,并确保开发环境和生产环境尽可能保持一致。例如:
CompoundingFunction:
Type: AWS::Serverless::Function
Metadata:
BuildMethod: makefile
Properties:
FunctionName: CompoundingFunction
Architectures: ["arm64"]
Handler: bootstrap
Runtime: provided.al2
CodeUri: ./functions/CompoundingFunction/
MemorySize: 512
Timeout: 10
Environment:
Variables:
COMPOUNDING_TABLE_NAME: !Ref CompoundingTable
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref CompoundingTable
Events:
ApiGatewayPost:
Type: Api
Properties:
RestApiId: !Ref ApiGateway
Path: /compounding
Method: POST
软件中良好的命名规范至关重要
如果你已经有了一套完善的测试用例,就不要害怕重构。重构是软件开发中必不可少的环节。命名非常重要,因为它们会出现在模块、函数、包、变量等各个地方。
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)
// Response is the structure for the response JSON
type Response struct {
Message string `json:"message"`
GainsPerYear []float64 `json:"gainsPerYear"`
}
type Request struct {
Principal float64 `json:"principal"`
AnnualRate float64 `json:"annualRate"`
Years int `json:"years"`
}
func HelloHandler(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
var req Request
err := json.Unmarshal([]byte(event.Body), &req)
if err != nil {
return createResponse(400, "Invalid request body")
}
fmt.Println("Request", req)
gainsPerYear := CalculateCompoundInterest(req.Principal, req.AnnualRate, req.Years)
fmt.Println(gainsPerYear)
response := Response{
Message: "Calculation successful",
GainsPerYear: gainsPerYear,
}
body, err := json.Marshal(response)
if err != nil {
return createResponse(500, "Error marshalling response")
}
return createResponse(200, string(body))
}
func createResponse(statusCode int, body string) (events.APIGatewayProxyResponse, error) {
return events.APIGatewayProxyResponse{
StatusCode: statusCode,
Body: body,
Headers: map[string]string{"Content-Type": "application/json"},
}, nil
}
func main() {
lambda.Start(HelloHandler)
}
永远要测试你的逻辑
在无服务器应用程序中,单元测试很重要,但不要忘记也包含集成测试,因为大多数此类应用程序都依赖于集成和策略来解决业务问题。
func TestCalculateCompoundInterest(t *testing.T) {
principal := 100000000.0
annualRate := 10.0
years := 10
result := CalculateCompoundInterest(principal, annualRate, years)
lastElement := round(result[len(result)-1], 2)
expected := round(259374246.01, 2)
if !reflect.DeepEqual(lastElement, expected) {
t.Errorf("Expected %v, but got %v", expected, lastElement)
}
}
可用性和可扩展性
无服务器架构默认具有高可用性,并且采用事件驱动模式,从而省去了大部分运维工作。但是,如果您选择依赖 ECS 和容器,则必须包含负载均衡器来分配服务器之间的流量,以确保可用性和可扩展性。
CompoundingLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: compounding-nlb
Scheme: internet-facing
Type: network
Subnets:
- !Ref PublicSubnetOne
- !Ref PublicSubnetTwo
部署管道
部署流水线实现了软件交付流程的自动化。我们创建了一个 Makefile 来简化这一流程,只需一条命令即可轻松部署并执行重复性任务。这种方法提高了部署工作流程的效率和一致性。
可观测性是强制性的
确保您已部署跟踪、日志记录和指标功能。对于无服务器应用程序,启用这些功能非常简单,只需添加相应的配置即可Tracing: Active。能够在 CloudWatch 等中心位置查看所有日志并监控服务的交互情况至关重要。
在云原生应用中,安全性至关重要
安全性在所有应用程序中都至关重要。使用 Amazon Cognito 可提供强大的用户身份验证,而 API 密钥则增加了一层额外的控制和授权,确保只有授权客户端才能访问您的 API。
Auth:
DefaultAuthorizer: CompoundingAuthorizer
Authorizers:
CompoundingAuthorizer:
UserPoolArn: XXXX
LambdaTokenAuthorizer:
FunctionArn: !GetAtt LambdaTokenAuthorizerFunction.Arn
FunctionPayloadType: REQUEST
Identity:
Headers:
- Authorization
ReauthorizeEvery: 100
AddDefaultAuthorizerToCorsPreflight: false
为每个服务、用户和组件分配最小必要权限,以减少攻击面并防止未经授权的访问。最小权限原则:
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref CompoundingTable
参考
- Terraform 实战- Terraform 的实际应用和实施策略,Terraform 是一款用于构建、更改和管理基础设施的工具。
- 持续交付管道
结论
软件不断发展演进,虽然一些工具和实践会随之改变,但其基本原则始终不变。我们需要不可变的基础设施、持续集成/持续交付 (CI/CD)、良好的命名规范、稳健的测试策略、API 的安全性以及应用程序的高效性。正因如此,我决定以无服务器的方式重构这个项目。
对于工程师而言,现在是通过软件为社会创造价值的最佳时机。
如果您喜欢这些文章,请访问我的博客jorgetovar.dev
文章来源:https://dev.to/aws-builders/build-go-serverless-rest-apis-and-deploy-to-aws-using-the-sam-framework-amazon-linux-2-runtime-4n5p

