aws / aws-lambda-dotnet

Libraries, samples and tools to help .NET Core developers develop AWS Lambda functions.
Apache License 2.0
1.55k stars 476 forks source link

Can't find an example of how to create a dockerfile using the dotnet Lambda Runtime Interface Client #1146

Open rhyek opened 2 years ago

rhyek commented 2 years ago

Describe the issue

The page Deploy .NET Lambda functions with container images provides a link to "downloading" the Runtime Interface Client for dotnet, but there are no instructions on how to construct a custom Dockerfile with the client nor is there anything to "download" that I can see.

How can I write a Dockerfile with a custom base and in a straightforward way install or "download" the runtime interface client? The only documentation I have found is about using the Amazon.Lambda.RuntimeSupport as class library which does not seem like something I want to do if I can avoid it, and also it's not clear how that is meant to be used alongside my actual function code (examples don't feel complete).

Btw, I'm trying to write a Dockerfile that like the official public.ecr.aws/lambda/dotnet:6 allows for specifying executable assembles as the Lambda Handler.

I tried hacking together something like this:

FROM mcr.microsoft.com/playwright/dotnet:v1.21.0-focal
ENV LAMBDA_TASK_ROOT=/var/task
COPY --from=public.ecr.aws/lambda/dotnet:6 /var/runtime /var/runtime
COPY --from=build /app/publish/* /var/task
RUN mkdir -p /var/lang/bin
RUN ln -s /usr/bin/dotnet /var/lang/bin/dotnet
ENTRYPOINT [ "/var/runtime/Amazon.Lambda.RuntimeSupport" ]
WORKDIR /var/task
CMD ["HttpApi"] # HttpApi is a .NET 6 Minimal API (Executable Assembly)

but that results in the following error:

2022-04-17T23:11:46.773Z        fail    Amazon.Lambda.RuntimeSupport.ExceptionHandling.LambdaValidationException: Invalid lambda function handler: '/bin/bash'. The valid format is 'ASSEMBLY::TYPE::METHOD'.

Ideally this should all feel as straightforward as it does for python: The Deploy Python Lambda functions with container images has the section: image Which provides a direct link to to a README such as the one in this dotnet repo, and they have a Usage section with clear instructions of how to construct a Dockerfile:

image

Links

https://docs.aws.amazon.com/lambda/latest/dg/csharp-image.html https://github.com/aws/aws-lambda-dotnet/blob/master/README.md

rhyek commented 2 years ago

Node is pretty straightforward, too: https://www.npmjs.com/package/aws-lambda-ric

In both Nodejs and Python cases there are clear examples of what to install and what to set the ENTRYPOINT to in the dockerfile.

danielbichuetti commented 2 years ago

@rhyek Hello ! I'm sorry to get into this issue. But I know you are trying to get Playwright to work with. On the other ##repository, I've sent the sample for Dockerfile. I think I can help you here too. Please see this Docker file into this repository:

Link to Dockerfile

I think you are missing the point regarding the ENTRYPOINT. It should be a bit different.

rhyek commented 2 years ago

Thanks. My comment here is about a different strategy where I want to have a playwright docker image as base and then install the lambda runtime interface client and invoke that as the ENTRYPOINT to the container (and the lambda handler as CMD) like you do when using the official aws lambda container images.

Please see the provided python and nodejs examples.

danielbichuetti commented 2 years ago

Yes, but the way you invoked the function is not the correct one. C# handlers should be invoked the same way, using Playwright base image or the Lambda base image. There is the point I noticed on your Dockerfile last time I took a look.

Please see this:

# ref. https://docs.aws.amazon.com/lambda/latest/dg/csharp-handler.html#csharp-handler-signatures
CMD [ "Sample::Sample.Function::FunctionHandler" ]

It's on the last lines of the sample Dockerfile.

rhyek commented 2 years ago

That is not the case when running executable assemblies. Eg Top level statements, minimal apis in dot net 6.

rhyek commented 2 years ago

They way I wrote the CMD works when using the official lambda container images with my project.

danielbichuetti commented 2 years ago

Oh, I see. You are using top-level statements. So it's the assembly name, correct.

danielbichuetti commented 2 years ago

Have you taken a look at https://github.com/aws/aws-lambda-dotnet/tree/master/LambdaRuntimeDockerfiles ?

Lambda base image is a dotnet image with RIC installed. I'll take a look at building from scratch the image and not using Lambda base image.

danielbichuetti commented 2 years ago

Here is the Dockerfile with RIC being installed:

# Based on Docker image from: https://github.com/dotnet/dotnet-docker/

ARG ASPNET_VERSION=6.0.4
ARG ASPNET_SHA512=eaff93db0a4cc0adc2fc54de5e9a6e4b0844398451c06bcf6b2867471b8ed4fd0528ad04fe7150aa5ed306d5e08a5e4219c6029b96da03ad2d1c58e7a0ddacaf

ARG LAMBDA_RUNTIME_NAME=dotnet6
ARG AMAZON_LINUX=public.ecr.aws/lambda/provided:al2

FROM $AMAZON_LINUX AS base

FROM base AS builder-net6
ARG ASPNET_VERSION
ARG ASPNET_SHA512

WORKDIR /dotnet

# Install tar and gzip for unarchiving downloaded tar.gz
RUN yum install tar gzip --assumeyes

# Install the ASP.NET Core shared framework
RUN curl -SL --output aspnetcore.tar.gz https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$ASPNET_VERSION/aspnetcore-runtime-$ASPNET_VERSION-linux-x64.tar.gz \
    && aspnetcore_sha512=$ASPNET_SHA512 \
    && echo "$aspnetcore_sha512  aspnetcore.tar.gz" | sha512sum -c - \
    && tar -ozxf aspnetcore.tar.gz -C /dotnet \
    && rm aspnetcore.tar.gz

FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim AS builder
WORKDIR /src
COPY ["Libraries/src/Amazon.Lambda.RuntimeSupport", "Repo/Libraries/src/Amazon.Lambda.RuntimeSupport/"]
COPY ["Libraries/src/Amazon.Lambda.Core", "Repo/Libraries/src/Amazon.Lambda.Core/"]
COPY ["buildtools/", "Repo/buildtools/"]
RUN dotnet restore "Repo/Libraries/src/Amazon.Lambda.RuntimeSupport/Amazon.Lambda.RuntimeSupport.csproj" /p:TargetFrameworks=net6.0
WORKDIR "Repo/Libraries/src/Amazon.Lambda.RuntimeSupport"
RUN dotnet build "Amazon.Lambda.RuntimeSupport.csproj" /p:ExecutableOutputType=true /p:GenerateDocumentationFile=false /p:TargetFrameworks=net6.0 --runtime linux-x64 -c Release -o /app/build

FROM builder AS publish
RUN dotnet publish "Amazon.Lambda.RuntimeSupport.csproj" /p:ExecutableOutputType=true /p:GenerateDocumentationFile=false /p:TargetFrameworks=net6.0 -f net6.0 --runtime linux-x64 --self-contained false -p:PublishReadyToRun=true -c Release -o /app/publish
RUN apt-get update && apt-get install -y dos2unix
RUN dos2unix /app/publish/bootstrap.sh && \
    mv /app/publish/bootstrap.sh /app/publish/bootstrap && \
    chmod +x /app/publish/bootstrap

# Generate runtime-release file
ARG LAMBDA_RUNTIME_NAME
RUN export BUILD_TIMESTAMP=$(printf '%x' $(date +%s)) && \
    export LOGGING_PROTOCOL="LOGGING=amzn-stdout-tlv" && \
    export LAMBDA_RUNTIME_NAME="LAMBDA_RUNTIME_NAME=${LAMBDA_RUNTIME_NAME}" && \
    echo "NAME=dotnet\nVERSION=${ASPNET_VERSION}-${BUILD_TIMESTAMP}\n${LOGGING_PROTOCOL}\n${LAMBDA_RUNTIME_NAME}\n" > /app/publish/runtime-release

FROM base

ARG ASPNET_VERSION

ENV \
    # Export .NET version as environment variable
    DOTNET_VERSION=$ASPNET_VERSION \
    # Enable detection of running in a container
    DOTNET_RUNNING_IN_CONTAINER=true \
    # Lambda is opinionated about installing tooling under /var
    DOTNET_ROOT=/var/lang/bin \
    # Don't display welcome message on first run
    DOTNET_NOLOGO=true \
    # Disable Microsoft's telemetry collection
    DOTNET_CLI_TELEMETRY_OPTOUT=true

COPY --from=builder-net6    /dotnet         ${DOTNET_ROOT}
COPY --from=publish         /app/publish    ${LAMBDA_RUNTIME_DIR}

# Entrypoint is inherited from public.ecr.aws/lambda/provided

Maybe you could try building it from scratch following these guidelines.

normj commented 2 years ago

I agree it is not initiative on how to add the .NET Lambda RIC to a base image. We distribute Amazon.Lamba.RuntimeSupport as a NuGet package where it is a class library. Seeing how NuGet is really good at delivering project dependencies. I'm open to suggests on how we can deliver Amazon.Lambda.RuntimeSupport as a console application that can be easily be baked into a container besides cloning this repo and building the project our our Docker files do.

Out of curiosity, I hadn't heard of Playwright before this issue. What is the use case of using it with Lambda?

danielbichuetti commented 2 years ago

@normj Hi! Playwright is a full-fledged testing tool for web apps. It's a great plus on end-to-end testing. It supports many browsers (not emulating, but “connecting” to then), supports local and remote testing, even mobile. Furthermore, it has a simple install and test workflow. Even has one container image ready to use.

Tests that act exactly like user on frontend. But there are other use cases beyond testing. Making usage of it for data mining is amazing. So imagine the integration of AWS Lambda and Playwright. Companies could deploy a completely serverless testing solution for their web apps. Another company could build a data mining solution on top of AWS Lambda, scaling to thousands of workers and going to zero after work finishes. Indeed, Scraping Ant has a post about doing this kind of job (not using .NET, but Node.JS). So we could build a complete web ETL on top of Lambda.

https://scrapingant.com/blog/how-to-run-playwright-on-aws-lambda

I think it's a project that Microsoft should talk more about, it's way better than old Selenium.

rhyek commented 2 years ago

Good answer by @danielbichuetti. In my case i am just doing some minor web scraping in response to a trigger (Api Gateway, currently, but later on it will be SNS).

@normj I'm new to this project so I'll just suggest what would make sense to me based on the documentation I saw for the nodejs and python projects I linked.

Perhaps you could take advantage of github releases and publish with each release a runtime-dependent dll as binary. Then in the main README.md you can show an example of how to create a Dockerfile where you download the latest binary using curl and set that as the ENTRYPOINT along with whatever environment variables are needed.

That would provide the best developer experience, I believe. In the meantime, you mention cloning the repo, building ourselves, and using that. What would also be useful is a brief example of what that would look like including build commands and then copying that to our container image and, again, setting the ENTRYPOINT, environment variables, etc.

zeusongit commented 1 year ago

Hey, Were you able to install the runtime client so that it works with a lambda? I am using this image mcr.microsoft.com/dotnet/sdk:6.0 and want to install Runtime Client on it to make it compatible with lambdas as well.

brianfeucht commented 1 month ago

Anyone able to figure out an easy way to do this? It looks like I might be able to just steal the LAMBDA_RUNTIME_DIR from the published docker image. Since this github issue has been silent for so long, I assume someone figured this out.