将本地数据库迁移到服务器_如何将Go API迁移到无服务器(不到10分钟)

韶亮
2023-12-01

将本地数据库迁移到服务器

我将引导您完成将现有Go API转换为无服务器并将其部署到具有AWS Severless Application Model(SAM)的 AWS Lambda和API Gateway的过程。 整个过程应少于10分钟。 让我们开始吧!

1.设定

我们的示例API使用HttpRouter 包,因此我们首先安装它。

$ go get github.com/julienschmidt/httprouter

我们定义了一个HTTP处理程序,它将返回带有ok主体的200 HTTP响应。

# handlers.go
package main
import "net/http"
func HealthHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
}

我们进入应用程序的入口点,即main功能,将HealthHandler/healthz路由,并在端口8080上侦听HTTP请求。

# main.go
package main
import (
"fmt"
"log"
"net/http"
"github.com/julienschmidt/httprouter"
)
const (
serverPort = 8000
)
func main() {
router := httprouter.New()
router.Handler("GET", "/healthz", http.HandlerFunc(goserverlessapi.HealthHandler))
fmt.Printf("Server listening on port: %d\n", serverPort)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", serverPort), router), nil)
}

让我们在本地构建并运行此程序以检查一切是否正常。

$ go build -o go-serverless-api && ./go-serverless-api
Server listening on port: 8080

2.将应用程序代码转换为无服务器

为了部署到无服务器后端,我们需要能够处理来自AWS Lambda的请求。 Lambda函数与常规HTTP处理程序具有不同的签名。 试想一下,如果我们的应用程序中只有一个而不是数百个。 我们将不得不手动更新所有功能并重新编写测试,这样做将丧失我们部署到非无服务器后端的能力。

有一种解决方案可以避免上述所有问题。 我们将仅为AWS Lambda创建修改后的入口点。 使用gateway 包,我们将net/httpListenAndServe gateway.ListenAndServe ,它将把AWS Lambda提供的有效负载转换为HTTP处理程序接受的*http.Request类型。

为了实现第二个切入点,我们需要重新组织项目。 我们将为AWS Lambda创建一个目录,并将原始入口点也移至新文件夹。

# original entrypoint moved to new location
$ mkdir -p cmd/go-serverless-api
# new entrypoint for lambda
$ mkdir -p cmd/go-serverless-api-lambda

我们将main.go文件复制到每个新目录中,然后将其从项目的根目录中删除。

$ cp main.go cmd/go-serverless-api
$ cp main.go cmd/go-serverless-api-lambda
$ rm main.go

我们项目根目录中的软件包将不再是main软件包(Go用来运行您的应用程序)。 我们将其重命名为goserverlessapi以便我们可以将其作为库导入到我们的新入口点中,这两个入口点都将成为main包。

$ grep -l 'package main' *.go | xargs sed -i 's/package main/package goserverlessapi/g'

简单查找并替换为项目根目录中的Go文件后,您的handlers.go应如下所示。

# handlers.go
package goserverlessapi
import "net/http"
func HealthHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
}

现在,我们需要更新原始入口点,以导入goserverlessapi软件包,并HealthHandler导出HealthHandler函数。 请注意,您将需要修改goserverlessapi软件包的导入路径,以匹配$GOPATH项目根目录的位置。 本教程中使用的github上示例应用程序的正确路径是github.com/techjacker/go-serverless-api

# cmd/go-serverless-api/main.go
package main
import (
"fmt"
"log"
"net/http"
"github.com/julienschmidt/httprouter"
"github.com/techjacker/go-serverless-api"
)
const (
serverPort = 8080
)
func main() {
router := httprouter.New()
router.Handler("GET", "/healthz", http.HandlerFunc(goserverlessapi.HealthHandler))

fmt.Printf("Server listening on port: %d\n", serverPort)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", serverPort), router), nil)
}

接下来,更新AWS Lambda入口点。 我们将使用gateway包,该gateway包在AWS Lambda和API网关上运行时可替代Go net / http,因此让我们先安装它。

$ go get github.com/apex/gateway

然后将AWS Lambda入口点文件更新为以下内容。

# cmd/go-serverless-api-lambda/main.go
package main
import (
"log"
"net/http"
"github.com/apex/gateway"
"github.com/julienschmidt/httprouter"
"github.com/techjacker/go-serverless-api"
)
func main() {
router := httprouter.New()
router.Handler("GET", "/healthz", http.HandlerFunc(goserverlessapi.HealthHandler))
log.Fatal(gateway.ListenAndServe("", router), nil)
}

我们已经为导入添加了gateway ,并将其替换为http.ListenAndServe 。 该端口值在Lambda上下文中是冗余的,并且gateway程序包将其丢弃,因此我们可以安全地删除端口常量,并用空字符串替换它。 另外,我们从go-serverless-api HealthHandler go-serverless-api程序包(项目根目录中的程序包,以前是我们的主程序包)中包含了我们的/healthz作为/healthz路径的处理程序。

3.在本地构建和运行

让我们再次构建并运行原始的HTTP API。

$ go build \
-o go-serverless-api \
./cmd/go-serverless-api
$ ./go-serverless-api
Server listening on port: 8080

打开另一个终端窗口并进行查询。

$ curl -s http://localhost:8080/healthz ok

一切仍然有效!

让我们对AWS Lambda版本执行相同的操作。

$ go build \
-o go-serverless-api-lambda \
./cmd/go-serverless-api-lambda
$ ./go-serverless-api-lambda

再次,打开一个新终端并进行查询。

$ curl -s http://localhost:8080/healthz
http: error: ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /healthz (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused',)) while doing GET request to URL: http://localhost:8080/healthz

不必担心会出现此错误,因为AWS Lambda版本不监听HTTP连接,而是期望被提供APIGatewayProxyRequest类型。

4. AWS Lambda的打包申请

构建适用于Linux的二进制文件,Linux是AWS Lambda使用的操作系统。

$ GOOS=linux go build \
-o go-serverless-api-lambda \
./cmd/go-serverless-api-lambda

AWS Lambda要求将功能代码捆绑到zip中,因此让我们继续压缩二进制文件。

$ zip go-serverless-api-lambda.zip go-serverless-api-lambda

在最后一步-部署中,我们将使用创建的go-serverless-api-lambda.zip

5.部署

我看过一些教程,这些教程使用AWS CLI工具通过临时命令部署到AWS Lambda。 这绝对是错误的方法! 您应该像应用程序的其他所有方面一样自动化基础结构。 部署的行业标准是TerraformAWS Cloudformation 。 两者都为您提供了一种声明性的方式来构建基础结构。 您可以将此配置保存到提交到存储库的YAML / JSON(Cloudformation)或HCL(Terraform)文件中。 这样处理的问题是您必须处理堆栈的所有低层详细信息。 如果我们能用不到20行代码而不是数百行代码来高层描述基础架构,那将是很好的。 输入AWS Severless Application Model(SAM)

AWS Severless应用程序模型(SAM)

SAM是由Amazon牵头的新标准,旨在使无服务器基础架构的部署更简单,更简洁。 SAM是一个开放源代码规范— 请参阅完整的参考指南 。 希望其他云供应商将来会采用此方法,并且有可能通过单一配置无缝部署到多个云。

使用AWS SAM进行部署

将以下YAML文件添加到项目的根目录。 这是一个SAM模板,用于配置运行您的Go应用程序的AWS Lambda函数,并将其部署在AWS API Gateway提供的HTTP接口后面。

# template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: 'Boilerplate Go API.'
Resources:
GoAPI:
Type: AWS::Serverless::Function
Properties:
Handler: go-serverless-api-lambda
Runtime: go1.x
CodeUri: ./go-serverless-api-lambda.zip
Events:
Request:
Type: Api
Properties:
Method: ANY
Path: /{proxy+}

Type: AWS::Serverless::FunctionType: AWS::Serverless::Function创建一个Lambda函数,该函数由我们之前构建的二进制文件处理( Handler: go-serverless-api-lambda )。 此Lambda函数可以响应由其他AWS服务触发的任何事件,例如由AWS S3和Kinesis触发的事件。 该文档包含事件源完整列表 。 在我们的例子中,我们希望它通过API网关响应HTTP请求,因此我们将事件设置为Type: Api 。 SAM为我们隐式创建一个API网关,作为其一部分,然后我们将方法设置为ANY以配置为响应任何类型的HTTP请求。 我们通过添加Path: /{proxy+}来告诉我们的API处理下面的所有路径,包括根。

我们仍然需要将包含Go二进制文件的zip上传到AWS S3。 确保您已创建一个S3存储桶,可以接收我们的邮政编码。 这是您要手动执行的一次性操作。

$ aws s3 mb s3://my-bucket

以下命令将上载zip并创建打包的SAM模板。

$ aws cloudformation package \
--template-file template.yaml \
--s3-bucket my-bucket \
--output-template-file packaged-template.yaml

现在,您应该具有一个指向上传的zip的packaged-template.yaml文件。

# packaged-template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: 'Boilerplate Go API.'
Resources:
GoAPI:
Type: AWS::Serverless::Function
Properties:
Handler: go-serverless-api-lambda
Runtime: go1.x
CodeUri: s3://my-bucket/8982639e71e0d433cd99f9fa4207ecbe
Events:
Request:
Type: Api
Properties:
Method: ANY
Path: /{proxy+}

现在,让我们使用这个新的打包模板进行部署。

$ aws cloudformation deploy \
--template-file packaged-template.yaml \
--stack-name go-serverless-api-stack \
--capabilities CAPABILITY_IAM

--capabilities CAPABILITY_IAM必须使用--capabilities CAPABILITY_IAM标志才能为您创建堆栈,因为这将涉及修改IAM权限-AWS强制您将其明确设置为安全措施。 在内部,SAM模板被编译成一个常规的cloudformation模板,该模板长了数百行。 所有这些对用户都是完全隐藏的(尽管您可以随意检查编译的cloudformation模板)。

设置6.测试已部署的无服务器API

为了发现我们部署的API的端点,我们需要找出API Gateway REST id

$ aws apigateway get-rest-apis
{
"items": [
{
"id": "0qu18x8pyd",
"name": "go-serverless-api-stack",
"createdDate": 1523987269,
"version": "1.0",
"apiKeySource": "HEADER",
"endpointConfiguration": {
"types": [
"EDGE"
]
}
}
]
}

AWS API Gateway地址采用以下格式。

https://<api-rest-id>.execute-api.<your-aws-region>.amazonaws.com/<api-stage>

阶段是Amazon进行部署的术语。 SAM为您创建两个不同的阶段: StageProd 。 注意URL中也使用的标题大小写! 我认为,AWS忘了,大家都叫他们的测试环境Staging没有Stage ,但没关系!

因此,SAM在以下位置为我们设置了端点。

https://0qu18x8pyd.execute-api.eu-west-1.amazonaws.com/Stage https://0qu18x8pyd.execute-api.eu-west-1.amazonaws.com/Prod

让我们调用我们的API。

$ curl -s https://0qu18x8pyd.execute-api.eu-west-1.amazonaws.com/Prod {"message":"Missing Authentication Token"}

不必惊慌! 当您向根资源请求但未为其定义处理程序时,这是标准的API网关错误 。 我们定义的唯一处理程序是/healthz ,所以让我们尝试一下。

$ curl -s https://0qu18x8pyd.execute-api.eu-west-1.amazonaws.com/Prod/healthz ok

瞧! 我们的API现在由无服务器后端提供支持。

本教程的完整代码可在github上找到

我将很快在Go和Serverless上发布更多文章。 跟随我在Twitter上获取通知

最初发布于 andrewgriffithsonline.com

翻译自: https://hackernoon.com/how-to-migrate-a-go-api-to-serverless-in-under-10-mins-e27b8a31202e

将本地数据库迁移到服务器

 类似资料: