diff --git a/.openpublishing.redirection.core.json b/.openpublishing.redirection.core.json index 4152011e87128..e9cb0f7e1e7bc 100644 --- a/.openpublishing.redirection.core.json +++ b/.openpublishing.redirection.core.json @@ -1349,6 +1349,10 @@ { "source_path_from_root": "/docs/fundamentals/networking/tcp/tcp-overview.md", "redirect_url": "/dotnet/fundamentals/networking/sockets/tcp-classes" + }, + { + "source_path_from_root": "/docs/core/docker/publish-as-container.md", + "redirect_url": "/dotnet/core/containers/sdk-publish" } ] } diff --git a/docs/core/containers/overview.md b/docs/core/containers/overview.md new file mode 100644 index 0000000000000..2f59244f51702 --- /dev/null +++ b/docs/core/containers/overview.md @@ -0,0 +1,207 @@ +--- +title: .NET SDK container creation overview +description: Learn about the .NET SDK container creation feature, including telemetry, publishing considerations, and build properties. +ms.date: 01/07/2025 +ms.topic: overview +--- + +# .NET SDK container creation overview + +While it's possible to [containerize .NET apps with a _Dockerfile_](../docker/build-container.md), there are compelling reasons for [containerizing apps directly with the .NET SDK](sdk-publish.md). This article provides an overview of the .NET SDK container creation feature, with details related to telemetry, publishing considerations, build properties, and authentication to container registries. + +## Telemetry + +When you publish a .NET app as a container, the .NET SDK container tooling collects and sends usage telemetry about how the tools are used. The collected data is in addition to the [telemetry sent by the .NET CLI](../tools/telemetry.md), but uses the same mechanisms and, importantly, adheres to the same [opt-out](../tools/telemetry#how-to-opt-out) controls. + +The telemetry gathered is intended to be general in nature and not leak any personal informationβ€”the intended purpose is to help measure: + +- Usage of the .NET SDK containerization feature overall. +- Success and failure rates, along with general information about what kinds of failures happen most frequently. +- Usage of specific features of the tech, like publishing to various registry kinds, or how the publish was invoked. + +To opt-out of telemetry, set the `DOTNET_CLI_TELEMETRY_OPTOUT` environment variable to `true`. For more information, see [.NET CLI telemetry](../tools/telemetry.md). + +### Inference telemetry + +The following information about how the base image inference process occurred is logged: + +| Date point | Explanation | Sample value | +|--|--|--| +| `InferencePerformed` | If users are manually specifying base images vs making use of inference. | `true` | +| `TargetFramework` | The `TargetFramework` chosen when doing base image inference. | `net8.0` | +| `BaseImage` | The value of the base image chosen, but only if that base image is one of the Microsoft-produced images. If a user specifies any image other than the Microsoft-produced images on mcr.microsoft.com, this value is null. | `mcr.microsoft.com/dotnet/aspnet` | +| `BaseImageTag` | The value of the tag chosen, but only if that tag is for one of the Microsoft-produced images. If a user specifies any image other than the Microsoft-produced images on mcr.microsoft.com, this value is null. | `8.0` | +| `ContainerFamily` | The value of the `ContainerFamily` property if a user used the `ContainerFamily` feature to pick a 'flavor' of one of our base images. This is only set if the user picked or inferred one of the Microsoft-produced .NET images from mcr.microsoft.com | `jammy-chiseled` | +| `ProjectType` | What kind of project was containerized | `AspNetCore` or `Console` | +| `PublishMode` | How the application was packaged | `Aot`, `Trimmed`, `SelfContained`, or `FrameworkDependent` | +| `IsInvariant` | If the image chosen requires invariant globalization or the user opted into it manually | `true` | +| `TargetRuntime` | The RID that this application was published for | `linux-x64` | + +### Image creation telemetry + +The following information about how the container creation and publishing process occurred is logged: + +| Date point | Explanation | Sample value | +|--|--|--| +| `RemotePullType` | If the base image came from a remote registry, what kind of registry was it? | Azure, AWS, Google, GitHub, DockerHub, MRC, or Other | +| `LocalPullType` | If the base image came from a local source, like a container daemon or a tarball. | Docker, Podman, Tarball | +| `RemotePushType` | If the image was pushed to a remote registry, what kind of registry was it? | Azure, AWS, Google, GitHub, DockerHub, MRC, or Other | +| `LocalPushType` | If the image was pushed to a local destination, what was it? | Docker, Podman, Tarball | + +In addition, if various kinds of errors occur during the process that data is collected about what kind of error it was: + +| Date point | Explanation | Sample value | +|--|--|--| +| `Error` | The kind of error that occurred | `unknown_repository`, `credential_failure`, `rid_mismatch`, `local_load`. | +| `Direction` | If the error is a `credential_failure`, was it to the push or pull registry? | `push` | +| Target RID | If the error was a `rid_mismatch`, what RID was requested | `linux-x64` | +| Available RIDs | If the error was a `rid_mismatch`, what RIDs did the base image support? | `linux-x64,linux-arm64` | + +## Publishing project considerations + +Now that you have a .NET app, you can publish it as a container. Before doing so, there are several important considerations to keep in mind. Prior to .NET SDK version 8.0.200, you needed the [πŸ“¦ Microsoft.NET.Build.Containers](https://www.nuget.org/packages/Microsoft.NET.Build.Containers) NuGet package. This package isn't required for .NET SDK version 8.0.200 and later, as the container support is included by default. + +To enable publishing a .NET app as a container, the following build properties are required: + +- `IsPublishable`: Set to `true`. This property is implicitly set to `true` for executable project types, such as `console`, `webapp`, and `worker`. +- `EnableSdkContainerSupport`: Set to `true` when your project type is a console app. + +To explicitly enable SDK container support, consider the following project file snippet: + +```xml + + true + true + +``` + +## Publish switches and build properties + +As with all .NET CLI commands, you can specify [MSBuild properties on the command line](/visualstudio/msbuild/msbuild-command-line-reference). There are many valid syntax forms available to provide properties, such as: + +- `/p:PropertyName=Value` +- `-p:PropertyName=Value` +- `-p PropertyName=Value` +- `--property PropertyName=Value` + +You're free to use whichever syntax you prefer, but the documentation shows examples using the `-p` form. + +> [!TIP] +> To help troubleshoot, consider using the MSBuid logs. To generate a binary log (binlog) file, add the `-bl` switch to the `dotnet publish` command. Binlog files are useful for diagnosing build issues and can be opened in the [MSBuild Structured Log Viewer](https://msbuildlog.com/). They provide a detailed trace of the build process, essential for MSBuild analysis. For more information, see [Troubleshoot and create logs for MSBuild](/visualstudio/ide/msbuild-logs#provide-msbuild-binary-logs-for-investigation). + +### Publish profiles and targets + +When using `dotnet publish`, specifying a profile with `-p PublishProfile=DefaultContainer` can set a property that causes the SDK to trigger another target after the publish process. This is an indirect way of achieving the desired result. On the other hand, using `dotnet publish /t:PublishContainer` directly invokes the `PublishContainer` target, achieving the same outcome but in a more straightforward manner. + +In other words, the following .NET CLI command: + +```dotnetcli +dotnet publish -p PublishProfile=DefaultContainer +``` + +Which sets the `PublishProfile` property to `DefaultContainer`, is equivalent to the following command: + +```dotnetcli +dotnet publish /t:PublishContainer +``` + +The difference between the two methods is that the former uses a profile to set the property, while the latter directly invokes the target. The reason this is important is that profiles are a feature of MSBuild, and they can be used to set properties in a more complex way than just setting them directly. + +One key issue is that not all project types support profiles or have the same set of profiles available. Additionally, there's a disparity in the level of support for profiles between different tooling, such as Visual Studio and the .NET CLI. Therefore, using targets is generally a clearer and more widely supported method to achieve the same result. + +## Authenticate to container registries + +Interacting with private container registries requires authenticating with those registries. + +Docker has an established pattern with this via the [`docker login`](https://docs.docker.com/engine/reference/commandline/login/) command, which is a way of interacting with a Docker config file that contains rules for authenticating with specific registries. This file, and the authentication types it encodes, are supported by Microsoft.Net.Build.Containers for registry authentication. This should ensure that this package works seamlessly with any registry you can `docker pull` from and `docker push`. This file is normally stored at _~/.docker/config.json_, but it can be specified additionally through the `DOCKER_CONFIG` variable, which points to a directory containing a _config.json_ file. + +## Kinds of authentication + +The _config.json_ file contains three kinds of authentication: + +- [Explicit username/password](#explicit-usernamepassword) +- [Credential helpers](#credential-helpers) +- [System keychain](#system-keychains) + +### Explicit username/password + +The `auths` section of the _config.json_ file is a key/value map between registry names and Base64-encoded username:password strings. In a common Docker scenario, running `docker login -u -p ` creates new items in this map. These credentials are popular in continuous integration (CI) systems, where logging in is done by tokens at the start of a run. However, they are less popular for end-user development machines due to the security risk of having bare credentials in a file. + +### Credential helpers + +The `credHelpers` section of the _config.json_ file is a key/value map between registry names and the names of specific programs that can be used to create and retrieve credentials for that registry. This is often used when particular registries have complex authentication requirements. In order for this kind of authentication to work, you must have an application named `docker-credential-{name}` on your system's `PATH`. These kinds of credentials tend to be secure, but can be hard to set up on development or CI machines. + +### System keychains + +The `credsStore` section is a single string property whose value is the name of a docker credential helper program that knows how to interface with the system's password manager. For Windows this might be `wincred` for example. These are popular with Docker installers for macOS and Windows. + +## Authentication via environment variables + +In some scenarios the standard Docker authentication mechanism described above just doesn't cut it. This tooling has an additional mechanism for providing credentials to registries: environment variables. If environment variables are used, the credential provide mechanism won't be used at all. The following environment variables are supported: + +- `DOTNET_CONTAINER_REGISTRY_UNAME`: This should be the username for the registry. If the password for the registry is a token, then the username should be `""`. +- `DOTNET_CONTAINER_REGISTRY_PWORD`: This should be the password or token for the registry. + +> [!NOTE] +> As of .NET SDK 8.0.400, the environment variables for container operations have been updated. The `SDK_CONTAINER_*` variables are now prefixed with `DOTNET_CONTAINER_*`. + +This mechanism is potentially vulnerable to credential leakage, so it should only be used in scenarios where the other mechanism isn't available. For example, if you're using the SDK Container tooling inside a Docker container itself. In addition, this mechanism isn't namespacedβ€”it attempts to use the same credentials for both the _source_ registry (where your base image is located) and the _destination_ registry (where you're pushing your final image). + +## Using insecure registries + +Most registry access is assumed to be secure, meaning HTTPS is used to interact with the registry. However, not all registries are configured with TLS certificates - especially in situations like a +private corporate registry behind a VPN. To support these use cases, container tools provide ways of declaring that a specific registry uses insecure communication. + +Starting in .NET 8.0.400, the SDK understands these configuration files and formats and will automatically use that configuration to determine if HTTP or HTTPS should be used. +Configuring a registry for insecure communication varies based on your container tool of choice. + +### Docker + +Docker stores its registry configuration in the [daemon configuration](https://docs.docker.com/config/daemon/#configuration-file). To add new insecure registries, new hosts are added to the `"insecure-registries"` array property: + +```json +{ + "insecure-registries": [ + "registry.mycorp.net" + ] +} +``` + +> [!NOTE] +> You must restart the Docker daemon to apply any changes to this file. + +### Podman + +Podman uses a [`registries.conf`](https://podman-desktop.io/docs/containers/registries#setting-up-a-registry-with-an-insecure-certificate) TOML file to store registry connection information. This file typically lives at `/etc/containers/registries.conf`. To add new insecure registries, a TOML section is added to hold the settings for the registry, then the `insecure` option must be set to `true`. + +```toml +[[registry]] +location = "registry.mycorp.net" +insecure = true +``` + +> [!NOTE] +> You must restart Podman to apply any changes to the _registries.conf_ file. + +### Environment variables + +Starting in 9.0.100, the .NET SDK recognizes insecure registries passed through the `DOTNET_CONTAINER_INSECURE_REGISTRIES` environment variable. This variable takes a comma-separated list of domains to treat as insecure in the same manner as the Docker and Podman examples above. + +#### [Windows](#tab/windows) + +```powershell +$Env:DOTNET_CONTAINER_INSECURE_REGISTRIES=localhost:5000,registry.mycorp.com; dotnet publish -t:PublishContainer -p:ContainerRegistry=registry.mycorp.com -p:ContainerBaseImage=localhost:5000/dotnet/runtime:9.0 +``` + +#### [Linux](#tab/linux) + +```bash +DOTNET_CONTAINER_INSECURE_REGISTRIES=localhost:5000,registry.mycorp.com dotnet publish -t:PublishContainer -p:ContainerRegistry=registry.mycorp.com -p:ContainerBaseImage=localhost:5000/dotnet/runtime:9.0 +``` + +--- + +## See also + +- [Publish .NET apps as containers](sdk-publish.md) +- [Containerize a .NET app reference](publish-configuration.md) diff --git a/docs/core/docker/publish-as-container.md b/docs/core/containers/publish-configuration.md similarity index 63% rename from docs/core/docker/publish-as-container.md rename to docs/core/containers/publish-configuration.md index 9a04a11f39d06..61e99e16beffb 100644 --- a/docs/core/docker/publish-as-container.md +++ b/docs/core/containers/publish-configuration.md @@ -1,219 +1,13 @@ --- -title: Containerize an app with dotnet publish -description: In this tutorial, you learn how to containerize a .NET application with dotnet publish command without the use of a Dockerfile. +title: Containerize a .NET app reference +description: Reference material for containerizing a .NET app and configuring the container image. +ms.topic: reference ms.date: 01/07/2025 -ms.topic: tutorial --- -# Containerize a .NET app with dotnet publish +# Containerize a .NET app reference -Containers have many features and benefits, such as being an immutable infrastructure, providing a portable architecture, and enabling scalability. The image can be used to create containers for your local development environment, private cloud, or public cloud. In this tutorial, you learn how to containerize a .NET application using the [dotnet publish](../tools/dotnet-publish.md) command without the use of a Dockerfile. Additionally, you explore how to configure the [container image](container-images.md) and execution, and how to clean up resources. - -## Prerequisites - -Install the following prerequisites: - -- [.NET 8+ SDK](https://dotnet.microsoft.com/download/dotnet/8.0)\ -If you have .NET installed, use the `dotnet --info` command to determine which SDK you're using. - -If you plan on running the container locally, you need an Open Container Initiative (OCI)-compatible container runtime, such as: - -- [Docker Desktop](https://www.docker.com/products/docker-desktop): Most common container runtime. -- [Podman](https://podman.io/): An open-source daemonless alternative to Docker. - -> [!IMPORTANT] -> The .NET SDK creates container images without Docker. Docker or Podman are only needed if you want to run the image locally. By default, when you [publish your .NET app](#publish-net-app) as a container image it's pushed to a local container runtime. Alternatively, you can save the [image as a tarball](#publish-net-app-to-a-tarball) or push it directly to a [container registry](#publish-net-app-to-container-registry) without using any container runtime at all. - -In addition to these prerequisites, it's recommended that you're familiar with [Worker Services in .NET](../extensions/workers.md) as the sample project is a worker. - -## Create .NET app - -You need a .NET app to containerize, so start by creating a new app from a template. Open your terminal, create a working folder (_sample-directory_) if you haven't already, and change directories so that you're in it. In the working folder, run the following command to create a new project in a subdirectory named _Worker_: - -```dotnetcli -dotnet new worker -o Worker -n DotNet.ContainerImage -``` - -Your folder tree looks similar to the following directory: - -```Directory -πŸ“ sample-directory - β””β”€β”€πŸ“‚ Worker - β”œβ”€β”€appsettings.Development.json - β”œβ”€β”€appsettings.json - β”œβ”€β”€DotNet.ContainerImage.csproj - β”œβ”€β”€Program.cs - β”œβ”€β”€Worker.cs - β”œβ”€β”€πŸ“‚ Properties - β”‚ └─── launchSettings.json - β””β”€β”€πŸ“‚ obj - β”œβ”€β”€ DotNet.ContainerImage.csproj.nuget.dgspec.json - β”œβ”€β”€ DotNet.ContainerImage.csproj.nuget.g.props - β”œβ”€β”€ DotNet.ContainerImage.csproj.nuget.g.targets - β”œβ”€β”€ project.assets.json - └── project.nuget.cache -``` - -The `dotnet new` command creates a new folder named _Worker_ and generates a worker service that, when run, logs a message every second. From your terminal session, change directories and navigate into the _Worker_ folder. Use the `dotnet run` command to start the app. - -```dotnetcli -dotnet run -Using launch settings from ./Worker/Properties/launchSettings.json... -Building... -info: DotNet.ContainerImage.Worker[0] - Worker running at: 01/06/2025 13:37:28 -06:00 -info: Microsoft.Hosting.Lifetime[0] - Application started. Press Ctrl+C to shut down. -info: Microsoft.Hosting.Lifetime[0] - Hosting environment: Development -info: Microsoft.Hosting.Lifetime[0] - Content root path: .\Worker -info: DotNet.ContainerImage.Worker[0] - Worker running at: 01/06/2025 13:37:29 -06:00 -info: DotNet.ContainerImage.Worker[0] - Worker running at: 01/06/2025 13:37:30 -06:00 -info: Microsoft.Hosting.Lifetime[0] - Application is shutting down... -``` - -The worker template loops indefinitely. Use the cancel command Ctrl+C to stop it. - -## Publishing project considerations - -Now that you have a .NET app, you can publish it as a container. Before doing so, there are several important considerations to keep in mind. Prior to .NET SDK version 8.0.200, you needed the [πŸ“¦ Microsoft.NET.Build.Containers](https://www.nuget.org/packages/Microsoft.NET.Build.Containers) NuGet package. This package isn't required for .NET SDK version 8.0.200 and later, as the container support is included by default. - -To enable publishing a .NET app as a container, the following build properties are required: - -- `IsPublishable`: Set to `true`. This property is implicitly set to `true` for executable project types, such as `console`, `webapp`, and `worker`. -- `EnableSdkContainerSupport`: Set to `true` when your project type is a console app. - -To explicitly enable SDK container support, consider the following project file snippet: - -```xml - - true - true - -``` - -## Publish switches and build properties - -As with all .NET CLI commands, you can specify [MSBuild properties on the command line](/visualstudio/msbuild/msbuild-command-line-reference). There are many valid syntax forms available to provide properties, such as: - -- `/p:PropertyName=Value` -- `-p:PropertyName=Value` -- `-p PropertyName=Value` -- `--property PropertyName=Value` - -You're free to use whichever syntax you prefer, but the documentation shows examples using the `-p` form. - -> [!TIP] -> To help troubleshoot, consider using the MSBuid logs. To generate a binary log (binlog) file, add the `-bl` switch to the `dotnet publish` command. Binlog files are useful for diagnosing build issues and can be opened in the [MSBuild Structured Log Viewer](https://msbuildlog.com/). They provide a detailed trace of the build process, essential for MSBuild analysis. For more information, see [Troubleshoot and create logs for MSBuild](/visualstudio/ide/msbuild-logs#provide-msbuild-binary-logs-for-investigation). - -### Publish profiles and targets - -When using `dotnet publish`, specifying a profile with `-p PublishProfile=DefaultContainer` can set a property that causes the SDK to trigger another target after the publish process. This is an indirect way of achieving the desired result. On the other hand, using `dotnet publish /t:PublishContainer` directly invokes the `PublishContainer` target, achieving the same outcome but in a more straightforward manner. - -In other words, the following .NET CLI command: - -```dotnetcli -dotnet publish -p PublishProfile=DefaultContainer -``` - -Which sets the `PublishProfile` property to `DefaultContainer`, is equivalent to the following command: - -```dotnetcli -dotnet publish /t:PublishContainer -``` - -The difference between the two methods is that the former uses a profile to set the property, while the latter directly invokes the target. The reason this is important is that profiles are a feature of MSBuild, and they can be used to set properties in a more complex way than just setting them directly. - -One key issue is that not all project types support profiles or have the same set of profiles available. Additionally, there's a disparity in the level of support for profiles between different tooling, such as Visual Studio and the .NET CLI. Therefore, using targets is generally a clearer and more widely supported method to achieve the same result. - -## Set the container image name - -There are various configuration options available when publishing an app as a container. By default, the container image name is the `AssemblyName` of the project. If that name is invalid as a container image name, you can override it by specifying a `ContainerRepository` as shown in the following project file: - -:::code language="xml" source="snippets/Worker/DotNet.ContainerImage.csproj" highlight="8"::: - -For further reference, see [ContainerRepository](#containerrepository). - -## Publish .NET app - -To publish the .NET app as a container, use the following [dotnet publish](../tools/dotnet-publish.md) command: - -```dotnetcli -dotnet publish --os linux --arch x64 /t:PublishContainer -``` - -The preceding .NET CLI command publishes the app as a container: - -- Targeting Linux as the OS (`--os linux`). -- Specifying an x64 architecture (`--arch x64`). - -> [!IMPORTANT] -> To publish the container locally, you must have an active OCI-compliant daemon running. If it isn't running when you attempt to publish the app as a container, you experience an error similar to the following: -> -> ```console -> ..\build\Microsoft.NET.Build.Containers.targets(66,9): error MSB4018: -> The "CreateNewImage" task failed unexpectedly. [..\Worker\DotNet.ContainerImage.csproj] -> ``` - -The `dotnet publish` command produces output similar to the example output: - -```dotnetcli -Restore complete (0.2s) - DotNet.ContainerImage succeeded (2.6s) β†’ bin\Release\net9.0\linux-x64\publish\ -``` - -This command compiles your worker app to the _publish_ folder and pushes the container image to your local Docker daemon by default. If you're using Podman, an alias - -## Publish .NET app to a tarball - -A tarball (or tar file) is a file that contains other files. It usually ends with a _*.tar.gz_ compound file extension to help indicate that it's a compressed archive. These file types are used to distribute software or to create backups. In this case, the tarball created is used to distribute a container image. - -To publish a .NET app as a container to a tarball, use the following command: - -```dotnetcli -dotnet publish --os linux --arch x64 \ - /t:PublishContainer \ - -p ContainerArchiveOutputPath=./images/container-image.tar.gz -``` - -The preceding command publishes the app as a container to a tarball: - -- Targeting Linux as the OS (`--os linux`). -- Specifying an x64 architecture (`--arch x64`). -- Setting the `ContainerArchiveOutputPath` property to `./images/container-image.tar.gz`. - -The command doesn't require a running OCI-compliant daemon. For more information, see [ContainerArchiveOutputPath](#containerarchiveoutputpath). - -### Load the tarball - -A common use case for exporting to a tarball is for security-focused organizations. They create containers, export them as tarballs, and then run security-scanning tools over the tarballs. This approach simplifies compliance as it avoids the complexities of scanning a live system. - -The tarball contains the entire container, which can then be loaded using the appropriate tool: - -- [Docker](https://docs.docker.com/reference/cli/docker/image/load/): `docker load -i ./images/container-image.tar.gz` -- [Podman](https://docs.podman.io/en/latest/markdown/podman-load.1.html): `podman load -i ./images/container-image.tar.gz` - -## Publish .NET app to container registry - -Container registries are services that store and manage container images. They're used to store and distribute container images across multiple environments. You can publish a .NET app as a container to a container registry by using the following command: - -```dotnetcli -dotnet publish --os linux --arch x64 \ - /t:PublishContainer \ - -p ContainerRegistry=ghcr.io -``` - -The preceding code publishes the app as a container to a container registry: - -- Targeting Linux as the OS (`--os linux`). -- Specifying an x64 architecture (`--arch x64`). -- Setting the `ContainerRegistry` property to `ghcr.io`. - -For more information, see [ContainerRegistry](#containerregistry). +In this reference article, you learn how to configure the container image that's generated when you publish a .NET app as a container. This article covers the various properties that you can set to control the image, the execution environment, and the commands that are run when the container starts. ## Configure container image @@ -607,35 +401,7 @@ Labels are often used to provide consistent metadata on container images. This p For more information, see [Implement conventional labels on top of existing label infrastructure](https://github.com/dotnet/sdk-container-builds/issues/96). -## Clean up resources - -In this article, you published a .NET worker as a container image. If you want, delete this resource. Use the `docker images` command to see a list of installed images. - -```console -docker images -``` - -Consider the following example output: - -```console -REPOSITORY TAG IMAGE ID CREATED SIZE -dotnet-worker-image 1.0.0 25aeb97a2e21 12 seconds ago 191MB -``` - -> [!TIP] -> Image files can be large. Typically, you would remove temporary containers you created while testing and developing your app. You usually keep the base images with the runtime installed if you plan on building other images based on that runtime. - -To delete the image, copy the image ID and run the `docker image rm` command: - -```console -docker image rm 25aeb97a2e21 -``` - -## Next steps +## See also -- [Announcing built-in container support for the .NET SDK](https://devblogs.microsoft.com/dotnet/announcing-builtin-container-support-for-the-dotnet-sdk) -- [Tutorial: Containerize a .NET app](build-container.md) -- [.NET container images](container-images.md) -- [Review the Azure services that support containers](https://azure.microsoft.com/overview/containers/) -- [Read about Dockerfile commands](https://docs.docker.com/engine/reference/builder/) -- [Explore the container tools in Visual Studio](/visualstudio/containers/overview) +- [Containerize a .NET app with dotnet publish](sdk-publish.md) +- [.NET container images](../docker/container-images.md) diff --git a/docs/core/containers/sdk-publish.md b/docs/core/containers/sdk-publish.md new file mode 100644 index 0000000000000..47f7ad1f8f3ca --- /dev/null +++ b/docs/core/containers/sdk-publish.md @@ -0,0 +1,200 @@ +--- +title: Containerize an app with dotnet publish +description: In this tutorial, you learn how to containerize a .NET application with dotnet publish command without the use of a Dockerfile. +ms.date: 01/07/2025 +ms.topic: tutorial +--- + +# Containerize a .NET app with dotnet publish + +Containers have many features and benefits, such as being an immutable infrastructure, providing a portable architecture, and enabling scalability. The image can be used to create containers for your local development environment, private cloud, or public cloud. In this tutorial, you learn how to containerize a .NET application using the [dotnet publish](../tools/dotnet-publish.md) command without the use of a Dockerfile. Additionally, you explore how to configure the [container image](container-images.md) and execution, and how to clean up resources. + +> [!TIP] +> If you're interested in using a _Dockerfile_ to containerize your .NET app, see [Tutorial: Containerize a .NET app](../docker/build-container.md). + +## Prerequisites + +Install the following prerequisites: + +- [.NET 8+ SDK](https://dotnet.microsoft.com/download/dotnet/8.0)\ +If you have .NET installed, use the `dotnet --info` command to determine which SDK you're using. + +If you plan on running the container locally, you need an Open Container Initiative (OCI)-compatible container runtime, such as: + +- [Docker Desktop](https://www.docker.com/products/docker-desktop): Most common container runtime. +- [Podman](https://podman.io/): An open-source daemonless alternative to Docker. + +> [!IMPORTANT] +> The .NET SDK creates container images without Docker. Docker or Podman are only needed if you want to run the image locally. By default, when you [publish your .NET app](#publish-net-app) as a container image it's pushed to a local container runtime. Alternatively, you can save the [image as a tarball](#publish-net-app-to-a-tarball) or push it directly to a [container registry](#publish-net-app-to-container-registry) without using any container runtime at all. + +In addition to these prerequisites, it's recommended that you're familiar with [Worker Services in .NET](../extensions/workers.md) as the sample project is a worker. + +## Create .NET app + +You need a .NET app to containerize, so start by creating a new app from a template. Open your terminal, create a working folder (_sample-directory_) if you haven't already, and change directories so that you're in it. In the working folder, run the following command to create a new project in a subdirectory named _Worker_: + +```dotnetcli +dotnet new worker -o Worker -n DotNet.ContainerImage +``` + +Your folder tree looks similar to the following directory: + +```Directory +πŸ“ sample-directory + β””β”€β”€πŸ“‚ Worker + β”œβ”€β”€appsettings.Development.json + β”œβ”€β”€appsettings.json + β”œβ”€β”€DotNet.ContainerImage.csproj + β”œβ”€β”€Program.cs + β”œβ”€β”€Worker.cs + β”œβ”€β”€πŸ“‚ Properties + β”‚ └─── launchSettings.json + β””β”€β”€πŸ“‚ obj + β”œβ”€β”€ DotNet.ContainerImage.csproj.nuget.dgspec.json + β”œβ”€β”€ DotNet.ContainerImage.csproj.nuget.g.props + β”œβ”€β”€ DotNet.ContainerImage.csproj.nuget.g.targets + β”œβ”€β”€ project.assets.json + └── project.nuget.cache +``` + +The `dotnet new` command creates a new folder named _Worker_ and generates a worker service that, when run, logs a message every second. From your terminal session, change directories and navigate into the _Worker_ folder. Use the `dotnet run` command to start the app. + +```dotnetcli +dotnet run +Using launch settings from ./Worker/Properties/launchSettings.json... +Building... +info: DotNet.ContainerImage.Worker[0] + Worker running at: 01/06/2025 13:37:28 -06:00 +info: Microsoft.Hosting.Lifetime[0] + Application started. Press Ctrl+C to shut down. +info: Microsoft.Hosting.Lifetime[0] + Hosting environment: Development +info: Microsoft.Hosting.Lifetime[0] + Content root path: .\Worker +info: DotNet.ContainerImage.Worker[0] + Worker running at: 01/06/2025 13:37:29 -06:00 +info: DotNet.ContainerImage.Worker[0] + Worker running at: 01/06/2025 13:37:30 -06:00 +info: Microsoft.Hosting.Lifetime[0] + Application is shutting down... +``` + +The worker template loops indefinitely. Use the cancel command Ctrl+C to stop it. + +## Set the container image name + +There are various configuration options available when publishing an app as a container. By default, the container image name is the `AssemblyName` of the project. If that name is invalid as a container image name, you can override it by specifying a `ContainerRepository` as shown in the following project file: + +:::code language="xml" source="snippets/Worker/DotNet.ContainerImage.csproj" highlight="8"::: + +For further reference, see [ContainerRepository](#containerrepository). + +## Publish .NET app + +To publish the .NET app as a container, use the following [dotnet publish](../tools/dotnet-publish.md) command: + +```dotnetcli +dotnet publish --os linux --arch x64 /t:PublishContainer +``` + +The preceding .NET CLI command publishes the app as a container: + +- Targeting Linux as the OS (`--os linux`). +- Specifying an x64 architecture (`--arch x64`). + +> [!IMPORTANT] +> To publish the container locally, you must have an active OCI-compliant daemon running. If it isn't running when you attempt to publish the app as a container, you experience an error similar to the following: +> +> ```console +> ..\build\Microsoft.NET.Build.Containers.targets(66,9): error MSB4018: +> The "CreateNewImage" task failed unexpectedly. [..\Worker\DotNet.ContainerImage.csproj] +> ``` + +The `dotnet publish` command produces output similar to the example output: + +```dotnetcli +Restore complete (0.2s) + DotNet.ContainerImage succeeded (2.6s) β†’ bin\Release\net9.0\linux-x64\publish\ +``` + +This command compiles your worker app to the _publish_ folder and pushes the container image to your local Docker daemon by default. If you're using Podman, an alias + +## Publish .NET app to a tarball + +A tarball (or tar file) is a file that contains other files. It usually ends with a _*.tar.gz_ compound file extension to help indicate that it's a compressed archive. These file types are used to distribute software or to create backups. In this case, the tarball created is used to distribute a container image. + +To publish a .NET app as a container to a tarball, use the following command: + +```dotnetcli +dotnet publish --os linux --arch x64 \ + /t:PublishContainer \ + -p ContainerArchiveOutputPath=./images/container-image.tar.gz +``` + +The preceding command publishes the app as a container to a tarball: + +- Targeting Linux as the OS (`--os linux`). +- Specifying an x64 architecture (`--arch x64`). +- Setting the `ContainerArchiveOutputPath` property to `./images/container-image.tar.gz`. + +The command doesn't require a running OCI-compliant daemon. For more information, see [ContainerArchiveOutputPath](#containerarchiveoutputpath). + +### Load the tarball + +A common use case for exporting to a tarball is for security-focused organizations. They create containers, export them as tarballs, and then run security-scanning tools over the tarballs. This approach simplifies compliance as it avoids the complexities of scanning a live system. + +The tarball contains the entire container, which can then be loaded using the appropriate tool: + +- [Docker](https://docs.docker.com/reference/cli/docker/image/load/): `docker load -i ./images/container-image.tar.gz` +- [Podman](https://docs.podman.io/en/latest/markdown/podman-load.1.html): `podman load -i ./images/container-image.tar.gz` + +## Publish .NET app to container registry + +Container registries are services that store and manage container images. They're used to store and distribute container images across multiple environments. You can publish a .NET app as a container to a container registry by using the following command: + +```dotnetcli +dotnet publish --os linux --arch x64 \ + /t:PublishContainer \ + -p ContainerRegistry=ghcr.io +``` + +The preceding code publishes the app as a container to a container registry: + +- Targeting Linux as the OS (`--os linux`). +- Specifying an x64 architecture (`--arch x64`). +- Setting the `ContainerRegistry` property to `ghcr.io`. + +For more information, see [ContainerRegistry](#containerregistry). + +## Clean up resources + +In this article, you published a .NET worker as a container image. If you want, delete this resource. Use the `docker images` command to see a list of installed images. + +```console +docker images +``` + +Consider the following example output: + +```console +REPOSITORY TAG IMAGE ID CREATED SIZE +dotnet-worker-image 1.0.0 25aeb97a2e21 12 seconds ago 191MB +``` + +> [!TIP] +> Image files can be large. Typically, you would remove temporary containers you created while testing and developing your app. You usually keep the base images with the runtime installed if you plan on building other images based on that runtime. + +To delete the image, copy the image ID and run the `docker image rm` command: + +```console +docker image rm 25aeb97a2e21 +``` + +## Next steps + +- [Announcing built-in container support for the .NET SDK](https://devblogs.microsoft.com/dotnet/announcing-builtin-container-support-for-the-dotnet-sdk) +- [Tutorial: Containerize a .NET app](build-container.md) +- [.NET container images](container-images.md) +- [Review the Azure services that support containers](https://azure.microsoft.com/overview/containers/) +- [Read about Dockerfile commands](https://docs.docker.com/engine/reference/builder/) +- [Explore the container tools in Visual Studio](/visualstudio/containers/overview) diff --git a/docs/core/docker/snippets/Worker/DotNet.ContainerImage.csproj b/docs/core/containers/snippets/Worker/DotNet.ContainerImage.csproj similarity index 100% rename from docs/core/docker/snippets/Worker/DotNet.ContainerImage.csproj rename to docs/core/containers/snippets/Worker/DotNet.ContainerImage.csproj diff --git a/docs/core/docker/snippets/Worker/Program.cs b/docs/core/containers/snippets/Worker/Program.cs similarity index 100% rename from docs/core/docker/snippets/Worker/Program.cs rename to docs/core/containers/snippets/Worker/Program.cs diff --git a/docs/core/docker/snippets/Worker/Properties/launchSettings.json b/docs/core/containers/snippets/Worker/Properties/launchSettings.json similarity index 100% rename from docs/core/docker/snippets/Worker/Properties/launchSettings.json rename to docs/core/containers/snippets/Worker/Properties/launchSettings.json diff --git a/docs/core/docker/snippets/Worker/Worker.cs b/docs/core/containers/snippets/Worker/Worker.cs similarity index 100% rename from docs/core/docker/snippets/Worker/Worker.cs rename to docs/core/containers/snippets/Worker/Worker.cs diff --git a/docs/core/docker/snippets/Worker/appsettings.Development.json b/docs/core/containers/snippets/Worker/appsettings.Development.json similarity index 100% rename from docs/core/docker/snippets/Worker/appsettings.Development.json rename to docs/core/containers/snippets/Worker/appsettings.Development.json diff --git a/docs/core/docker/snippets/Worker/appsettings.json b/docs/core/containers/snippets/Worker/appsettings.json similarity index 100% rename from docs/core/docker/snippets/Worker/appsettings.json rename to docs/core/containers/snippets/Worker/appsettings.json diff --git a/docs/core/docker/build-container.md b/docs/core/docker/build-container.md index 53a898a5535c3..43e9b2f5f3326 100644 --- a/docs/core/docker/build-container.md +++ b/docs/core/docker/build-container.md @@ -1,7 +1,7 @@ --- title: Containerize an app with Docker tutorial description: In this tutorial, you learn how to containerize a .NET application with Docker. -ms.date: 03/20/2024 +ms.date: 01/07/2025 ms.topic: tutorial ms.custom: "mvc" zone_pivot_groups: dotnet-version @@ -23,6 +23,9 @@ In this tutorial, you: You explore the Docker container build and deploy tasks for a .NET application. The _Docker platform_ uses the _Docker engine_ to quickly build and package apps as _Docker images_. These images are written in the _Dockerfile_ format to be deployed and run in a layered container. +> [!TIP] +> If you're interested in publishing your .NET app as a container without the need for Docker or Podman, see [Containerize a .NET app with dotnet publish](../containers/sdk-publish.md). + > [!NOTE] > This tutorial **is not** for ASP.NET Core apps. If you're using ASP.NET Core, see the [Learn how to containerize an ASP.NET Core application](/aspnet/core/host-and-deploy/docker/building-net-docker-images) tutorial. diff --git a/docs/navigate/devops-testing/toc.yml b/docs/navigate/devops-testing/toc.yml index 78fa8c1354859..70bec53d2eec0 100644 --- a/docs/navigate/devops-testing/toc.yml +++ b/docs/navigate/devops-testing/toc.yml @@ -559,8 +559,16 @@ items: href: ../../core/rid-catalog.md - name: Resource manifest names href: ../../core/resources/manifest-file-names.md - - name: Docker + - name: Containers items: + - name: Overview + href: ../../core/containers/overview.md + - name: .NET SDK publish container + href: ../../core/containers/sdk-publish.md + - name: .NET SDK publish configuration + href: ../../core/containers/publish-configuration.md + - name: Docker + items: - name: Introduction to .NET and Docker href: ../../core/docker/introduction.md - name: Containerize a .NET app