【问题标题】:When I add this BucketDeployment to my CDK CodePipeline, cdk synth never finishes当我将此 BucketDeployment 添加到我的 CDK CodePipeline 时,cdk 合成器永远不会完成
【发布时间】:2022-03-25 17:19:26
【问题描述】:

我正在尝试使用 CDK 和 CodePipeline 构建 React 应用程序并将其部署到 S3。在CodePipeline 阶段之后,在我自己的堆栈中,我这样定义了 S3 存储桶:

const bucket = new Bucket(this, "Bucket", {
    websiteIndexDocument: "index.html",
    websiteErrorDocument: "error.html",
})

这很有效。然后我定义了我构建的 React 应用程序的部署,如下所示:

new BucketDeployment(this, "WebsiteDeployment", {
    sources: [Source.asset("./")],
    destinationBucket: bucket
})

这似乎不起作用。 BucketDeployment 的用法正确吗?

当我添加BucketDeployment 行时发生的一些奇怪的事情是cdk synthcdk deploy,它们永远不会完成,而且它们似乎在cdk.out 中生成了一个无限递归树,所以那里肯定有问题。

如果我更改为 Source.asset("./build"),我会收到错误消息:

> cdk synth
C:\Users\pupeno\Code\ww3fe\node_modules\aws-cdk-lib\core\lib\asset-staging.ts:109
      throw new Error(`Cannot find asset at ${this.sourcePath}`);
            ^
Error: Cannot find asset at C:\Users\pupeno\Code\ww3fe\build
    at new AssetStaging (C:\Users\pupeno\Code\ww3fe\node_modules\aws-cdk-lib\core\lib\asset-staging.ts:109:13)
    at new Asset (C:\Users\pupeno\Code\ww3fe\node_modules\aws-cdk-lib\aws-s3-assets\lib\asset.ts:72:21)
    at Object.bind (C:\Users\pupeno\Code\ww3fe\node_modules\aws-cdk-lib\aws-s3-deployment\lib\source.ts:55:23)
    at C:\Users\pupeno\Code\ww3fe\node_modules\aws-cdk-lib\aws-s3-deployment\lib\bucket-deployment.ts:170:83
    at Array.map (<anonymous>)
    at new BucketDeployment (C:\Users\pupeno\Code\ww3fe\node_modules\aws-cdk-lib\aws-s3-deployment\lib\bucket-deployment.ts:170:51)
    at new MainStack (C:\Users\pupeno\Code\ww3fe\infra\pipeline-stack.ts:16:9)
    at new DeployStage (C:\Users\pupeno\Code\ww3fe\infra\pipeline-stack.ts:28:26)
    at new PipelineStack (C:\Users\pupeno\Code\ww3fe\infra\pipeline-stack.ts:56:24)
    at Object.<anonymous> (C:\Users\pupeno\Code\ww3fe\infra\pipeline.ts:6:1)
Subprocess exited with error 1

这表明这是非常错误的。为什么它在我的机器上搜索build 目录?它应该在构建之后在 CodePipeline 中搜索它。

我的整个管道是:

import {Construct} from "constructs"
import {CodeBuildStep, CodePipeline, CodePipelineSource} from "aws-cdk-lib/pipelines"
import {Stage, CfnOutput, StageProps, Stack, StackProps} from "aws-cdk-lib"
import {Bucket} from "aws-cdk-lib/aws-s3"
import {BucketDeployment, Source} from "aws-cdk-lib/aws-s3-deployment"

export class MainStack extends Stack {
    constructor(scope: Construct, id: string, props?: StageProps) {
        super(scope, id, props)

        const bucket = new Bucket(this, "Bucket", {
            websiteIndexDocument: "index.html",
            websiteErrorDocument: "error.html",
        })

        new BucketDeployment(this, "WebsiteDeployment", {
            sources: [Source.asset("./")],
            destinationBucket: bucket
        })

        new CfnOutput(this, "BucketOutput", {value: bucket.bucketArn})
    }
}

export class DeployStage extends Stage {
    public readonly mainStack: MainStack

    constructor(scope: Construct, id: string, props?: StageProps) {
        super(scope, id, props)
        this.mainStack = new MainStack(this, "example")
    }
}

export class PipelineStack extends Stack {
    constructor(scope: Construct, id: string, props?: StackProps) {
        super(scope, id, props)

        const pipeline = new CodePipeline(this, id, {
            pipelineName: id,
            synth: new CodeBuildStep("Synth", {
                    input: CodePipelineSource.connection("user/example", "main", {
                        connectionArn: "arn:aws:codestar-connections:....",
                    }),
                    installCommands: [
                        "npm install -g aws-cdk"
                    ],
                    commands: [
                        "npm ci",
                        "npm run build",
                        "npx cdk synth"
                    ]
                }
            ),
        })

        const deploy = new DeployStage(this, "Staging")
        const deployStage = pipeline.addStage(deploy)
    }
}

我在 AWS 中遇到的实际错误是:

[Container] 2022/01/25 20:28:27 Phase complete: BUILD State: SUCCEEDED
[Container] 2022/01/25 20:28:27 Phase context status code:  Message: 
[Container] 2022/01/25 20:28:27 Entering phase POST_BUILD
[Container] 2022/01/25 20:28:27 Phase complete: POST_BUILD State: SUCCEEDED
[Container] 2022/01/25 20:28:27 Phase context status code:  Message: 
[Container] 2022/01/25 20:28:27 Expanding base directory path: cdk.out
[Container] 2022/01/25 20:28:27 Assembling file list
[Container] 2022/01/25 20:28:27 Expanding cdk.out
[Container] 2022/01/25 20:28:27 Skipping invalid file path cdk.out
[Container] 2022/01/25 20:28:27 Phase complete: UPLOAD_ARTIFACTS State: FAILED
[Container] 2022/01/25 20:28:27 Phase context status code: CLIENT_ERROR Message: no matching base directory path found for cdk.out

如果它很重要,cdk.json,在我的回购的根目录中,包含:

{
  "app": "npx ts-node --project infra/tsconfig.json --prefer-ts-exts infra/pipeline.ts",
  "watch": {
    "include": [
      "**"
    ],
    "exclude": [
      "README.md",
      "cdk*.json",
      "**/*.d.ts",
      "**/*.js",
      "tsconfig.json",
      "package*.json",
      "yarn.lock",
      "node_modules",
      "test"
    ]
  },
  "context": {
    "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true,
    "@aws-cdk/core:stackRelativeExports": true,
    "@aws-cdk/aws-rds:lowercaseDbIdentifier": true,
    "@aws-cdk/aws-lambda:recognizeVersionProps": true,
    "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true
  }
}

【问题讨论】:

    标签: amazon-web-services aws-cdk aws-codepipeline


    【解决方案1】:

    TL;DR 通过两个更改,管道成功部署了 React 应用程序:(1) Source.asset 需要 build 目录的完整路径和 (2) React 构建命令需要被添加到合成步骤中。

    Source.asset React build 目录的完整路径:

    new BucketDeployment(this, "WebsiteDeployment", {
        sources: [Source.asset(path.join(__dirname, './build'))], // relative to the Stack dir
        destinationBucket: bucket
    })
    

    React 构建工件通常是 .gitignored,因此 CodePipeline 需要构建 React 应用程序。我的版本有一个单独的 package.json 用于 React 应用程序,因此构建步骤 1 需要更多命令:

    synth: new pipelines.CodeBuildStep('Synth', {
      commands: [
        // build react (new)
        'cd react-app',  // path from project root to React app package.json
        'npm ci',
        'npm run build',
        'cd ..',
        // synth cdk (as in OP)
        "npm ci",
        "npm run build",
        "npx cdk synth"  // synth must run AFTER the React build step
      ],
    

    React 应用部署到 S3 URL:

    // MainStack
    new cdk.CfnOutput(this, 'WebsiteUrl', {
      value: `http://${this.websiteBucket.bucketName}.s3-website-${this.region}.amazonaws.com`,
    });
    

    (1) pipelines.CodePipelineopinionated construct for deploying Stacks。较低级别的 codepipeline.Pipeline 构造具有许多应用程序需要的功能,例如分离构建步骤和在步骤之间传递构建时环境变量(例如,使用 REACT_APP_API_URL 环境变量将 API URL 注入客户端包)。

    【讨论】:

    • 在我的解决方案中,react 应用程序是 repo 中的主要内容,如何部署它位于子目录中,但我能够根据这种情况调整此解决方案。
    【解决方案2】:

    第一个问题:

    如果我更改为 Source.asset("./build") 我会收到错误消息:... 为什么要在我的机器上搜索构建目录?

    当您在本地运行 cdk synth 时会发生这种情况。请记住,cdk synth 将始终引用运行此命令的文件系统。在本地,它将是您的机器,在管道中,它将位于 AWS CodePipeline 正在使用的容器或环境中。

    深入了解 BucketDeployment
    而且,这里发生的一些有趣的事情可能会有所帮助。 BucketDeployment 不只是从您在 BucketDeployment.sources 中引用的源中提取并将其上传到您在 BucketDeployment.destinationBucket 中指定的存储桶。根据BucketDeployment docs,资产被上传到中间存储桶,然后合并到您的存储桶中。这很重要,因为它将解释您收到的错误Error: Cannot find asset at C:\Users\pupeno\Code\ww3fe\build,因为当您运行cdk synth 时,它会期望Source.asset("./build") 中所述的目录./build 存在。

    在您的案例中尝试使用CodePipeline 构建和部署像 React 这样的单页应用程序时,这变得非常有趣。默认情况下,CodePipeline 将执行 Source 步骤,然后执行 Synth 步骤,然后是您添加的任何 wave 或阶段。添加构建您的 react 应用程序的 wave 不会立即起作用,因为我们现在看到在 Synth 步骤期间需要构建您的 react 应用程序的输出目录,因为 BucketDeployment 是如何工作的。我们需要能够使顺序为 Source -> Build -> Synth -> Deploy。如发现in this question,我们可以通过使用输入和输出来控制步骤的顺序。 CodePipeline 将对步骤进行排序以确保满足输入/输出依赖性。所以我们需要让我们的 Synth 步骤使用 Build 的输出作为其输入。

    对当前定义的管道的担忧
    我相信您当前的管道缺少CodeBuildStep,它将捆绑您的反应应用程序并将其输出到您在BucketDeployment.sources 中指定的目录。我们还需要设置输入以正确排序这些操作。以下是管道定义的一些更新,但可能需要进行一些更改才能获得正确的文件路径。另外,将BucketDeployment.sources 设置为您的应用程序包被写入的目录。

    export class PipelineStack extends Stack {
        constructor(scope: Construct, id: string, props?: StackProps) {
            super(scope, id, props)
    
            const sourceAction = CodePipelineSource.connection("user/example", "main", {
                connectionArn: "arn:aws:codestar-connections:....",
            });
    
            const buildAction = new CodeBuildStep("Build", {
                input: sourceAction, // This places Source first
                installCommands: [ "npm ci" ],
                commands: [ "npm run build" ], // Change this to your build command for your react app
                // We need to output the entire contents of our file structure
                // this is both the CDK code needed for synth and the bundled app
                primaryOutputDirectory: "./",
            });
    
            const synthAction = new ShellStep("Synth", {
                input: buildAction, // This places Synth after Build
                installCommands: [
                    "npm install -g aws-cdk"
                ],
                commands: [
                    "npm ci",
                    "npm run build",
                    "npx cdk synth"
                ],
                // Synth step must output to cdk.out
                // if your CDK code is nested, this will have to match that structure
                primaryOutputDirectory: "./cdk.out",
              });
    
            const pipeline = new CodePipeline(this, id, {
                pipelineName: id,
                synth: synthAction,
            });
    
            const deploy = new DeployStage(this, "Staging");
            const deployStage = pipeline.addStage(deploy);
        }
    }
    

    【讨论】:

      【解决方案3】:

      如果某些文件(如 json 或 html 文件)未提交到 Repo 中,请查看您的 gitignore 文件。

      【讨论】:

        猜你喜欢
        • 2022-07-13
        • 2023-01-09
        • 1970-01-01
        • 1970-01-01
        • 2017-05-04
        • 2015-09-15
        • 1970-01-01
        • 2017-01-27
        • 1970-01-01
        相关资源
        最近更新 更多