Dela via


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 denna handledning:

  • 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 utforskar Docker-containerbygget 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 i flera lager.

Tips

Om du är intresserad av att publicera .NET-appen som en container utan behov av Docker eller Podman kan du läsa Containerize a .NET app with dotnet publish.

Obs

Den här självstudien är inte för ASP.NET Core-appar. Om du använder ASP.NET Core kan du läsa handledningen Lär dig hur du containeriserar en ASP.NET Core-applikation.

Förutsättningar

Installera följande krav:

  • .NET 8+ SDK.
    Om du har installerat .NET använder du kommandot dotnet --info 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 handledningen 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 ungefär som i följande katalogstruktur:

📁 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. Nu ändrar du kataloger och navigerar till mappen App från terminalsessionen. Använd kommandot dotnet run för att starta appen. Programmet körs och skriver 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.

Tips

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

code .

Det här kommandot öppnar 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. Överväg följande exempelutdata:

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

Om du skickar ett tal på kommandoraden till appen begränsas antalet till det beloppet och avslutas sedan. Prova med dotnet run -- 5 att räkna till fem.

Viktig

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

Publicera .NET-app

För att appen ska vara lämplig för att skapa en avbildning måste den kompileras. Kommandot dotnet publish är mest passande för detta, eftersom det skapar och publicerar appen. En djupgående referens finns i dotnet build och dotnet publish kommandodokumentation.

dotnet publish -c Release

Tips

Om du är intresserad av att publicera .NET-appen som en container utan behov av Docker kan du läsa Containerize a .NET app with dotnet publish.

Kommandot dotnet publish kompilerar appen till mappen publicera. Sökvägen till mappen publish från arbetsmappen ska vara ./App/bin/Release/<TFM>/publish/:

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

dir .\bin\Release\net9.0\publish\

    Directory: C:\Users\default\docker-working\App\bin\Release\net9.0\publish

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----          1/6/2025  10:11 AM            431 DotNet.Docker.deps.json
-a----          1/6/2025  10:11 AM           6144 DotNet.Docker.dll
-a----          1/6/2025  10:11 AM         145408 DotNet.Docker.exe
-a----          1/6/2025  10:11 AM          11716 DotNet.Docker.pdb
-a----          1/6/2025  10:11 AM            340 DotNet.Docker.runtimeconfig.json
dir .\bin\Release\net8.0\publish\

    Directory: C:\Users\default\docker-working\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

Skapa Dockerfile

Filen Dockerfile används av kommandot docker build 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-konsolapplikationen.

FROM mcr.microsoft.com/dotnet/sdk:9.0@sha256:3fcf6f1e809c0553f9feb222369f58749af314af6f063f389cbd2f913b4ad556 AS build
WORKDIR /App

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

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

Notis

ASP.NET Core runtime-avbildningen används avsiktligt här, även om mcr.microsoft.com/dotnet/runtime:9.0-avbildningen kan användas i stället.

FROM mcr.microsoft.com/dotnet/sdk:8.0@sha256:35792ea4ad1db051981f62b313f1be3b46b1f45cadbaa3c288cd0d3056eefb83 AS build
WORKDIR /App

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

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

Note

ASP.NET Core-körningsavbildningen används avsiktligt här, även om avbildningen mcr.microsoft.com/dotnet/runtime:8.0 kan användas i stället.

Viktig

Det är en god praxis att inkludera en säker hashalgoritm (SHA) efter en bildtagg i en Dockerfile. Detta säkerställer att avbildningen inte manipuleras och att bilden är densamma som den du förväntar dig. SHA är en unik identifierare för avbildningen. Mer information finns i Docker Docs: Hämta en avbild från digest.

Tips

Den här Dockerfile- använder flerstegsversioner, som optimerar avbildningens slutliga storlek genom att skikta 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. Det dotnet segmentet är containerlagringsplatsen, medan sdk- eller aspnet-segmentet är containeravbildningsnamnet. Avbildningen är taggad med 9.0, som används för versionshantering. Därför är mcr.microsoft.com/dotnet/aspnet:9.0 .NET 9.0-runtime. Se till att du hämtar den körningsversion som matchar den körningsversion som din SDK riktar sig mot. Till exempel använde appen som skapades i föregående avsnitt .NET 9.0 SDK och basavbildningen som anges i Dockerfile är taggad med 9.0.

Viktig

När du använder Windows-baserade containeravbildningar måste du ange avbildningstaggen utöver 9.0, till exempel mcr.microsoft.com/dotnet/aspnet:9.0-nanoserver-1809 i stället för mcr.microsoft.com/dotnet/aspnet:9.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 filen Dockerfile. Arbetsmappens katalogstruktur bör se ut så här. Några av filerna och mapparna på djupare nivå utelämnas för att spara utrymme i artikeln:

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

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. Det dotnet segmentet är containerlagringsplatsen, medan sdk- eller aspnet-segmentet är containeravbildningsnamnet. Avbildningen är taggad med 8.0, som används för versionshantering. Därför är mcr.microsoft.com/dotnet/aspnet:8.0 .NET 8.0 runtime-miljön. Se till att du hämtar runtime-versionen som matchar den runtime som din SDK riktar sig mot. 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.

Viktig

När du använder Windows-baserade containeravbildningar måste du ange avbildningstaggen utöver 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 filen Dockerfile. Arbetsmappens katalogstruktur bör se ut så här. Några av filerna och mapparna på djupare nivå utelämnas 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
            └──...

Instruktionen ENTRYPOINT anger dotnet som värd för DotNet.Docker.dll. Det är dock möjligt att i stället definiera ENTRYPOINT som själva appens körbara fil och förlita sig på operativsystemet som appvärd:

ENTRYPOINT ["./DotNet.Docker"]

Detta gör att appen körs direkt, utan dotnet, och förlitar sig i stället på appvärden och det underliggande operativsystemet. Mer information om hur du distribuerar plattformsoberoende binärfiler finns i Skapa en plattformsoberoende binär.

Kör följande kommando för att skapa containern från terminalen:

docker build -t counter-image -f Dockerfile .

Docker bearbetar varje rad i Dockerfile. . i kommandot docker build anger 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 motbild som pekar på avbildningen. När det här kommandot har slutförts kör du docker images för att se en lista över installerade avbildningar:

REPOSITORY       TAG       IMAGE ID       CREATED          SIZE
counter-image    latest    1c1f1433e51d   32 seconds ago   223MB
docker images
REPOSITORY       TAG       IMAGE ID       CREATED          SIZE
counter-image    latest    2f15637dc1f6   10 minutes ago   217MB

Den counter-image lagringsplatsen är namnet på avbildningen. Dessutom är bildtaggen, bildidentifieraren, storleken och när den skapades en del av utdata. 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:9.0
WORKDIR /App
COPY --from=build /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /App
COPY --from=build /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]

Kommandot FROM anger basavbildningen och taggen som ska användas. Kommandot WORKDIR ändrar den aktuella katalogen inuti containern till App.

Kommandot COPY instruerar Docker att kopiera den angivna källkatalogen till en målmapp. I det här exemplet förs publiceringsinnehåll från till lagret build och exporteras till mappen med namnet App/out, vilket utgör källan att kopiera från. Allt publicerat innehåll i katalogen App/out kopieras till den aktuella arbetskatalogen (App).

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

Tips

Innan .NET 8 kunde containrar som konfigurerades 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.

Not

.NET 6 standardiserar på prefixet DOTNET_ i stället för COMPlus_ 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 COMPlus_-prefixet 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 är stoppad.

docker create --name core-counter counter-image

Det här -kommandot skapar en container baserat på avbildningen från -motbilden. Utdata från kommandot docker create visar CONTAINER-ID för containern (din identifierare kommer att vara annorlunda):

d0be06126f7db6dd1cee369d911262a353c9b7fb4829a0c11b4b2eb7b2d429cf

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

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 kommandot docker start för att starta containern och använder sedan kommandot docker ps 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

På samma sätt stoppar kommandot docker stop containern. I följande exempel används kommandot docker stop för att stoppa containern och använder sedan kommandot docker ps 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

Ansluta 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 tangenttryckningen Ctrl+C för att koppla från den körande containern. 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, koppla tillbaka så att du kan 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 sedan kommandot docker rm 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 kommandot docker run 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 kommandot Ctrl+C 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 kommandot docker run kan du också ändra kommandot ENTRYPOINT 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 ändras ENTRYPOINT 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

Notat

Det här exemplet fungerar bara på Windows-containrar. Linux-containrar har inte cmd.exe.

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 handledningen skapade du containrar och bilder. 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 eller REPOSITORY:TAG formaterad sträng.

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

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

Tips

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 basavbildningar med den körmiljön installerad när du planerar att skapa andra avbildningar baserade på den körmiljön.

Nästa steg