我正在尝试实现一个 CDK 项目,该项目将在 s3 存储桶中部署静态网站以及 CloudFront 发行版。我还有一个 API 网关,需要通过相同的云前端 URL 进行访问。我可以从 AWS 管理控制台执行此操作。但是当我尝试使用 CDK 实现此功能时,我遇到了循环依赖错误。
const cdn = new cloudfront.Distribution(this, "websitecdn", {
defaultBehavior: {origin: new origins.S3Origin(s3_bucket)}
});
const api = new apigw.RestApi(this, 'someapi', {defaultCorsPreflightOptions: enableCors})
const loginApi = api.root.addResource('login', {defaultCorsPreflightOptions: enableCors})
loginApi.addMethod('POST', new apigw.LambdaIntegration(loginLambda, {
proxy: false,
integrationResponses: [LambdaIntegrationResponses]}),
{
methodResponses: [LambdaMethodResponses]
})
const apiOrigin = new origins.RestApiOrigin(api)
cdn.addBehavior("/prod/*",apiOrigin,{
allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
viewerProtocolPolicy: ViewerProtocolPolicy.HTTPS_ONLY,
})
一切正常,直到我尝试在 CDN 中添加 API 网关的行为。但是当我添加它时,它开始抛出循环依赖错误。
我尝试使用 AWS CDK typescript 执行以下操作:
部署静态 s3 网站
为此网站创建一个 CloudFront 发行版 -> 我们称之为 cdn_x
部署后端API(带有API网关的Lambda函数)
将 API 网关 URL 作为行为添加到 cdn_x,以便我也可以使用相同的 URL 进行 API 调用(我没有自定义域)
我预计部署会顺利进行,因为我能够在 AWS 管理控制台(AWS 的 Web UI)中进行部署。但尝试使用 AWS CDK 执行相同操作会引发循环依赖错误。
从您的示例中尚不清楚 CDK 项目中的堆栈和资源是如何创建和关联的。我无法使用您的代码示例。
与此同时,我使用 CloudFront 中的多个行为以及 Amazon API Gateway 在
/api/*
路径下创建了一个 TypeScript 示例,并将 S3 存储桶作为默认行为来提供静态资产 /*
最终的CDK结构如下:
CDK 代码库使用 多个堆栈:
cloudfront-stack.ts
rest-api-stack.ts
s3-stack.ts
waf-stack.ts
bin/infra.ts
const app = new cdk.App();
const s3Stack = new S3Stack(app, "S3Stack");
const restApiStack = new RestApiStack(app, "RestApiStack");
const wafStack = new WafStack(app, "WafStack", {
restApi: restApiStack.restApi,
});
const cloudFrontStack = new CloudFrontStack(app, "CloudFrontStack", {
bucketAssets: s3Stack.bucketAssets,
restApi: restApiStack.restApi,
wafCloudFrontAclArn: wafStack.wafCloudFrontAclArn,
wafRestApiOriginVerifyHeader: wafStack.wafRestApiOriginVerifyHeader,
wafRestApiOriginVerifyHeaderValue: wafStack.wafRestApiOriginVerifyHeaderValue,
});
GitHub 存储库:
我相信上面的例子会澄清你的一些问题。
我整理了一篇文章,准确描述了如何使用 CDK(以及 Pulumi)执行此操作,您可以在这里阅读它,我相信它会回答您的很多问题。
在extremely简短的总结中:您需要为每个源(s3和您的api)设置一个源,并且您需要授予正确访问这些源的权限,并且您需要配置行为相应地分配给您。
// CDK example
const domainName = "abc.com";
const subdomainName = `www.${domainName}`;
const siteBucket = new Bucket(this, "static-website", {
bucketName: domainName,
encryption: BucketEncryption.S3_MANAGED,
publicReadAccess: false,
// the following is NOT recommended for production code
removalPolicy: RemovalPolicy.DESTROY,
autoDeleteObjects: true,
});
const originAccessIdentity = new OriginAccessIdentity(this, 'OriginAccessIdentity', {
comment: 'abc.com website OAI'
});
const s3Origin = new S3Origin(siteBucket, {originAccessIdentity: originAccessIdentity})
// assumes relevant certificateArn, see
// https://therightstuff.medium.com/aws-cdk-dnsvalidatedcertificate-is-deprecated-c347fdc0a3e6
// for more details
const certificate = acm.Certificate.fromCertificateArn(this, 'Certificate', certificateArn);
// assumes api created (RestApi construct from aws-cdk-lib/aws-apigateway)
// and region available.
// NOTE: it amazes me that manually reconstructing this url is required.
const apiOriginName = `${api.restApiId}.execute-api.${region}.amazonaws.com`;
// origin path must be set to the stage name, and this assumes that after
// the matched path there is a matching resource that requests will be mapped
// to. in other words, if the cloudfront distribution path matches
// /api/my-resource, which in our example is abc.com/api/my-resource, then the
// request will redirect to the /{stage}/api/my-resource resource in the
// API Gateway endpoint
const apiOriginPath = `/${api.deploymentStage.stageName}`;
// CloudFront distribution that provides HTTPS (including S3 and API access)
const distribution = new Distribution(
this,
"SiteDistribution",
{
domainNames: [domainName, subdomainName],
certificate,
defaultBehavior: {
origin: s3Origin,
allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
cachePolicy: CachePolicy.CACHING_OPTIMIZED,
viewerProtocolPolicy: ViewerProtocolPolicy.ALLOW_ALL,
},
additionalBehaviors: {
"api/*": {
origin: new HttpOrigin(apiOriginName, {
originId: apiOriginName,
protocolPolicy: OriginProtocolPolicy.HTTPS_ONLY,
httpPort: 80,
httpsPort: 443,
originPath: apiOriginPath,
}),
viewerProtocolPolicy: ViewerProtocolPolicy.HTTPS_ONLY,
cachePolicy: CachePolicy.CACHING_DISABLED,
allowedMethods: AllowedMethods.ALLOW_ALL,
originRequestPolicy: OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER,
},
},
defaultRootObject: "index.html",
errorResponses: [
{
httpStatus: 403,
responseHttpStatus: 200,
responsePagePath: "/index.html",
},
{
httpStatus: 404,
responseHttpStatus: 200,
responsePagePath: "/index.html",
},
],
}
);
// Route53 alias record for the naked domain's CloudFront distribution
new ARecord(this, "SiteAliasRecord", {
recordName: domainName,
target: RecordTarget.fromAlias(
new targets.CloudFrontTarget(distribution)
),
// assumes zone (IHostedZone construct from 'aws-cdk-lib/aws-route53')
// available
zone: zone,
});
// Route53 alias record for a subdomain's CloudFront distribution
new CnameRecord(this, "SiteCnameRecord", {
recordName: subdomainName,
domainName: distribution.distributionDomainName,
// assumes zone (IHostedZone construct from 'aws-cdk-lib/aws-route53')
// available
zone: zone,
});
// Deploy the static website to the site bucket
new BucketDeployment(this, "static-website-deployment", {
// assumes that bucket content is in the `static-website` folder
// under the project root
sources: [Source.asset("./static-website")],
destinationBucket: siteBucket,
distribution: distribution
});