概述
在10/28/2022,PHP, GO ,.net和Ruby最终在 aws App App Runner的托管运行时得到支持(可以直接从Github部署源代码,而无需创建一个创建一个容器图像),请参阅this page。
因此,如果您正在使用GO语言开发后端,我相信您想使用AWS App Runner用GO语言作为托管运行时。
我认为,如果我可以在使用AWS CDK的GO语言的GO中构建应用程序和基础架构,那将是很棒的
此外,截至2023年6月,App Runner的L2 Construct在CDK上的Alpha版本中,但我尝试将其构建 ,“ L1构造”和“ alpha版本中的L2构造” 。
假设
App Runner支持的GO版本来自1.18。
因此,此处介绍的代码使用1.18.7
。
另外,AWS CDK版本是2.52.0
。
github
所有代码都可以在GitHub。
上找到目录结构
使用以下目录结构。 (省略了与主要主题无关的文件。)
- 应用
- 应用程序由App Runner发布的应用程序
- CDK
- CDK目录
- 自定义
- 此CDK中使用的自定义资源lambda目录
- create_connection.sh
- 创建GitHub连接的脚本
- go.work,go.work.sum
- GO Workspaces模式的文件
├── app
│ ├── go.mod
│ ├── go.sum
│ └── main.go
├── cdk
│ ├── Makefile
│ ├── cdk.context.json
│ ├── cdk.json
│ ├── go-cdk-go-managed-apprunner.go
│ ├── go-cdk-go-managed-apprunner_test.go
│ ├── go.mod
│ ├── go.sum
│ └── input
│ └── input.go
├── create_connection.sh
├── custom
│ ├── custom.go
│ ├── go.mod
│ └── go.sum
├── go.work
└── go.work.sum
工作区模式
这次,如上所述,将出现三组应用程序代码:“应用程序跑步者代码”,“ CDK代码”和“自定义资源lambda的代码”。
可以在一个存储库中准备这些目录,并通过共享go.mod在根目录中构建它们,但是我想准备一个go.mod。 。
因此,我使用了一个名为Workspaces mode的功能,该功能在GO 1.18中引入。
这使您可以在子目录中准备go.mod,并使用多模型开发和构建。
您可以通过点击以下命令进入工作空间模式。
go work init app cdk custom
然后将创建以下go.work文件。
go 1.18
use (
./app
./cdk
./custom
)
CDK代码
使用AWS CDK构建App Runner(批准服务,VPC连接器等)时, l2构造尚未“提供” 。
但是,在Alpha版本中提供了 。
因此,在本文中,我将向您展示如何使用“ L1”和“ L2 Alpha版本”构建应用程序跑步者。
go-cdk-go-go-managed-apprunner.go(stack定义)
整个堆栈
为外部参数创建结构称为AppRunnerStackProps
,并使用NewAppRunnerStack
函数创建堆栈。
对于AppRunnerStackProps
的AppRunnerStackInputProps
,一个名为input.go
的文件管理类型和设置值传递给堆栈的参数。
type AppRunnerStackProps struct {
awscdk.StackProps
AppRunnerStackInputProps *input.AppRunnerStackInputProps
}
func NewAppRunnerStack(scope constructs.Construct, id string, props *AppRunnerStackProps) awscdk.Stack {
var sprops awscdk.StackProps
if props != nil {
sprops = props.StackProps
}
stack := awscdk.NewStack(scope, &id, &sprops)
// Resource definition from here
请参见下文的input.go(Adrunnerstackinputprops)。
自定义资源lambda(用于自动configuration)
这突然成为自定义资源的lambda。
为什么突然如此?实际上, App Runner的某些资源还不是“” cloudformation兼容。
由于CDK无法创建不支持云形式支持的资源,因此我使用自定义资源在CDK中构建它们。
此外,还有一个用于使用CDK构建自定义资源的模块,它允许您构建简单的事物,而无需自己构建lambda,可以单独构建AWS API(请参阅doc)。
这次,我将创建一个称为“ AutoscalingConfiguration”的自定义资源,但是如果我只想创建它(ongreate),我可以通过在上述模块中调用Create API来做到这一点,但是为了删除,我需要它自己的arn。 ARN本身是在资源创建时创建的随机字符。
创建资源时,ARN本身是作为随机字符串生成的,因此在定义自定义资源时,我还不知道它将是什么字符串,并且我无法在OnDelete定义中指定ARN。
由于仅定义创建不是一个好主意,因此我使用lambda不仅启用创建,而且可以通过“动态列表,检索和指定ARN”时的删除” 在删除时。 (也可以更新。)
长话短说,这是自定义资源的lambda创建部分的CDK定义。
customResourceLambda := awslambda.NewFunction(stack, jsii.String("CustomResourceLambda"), &awslambda.FunctionProps{
Runtime: awslambda.Runtime_GO_1_X(),
Handler: jsii.String("main"),
Code: awslambda.AssetCode_FromAsset(jsii.String("../"), &awss3assets.AssetOptions{
Bundling: &awscdk.BundlingOptions{
Image: awslambda.Runtime_GO_1_X().BundlingImage(),
Command: jsii.Strings("bash", "-c", "GOOS=linux GOARCH=amd64 go build -o /asset-output/main custom/custom.go"),
User: jsii.String("root"),
},
}),
Timeout: awscdk.Duration_Seconds(jsii.Number(900)),
InitialPolicy: &[]awsiam.PolicyStatement{
awsiam.NewPolicyStatement(&awsiam.PolicyStatementProps{
Actions: &[]*string{
jsii.String("apprunner:*AutoScalingConfiguration*"),
jsii.String("apprunner:UpdateService"),
jsii.String("apprunner:ListOperations"),
},
Resources: &[]*string{
jsii.String("*"),
},
}),
awsiam.NewPolicyStatement(&awsiam.PolicyStatementProps{
Actions: &[]*string{
jsii.String("cloudformation:DescribeStacks"),
},
Resources: &[]*string{
stack.StackId(),
},
}),
},
})
这是由Docker捆绑的,但是您可以在Code.Bundling.Command
中指定构建命令。
从CDK目录开始,通过AssetCode_FromAsset(jsii.String("../"),
指定父目录路径以指定父目录路径,而构建命令(Command
)则指定custom/custom.go
以指定自定义资源lambda的文件,然后Handler: jsii.String("main")
指定函数(handler)(handler)(handler) )名称。
jsii.Xxx
是一个仅返回指针的函数,原因是省略的,但是由于CDK的GO版本要求将参数指定为指针,因此指定它有点麻烦。
另外,使用InitialPolicy
,您可以授予自定义资源Lambda许可来操纵App Runner的自动configuration。
实际创建此自动configuration的自定义资源lambda也写在GO中,但是此代码也很长,并且与主要主题不直接相关。
如果您有兴趣,可以找到here。
(我分别使用创建和删除分支编写了创建,更新和删除过程,并且使用创建和更新时返回创建资源的ARN。)
AutoscalingConfiguration
然后使用上面的自定义资源lambda创建自动configuration本身。
这就是名称所暗示的,是自动化的阈值设置资源。
autoScalingConfiguration := awscdk.NewCustomResource(stack, jsii.String("AutoScalingConfiguration"), &awscdk.CustomResourceProps{
ResourceType: jsii.String("Custom::AutoScalingConfiguration"),
Properties: &map[string]interface{}{
"AutoScalingConfigurationName": *stack.StackName(),
"MaxConcurrency": strconv.Itoa(props.AppRunnerStackInputProps.AutoScalingConfigurationArnProps.MaxConcurrency),
"MaxSize": strconv.Itoa(props.AppRunnerStackInputProps.AutoScalingConfigurationArnProps.MaxSize),
"MinSize": strconv.Itoa(props.AppRunnerStackInputProps.AutoScalingConfigurationArnProps.MinSize),
"StackName": *stack.StackName(),
},
ServiceToken: customResourceLambda.FunctionArn(),
})
autoScalingConfigurationArn := autoScalingConfiguration.GetAttString(jsii.String("AutoScalingConfigurationArn"))
AutoScalingConfigurationName
是配置名称,MaxConcurrency
是与fire缩放的并发连接的数量,而MaxSize
和MinSize
分别是最小和最大数字。
所有值均在input.go中指定,并通过props传递。
autoScalingConfigurationArn
由以后定义的批准服务使用,因此我通过GetAttString
从自定义资源lambda中收到ARN并将其存储在变量中。
GitHub连接
如果您正在使用托管运行时构建App Runner,则需要设置与源代码所在的GitHub的连接。
此需要使用GitHub进行身份验证,并且在创建连接(一种称为资源)并手动按控制台的“完整握手”按钮之前,该构建将无法完成。
。因此,由于无法通过整个过程自动构建(手动操作是不可避免的),“如果尚未创建与CreateConnection API创建连接在此期间操作,完成握手的握手,并在完成握手时恢复CDK建筑过程。如果完成握手,则重新启动CDK施工过程。
connectionArn, err := createConnection(props.AppRunnerStackInputProps.SourceConfigurationProps.ConnectionName, *props.Env.Region)
if err != nil {
panic(err)
}
这里的功能本身也有点长,所以请参阅the link。
这将等待第一次输入,但是创建后,CDK将完全自动运行。
另外,github连接本身可能会按帐户为基础设置,而不是与应用程序一对一设置,因此,如果您已经具有GitHub连接,它将使用连接您指定而不是创建一个新的。因此,如果尚不存在连接,并且要创建一个连接,则可以使用API在堆栈管理之外创建它,而不是使用自定义资源,以更改应用程序的生命周期。
- “我不希望第一个CDK部署完全自动化。”
- “我宁愿先手工制作,然后将CDK部署完全自动化。
然而,基于这样的声音(虚拟),我创建了一个“ shell脚本只是为了创建github连接” ,因此,如果我先扔这个连接以创建连接,那么 CDK将是完全自动的,请参见this page。
上面的解释是针对CDK和AWS侧的,但是它假设“ GitHub的AWS连接器”安装在GitHub侧。 (创建GitHub连接并在控制台中完成握手时,您可以跳到GitHub页面。)
Instancerole
IAM角色“实例角色”,用于App Runner发布的应用程序,以访问AWS Resources。
指定它,以便可以将tasks.apprunner.amazonaws.com
弄脏。
在这种情况下,我没有编写任何程序以访问样本的AWS,因此我没有指定任何许可(操作)。
appRunnerInstanceRole := awsiam.NewRole(stack, jsii.String("AppRunnerInstanceRole"), &awsiam.RoleProps{
AssumedBy: awsiam.NewServicePrincipal(jsii.String("tasks.apprunner.amazonaws.com"), nil),
})
还有一个与实例角色不同的IAM角色,即访问ECR的“访问角色”,该角色在App Runner中可用,但对于托管运行时并不是必需的。
。VPC连接器(L2 Alpha版本)
这是允许访问VPC资源的功能。
App Runner当前具有L1构造和L2构造的alpha版本。
因此,我分别在 l1和l2 alpha版本中撰写。
请注意,VPC连接器还具有一个安全组,该组被定义在一起,VPC和子网ID通过Props传递为外部参数。
首先是 l2 。
securityGroupForVpcConnectorL2 := awsec2.NewSecurityGroup(stack, jsii.String("SecurityGroupForVpcConnectorL2"), &awsec2.SecurityGroupProps{
Vpc: awsec2.Vpc_FromLookup(stack, jsii.String("VPCForSecurityGroupForVpcConnectorL2"), &awsec2.VpcLookupOptions{
VpcId: jsii.String(props.AppRunnerStackInputProps.VpcConnectorProps.VpcID),
}),
Description: jsii.String("for AppRunner VPC Connector L2"),
})
vpcConnectorL2 := apprunner.NewVpcConnector(stack, jsii.String("VpcConnectorL2"), &apprunner.VpcConnectorProps{
Vpc: awsec2.Vpc_FromLookup(stack, jsii.String("VPCForVpcConnectorL2"), &awsec2.VpcLookupOptions{
VpcId: jsii.String(props.AppRunnerStackInputProps.VpcConnectorProps.VpcID),
}),
SecurityGroups: &[]awsec2.ISecurityGroup{securityGroupForVpcConnectorL2},
VpcSubnets: &awsec2.SubnetSelection{
Subnets: &[]awsec2.ISubnet{
awsec2.Subnet_FromSubnetId(stack, jsii.String("Subnet1"), jsii.String(props.AppRunnerStackInputProps.VpcConnectorProps.SubnetID1)),
awsec2.Subnet_FromSubnetId(stack, jsii.String("Subnet2"), jsii.String(props.AppRunnerStackInputProps.VpcConnectorProps.SubnetID2)),
},
},
})
VPC连接器(L1版本)
这是 l1 One的VPC连接器(和安全组)。
securityGroupForVpcConnectorL1 := awsec2.NewSecurityGroup(stack, jsii.String("SecurityGroupForVpcConnectorL1"), &awsec2.SecurityGroupProps{
Vpc: awsec2.Vpc_FromLookup(stack, jsii.String("VPCForVpcConnectorL1"), &awsec2.VpcLookupOptions{
VpcId: jsii.String(props.AppRunnerStackInputProps.VpcConnectorProps.VpcID),
}),
Description: jsii.String("for AppRunner VPC Connector L1"),
})
vpcConnectorL1 := awsapprunner.NewCfnVpcConnector(stack, jsii.String("VpcConnectorL1"), &awsapprunner.CfnVpcConnectorProps{
SecurityGroups: jsii.Strings(*securityGroupForVpcConnectorL1.SecurityGroupId()),
Subnets: jsii.Strings(
props.AppRunnerStackInputProps.VpcConnectorProps.SubnetID1,
props.AppRunnerStackInputProps.VpcConnectorProps.SubnetID2,
),
})
批准服务(L2 Alpha版本)
和主要批准者本身,批准服务。
我在l1和l2 alpha版本中制作了这个。
让我们从L2 alpha版本开始。
apprunnerServiceL2 := apprunner.NewService(stack, jsii.String("AppRunnerServiceL2"), &apprunner.ServiceProps{
InstanceRole: appRunnerInstanceRole,
Source: apprunner.Source_FromGitHub(&apprunner.GithubRepositoryProps{
RepositoryUrl: jsii.String(props.AppRunnerStackInputProps.SourceConfigurationProps.RepositoryUrl),
Branch: jsii.String(props.AppRunnerStackInputProps.SourceConfigurationProps.BranchName),
ConfigurationSource: apprunner.ConfigurationSourceType_API,
CodeConfigurationValues: &apprunner.CodeConfigurationValues{
Runtime: apprunner.Runtime_GO_1(),
Port: jsii.String(strconv.Itoa(props.AppRunnerStackInputProps.SourceConfigurationProps.Port)),
StartCommand: jsii.String(props.AppRunnerStackInputProps.SourceConfigurationProps.StartCommand),
BuildCommand: jsii.String(props.AppRunnerStackInputProps.SourceConfigurationProps.BuildCommand),
Environment: &map[string]*string{
"ENV1": jsii.String("L2"),
},
},
Connection: apprunner.GitHubConnection_FromConnectionArn(jsii.String(connectionArn)),
}),
Cpu: apprunner.Cpu_Of(jsii.String(props.AppRunnerStackInputProps.InstanceConfigurationProps.Cpu)),
Memory: apprunner.Memory_Of(jsii.String(props.AppRunnerStackInputProps.InstanceConfigurationProps.Memory)),
VpcConnector: vpcConnectorL2,
AutoDeploymentsEnabled: jsii.Bool(true),
})
var cfnAppRunner awsapprunner.CfnService
//lint:ignore SA1019 This is deprecated, but Go does not support escape hatches yet.
jsii.Get(apprunnerServiceL2.Node(), "defaultChild", &cfnAppRunner)
cfnAppRunner.SetAutoScalingConfigurationArn(autoScalingConfigurationArn)
cfnAppRunner.SetHealthCheckConfiguration(&awsapprunner.CfnService_HealthCheckConfigurationProperty{
Path: jsii.String("/"),
Protocol: jsii.String("HTTP"),
})
配置比其他配置更长,但是为了匹配L1配置,也指定了不需要的项目。
再次通过道具获取每个信息。
这里重要的是,由于某些项目无法仅使用L2构造设置,因此L2资源被视为L1资源,然后属性被覆盖 “ Escape Hatch” (某物这样)。
这样做的原因是GO当前不支持Escape Hatch,请参见this page。
CDK具有一个名为“逃生舱口”的概念,它允许您修改L2构造的基础CFN资源以访问由云形式支持但尚未得到CDK支持的功能。不幸的是,GO的CDK尚未支持此功能,因此我必须通过L1构造创建资源。有关更多信息,请参见此GitHub问题,并在GO中遵循支持CDK逃生舱口的支持。
因此,我代替了jsii.Get
的方法。
jsii.Get(apprunnerServiceL2.Node(), "defaultChild", &cfnAppRunner)
cfnAppRunner.SetAutoScalingConfigurationArn(autoScalingConfigurationArn)
cfnAppRunner.SetHealthCheckConfiguration(&awsapprunner.CfnService_HealthCheckConfigurationProperty{
Path: jsii.String("/"),
Protocol: jsii.String("HTTP"),
})
请注意,jsii.Get
已弃用。 (但是这次我使用了它,因为我忍不住了。)
参见doc。
应用服务(L1版)
这是L1版本。
apprunnerServiceL1 := awsapprunner.NewCfnService(stack, jsii.String("AppRunnerServiceL1"), &awsapprunner.CfnServiceProps{
SourceConfiguration: &awsapprunner.CfnService_SourceConfigurationProperty{
AutoDeploymentsEnabled: jsii.Bool(true),
AuthenticationConfiguration: &awsapprunner.CfnService_AuthenticationConfigurationProperty{
ConnectionArn: jsii.String(connectionArn),
},
CodeRepository: &awsapprunner.CfnService_CodeRepositoryProperty{
RepositoryUrl: jsii.String(props.AppRunnerStackInputProps.SourceConfigurationProps.RepositoryUrl),
SourceCodeVersion: &awsapprunner.CfnService_SourceCodeVersionProperty{
Type: jsii.String("BRANCH"),
Value: jsii.String(props.AppRunnerStackInputProps.SourceConfigurationProps.BranchName),
},
CodeConfiguration: &awsapprunner.CfnService_CodeConfigurationProperty{
ConfigurationSource: jsii.String("API"),
CodeConfigurationValues: &awsapprunner.CfnService_CodeConfigurationValuesProperty{
Runtime: jsii.String("GO_1"),
Port: jsii.String(strconv.Itoa(props.AppRunnerStackInputProps.SourceConfigurationProps.Port)),
StartCommand: jsii.String(props.AppRunnerStackInputProps.SourceConfigurationProps.StartCommand),
BuildCommand: jsii.String(props.AppRunnerStackInputProps.SourceConfigurationProps.BuildCommand),
RuntimeEnvironmentVariables: []interface{}{
&awsapprunner.CfnService_KeyValuePairProperty{
Name: jsii.String("ENV1"),
Value: jsii.String("L1"),
},
},
},
},
},
},
HealthCheckConfiguration: &awsapprunner.CfnService_HealthCheckConfigurationProperty{
Path: jsii.String("/"),
Protocol: jsii.String("HTTP"),
},
InstanceConfiguration: &awsapprunner.CfnService_InstanceConfigurationProperty{
Cpu: jsii.String(props.AppRunnerStackInputProps.InstanceConfigurationProps.Cpu),
Memory: jsii.String(props.AppRunnerStackInputProps.InstanceConfigurationProps.Memory),
InstanceRoleArn: appRunnerInstanceRole.RoleArn(),
},
NetworkConfiguration: &awsapprunner.CfnService_NetworkConfigurationProperty{
EgressConfiguration: awsapprunner.CfnService_EgressConfigurationProperty{
EgressType: jsii.String("VPC"),
VpcConnectorArn: vpcConnectorL1.AttrVpcConnectorArn(),
},
},
AutoScalingConfigurationArn: autoScalingConfigurationArn,
})
go-cdk-go-go-managed-apprunner_test.go(单位测试)
我还创建了a simple sample unit test for CDK。
我有快照测试,细粒度的主张测试和快照lambda S3资产替换(每次执行都会更改的值掩盖)。
应用程序代码(App/main.go)
这是要在App Runner中部署的示例代码。
这真的很简单,只需以杜松子酒为Web服务器,并显示CDK中传递的环境变量。
package main
import (
"os"
"github.com/gin-gonic/gin"
)
func main() {
outputValue := os.Getenv("ENV1")
engine := gin.Default()
engine.GET("/", func(c *gin.Context) {
c.String(200, outputValue)
})
engine.Run(":8080")
}
自定义资源Lambda代码(自定义/custom.go)
这在上面的CDK部分中有点提及,但这是重述。 (这很长,所以请参阅the link。)
补充
顺便说一句,如果您想阅读它,我已经写了有关the features and usage of writing CDK in Go language and the difference from TypeScript的文章。
最后
由于现在在App Runner的托管运行时和AWS CDK中支持GO,所以我认为您中的一些人可能想完全编写您的应用程序和基础架构。
关于GO中CDK的信息仍然很少,但我希望它会稍微传播。