Självstudie: Containerisera en .NET-app

I den här självstudien får du lära dig hur du containeriserar ett .NET-program med Docker. Containrar har många funktioner och fördelar, till exempel att vara en oföränderlig infrastruktur, tillhandahålla en bärbar arkitektur och möjliggöra skalbarhet. Avbildningen kan användas för att skapa containrar för din lokala utvecklingsmiljö, privata moln eller offentliga moln.

I den här kursen får du:

  • Skapa och publicera en enkel .NET-app
  • Skapa och konfigurera en Dockerfile för .NET
  • Skapa en Docker-avbildning
  • Skapa och köra en Docker-container

Du kommer att förstå hur Docker-containern skapar och distribuerar uppgifter för ett .NET-program. Docker-plattformen använder Docker-motorn för att snabbt skapa och paketera appar som Docker-avbildningar. Dessa avbildningar skrivs i Dockerfile-format som ska distribueras och köras i en container med flera lager.

Kommentar

Den här självstudien gäller inte för ASP.NET Core-appar. Om du använder ASP.NET Core läser du självstudien Lär dig hur du containeriserar en ASP.NET Core-program .

Förutsättningar

Installera följande krav:

  • .NET 8+ SDK
    Om du har .NET installerat använder dotnet --info du kommandot för att avgöra vilken SDK du använder.
  • Docker Community Edition
  • En tillfällig arbetsmapp för Dockerfile- och .NET-exempelappen. I den här självstudien används namnet docker-working som arbetsmapp.
  • .NET 7+ SDK
    Om du har .NET installerat använder dotnet --info du kommandot för att avgöra vilken SDK du använder.
  • Docker Community Edition
  • En tillfällig arbetsmapp för Dockerfile- och .NET-exempelappen. I den här självstudien används namnet docker-working som arbetsmapp.

Skapa .NET-app

Du behöver en .NET-app som Docker-containern kör. Öppna terminalen, skapa en arbetsmapp om du inte redan har gjort det och ange den. I arbetsmappen kör du följande kommando för att skapa ett nytt projekt i en underkatalog med namnet App:

dotnet new console -o App -n DotNet.Docker

Mappträdet ser ut så här:

📁 docker-working
    └──📂 App
        ├──DotNet.Docker.csproj
        ├──Program.cs
        └──📂 obj
            ├── DotNet.Docker.csproj.nuget.dgspec.json
            ├── DotNet.Docker.csproj.nuget.g.props
            ├── DotNet.Docker.csproj.nuget.g.targets
            ├── project.assets.json
            └── project.nuget.cache

Kommandot dotnet new skapar en ny mapp med namnet App och genererar ett "Hello World"-konsolprogram. Ändra kataloger och navigera till mappen App från terminalsessionen. dotnet run Använd kommandot för att starta appen. Programmet körs och skrivs ut Hello World! under kommandot:

cd App
dotnet run
Hello World!

Standardmallen skapar en app som skriver ut till terminalen och sedan omedelbart avslutas. I den här självstudien använder du en app som loopar på obestämd tid. Öppna filen Program.cs i en textredigerare.

Dricks

Om du använder Visual Studio Code skriver du följande kommando från föregående terminalsession:

code .

Då öppnas mappen App som innehåller projektet i Visual Studio Code.

Program.cs bör se ut som följande C#-kod:

Console.WriteLine("Hello World!");

Ersätt filen med följande kod som räknar tal varje sekund:

var counter = 0;
var max = args.Length is not 0 ? Convert.ToInt32(args[0]) : -1;
while (max is -1 || counter < max)
{
    Console.WriteLine($"Counter: {++counter}");
    await Task.Delay(TimeSpan.FromMilliseconds(1_000));
}
var counter = 0;
var max = args.Length is not 0 ? Convert.ToInt32(args[0]) : -1;
while (max is -1 || counter < max)
{
    Console.WriteLine($"Counter: {++counter}");
    await Task.Delay(TimeSpan.FromMilliseconds(1_000));
}

Spara filen och testa programmet igen med dotnet run. Kom ihåg att den här appen körs på obestämd tid. Använd kommandot Avbryt Ctrl+C för att stoppa det. Följande är ett exempel på utdata:

dotnet run
Counter: 1
Counter: 2
Counter: 3
Counter: 4
^C

Om du skickar ett tal på kommandoraden till appen räknas det bara upp till det beloppet och avslutas sedan. Försök med dotnet run -- 5 för att räkna till fem.

Viktigt!

Eventuella parametrar efter -- skickas inte till dotnet run kommandot och skickas i stället till ditt program.

Publicera .NET-app

Innan du lägger till .NET-appen i Docker-avbildningen måste den först publiceras. Det är bäst att låta containern köra den publicerade versionen av appen. Om du vill publicera appen kör du följande kommando:

dotnet publish -c Release

Det här kommandot kompilerar appen till publiceringsmappen. Sökvägen till publiceringsmappen från arbetsmappen ska vara .\App\bin\Release\net8.0\publish\.

Det här kommandot kompilerar appen till publiceringsmappen. Sökvägen till publiceringsmappen från arbetsmappen ska vara .\App\bin\Release\net7.0\publish\.

Från mappen App hämtar du en kataloglista över publiceringsmappen för att kontrollera att DotNet.Docker.dll filen skapades.

dir .\bin\Release\net8.0\publish\

    Directory: C:\Users\default\App\bin\Release\net8.0\publish

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           9/22/2023  9:17 AM            431 DotNet.Docker.deps.json
-a---           9/22/2023  9:17 AM           6144 DotNet.Docker.dll
-a---           9/22/2023  9:17 AM         157696 DotNet.Docker.exe
-a---           9/22/2023  9:17 AM          11688 DotNet.Docker.pdb
-a---           9/22/2023  9:17 AM            353 DotNet.Docker.runtimeconfig.json
dir .\bin\Release\net7.0\publish\

    Directory: C:\Users\default\App\bin\Release\net7.0\publish

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           2/13/2023  1:52 PM            431 DotNet.Docker.deps.json
-a---           2/13/2023  1:52 PM           6144 DotNet.Docker.dll
-a---           2/13/2023  1:52 PM         153600 DotNet.Docker.exe
-a---           2/13/2023  1:52 PM          11052 DotNet.Docker.pdb
-a---           2/13/2023  1:52 PM            253 DotNet.Docker.runtimeconfig.json

Skapa Dockerfile

Dockerfile-filen används av docker build kommandot för att skapa en containeravbildning. Den här filen är en textfil med namnet Dockerfile som inte har något tillägg.

Skapa en fil med namnet Dockerfile i katalogen som innehåller .csproj och öppna den i en textredigerare. I den här självstudien används ASP.NET Core-körningsavbildningen (som innehåller .NET-körningsavbildningen) och motsvarar .NET-konsolprogrammet.

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
WORKDIR /App

# Copy everything
COPY . ./
# Restore as distinct layers
RUN dotnet restore
# Build and publish a release
RUN dotnet publish -c Release -o out

# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /App
COPY --from=build-env /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]

Kommentar

ASP.NET Core-körningsavbildningen används avsiktligt här, även om avbildningen mcr.microsoft.com/dotnet/runtime:8.0 kunde ha använts.

Dricks

Den här Dockerfile använder flerstegsversioner, vilket optimerar avbildningens slutliga storlek genom att lagerföra bygget och endast lämna nödvändiga artefakter. Mer information finns i Docker Docs: flerstegsversioner.

Nyckelordet FROM kräver ett fullständigt kvalificerat Docker-containeravbildningsnamn. Microsoft Container Registry (MCR, mcr.microsoft.com) är ett syndikat av Docker Hub, som är värd för offentligt tillgängliga containrar. Segmentet dotnet är containerlagringsplatsen, medan sdk eller-segmentet aspnet är containeravbildningens namn. Bilden är taggad med 8.0, som används för versionshantering. mcr.microsoft.com/dotnet/aspnet:8.0 Därför är .NET 8.0-körningen. Se till att du hämtar körningsversionen som matchar den körning som mål för din SDK. Till exempel använde appen som skapades i föregående avsnitt .NET 8.0 SDK och basavbildningen som refereras till i Dockerfile är taggad med 8.0.

Viktigt!

När du använder Windows-baserade containeravbildningar måste du ange avbildningstaggen utöver bara 8.0, till exempel mcr.microsoft.com/dotnet/aspnet:8.0-nanoserver-1809 i stället för mcr.microsoft.com/dotnet/aspnet:8.0. Välj ett avbildningsnamn baserat på om du använder Nano Server eller Windows Server Core och vilken version av operativsystemet. Du hittar en fullständig lista över alla taggar som stöds på . NET:s Docker Hub-sida.

Spara Dockerfile-filen. Arbetsmappens katalogstruktur bör se ut så här. Några av filerna och mapparna på djupare nivå har utelämnats för att spara utrymme i artikeln:

📁 docker-working
    └──📂 App
        ├── Dockerfile
        ├── DotNet.Docker.csproj
        ├── Program.cs
        ├──📂 bin
        │   └──📂 Release
        │       └──📂 net8.0
        │           └──📂 publish
        │               ├── DotNet.Docker.deps.json
        │               ├── DotNet.Docker.exe
        │               ├── DotNet.Docker.dll
        │               ├── DotNet.Docker.pdb
        │               └── DotNet.Docker.runtimeconfig.json
        └──📁 obj
            └──...
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env
WORKDIR /App

# Copy everything
COPY . ./
# Restore as distinct layers
RUN dotnet restore
# Build and publish a release
RUN dotnet publish -c Release -o out

# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:7.0
WORKDIR /App
COPY --from=build-env /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]

Kommentar

ASP.NET Core-körningsavbildningen används avsiktligt här, även om avbildningen mcr.microsoft.com/dotnet/runtime:7.0 kunde ha använts.

Dricks

Den här Dockerfile använder flerstegsversioner, vilket optimerar avbildningens slutliga storlek genom att lagerföra bygget och endast lämna nödvändiga artefakter. Mer information finns i Docker Docs: flerstegsversioner.

Nyckelordet FROM kräver ett fullständigt kvalificerat Docker-containeravbildningsnamn. Microsoft Container Registry (MCR, mcr.microsoft.com) är ett syndikat av Docker Hub – som är värd för offentligt tillgängliga containrar. Segmentet dotnet är containerlagringsplatsen, medan sdk eller-segmentet aspnet är containeravbildningens namn. Bilden är taggad med 7.0, som används för versionshantering. mcr.microsoft.com/dotnet/aspnet:7.0 Därför är .NET 7.0-körningen. Se till att du hämtar körningsversionen som matchar den körning som mål för din SDK. Till exempel använde appen som skapades i föregående avsnitt .NET 7.0 SDK och basavbildningen som refereras till i Dockerfile är taggad med 7.0.

Spara Dockerfile-filen. Arbetsmappens katalogstruktur bör se ut så här. Några av filerna och mapparna på djupare nivå har utelämnats för att spara utrymme i artikeln:

📁 docker-working
    └──📂 App
        ├── Dockerfile
        ├── DotNet.Docker.csproj
        ├── Program.cs
        ├──📂 bin
        │   └──📂 Release
        │       └──📂 net7.0
        │           └──📂 publish
        │               ├── DotNet.Docker.deps.json
        │               ├── DotNet.Docker.exe
        │               ├── DotNet.Docker.dll
        │               ├── DotNet.Docker.pdb
        │               └── DotNet.Docker.runtimeconfig.json
        └──📁 obj
            └──...

Kör följande kommando från terminalen:

docker build -t counter-image -f Dockerfile .

Docker bearbetar varje rad i Dockerfile. I .docker build kommandot anges byggkontexten för avbildningen. Växeln -f är sökvägen till Dockerfile. Det här kommandot skapar avbildningen och skapar en lokal lagringsplats med namnet counter-image som pekar på avbildningen. När det här kommandot är klart kör du docker images för att se en lista över installerade avbildningar:

docker images
REPOSITORY                         TAG       IMAGE ID       CREATED          SIZE
counter-image                      latest    2f15637dc1f6   10 minutes ago   217MB

Lagringsplatsen counter-image är namnet på avbildningen. Taggen latest är taggen som används för att identifiera avbildningen. 2f15637dc1f6 är avbildnings-ID:t. Är 10 minutes ago den tid då avbildningen skapades. Är 217MB storleken på bilden. De sista stegen i Dockerfile är att skapa en container från avbildningen och köra appen, kopiera den publicerade appen till containern och definiera startpunkten.

FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /App
COPY --from=build-env /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]
docker images
REPOSITORY                         TAG       IMAGE ID       CREATED          SIZE
counter-image                      latest    2f15637dc1f6   10 minutes ago   208MB

Lagringsplatsen counter-image är namnet på avbildningen. Taggen latest är taggen som används för att identifiera avbildningen. 2f15637dc1f6 är avbildnings-ID:t. Är 10 minutes ago den tid då avbildningen skapades. Är 208MB storleken på bilden. De sista stegen i Dockerfile är att skapa en container från avbildningen och köra appen, kopiera den publicerade appen till containern och definiera startpunkten.

FROM mcr.microsoft.com/dotnet/aspnet:7.0
WORKDIR /App
COPY --from=build-env /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]

Kommandot COPY instruerar Docker att kopiera den angivna mappen på datorn till en mapp i containern. I det här exemplet kopieras publiceringsmappen till en mapp med namnet App/out i containern.

Kommandot WORKDIR ändrar den aktuella katalogen i containern till App.

Nästa kommando, ENTRYPOINT, instruerar Docker att konfigurera containern så att den körs som en körbar fil. När containern startar ENTRYPOINT körs kommandot. När det här kommandot avslutas stoppas containern automatiskt.

Dricks

Innan .NET 8 kan containrar som konfigurerats för att köras som skrivskyddade misslyckas med Failed to create CoreCLR, HRESULT: 0x8007000E. Du kan åtgärda det här problemet genom att ange en DOTNET_EnableDiagnostics miljövariabel som 0 (precis före ENTRYPOINT steget):

ENV DOTNET_EnableDiagnostics=0

Mer information om olika .NET-miljövariabler finns i .NET-miljövariabler.

Kommentar

.NET 6 standardiserar på prefixet DOTNET_ i stället COMPlus_ för för miljövariabler som konfigurerar .NET-körningsbeteende. Prefixet COMPlus_ fortsätter dock att fungera. Om du använder en tidigare version av .NET-körningen bör du fortfarande använda prefixet COMPlus_ för miljövariabler.

Skapa en container

Nu när du har en avbildning som innehåller din app kan du skapa en container. Du kan skapa en container på två sätt. Skapa först en ny container som har stoppats.

docker create --name core-counter counter-image

Det här docker create kommandot skapar en container baserat på motavbildningsavbildningen. Utdata från kommandot visar container-ID :t (ditt kommer att vara annorlunda) för den skapade containern:

d0be06126f7db6dd1cee369d911262a353c9b7fb4829a0c11b4b2eb7b2d429cf

Om du vill se en lista över alla containrar använder du docker ps -a kommandot:

docker ps -a
CONTAINER ID   IMAGE           COMMAND                  CREATED          STATUS    PORTS     NAMES
d0be06126f7d   counter-image   "dotnet DotNet.Docke…"   12 seconds ago   Created             core-counter

Hantera containern

Containern skapades med ett specifikt namn core-counter, det här namnet används för att hantera containern. I följande exempel används docker start kommandot för att starta containern och använder docker ps sedan kommandot för att endast visa containrar som körs:

docker start core-counter
core-counter

docker ps
CONTAINER ID   IMAGE           COMMAND                  CREATED          STATUS          PORTS     NAMES
cf01364df453   counter-image   "dotnet DotNet.Docke…"   53 seconds ago   Up 10 seconds             core-counter

docker stop På samma sätt stoppar kommandot containern. I följande exempel används docker stop kommandot för att stoppa containern och använder docker ps sedan kommandot för att visa att inga containrar körs:

docker stop core-counter
core-counter

docker ps
CONTAINER ID    IMAGE    COMMAND    CREATED    STATUS    PORTS    NAMES

Anslut till en container

När en container har körts kan du ansluta till den för att se utdata. Använd kommandona docker start och docker attach för att starta containern och titta på utdataströmmen. I det här exemplet används Ctrl+C-tangenttryckningen för att koppla från containern som körs. Den här tangenttryckningen avslutar processen i containern om inget annat anges, vilket skulle stoppa containern. Parametern --sig-proxy=false ser till att Ctrl+C inte stoppar processen i containern.

När du har kopplat från containern kopplar du tillbaka för att kontrollera att den fortfarande körs och räknar.

docker start core-counter
core-counter

docker attach --sig-proxy=false core-counter
Counter: 7
Counter: 8
Counter: 9
^C

docker attach --sig-proxy=false core-counter
Counter: 17
Counter: 18
Counter: 19
^C

Ta bort en container

I den här artikeln vill du inte att containrar ska hänga runt som inte gör något. Ta bort containern som du skapade tidigare. Om containern körs stoppar du den.

docker stop core-counter

I följande exempel visas alla containrar. Den använder docker rm sedan kommandot för att ta bort containern och kontrollerar sedan en andra gång för alla containrar som körs.

docker ps -a
CONTAINER ID    IMAGE            COMMAND                   CREATED          STATUS                        PORTS    NAMES
2f6424a7ddce    counter-image    "dotnet DotNet.Dock…"    7 minutes ago    Exited (143) 20 seconds ago            core-counter

docker rm core-counter
core-counter

docker ps -a
CONTAINER ID    IMAGE    COMMAND    CREATED    STATUS    PORTS    NAMES

Enkel körning

Docker tillhandahåller docker run kommandot för att skapa och köra containern som ett enda kommando. Det här kommandot eliminerar behovet av att köra docker create och sedan docker start. Du kan också ange att det här kommandot ska ta bort containern automatiskt när containern stoppas. Använd till exempel docker run -it --rm för att göra två saker, först automatiskt använda den aktuella terminalen för att ansluta till containern och sedan ta bort den när containern är klar:

docker run -it --rm counter-image
Counter: 1
Counter: 2
Counter: 3
Counter: 4
Counter: 5
^C

Containern skickar också parametrar till körningen av .NET-appen. Om du vill instruera .NET-appen att bara räkna till tre skickar du in 3.

docker run -it --rm counter-image 3
Counter: 1
Counter: 2
Counter: 3

Med docker run -itstoppar Ctrl+C-kommandot den process som körs i containern, vilket i sin tur stoppar containern. Eftersom parametern --rm angavs tas containern bort automatiskt när processen stoppas. Kontrollera att den inte finns:

docker ps -a
CONTAINER ID    IMAGE    COMMAND    CREATED    STATUS    PORTS    NAMES

Ändra ENTRYPOINT

Med docker run kommandot kan du också ändra ENTRYPOINT kommandot från Dockerfile och köra något annat, men bara för containern. Använd till exempel följande kommando för att köra bash eller cmd.exe. Redigera kommandot efter behov.

I det här exemplet ENTRYPOINT ändras till cmd.exe. Ctrl+C trycks ned för att avsluta processen och stoppa containern.

docker run -it --rm --entrypoint "cmd.exe" counter-image

Microsoft Windows [Version 10.0.17763.379]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\>dir
 Volume in drive C has no label.
 Volume Serial Number is 3005-1E84

 Directory of C:\

04/09/2019  08:46 AM    <DIR>          app
03/07/2019  10:25 AM             5,510 License.txt
04/02/2019  01:35 PM    <DIR>          Program Files
04/09/2019  01:06 PM    <DIR>          Users
04/02/2019  01:35 PM    <DIR>          Windows
               1 File(s)          5,510 bytes
               4 Dir(s)  21,246,517,248 bytes free

C:\>^C

Viktiga kommandon

Docker har många olika kommandon som skapar, hanterar och interagerar med containrar och avbildningar. Dessa Docker-kommandon är viktiga för att hantera dina containrar:

Rensa resurser

Under den här självstudien skapade du containrar och avbildningar. Ta bort dessa resurser om du vill. Använd följande kommandon för att

  1. Visa en lista över alla containrar

    docker ps -a
    
  2. Stoppa containrar som körs med deras namn.

    docker stop core-counter
    
  3. Ta bort containern

    docker rm core-counter
    

Ta sedan bort alla avbildningar som du inte längre vill ha på datorn. Ta bort avbildningen som skapades av Dockerfile och ta sedan bort .NET-avbildningen som Dockerfile baserades på. Du kan använda bild-ID:t eller den formaterade strängen REPOSITORY:TAG.

docker rmi counter-image:latest
docker rmi mcr.microsoft.com/dotnet/aspnet:8.0
docker rmi counter-image:latest
docker rmi mcr.microsoft.com/dotnet/aspnet:7.0

docker images Använd kommandot för att se en lista över installerade avbildningar.

Dricks

Bildfiler kan vara stora. Vanligtvis tar du bort tillfälliga containrar som du skapade när du testade och utvecklade din app. Du brukar ha basavbildningarna med körningen installerad om du planerar att skapa andra avbildningar baserat på den körningen.

Nästa steg