仅合成单个CDK堆栈时因其他堆栈资源缺失报错的解决办法
问题描述
我的CDK应用定义了两个堆栈,分别对应prod(生产)和dev(开发)环境。堆栈中定义的CloudFront函数,其构建文件路径会根据环境不同而变化:dev环境的函数包含基础认证逻辑(避免开发站点公开访问),prod环境则无此代码,对应路径分别为build-dev和build-prod。示例代码如下:
const cfViewerRequest = new cloudfront.Function(this, "MySite-CFViewerRequest", { code: cloudfront.FunctionCode.fromFile({filePath: `backend/functions/cf-viewer-request/build-${props.stage}/index.js`}), functionName: `MySite-CFViewerRequest-${props.stage}`, comment: `MySite-CFViewerRequest-${props.stage}` });
入口代码会同时实例化两个堆栈:
#!/usr/bin/env node import "source-map-support/register"; import * as cdk from "aws-cdk-lib"; import { MySiteStack } from "../lib/my-site-stack"; const app = new cdk.App(); new MySiteStack(app, "MySiteDev", { env: { account: "012345678901", region: "us-east-1" }, stage: "dev", }); new MySiteStack(app, "MySiteProd", { env: { account: "109876543210", region: "us-east-1" }, stage: "prod", });
问题:仅构建dev资源后,运行cdk synth MySiteDev会报错ENOENT: no such file or directory, open 'backend/functions/cf-viewer-request/build-prod/index.js',因为CDK初始化时会创建所有堆栈实例,即使指定合成单个堆栈,也会执行prod堆栈的代码,尝试读取未构建的prod文件。
解决方案
方案1:通过环境变量/命令行参数控制堆栈实例化
修改入口文件,根据环境变量或上下文参数,仅实例化当前需要的环境堆栈,避免CDK加载未使用的堆栈代码。
方式A:使用环境变量
修改入口代码:
#!/usr/bin/env node import "source-map-support/register"; import * as cdk from "aws-cdk-lib"; import { MySiteStack } from "../lib/my-site-stack"; const app = new cdk.App(); // 从环境变量获取stage,默认dev const stage = process.env.STAGE || "dev"; if (stage === "dev") { new MySiteStack(app, "MySiteDev", { env: { account: "012345678901", region: "us-east-1" }, stage: "dev", }); } else if (stage === "prod") { new MySiteStack(app, "MySiteProd", { env: { account: "109876543210", region: "us-east-1" }, stage: "prod", }); }
运行命令时指定环境变量:
# 合成dev堆栈 STAGE=dev cdk synth MySiteDev # 合成prod堆栈 STAGE=prod cdk synth MySiteProd
方式B:使用CDK上下文参数
修改入口代码:
#!/usr/bin/env node import "source-map-support/register"; import * as cdk from "aws-cdk-lib"; import { MySiteStack } from "../lib/my-site-stack"; const app = new cdk.App(); // 从CDK上下文获取stage,默认dev const stage = app.node.tryGetContext("stage") || "dev"; if (stage === "dev") { new MySiteStack(app, "MySiteDev", { env: { account: "012345678901", region: "us-east-1" }, stage: "dev", }); } else if (stage === "prod") { new MySiteStack(app, "MySiteProd", { env: { account: "109876543210", region: "us-east-1" }, stage: "prod", }); }
运行命令时传递上下文参数:
# 合成dev堆栈 cdk synth MySiteDev -c stage=dev # 合成prod堆栈 cdk synth MySiteProd -c stage=prod
方案2:使用独立入口文件
创建两个入口文件,分别对应dev和prod环境:
dev-app.js
#!/usr/bin/env node import "source-map-support/register"; import * as cdk from "aws-cdk-lib"; import { MySiteStack } from "../lib/my-site-stack"; const app = new cdk.App(); new MySiteStack(app, "MySiteDev", { env: { account: "012345678901", region: "us-east-1" }, stage: "dev", });
prod-app.js
#!/usr/bin/env node import "source-map-support/register"; import * as cdk from "aws-cdk-lib"; import { MySiteStack } from "../lib/my-site-stack"; const app = new cdk.App(); new MySiteStack(app, "MySiteProd", { env: { account: "109876543210", region: "us-east-1" }, stage: "prod", });
运行时指定入口文件:
# 合成dev堆栈 cdk synth MySiteDev -a "node dev-app.js" # 合成prod堆栈 cdk synth MySiteProd -a "node prod-app.js"
方案3:使用惰性值延迟文件路径解析(可选)
如果不想修改入口文件,可以使用CDK的Lazy值延迟文件路径的解析,只有当堆栈被实际合成时才会读取文件。注意:此方法需要确认CloudFront.FunctionCode.fromFile是否支持惰性值传递,部分CDK构造可能在初始化时就会尝试读取文件,导致此方法失效。
修改堆栈内的CloudFront函数代码:
import { Lazy } from "aws-cdk-lib"; // ... const cfViewerRequest = new cloudfront.Function(this, "MySite-CFViewerRequest", { code: cloudfront.FunctionCode.fromFile({ filePath: Lazy.string({ produce: () => `backend/functions/cf-viewer-request/build-${props.stage}/index.js` }) }), functionName: `MySite-CFViewerRequest-${props.stage}`, comment: `MySite-CFViewerRequest-${props.stage}` });
内容的提问来源于stack exchange,提问作者Aaron J Spetner
相关产品推荐
相关产品推荐

