Microsoft guidance on running asp.net core projects via a Docker container advocate copying our program files to the microsoft/aspnetcore image in our Dockerfile.
We are using image save and image load commands to distribute our code changes to a test server.
This means the image is quite large and harder to distribute.
Would it be possible to use the "scratch" image, copy our files to it and then via docker-compose bring in the microsoft/aspnetcore image and then our program files image built using "scratch" image?
In this way our own code image is tiny and can easily be distributed via image save and image load commands.
I've tried it and am getting the following error: "ERROR: for myapp Cannot start service myapp: OCI runtime create failed: container_linux.go:296: starting container process caused "exec: \"dotnet\": executable file not found in $PATH": unknown"
compose file:
version: '2'
services:
postgresserver:
image: postgres:alpine
restart: always
ports:
- 5432:5432
environment:
POSTGRES_PASSWORD: XXXX
volumes:
- /var/lib/myapp/db:/var/lib/postgresql/data
networks:
- myapp-network
aspnetcoreruntime:
image: microsoft/aspnetcore
working_dir: /app
myapp:
image: mycorp/myapp:v8.0.0-alpha.2
restart: always
ports:
- 7575:7575
volumes:
- /var/lib:/var/lib
environment:
myapp_USE_URLS: http://*:7575
myapp_DB_CONNECTION: User ID=postgres;Password=letmein;Host=postgresserver;Port=5432;Database=myapp;Pooling=true;
myapp_FOLDER_USER_FILES: /var/lib/myapp/files/user
myapp_FOLDER_BACKUP_FILES: /var/lib/myapp/files/backup
build:
context: ./myappdocker
dockerfile: Dockerfile
links:
- postgresserver
depends_on:
- "postgresserver"
- "aspnetcoreruntime"
networks:
- myapp-network
networks:
myapp-network:
driver: bridge
Let me start by saying I don't recommend trying to use "scratch". The image may be a few hundred MB right now, but that is for good reason: the image contains all .NET Core prerequisites, enables a few network settings by default to make deployment easier, and contains the ASP.NET Core runtime that is (1) pre-optimized for Linux using a tool called crossgen
and (2) serviced regularly with security patches.
If size is your primary concern, checkout https://github.com/aspnet/Universe/issues/833. In ASP.NET Core 2.1, there should be an image based on Alpine Linux, which will be smaller than the current base: Debian Linux.
That said, here is what you would need to use "scratch" as your base.
If you want to base this on "scratch", you need to ensure you have all of .NET Core's native dependencies present. See https://github.com/dotnet/core/blob/master/Documentation/prereqs.md. This includes things like libcurl3
, libssl
, libuuid
, and others.
Self-contained deployment means the output of dotnet publish
will contain almost all files needed to run your app. This output will be quite large as it includes a copy of all the System.*.dll files that make up .NET Core and all Microsoft.AspNetCore.*.dll files. To prepare a self-contained application, you need to:
Set the RuntimeIdentifier in your .csproj file
<PropertyGroup>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
</PropertyGroup>
Execute dotnet publish --self-contained --output ./publish/ --configuration Release
./publish/
into your Docker image./publish/my-app
instead of dotnet ./publish/my-app.dll
.You can also condense the first 2 steps to one action with dotnet publish --runtime linux-x64
. For more details see https://learn.microsoft.com/en-us/dotnet/core/deploying/
Setup ASP.NET Core to expose 0.0.0.0
. By default, ASP.NET Core only binds to localhost. In Docker, this means you must change this to bind to IPAny
in order to access the web server from outside the Docker container. You can do this in a variety of ways, but the simplest is to set the environment variable ENV ASPNETCORE_URLS http://*:80
(Optional) Finally, consider pre-optimizing your application using a tool called "crossgen". For large applications, it can reduce startup time of your application time by 15-30 seconds. crossgen
pre-JITs your *.dll assemblies. See https://github.com/dotnet/coreclr/blob/v2.0.5/Documentation/building/crossgen.md for more details.
There is one more thing to consider here. By building your own image on "scratch", you lose on potential disk-space savings if you have more than one application on the server running .NET Core. If you had two or three apps all using microsoft/aspnetcore
as the base, Docker only keeps one copy of microsoft/aspnetcore
. If you based these two or three apps on scratch, you will end up using more disk space because most of what goes into each image will be duplicated content: the ASP.NET Core runtime. Your program files are typically the smaller part in terms of file-size in what is required to run a .NET Core app.
I don't know about mentioned guidance, but if you look into official dotnet/dotnet-docker-samples and check Dockerfile, you will see that multi-stage build is using:
FROM microsoft/aspnetcore-build:2.0 AS build-env
WORKDIR /app
# copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore
# copy everything else and build
COPY . ./
RUN dotnet publish -c Release -o out
# build runtime image
FROM microsoft/aspnetcore:2.0
WORKDIR /app
COPY --from=build-env /app/out .
so yes, one of steps do copy of all program files, but docker images is building only on the final stage.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With