首页 > 解决方案 > AWS CDK CI/CD 管道 - 部署的 Lambda 返回 ClassNotFoundException

问题描述

我正在尝试使用 AWS CDK 的 Lambda 构建 CI/CD 管道。我们在这里使用一个 gradle 项目。此外,我遵循了示例文档。我们定义了两个堆栈,它们是 APIStack 和 ApiStackPipeline,其中 APIStack 由处理,Lambda_BuildApiStackPipeline 由 处理CDK_BUILD

我们正在 ApiStack 中初始化 Lambda 函数,例如;

final Function contactFunction = Function.Builder.create(this, "contactFunction").role(roleLambda)
    .runtime(Runtime.JAVA_8)
    .code(lambdaCode)
    .handler("com.buraktas.contact.main.ContactLambda::handleRequest")
    .memorySize(512)
    .timeout(Duration.minutes(1))
    .environment(environment)
    .description(Instant.now().toString()).build();

在这种情况下,我们设置的lambdaCode参数与this.lambdaCode = new CfnParametersCode();文档中显示的相同(即使我不确定它是如何得到的)。

现在我们将它传递lambdaCode给 ApiStackPipeline,它看起来像;

IRepository repository = Repository.fromRepositoryName(this, repoName, repoName);
IBucket bucket = Bucket.fromBucketName(this, "codepipeline-api", "codepipeline-api");

PipelineProject lambdaBuild = PipelineProject.Builder.create(this, "ApiBuild")
    .buildSpec(BuildSpec.fromSourceFilename("lambda-buildspec.yml"))
    .environment(BuildEnvironment.builder().buildImage(LinuxBuildImage.STANDARD_4_0).build())
    .build();

PipelineProject cdkBuild = PipelineProject.Builder.create(this, "ApiCDKBuild")
    .buildSpec(BuildSpec.fromSourceFilename("cdk-buildspec.yml"))
    .environment(BuildEnvironment.builder().buildImage(LinuxBuildImage.STANDARD_4_0).build())
    .build();

Artifact sourceOutput = new Artifact();
Artifact cdkBuildOutput = new Artifact("CdkBuildOutput");
Artifact lambdaBuildOutput = new Artifact("LambdaBuildOutput");

Pipeline.Builder.create(this, "ApiPipeline")
    .stages(Arrays.asList(
        StageProps.builder()
            .stageName("Source")
            .actions(Arrays.asList(
                CodeCommitSourceAction.Builder.create()
                    .actionName("Source")
                    .repository(repository)
                    .output(sourceOutput)
                    .build()))
            .build(),
        StageProps.builder()
            .stageName("Build")
            .actions(Arrays.asList(
                CodeBuildAction.Builder.create()
                    .actionName("Lambda_Build")
                    .project(lambdaBuild)
                    .input(sourceOutput)
                    .outputs(Arrays.asList(lambdaBuildOutput)).build(),
                CodeBuildAction.Builder.create()
                    .actionName("CDK_Build")
                    .project(cdkBuild)
                    .input(sourceOutput)
                    .outputs(Arrays.asList(cdkBuildOutput))
                    .build()))
            .build(),
        StageProps.builder()
            .stageName("Deploy")
            .actions(Arrays.asList(
                CloudFormationCreateUpdateStackAction.Builder.create()
                    .actionName("Lambda_CFN_Deploy")
                    .templatePath(cdkBuildOutput.atPath("ApiStackAlfa.template.json"))
                    .adminPermissions(true)
                    .parameterOverrides(lambdaCode.assign(lambdaBuildOutput.getS3Location()))
                    .extraInputs(Arrays.asList(lambdaBuildOutput))
                    .stackName("ApiStackAlfaDeployment")
                    .build()))
            .build()))
    .artifactBucket(bucket)
    .restartExecutionOnUpdate(true)
    .build();

这里我还分享了 *-buildspec.yml 文件;

lambda-buildspec.yml

version: 0.2

phases:
  install:
    runtime-versions:
      java: corretto8
  build:
    commands:
      - echo current directory `pwd`
      - echo building gradle project on `date`
      - ./gradlew clean build
artifacts:
  files:
    - build/distributions/src.zip
  discard-paths: yes

cdk-buildspec.yml

version: 0.2

phases:
  install:
    runtime-versions:
      nodejs: 10
      java: corretto8
    commands:
      - echo installing aws-cdk on `date`
      - npm install aws-cdk
  build:
    commands:
      - echo current directory `pwd`
      - ls -l
      - echo building cdk project on `date`
      - ./gradlew clean build
      - npx cdk synth -o dist
  post_build:
    commands:
      - echo listing files after build under dist
      - ls -l dist
artifacts:
  files:
    - ApiStackAlfa.template.json
  base-directory: dist

这是我得到的异常堆栈跟踪

Class not found: com.buraktas.api.main.Lambda: java.lang.ClassNotFoundException
java.lang.ClassNotFoundException: com.buraktas.api.main.Lambda
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)

最后在这里我分享了一个简化版本的项目结构,如果有帮助的话

├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── buraktas
│   │   │           └── api
│   │   │               ├── main
│   │   │               │   ├── ApiMain.java
│   │   │               │   ├── ApiPipelineStack.java
│   │   │               │   ├── ApiStack.java
│   │   │               │   └── Lambda.java
│   │   │               └── repository
│   │   │                   └── Repository.java
│   │   └── resources
│   │       └── log4j.properties
│   └── test
│       ├── java
│       │   ├── DocumentTest.java
│       │   └── JsonWriterSettingsTest.java
│       └── resources
│           └── request.http

看起来一切正常,管道已成功创建,并且 Source -> Build -> Deploy 步骤运行顺利。但是,当我触发我的 lambda 函数时,我得到了 ClassNotFoundException。我尝试使用 .zip 或 .jar (fat jar) 工件,但没有任何改变。

谢谢你的帮助。

标签: amazon-web-servicesaws-code-deployaws-codepipelineaws-cdk

解决方案


我发现问题正在发生,因为 CodeBuild 从给定的工件创建了一个 zip。这意味着将有一个包含src.zip自身的 zip 文件,其中包含正确的项目构建文件。由于这个主 zip 文件正在上传到 Lambda,它无法找到处理程序定义,因此它会抛出 ClassNotFoundException。但是,示例文档和构建规范的 AWS CodeBuild 参考文档中均未提及此额外的 zip 过程。我们需要手动解压缩 zip 文件的内容并将其作为工件输出。这是我们的 buildspec.yml 的最终版本。此外,如果您不想处理解压缩内容,那么您需要配置您的构建工具(我们在这里使用 gradle)在运行构建命令后不将内容压缩到 zip 文件中。

version: 0.2

phases:
  install:
    runtime-versions:
      java: corretto8
  build:
    commands:
      - echo current directory `pwd`
      - echo building gradle project on `date`
      - ./gradlew clean build
  post_build:
    commands:
      - mkdir build/distributions/api
      - unzip build/distributions/api.zip -d build/distributions/api
artifacts:
  files:
    - '**/*'
  base-directory: build/distributions/api

推荐阅读