My API call is throwing a 500 hundred error because it cannot locate the dll file. The dll file exists inside the published folder. I can see it in the container shell (Submissions.ITCollect.API.Models.dll). This dll file is located inside of the /lib folder. Also, including my dockerfile below. I'm unsure of how to correct this issue.
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS base
ARG buildconfig=1.0.0
ARG api_version=1.0.0
ARG package_version=1.0.0
ARG solutionFile=Folio.Portfolio.Api.sln
ARG api_project=Portfolio.API
ARG api_dll=Portfolio.API.dll
ENV api_dll="${api_dll}"
ARG run_test
EXPOSE 80
WORKDIR /
COPY $solutionFile .
WORKDIR /src
COPY /src .
WORKDIR /
# set the path so that the dotnet tool can be found
ENV PATH="${PATH}:/root/.dotnet/tools"
RUN if [ "${run_test}" = "test" ]; then \
# install dotcover tool globally
dotnet tool install JetBrains.dotCover.GlobalTool --global ; \
# run the unit tests with code coverage
dotnet dotcover test $solutionFile --dcReportType=HTML --dcAttributeFilters=System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute ; \
else \
# create mock coverage files so that we dont need to handle conditional copy
mkdir dotCover.Output; \
echo "mock report" >> /dotCover.Output/coverage.html ;\
echo "mock report" >> dotCover.Output.html ;\
fi
WORKDIR /src/$api_project
RUN dotnet build -c Release -o /app/build -p:Version=$api_version
RUN dotnet pack -o /app/publish -p:PackageVersion=$package_version
RUN if [ "${buildconfig}" = "Debug" ]; then \
dotnet publish "${api_project}.csproj" -o /app/publish -c Debug; \
else \
dotnet publish "${api_project}.csproj" -o /app/publish -c Release; \
fi
COPY /lib /app/publish/
FROM base AS final
WORKDIR /app
COPY --from=base /app/publish .
COPY --from=base /dotCover.Output.html ./dotCover.Output.html
COPY --from=base /dotCover.Output ./dotCover.Output
ENTRYPOINT ["/bin/bash", "-c", "exec dotnet ${api_dll}"]
Related
I have four projects in one solution:
App.Data - class library
App.Core - class library
App.LambdaOne - AWS Lambda project
App.LambdaTwo - AWS Lambda project
The solution structure looks like this:
Solution:
App.Data
App.Core
App.LambdaOne
Dockerfile
App.LambdaTwo
Dockerfile
I am trying to create two docker images for App.LambdaOne and App.LambdaTwo, so I can deploy them separately.
So far, I have got a simple lambda project without any dependent project works using the Dockfile below.
Docker command in App.LambdaSimple's directory
docker build -t LambdaSimpleImage .
Dockerfile
FROM public.ecr.aws/lambda/dotnet:6 AS base
FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim as build
WORKDIR /src
COPY ["App.LambdaSimple.csproj", "App.LambdaSimple/"]
RUN dotnet restore "App.LambdaSimple.csproj"
WORKDIR "/src/App.LambdaSimple"
COPY . .
RUN dotnet build "App.LambdaSimple.csproj" --configuration Release --output /app/build
FROM build AS publish
RUN dotnet publish "App.AWSLambdaOne.csproj" \
--configuration Release \
--runtime linux-x64 \
--self-contained false \
--output /app/publish \
-p:PublishReadyToRun=true
FROM base AS final
WORKDIR /var/task
COPY --from=publish /app/publish .
CMD ["App.LambdaSimple::App.LambdaSimple.Function::FunctionHandler"]
But the tricky part is that App.LambdaOnedepend on App.Data and App.Core. I tried to modify the working sample to deploy App.LambdaOne, but no luck so far. By running the same Docker command. Errors occurred, I have added those to the comments
FROM public.ecr.aws/lambda/dotnet:6 AS base
FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim as build
WORKDIR /src
COPY ["App.AWSLambdaOne.csproj", "App.AWSLambdaOne/"]
#Error: cannot find App.Data.csproj
COPY ["App.Data.csproj", "App.Data/"]
RUN dotnet restore "App.LambdaOne.csproj"
WORKDIR "/src/App.AWSLambdaOne"
COPY . .
RUN dotnet build "App.AWSLambdaOne.csproj" --configuration Release --output /app/build
FROM build AS publish
RUN dotnet publish "App.AWSLambdaOne.csproj" \
--configuration Release \
--runtime linux-x64 \
--self-contained false \
--output /app/publish \
-p:PublishReadyToRun=true
FROM base AS final
WORKDIR /var/task
COPY --from=publish /app/publish .
CMD ["App.AWSLambdaOne::App.AWSLambdaOne.Function::FunctionHandler"]
I can understand why the error occurred, which is due to the Docker Context being under App.AWSLambdaOne. But cannot find any solution to fix it.
A partial solution I found is to have the Dockerfile at the Solution level, so Docker context includes all projects when building it. However, it does not fit my purpose since I want to build two images for different projects.
I have been searching for a clue in the last two days, it is driving me crazy. May I ask if it is possible to achieve what I am after with the existing project structure? If not can anyone please point me to the right direction?
THANKS A LOT in advance!
You can't access host files outside the build context, so you must move the build context up a directory to be able to access the code for the other projects. So to start, let's change the build command to
docker build -t LambdaSimpleImage ..
Now we need to adjust the Dockerfile so it fits the new context
FROM public.ecr.aws/lambda/dotnet:6 AS base
FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim as build
WORKDIR /src
COPY ["App.LambdaSimple/App.LambdaSimple.csproj", "App.LambdaSimple/"]
COPY ["App.Data/App.Data.csproj", "App.Data/"]
COPY ["App.Core/App.Core.csproj", "App.Core/"]
RUN dotnet restore "App.LambdaSimple/App.LambdaSimple.csproj"
COPY . .
RUN dotnet build "App.LambdaSimple/App.LambdaSimple.csproj" --configuration Release --output /app/build
FROM build AS publish
RUN dotnet publish "App.LambdaSimple/App.LambdaSimple.csproj" \
--configuration Release \
--runtime linux-x64 \
--self-contained false \
--output /app/publish \
-p:PublishReadyToRun=true
FROM base AS final
WORKDIR /var/task
COPY --from=publish /app/publish .
CMD ["App.LambdaSimple::App.LambdaSimple.Function::FunctionHandler"]
I have this Dockerfile
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app
EXPOSE 5000
RUN apt-get -qq update && apt-get -qqy --no-install-recommends install wget gnupg \
git \
unzip
RUN curl -sL https://deb.nodesource.com/setup_6.x | bash -
RUN apt-get install -y nodejs
WORKDIR /app
#EXPOSE 5001
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["mediere.csproj", "."]
RUN dotnet restore "./mediere.csproj"
COPY . .
WORKDIR "/src/."
RUN apt-get -qq update && apt-get -qqy --no-install-recommends install wget gnupg \
git \
unzip
RUN curl -sL https://deb.nodesource.com/setup_6.x | bash -
RUN apt-get install -y nodejs
#RUN npm install zone.js#0.11.3 --save
WORKDIR /app
RUN dotnet build "mediere.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "mediere.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "mediere.dll"]
and running docker-compose up throws me this error:
=> ERROR [build 11/11] RUN dotnet build "mediere.csproj" -c Release -o /app/build 2.5s
------
> [build 11/11] RUN dotnet build "mediere.csproj" -c Release -o /app/build:
#23 2.241 Microsoft (R) Build Engine version 16.11.2+f32259642 for .NET
#23 2.241 Copyright (C) Microsoft Corporation. All rights reserved.
#23 2.241
#23 2.245 MSBUILD : error MSB1009: Project file does not exist.
#23 2.245 Switch: mediere.csproj
------
executor failed running [/bin/sh -c dotnet build "mediere.csproj" -c Release -o /app/build]: exit code: 1
ERROR: Service 'web' failed to build : Build failed
and I don't see why.
I'm using a default ASP.Net core & Angular template from VS 2019 and I'm trying to dockerize it.
Here's the folder structure:
why am I getting this error?
Am I missing something obvious?
It's because you're setting the current working directory to /app right before building the project:
WORKDIR /app
The project doesn't exist in that directory. There should be no need to have that line at all because the working directory before this instruction gets executed is the correct one.
There are two ways to run a dot .net core application on a Linux based container.
Getting a base image which has .net core framework install (on top of Linux image) & then copy code using dotnet publish.
Use a Linux base image and then copy the dot net runtime & code using dotnet publish --self-contained option.
--self-contained [true|false]
Publishes the .NET runtime with your application so the runtime doesn't need to be installed on the target machine. Default is true if a runtime identifier is specified and the project is an executable project (not a library project).
Is there any advantage of using one option over other ?
The self-contained and trimmed app can be published in a container that only has runtime dependencies for dotnet without the need for the whole runtime. This will lead to much smaller images overall.
mcr.microsoft.com/dotnet/runtime:6.0-alpine -> 79.7MB
mcr.microsoft.com/dotnet/runtime-deps:6.0-alpine -> 10.1MB
For one of my apps:
dotnet publish -c Release -r linux-musl-x64 --self-contained false -> 30MB
dotnet publish -c Release -r linux-musl-x64 --self-contained true /p:PublishTrimmed=true -> 74.6MB
Final image:
Using runtime base: 120MB
Using runtime-deps base and trimming: 96.9MB
You can also cache the non-app binaries in a layer so when only app code changes only the layer with the app binaries will be pushed. Here is my setup for the whole context:
# Dockerfile-build
ARG BASE=mcr.microsoft.com/dotnet/sdk:6.0-alpine
FROM ${BASE} AS build
RUN apk add --no-cache rsync
WORKDIR /src
COPY *.sln .
COPY **/*.csproj ./
COPY **/**/*.csproj ./
COPY **/**/**/*.csproj ./
RUN dotnet sln list | grep ".csproj" | while read -r line; do mkdir -p $(dirname $line); mv $(basename $line) $(dirname $line); done;
WORKDIR /src
# The build has a memory leak if the proxy is not specified
# https://stackoverflow.com/questions/72885244/net-6-building-solution-at-docker-conteiner-taking-a-long-time-and-consuming
RUN export http_proxy=proxy:80
RUN export https_proxy=$http_proxy
COPY .cache* .cache
RUN --mount=type=cache,target=/root/.nuget/packages \
test -d .cache && rsync -a .cache/ /root/.nuget/packages/ && rm -rf .cache && echo "Cache applied"; \
dotnet restore -r linux-musl-x64
COPY . .
# We cannot use the -r flag for a global sln build https://github.com/dotnet/sdk/issues/14281#issuecomment-876510589
# The UnitTests project depends on all the other projects so this command builds the whole thing
RUN --mount=type=cache,target=/root/.nuget/packages \
dotnet build -c Release -r linux-musl-x64 -f net6.0 --no-restore Tests/IntegrationTests
FROM build as test
RUN --mount=type=cache,target=/root/.nuget/packages \
dotnet test -c Release -r linux-musl-x64 -f net6.0 --no-restore
FROM build as cache-prep
RUN --mount=type=cache,target=/root/.nuget/packages \
mkdir -p /packages && cp -R /root/.nuget/packages/* /packages
FROM scratch as cache
COPY --from=cache-prep /packages .
# To not trigger the stages above
FROM build
# Dockerfile-publish
ARG BASE=build
FROM ${BASE}
ONBUILD ARG DIR
ONBUILD ARG APP_NAME
ONBUILD ARG OUT_DIR=./out
# Set to 0 to disable trimming and layered publish
ONBUILD ARG TRIM=1
ONBUILD WORKDIR /src/${DIR}/${APP_NAME}
ONBUILD RUN --mount=type=cache,target=/root/.nuget/packages \
dotnet publish -c Release -r linux-musl-x64 --no-restore --no-dependencies \
$(test ${TRIM} -eq 1 && echo '--self-contained -p:PublishTrimmed=true') -o ${OUT_DIR}
# Move the app binaries to a different folder so we can try to cache the dependencies in a layer.
# That might not work for the trimmed build but if not much changed, this saves a lot of container space.
ONBUILD RUN test ${TRIM} -eq 1 \
&& mkdir -p ./app-bin \
&& mv ${OUT_DIR}/${APP_NAME}* ./app-bin \
&& mv ${OUT_DIR}/Kernel* ./app-bin \
|| echo 0
# Dockerfile-runtime
ARG BASE=publish
ARG RUNTIME=mcr.microsoft.com/dotnet/runtime-deps:6.0-alpine
# Name the layer so it can be used in a COPY command
FROM ${BASE} as publish
# Build runtime image
FROM ${RUNTIME}
# As per https://www.abhith.net/blog/docker-sql-error-on-aspnet-core-alpine/
RUN apk add --no-cache icu-libs tzdata \
&& cp /usr/share/zoneinfo/Europe/Prague /etc/localtime
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
ENV TZ="Europe/Prague"
RUN adduser --disabled-password --home /app --gecos '' dotnetuser && chown -R dotnetuser /app
USER dotnetuser
ONBUILD ARG DIR
ONBUILD ARG APP_NAME
ONBUILD ENV APP_NAME=${APP_NAME}
ONBUILD ARG OUT_DIR=./out
ONBUILD WORKDIR /app
# TODO: use this once https://github.com/moby/buildkit/issues/816 is resolved
# For now just add the COPY commands to the project dockerfile
#ONBUILD COPY --from=publish /src/${DIR}/${APP_NAME}/out .
#ONBUILD COPY --from=publish /src/${DIR}/${APP_NAME}/app-bin .
ONBUILD ENTRYPOINT "./${APP_NAME}"
# project Dockerfile
ARG DIR=Service
ARG APP_NAME=XXXService
ARG BUILD=build
ARG PUBLISH=publish
ARG RUNTIME=runtime
FROM ${BUILD} as build
FROM ${PUBLISH} as publish
FROM ${RUNTIME}
# TODO: use ONBUILD in `publish` once https://github.com/moby/buildkit/issues/816 is resolved
COPY --from=publish /src/${DIR}/${APP_NAME}/out .
COPY --from=publish /src/${DIR}/${APP_NAME}/app-bin .
To build and publish you simply prepare the base images by
docker build -t build -f Dockerfile-build .
docker build -t publish -f Dockerfile-publish .
docker build -t runtime -f Dockerfile-runtime .
you test the app by
docker build -f Dockerfile-build --target test .
you can cache the nuget dependencies by exporting them from the image (in a CI environment for example)
docker buildx create --name buildx || true
docker build -f Dockerfile-build --builder buildx --target cache -o type=local,dest=./.cache .
and you build the final image by
docker build -t "$TAG" Services/XXXService
Hope this helps a little 🙂
Basically, you don't want self-contained as long as you don't need to. The package will stay as small as possible which is what you want. Only when you're not sure the targeting platform will be able to host your system because of missing runtime components, you can add these runtime components with the self-contained option.
You can install the dotnet runtime on several platforms including Linux. If you have control over doing so, I'd go for that option.
I have this Dockerfile for .net core
FROM mcr.microsoft.com/dotnet/core/aspnet:3.0-buster-slim AS base
WORKDIR /app
EXPOSE 8080
FROM mcr.microsoft.com/dotnet/core/sdk:3.0-buster AS build
WORKDIR /src
COPY ["./WebApplication1.csproj", "WebApplication1/"]
RUN dotnet restore "WebApplication1/WebApplication1.csproj"
COPY . .
WORKDIR "/src/WebApplication1"
RUN dotnet build "WebApplication1.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "WebApplication1.csproj" -c Release -o /app/publish --self-contained
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "WebApplication1.dll"]
When I run the docker I get the error:
A fatal error was encountered. The library 'libhostpolicy.so' required to execute the application was not found in '/app/'.
I read this, and it is not related to my problem
--self-contained require a -r (--runtime)
-r, --runtime The target runtime to publish for. This is used when creating a self-contained deployment.
The default is to publish a framework-dependent application.
I'd start with changing the publish command in Dockerfile to this:
RUN dotnet publish "WebApplication1.csproj" -c Release -o /app/publish --self-contained --runtime linux-64
New Part:
I run the below command
$ RUN dotnet publish "WebApplication1.csproj" -c Release -o /app/publish --self-contained
and I got below error:
/usr/local/share/dotnet/sdk/3.0.100/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.RuntimeIdentifierInference.targets(127,5): error NETSDK1031: It is not supported to build or publish a self-contained application without specifying a RuntimeIdentifier. Please either specify a RuntimeIdentifier or set SelfContained to false. [/Users/ziaullahkhan/Code/SelfContainedApp/SelfContainedApp.csproj]
I changed the command to this:
$ dotnet publish -c Release --self-contained --runtime linux-x64 -o out
and it published everything successfully.
I then built the docker image. Started shell on the container
$ docker run -it khanziaullah/sca bash
root#eb5dc8083aa3:/app/out# ls -l libhostpolicy.so
-rwxr--r-- 1 root root 327384 Sep 13 15:21 libhostpolicy.so
root#eb5dc8083aa3:/app/out# dotnet SelfContainedApp.dll
Hello World!
so I see the libhostpolicy.so file.
Try starting a shell and look for your build and copy location for all the dependencies. COPY command might have gone wrong.
Try to add the following code to the csproj
<PackageReference Include="Microsoft.NETCore.App" Version="1.0.1" />
I'm trying to convert HTML to PDF using wkhtmltopdf in my ASP.NET Core app.
I've added a wkhtmltopdf.exe to my project and marked as a Copy to an output to always.
Here's my code:
var htmlContent = receiptBody;
var wkhtmltopdf = new FileInfo(#"/app/Configuration/Wkhtmltopdf/wkhtmltopdf.exe");
var converter = new HtmlToPdfConverter(wkhtmltopdf);
var pdfBytes = converter.ConvertToPdf(htmlContent);
The file has been founded but on:
var pdfBytes = converter.ConvertToPdf(htmlContent);
I'm getting an error:
System.Exception: Cannot generate PDF: Broken pipe --->
System.IO.IOException: Broken pipe
I run my app using Docker and here is my dockerfile:
FROM microsoft/aspnetcore:1.1.2 ARG source WORKDIR /app
ENV ASPNETCORE_URLS http://project-test:80
EXPOSE 80
COPY ${source:-obj/Docker/publish} .
RUN apt-get update
RUN apt-get install-y libgdiplus
ENTRYPOINT ["dotnet", "ProjectTest.dll"]
Maybe should somehow install wkhtmltopdf for linux?
This piece of code works fine for me with the latest .net core 6.0.1 image:
FROM mcr.microsoft.com/dotnet/aspnet:6.0.1-bullseye-slim AS base
RUN apt update
RUN apt install -y libgdiplus
RUN ln -s /usr/lib/libgdiplus.so /lib/x86_64-linux-gnu/libgdiplus.so
RUN apt-get install -y --no-install-recommends zlib1g fontconfig libfreetype6 libx11-6 libxext6 libxrender1 wget gdebi
RUN wget https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.5/wkhtmltox_0.12.5-1.stretch_amd64.deb
RUN gdebi --n wkhtmltox_0.12.5-1.stretch_amd64.deb
RUN apt install libssl1.1
RUN ln -s /usr/local/lib/libwkhtmltox.so /usr/lib/libwkhtmltox.so
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:6.0.1 AS build
WORKDIR /src
COPY ["docgen/docgen.csproj", "docgen/"]
RUN dotnet restore "docgen/docgen.csproj"
COPY . .
WORKDIR "/src/docgen"
RUN dotnet build "docgen.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "docgen.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "docgen.dll"]
It's all about platform and binaries.
You can generate your own docker image with WKHTMLTOPDF and use it.
In my opinion, it is a cleaner way.
Here is a simple example:
Download WkHtmlToPdf - Debian 10 (Buster) - amd64 file:
https://wkhtmltopdf.org/downloads.html
Put downloaded .deb file into "deps" subfolder below where the Dockerfile is
Add WkHtmlToPdf installation into the Dockerfile.
Make sure the download .deb filename matches the line:
ENV WKHTMLTOX wkhtmltox_0.12.6-1.buster_amd64.deb
And ensure the .NET Core images use Debian Buster.
Dockerfile:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /app
## copy csproj and restore as distinct layers
COPY *.sln .
COPY aspnetapp/*.csproj ./aspnetapp/
RUN dotnet restore
## copy everything else and build app
COPY aspnetapp/. ./aspnetapp/
WORKDIR /app/aspnetapp
RUN dotnet publish -c Release -o out
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS runtime
WORKDIR /app
## START - INSTALL WKHTMLTOPDF
ENV WKHTMLTOX wkhtmltox_0.12.6-1.buster_amd64.deb
ENV BUILD_PACKAGES build-essential
ENV MAIN_PACKAGES fontconfig libfreetype6 libjpeg62-turbo libxext6 libpng16-16 libx11-6 libxcb1 libxrender1 xfonts-75dpi xfonts-base
COPY deps/$WKHTMLTOX ./
RUN set -xe \
&& apt-get update -qq \
&& apt-get install --no-install-recommends -yq $BUILD_PACKAGES $MAIN_PACKAGES \
&& dpkg -i ${WKHTMLTOX} \
&& apt-get remove -y $BUILD_PACKAGES \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& rm -rf ${WKHTMLTOX} \
&& truncate -s 0 /var/log/*log
## END - INSTALL WKHTMLTPDF
COPY --from=build /app/aspnetapp/out ./
ENTRYPOINT ["dotnet", "aspnetapp.dll"]
The INSTALL WKHTMLTOPDF code is based on:
https://gist.github.com/berkayakcay/1e4f0a355437f0db9c94935aa52283d2
The Dockerfile itself is based on:
https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/docker/building-net-docker-images?view=aspnetcore-3.1#the-dockerfile-1
You should download files wkhtmltopdf for linux and copy to output publish.
Also, give permissions to these files with this step in dockerfile
RUN chmod 755 ./wkhtmltopdf/Linux/wkhtmltopdf
RUN chmod 755 ./wkhtmltopdf/Linux/wkhtmltoimage
Be sure that you add the line after WORKDIR/app:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /app
RUN apt-get update -qq && apt-get -y install libgdiplus libc6-dev
For more info check https://github.com/fpanaccia/Wkhtmltopdf.NetCore