Today, I spent some time trying to publish a .NET 6 AWS Serverless Application that consisted of:
- A few serverless functions that I wanted to run using the default CPU architecture, x64 (or using AWS terminology, x86_64)
- A few other serverless functions that I wanted to run on arm64 architecture (on Graviton2 CPUs)
I made sure that each a configuration for functions targeting arm64 differed by setting the following non-default property in the serverless.template
file:
// (...)
"Properties": {
"Runtime": "dotnet6",
"MemorySize": 512,
"Architectures": [
"arm64"
],
// (...)
}
Code language: JSON / JSON with Comments (json)
A problem: Internal Server Error from arm64 lambdas
Unfortunately, after I published the application, I noticed that:
- The functions that targeted the default x86_64 architecture worked flawlessly
- The functions that targeted arm64 responded with Internal Server Error. Looking at the logs in CloudWatch I noticed the exception:
Unhandled exception. System.BadImageFormatException: Could not load file or assembly 'Amazon.Lambda.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. An attempt was made to load a program with an incorrect format.
Code language: JavaScript (javascript)
Such an error might suggest a mismatch between the runtime environment supported by the application and the actual runtime environment.
What happened when I targeted different architectures in a single project?
The hint came from the publish operation logs. I noticed the tooling used the following command to prepare the deployment package for the first of my functions (which was x64):
dotnet publish "D:\Projekty\Benchmark.AwsServerless\Benchmark.AwsServerless." --output "D:\Projekty\Benchmark.AwsServerless\Benchmark.AwsServerless.\bin\Release\net6.0\publish" --configuration "Release" --framework "net6.0" /p:GenerateRuntimeConfigurationFiles=true --runtime linux-x64 --self-contained False
What happened next was that the compiled artifacts got packed into a ZIP package and uploaded to AWS S3 storage. However, the same artifacts were set to be re-used by subsequent functions as well. Even those configured to target arm64! Hence the mismatch in runtime.
How I solved the issue
This looks like an assumption hard-coded in the tooling that architecture is the same for all Lambdas within the project.
If that’s the case, the easiest solution seems to just split the C# project into two separate projects, one containing only x64 Lambdas, and the other containing only ARM64 Lambdas. But the projects can still share a code via a class library.
When I did that, the deployment tooling properly auto-detected the linux-arm64
target in the arm64 project, and no other code changes were needed beside the one in the serverless.template
file mentioned earlier.
No comments yet, you can leave the first one!