几天前,我遇到了AWS RDS服务的问题,特别是使用AWS备份服务无法备份的SQL Server数据库。当前,请求RDS备份数据库存在局限性。但是,仍然可以使用RDS API为我们的数据库创建备份请求。
因此,我想删除必须通过API或Web控制台手动创建备份的工程师的活动,而是开发无服务器服务以自动化此过程。这种方法也可以扩展以解决其他问题。
让我们从这个准则
准备
在创建任何lambda函数之前,我们需要阅读一个文档,以说明如何开发它们。您可以找到它here。然后,您应该有以下内容。
-
Golang
版本1.16或更高版本 - 用于确保您将经过验证的源代码推向SCM 的
-
Taskfile
可选的,用于减少开发中的常规步骤。
Pre-commit
概述解决方案是
我们专注于创建快照时RDS生成的事件。这些事件是由AWS EventBridge触发的,该事件在AWS Lambda中提供了一个函数以执行必要的操作。
第一件事
您可以提供一个文件夹结构以在工作区中开发lambda功能。
-
创建新的GitHub存储库并将其克隆到您的本地计算机中。
git clone <your_repo_url>
-
转到您的文件夹,然后启动一个新的Golang项目。它将创建一个新的
go.mod
文件。
go mod init <your_repo_url> #Example go mod init github.com/StartloJ/lambda-rds-utils
-
(可选)创建一个预先承诺的策略文件,以帮助您提高开发质量。
#.pre-commit-config.yaml repos: - repo: https://github.com/dnephin/pre-commit-golang rev: master hooks: - id: go-fmt - id: go-vet - id: go-imports - id: go-mod-tidy
创建第一个功能
现在,我们准备开发lambda功能来处理事件。您可以按照下面的步骤来创建一个真实的用例函数,该功能复制跨区域的RDS快照。
记住AWS EventBridge的活动结构
您可以在官方文档here上看到更多活动。
{
"version": "0",
"id": "844e2571-85d4-695f-b930-0153b71dcb42",
"detail-type": "RDS DB Snapshot Event",
"source": "aws.rds",
"account": "123456789012",
"time": "2018-10-06T12:26:13Z",
"region": "us-east-1",
"resources": ["arn:aws:rds:us-east-1:123456789012:snapshot:rds:snapshot-replica-2018-10-06-12-24"],
"detail": {
"EventCategories": ["creation"],
"SourceType": "SNAPSHOT",
"SourceArn": "arn:aws:rds:us-east-1:123456789012:snapshot:rds:snapshot-replica-2018-10-06-12-24",
"Date": "2018-10-06T12:26:13.882Z",
"SourceIdentifier": "rds:snapshot-replica-2018-10-06-12-24",
"Message": "Automated snapshot created",
"EventID": "RDS-EVENT-0091"
}
}
定义功能的配置
我将定义参数以在下面重复此功能。
-
OPT_SRC_REGION
是复制的RDS快照的来源区域。 -
OPT_TARGET_REGION
是我们需要存储快照的目标区域。 -
OPT_DB_NAME
是我们需要复制快照的RDS DB名称的身份。 -
OPT_OPTION_GROUP_NAME
是一个目标选项组,用于附加以复制目标区域中的快照。 -
OPT_KMS_KEY_ID
是一个KMS密钥,用于加密目标区域的快照。
定义主要功能
package main
import (
"github.com/aws/aws-lambda-go/lambda"
"github.com/sirupsen/logrus"
)
func main() {
// Make the hangler available for remote procedure call by Lambda
logrus.Info("we starting handle lambda...")
lambda.Start(HandleEvents)
}
通过lambda创建功能句柄
// Define func to receive a variable from `BridgeEvent`
// and it should return `error` if any mistake.
func HandleEvents(events BridgeEvent) error {
return nil
}
定义新的AWS会话
// define AWS session for source and target region to copy snapshot
func HandleEvents(event BridgeEvent) error {
// start copy code
des_sess := session.Must(session.NewSessionWithOptions(session.Options{
Config: aws.Config{
Region: aws.String(viper.GetString("target_region")),
},
}))
src_sess := session.Must(session.NewSessionWithOptions(session.Options{
Config: aws.Config{
Region: aws.String(viper.GetString("src_region")),
},
}))
// end copy code
return nil
}
创建功能以获取所有现有快照
// This func input session of RDS service. Then, it get all list of database
// snapshot that already in your resources in List of Snapshot name.
func ListAllSnapshot(svc *rds.RDS) ([]string, error) {
db := viper.GetString("DB_NAME")
// This line will return a list of snapshots included snapshot name.
out, err := svc.DescribeDBSnapshots(&rds.DescribeDBSnapshotsInput{
DBInstanceIdentifier: &db,
})
if err != nil {
panic(err)
}
var snapShotName []string
// this loop is extract a snapshot name only.
for _, b := range out.DBSnapshots {
logrus.Infof("We have %s", ConvertToString(*b.DBSnapshotArn))
snapShotName = append(snapShotName, ConvertToString(*b.DBSnapshotArn))
}
return snapShotName, nil
}
从源和目标区域获取RDS快照名称的列表
func HandleEvents(event BridgeEvent) error {
...
// start copy code
targetSvc := rds.New(des_sess)
rep_dbSnapshots, err := ListAllSnapshot(targetSvc)
if err != nil {
return fmt.Errorf("we can't get any Snapshot name")
}
srcSvc := rds.New(src_sess)
src_dbSnapshots, err := ListAllSnapshot(srcSvc)
if err != nil {
return fmt.Errorf("we can't get any Snapshot name")
}
// End copy code
...
}
创建一个功能以删除重复快照作为源和目标
// this func use to remove duplicate name source if found in target
// you shouldn't switch position input to this func.
// `t` is mean a target that use double check to `s`
// it will remove a value in `s` if found it in `t`
func GetUniqueSnapShots(t, s []string) ([]string, error) {
//Create a map to keep track a unique strings
uniqueMap := make(map[string]bool)
//Iterate over `s` and add each string to map
for _, str := range s {
uniqueMap[str] = true
}
//Iterate over `t` and remove any string that are already in uniqueMap
for _, str2 := range t {
delete(uniqueMap, str2)
}
//Convert the unique string from Map to slice string
result := make([]string, 0, len(uniqueMap))
for str := range uniqueMap {
result = append(result, str)
}
if len(result) == 0 {
return nil, fmt.Errorf("not any Snapshot unique between source and target region")
}
return result, nil
}
定义独特的快照
func HandleEvents(event BridgeEvent) error {
...
// start copy code
dbSnapShots2Copy, err := GetUniqueSnapShots(rep_dbSnapshots, src_dbSnapshots)
if err != nil {
logrus.Warnf("it doesn't any task copy snapshot to %s", viper.GetString("target_region"))
return nil
}
// End copy code
...
}
创建一个跨区域的Func复制RDS快照
// Request to AWS RDS API for create event copy a specific snapshot
// into across region and encrypt it by KMS key multi-region
func CopySnapshotToTarget(svc *rds.RDS, snap string) (string, error) {
targetSnapArn := strings.Split(snap, ":")
targetSnapName := targetSnapArn[len(targetSnapArn)-1]
// Copy the snapshot to the target region
copySnapshotInput := &rds.CopyDBSnapshotInput{
OptionGroupName: aws.String(viper.GetString("option_group_name")),
KmsKeyId: aws.String(viper.GetString("kms_key_id")),
CopyTags: aws.Bool(true),
SourceDBSnapshotIdentifier: aws.String(snap),
TargetDBSnapshotIdentifier: aws.String(targetSnapName),
SourceRegion: aws.String(viper.GetString("src_region")),
}
_, err := svc.CopyDBSnapshot(copySnapshotInput)
if err != nil {
logrus.Errorf("Copy request %s is failed", snap)
return "", err
}
logrus.Infof("Copy %s is created", snap)
return fmt.Sprintf("Copy %s is created", snap), nil
}
定义循环以执行所有快照值
func HandleEvents(event BridgeEvent) error {
...
// start copy code
for s := range dbSnapShots2Copy {
logrus.Infof("trying to copy DBSnapshot to %s...", viper.GetString("target_region"))
_, err := CopySnapshotToTarget(targetSvc, dbSnapShots2Copy[s])
if err != nil {
logrus.Error(err.Error())
}
}
// End copy code
...
}
如果所有工作流程都完成,则它将从AWS lambda控制器中正常结束功能。
最后步骤
-
如果您完成了开发功能,则将用zip格式构建并打包它。
export BINARY_NAME="lambda-rds-util" #you can change this name. go build -o $BINARY_NAME main.go zip lambda-$BINARY_NAME.zip $BINARY_NAME
-
您可以将压缩文件(也可以zip文件)上传到S3。
#you can push it with below command export S3_NAME="<your_s3_bucket_name>" aws s3api put-object --bucket $S3_NAME --key lambda-$BINARY_NAME.zip --body ./lambda-$BINARY_NAME.zip
-
在AWS lambda的Web控制台中,您可以定义一个新资源,然后从S3中选择源。
-
在Web控制台中,您可以在AWS lambda中测试功能,以确保您的代码工作!。
结论
您可以使用Serverless
服务(例如AWS Lambda)在AWS或其他云提供商上自动化某些活动。它可以增强您的发展经验,并使您能够探索新的可能性。您甚至可以扩展此想法以在您的工作中开发新的工作流程。
我希望此博客能激发您考虑在开发旅程中使用Serverless
。