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.
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"]
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}"]
I have private Nuget feed from Azure Artifact that I want to use inside docker, but I am getting the following error:
/usr/share/dotnet/sdk/5.0.402/NuGet.targets(131,5): error : Unable to load the service index for source http://test/DefaultCollection/test/_packaging/company/nuget/v3/index.json. [/src/App1/App1.csproj]
/usr/share/dotnet/sdk/5.0.402/NuGet.targets(131,5): error : NTLM authentication is not possible with default credentials on this platform. [/src/App1/App1.csproj]
The command '/bin/sh -c dotnet restore "App1/App1.csproj" --configfile nuget.config' returned a non-zero code: 1
##[error]The command '/bin/sh -c dotnet restore "App1/App1h.csproj" --configfile nuget.config' returned a non-zero code: 1
##[error]/usr/bin/docker failed with return code: 1
I tried few thing, but I am getting same issues. Do you have any suggestion how to fix this?
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
#############
## Stage 1 ##
#############
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
#Personal Access Token to Authenticate To Artifactory
ARG FEED_URL
ARG PAT
RUN apt-get update && apt-get install -y --no-install-recommends gss-ntlmssp
#ENV DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER=0
#ENV NUGET_CREDENTIALPROVIDER_SESSIONTOKENCACHE_ENABLED true
#ENV VSS_NUGET_EXTERNAL_FEED_ENDPOINTS {\"endpointCredentials\": [{\"endpoint\":\"${FEED_URL}\", \"username\":\"App1\", \"password\":\"${PAT}\"}]}
#
#
#RUN wget -qO- https://raw.githubusercontent.com/Microsoft/artifacts-credprovider/master/helpers/installcredprovider.sh | bash
#
WORKDIR /src
## Copy the applications .csproj
COPY ["App1/App1.csproj", "App1/"]
COPY ["App2/App2.csproj", "App2/"]
COPY nuget.config ./
## Restore packages
#RUN dotnet restore "App1.csproj" -s "${FEED_URL}" -s "https://api.nuget.org/v3/index.json"
#RUN sed -i "s|</configuration>|<packageSourceCredentials><Company><add key=\"Username\" value=\"Company\" /><add key=\"ClearTextPassword\" value=\"${PAT}\" /></Company></packageSourceCredentials></configuration>|" nuget.config
#RUN dotnet restore . -s "${FEED_URL}" -s "https://api.nuget.org/v3/index.json" "App1.csproj"
RUN dotnet restore "App1.csproj" --configfile nuget.config
#RUN dotnet restore \
#-s https://api.nuget.org/v3/index.json \
#-s ${FEED_URL} \
#"App1.csproj"
## Copy everything else
COPY . .
WORKDIR "/src/App1"
## Build the app
RUN dotnet build "App1.csproj" -c Release -o /app/build
## Run dotnet test setting the output on the /coverage folder
## ...
## Create the code coverage file in sonarqube format using the cobertura file generated from the dotnet test command
## ...
## Publish the app
FROM build AS publish
RUN dotnet publish "App1.csproj" -c Release -o /app/publish
#############
## Stage 2 ##
#############
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "App1.dll"]
My task for building docker container:
- task: Docker#0
displayName: 'Build an image: App1'
inputs:
containerregistrytype: 'Azure Container Registry'
azureSubscription: 'azure connection'
azureContainerRegistry: '{"loginServer":"test.azurecr.io", "id" : "/subscriptions/be40d993-b17f-4873-a724-11111111111/resourceGroups/aks-test/providers/Microsoft.ContainerRegistry/registries/test"}'
action: 'Build an image'
dockerFile: 'App1/App1/Dockerfile'
buildArguments: |
PAT=$(PAT)
FEED_URL=$(FEED_URL)
defaultContext: false
imageName: '$(Build.Repository.Name):$(Build.BuildId)'
I am using private agents on Azure DevOps Server.
I have dotnet core 2.2 (aspnet core) app running in Docker container. I'm using the simplest possible Dockerfile you can find in any basic tutorial:
use microsoft/dotnet:2.2-sdk as base image
copy *.csproj
restore packages
build
publish to /app folder
use microsoft/dotnet:2.2.1-aspnetcore-runtime to run the app from /app folder
Now I'd like to grab some data from another website. It is a SPA, so I need to use a browser to render the page first - I decided to use Selenium with ChromeDriver because I'm already a little bit familiar with them.
I've added Selenium.WebDriver v3.141 and Selenium.WebDriver.ChromeDriver v73.0 to my project, set Selenium there. Locally on Windows it works fine. But when I run this via Docker I'm getting:
The file /app/chromedriver does not exist. The driver can be downloaded at http://chromedriver.storage.googleapis.com/index.html
So now I'm wondering how can I run Selenium + single instance Chrome (there is no need to set up Selenium Grid for my purpose) with dotnet core 2.2 in Docker.
I suppose I need to create custom Dockerfile which:
installs selenium, chrome and all their dependencies
installs dotnet
does the same as my current Dockerfile to build and run my app
But I'm not really sure how to do this. Especially how to "nest" Dockerfiles.
Should I do this composition in a single Dockerfile? Should I create Dockerfile for Selenium + ChromeDriver and use it as base image for next step?
So I recently had the same problem.
TL;DR; You have to install chrome into the docker image by putting the
commands in the Docker file.
FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch
# Install Chrome
RUN apt-get update && apt-get install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg \
hicolor-icon-theme \
libcanberra-gtk* \
libgl1-mesa-dri \
libgl1-mesa-glx \
libpango1.0-0 \
libpulse0 \
libv4l-0 \
fonts-symbola \
--no-install-recommends \
&& curl -sSL https://dl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& echo "deb [arch=amd64] https://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list \
&& apt-get update && apt-get install -y \
google-chrome-stable \
--no-install-recommends \
&& apt-get purge --auto-remove -y curl \
&& rm -rf /var/lib/apt/lists/*
# Add your dotnet core project build stuff here
Easier solution - I pushed this as a docker image in my docker hub repo so you can use it as your base image. See this example of my dotnet core 2.2
FROM masteroleary/selenium-dotnetcore2.2-linux:v2 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM masteroleary/selenium-dotnetcore2.2-linux:v2 AS build WORKDIR /src
COPY ["MyProject.csproj", ""]
RUN dotnet restore "MyProject.csproj"
COPY . .
WORKDIR "/src/"
RUN dotnet build "MyProject.csproj" -c Prod -o /app
FROM build AS publish
RUN dotnet publish "MyProject.csproj" -c Prod -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "MyProject.dll"]
How did this happen?
Basically created a new project in visual studio for dotnet core 2.2 mvc with docker support.
Intentions are to run my dotnet core app in a linux container
Assumed that by installing nuget packages Selenium.Support, Selenium.WebDriver, Selenium.WebDriver.ChromeDriver anything I needed would be included in the docker container automatically since Selenium.WebDriver supports .NetStandard 2.0 (BTW the others don't, just realized that)
Turns out you have to install chrome into the docker image by putting the commands in the Docker file.
I've explained the whole learning process here including how I found this working code: https://hub.docker.com/r/masteroleary/selenium-dotnetcore2.2-linux
Since the appearance in dotnet core of self-contained applications I think a better approach is to use the official selenium docker:
https://hub.docker.com/r/selenium/standalone-chrome
and build the application self contained.
Here is my dockerfile:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 as build-env
WORKDIR /app
COPY . ./
RUN dotnet publish MyApp.csproj -c Release -o out --self-contained -r linux-x64 /p:PublishTrimmed=true
FROM selenium/standalone-chrome
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["./MyApp"]
this is updated version for dotnet 6.0
also This is a multi stage Docker File and help with faster build
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
RUN apt update && apt install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg \
hicolor-icon-theme \
libcanberra-gtk* \
libgl1-mesa-dri \
libgl1-mesa-glx \
libpango1.0-0 \
libpulse0 \
libv4l-0 \
fonts-symbola \
--no-install-recommends \
&& curl -sSL https://dl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& echo "deb [arch=amd64] https://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list \
&& apt-get update && apt-get install -y \
google-chrome-stable \
--no-install-recommends \
&& apt-get purge --auto-remove -y curl \
&& rm -rf /var/lib/apt/lists/*
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
/* The .net App Docker Configuration */
FROM build AS publish
RUN dotnet publish -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
RUN echo ls -a
ENTRYPOINT ["dotnet", "{Entry Point File Name}.dll"]
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