amazon-web-services - AWS CDK CI/CD 管道 - 部署的 Lambda 返回 ClassNotFoundException
问题描述
我正在尝试使用 AWS CDK 的 Lambda 构建 CI/CD 管道。我们在这里使用一个 gradle 项目。此外,我遵循了示例文档。我们定义了两个堆栈,它们是 APIStack 和 ApiStackPipeline,其中 APIStack 由处理,Lambda_Build
ApiStackPipeline 由 处理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) 工件,但没有任何改变。
谢谢你的帮助。
解决方案
我发现问题正在发生,因为 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
推荐阅读
- ffmpeg - FFMPEG缩放平移多个图像
- vb.net - vb .net基于同一位置的两个列表框重命名文件
- python - flask-pymongo:我把 mongo 放在一个单独的 python 文件中,它变成了 NoneType
- vue.js - 如何扩展 Vuetify 组件并保留 ref?
- azure - 如何将多个 Web 应用程序添加到单个网络中,以便连接到一个 Web 应用程序的 vnet 也可以访问其他 Web 应用程序
- d3.js - 如何将 csv 文件加载到 nuxt vue 组件中
- php - 我怎样才能获得永久IP
- python - 每 5 分钟删除一次重复项
- reactjs - 如何在表格单元格中添加图像?
- c# - 如何将带有fromdata的json对象发送到mvc控制器