Skip to content

.NET Framework 4.8 Container Improvements (November 2021 Update, KB9008392) #849

Closed
@richlander

Description

@richlander

.NET Framework 4.8 Container Improvements (November 2021 Update, KB9008392)

.NET Framework 4.8 has been updated to better support containers with the November 2021 update. With .NET 6 releasing, we backported all of the .NET 6 container functionality to .NET Framework 4.8. The goal of these changes is to enable .NET Framework 4.8 applications to work well in Azure Kubernetes Service (AKS).

.NET Framework 4.8 now has the following capabilities:

For the .NET Framework implementation, environment variables continue to use the COMPLUS_ not DOTNET_ format.

This update only affects .NET Framework 4.8 container images and is applied via the KB9008392 patch. There are no plans to backport these changes to earlier .NET Framework versions. The changes are not delivered via Windows Update to Windows Server or Windows client machines. The changes will be deployed to .NET Framework 4.8 more generally at a later date.

You can test these changes with with our sample apps on MCR. You may find it easier to build and test our samples directly, as is demonstrated below.

Process-isolated containers

The biggest change is support for process-isolated containers. This same change was added to .NET 6.

From the .NET 6 post:

If you use Windows containers in Azure Kubernetes Service (AKS), then you are relying on process-isolated containers. Process-isolated containers can be thought of as very similar to Linux containers. Linux containers use cgroups and Windows process-isolated containers use Job Objects. Windows also offer Hyper-V containers, which offers greater isolation through greater virtualization. There are no changes in .NET 6 for Hyper-V containers.

The primary value of this change is that Environment.ProcessorCount will now report the correct value with Windows process-isolated containers. If you create a 2-core container on a 64-core machine, Environment.ProcessorCount will return 2. In prior versions, this property would report the total number of processors on a machine, independent of the limit specified by the Docker CLI, Kubernetes, or other container orchestrator/runtime. This value is used by various parts of .NET for scaling purposes, including the .NET garbage collector (although it relies on a related, lower-level, API). Community libraries also rely on this API for scaling.

Example console app usage

The following example uses a sample console app to demonstrate the new process-isolated container image support.

This scenario only works well/reliably if your Windows host and container guest match. As a result, the sample app was built with a special technique. The primary issue is that the sample depends on a multi-arch tag.

Building the image:

PS C:\git\dotnet-framework-docker\samples\dotnetapp> type .\Dockerfile | findstr FROM
FROM mcr.microsoft.com/dotnet/framework/sdk:4.8 AS build
FROM mcr.microsoft.com/dotnet/framework/runtime:4.8 AS runtime
PS C:\git\dotnet-framework-docker\samples\dotnetapp> docker pull mcr.microsoft.com/dotnet/framework/runtime:4.8-windowsservercore-ltsc2022
4.8-windowsservercore-ltsc2022: Pulling from dotnet/framework/runtime
Digest: sha256:c6e169c86508cd70bf81e881c7084124bb4c765cb171cb9c1508851b627efb84
Status: Image is up to date for mcr.microsoft.com/dotnet/framework/runtime:4.8-windowsservercore-ltsc2022
mcr.microsoft.com/dotnet/framework/runtime:4.8-windowsservercore-ltsc2022
PS C:\git\dotnet-framework-docker\samples\dotnetapp> docker tag mcr.microsoft.com/dotnet/framework/runtime:4.8-windowsservercore-ltsc2022 mcr.microsoft.com/dotnet/framework/runtime:4.8
PS C:\git\dotnet-framework-docker\samples\dotnetapp> docker build -t dotnetapp .

Note: The commands above are oriented on Windows 11. If you are using Windows 10 or Windows Server 2019, then switch the ltsc2022 tags to ltsc2019.

Launching the container:

PS C:\git\dotnet-framework-docker\samples\dotnetapp> docker run --rm --isolation=process dotnetapp
                                 ad88
                        ,d      d8"
                        88      88
8b,dPPYba,   ,adPPYba, MM88MMM MM88MMM 8b,     ,d8
88P'   `"8a a8P_____88   88      88     `Y8, ,8P'
88       88 8PP"""""""   88      88       )888(
88       88 "8b,   ,aa   88,     88     ,d8" "8b,
88       88  `"Ybbd8"'   "Y888   88    8P'     `Y8

.NET Framework 4.8.4465.0
Microsoft Windows 10.0.22000

OSArchitecture: X64
ProcessorCount: 16
PS C:\git\dotnet-framework-docker\samples\dotnetapp> docker run --rm --cpus=0.5 --isolation=process dotnetapp
                                 ad88
                        ,d      d8"
                        88      88
8b,dPPYba,   ,adPPYba, MM88MMM MM88MMM 8b,     ,d8
88P'   `"8a a8P_____88   88      88     `Y8, ,8P'
88       88 8PP"""""""   88      88       )888(
88       88 "8b,   ,aa   88,     88     ,d8" "8b,
88       88  `"Ybbd8"'   "Y888   88    8P'     `Y8

.NET Framework 4.8.4465.0
Microsoft Windows 10.0.22000

OSArchitecture: X64
ProcessorCount: 1
PS C:\git\dotnet-framework-docker\samples\dotnetapp> docker run --rm --cpus=8 -m 100mb --isolation=process dotnetapp
                                 ad88
                        ,d      d8"
                        88      88
8b,dPPYba,   ,adPPYba, MM88MMM MM88MMM 8b,     ,d8
88P'   `"8a a8P_____88   88      88     `Y8, ,8P'
88       88 8PP"""""""   88      88       )888(
88       88 "8b,   ,aa   88,     88     ,d8" "8b,
88       88  `"Ybbd8"'   "Y888   88    8P'     `Y8

.NET Framework 4.8.4465.0
Microsoft Windows 10.0.22000

OSArchitecture: X64
ProcessorCount: 8

The first container launch is unconstrained, and the following two launches constrain the container in various ways.

Example ASP.NET app usage

The following example uses a sample ASP.NET app to demonstrate the new process-isolated container image support.

The same model is used as is used earlier.

PS C:\git\dotnet-framework-docker\samples\aspnetmvcapp> type .\Dockerfile | findstr FROM
FROM mcr.microsoft.com/dotnet/framework/sdk:4.8 AS build
FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8 AS runtime
PS C:\git\dotnet-framework-docker\samples\aspnetmvcapp> docker pull mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2022
4.8-windowsservercore-ltsc2022: Pulling from dotnet/framework/aspnet
Digest: sha256:b6a565f5242df03ba025d250a2af06fbde85373549af885159d7e8b9e93ec615
Status: Image is up to date for mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2022
mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2022
PS C:\git\dotnet-framework-docker\samples\aspnetmvcapp> docker tag mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2022 mcr.microsoft.com/dotnet/framework/aspnet:4.8
PS C:\git\dotnet-framework-docker\samples\aspnetmvcapp> docker build -t aspnetmvcapp .

Note: The commands above are oriented on Windows 11. If you are using Windows 10 or Windows Server 2019, then switch the ltsc2022 tags to ltsc2019.

Launching the container unconstrained:

docker run --rm -p 8000:80 --isolation=process aspnetmvcapp

image

Launching the container constrained to 1 core:

docker run --rm -p 8000:80 --cpus 1 --isolation=process aspnetmvcapp

image

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions