get-started: update .net guide (#18184)
* update dotnet guide Signed-off-by: Craig Osterhout <craig.osterhout@docker.com>
|
@ -1,24 +1,21 @@
|
|||
---
|
||||
description: Containerize .NET apps using Docker
|
||||
keywords: Docker, getting started, .net, language
|
||||
title: What will you learn in this module?
|
||||
description: Containerize and develop .NET apps using Docker
|
||||
keywords: getting started, .net
|
||||
title: .NET language-specific guide
|
||||
toc_min: 1
|
||||
toc_max: 2
|
||||
---
|
||||
|
||||
The .NET getting started guide teaches you how to create a containerized .NET application using Docker. In this guide, you'll learn how to:
|
||||
|
||||
* Create a sample .NET application
|
||||
* Create a new Dockerfile which contains instructions required to build a .NET image
|
||||
* Build an image and run the newly built image as a container
|
||||
* Set up volumes and networking
|
||||
* Orchestrate containers using Compose
|
||||
* Use containers for development
|
||||
* Configure a CI/CD pipeline for your application using GitHub Actions
|
||||
* Containerize and run a .NET application
|
||||
* Set up a local environment to develop a .NET application using containers
|
||||
* Run tests for a .NET application using containers
|
||||
* Configure a CI/CD pipeline for a containerized .NET application using GitHub Actions
|
||||
* Deploy your application to the cloud
|
||||
|
||||
After completing the .NET getting started modules, you should be able to containerize your own .NET application based on the examples and instructions provided in this guide.
|
||||
|
||||
Let's get started!
|
||||
Start by containerizing an existing .NET application.
|
||||
|
||||
{{< button text="Build your first .NET image" url="build-images.md" >}}
|
||||
{{< button text="Containerize a .NET app" url="containerize.md" >}}
|
|
@ -1,295 +0,0 @@
|
|||
---
|
||||
title: Build your .NET image
|
||||
keywords: .net, build, images, dockerfile
|
||||
description: Learn how to build your first Docker image by writing a Dockerfile
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Work through the [Get started guide](../../get-started/index.md) to understand Docker concepts.
|
||||
|
||||
## Overview
|
||||
|
||||
Now that we have a good overview of containers and the Docker platform, let’s take a look at building our first image. An image includes everything needed to run an application - the code or binary, runtime, dependencies, and any other file system objects required.
|
||||
|
||||
To complete this tutorial, you need the following:
|
||||
|
||||
- .NET SDK version 6.0 or later. [Download .NET SDK](https://dotnet.microsoft.com/download).
|
||||
- Docker running locally. Follow the instructions to [download and install Docker](../../get-docker.md).
|
||||
- An IDE or a text editor to edit files. We recommend using [Visual Studio Code](https://code.visualstudio.com/).
|
||||
|
||||
## Sample application
|
||||
|
||||
For our sample application, let’s create a simple application from a template using .NET. Create a directory in your local machine named `dotnet-docker`. Open a terminal and change to that directory. Run the following `dotnet new` command to create a C# app using the ASP.NET Core Web App template.
|
||||
|
||||
```console
|
||||
$ mkdir dotnet-docker
|
||||
$ cd dotnet-docker
|
||||
$ dotnet new webapp -n myWebApp -o src --no-https
|
||||
```
|
||||
|
||||
Output similar to the following appears.
|
||||
|
||||
```console
|
||||
The template ASP.NET Core Web App was created successfully.
|
||||
This template contains technologies from parties other than Microsoft, see https://aka.ms/aspnetcore/6.0-third-party-notices for details.
|
||||
```
|
||||
|
||||
The command will create a new directory called `src`. View the `src` directory and verify the contents. You should see the following directories and files.
|
||||
|
||||
```shell
|
||||
├── Pages
|
||||
│ ├── Error.cshtml
|
||||
│ ├── Error.cshtml.cs
|
||||
│ ├── Index.cshtml
|
||||
│ ├── Index.cshtml.cs
|
||||
│ ├── Privacy.cshtml
|
||||
│ ├── Privacy.cshtml.cs
|
||||
│ ├── Shared
|
||||
│ ├── _ViewImports.cshtml
|
||||
│ └── _ViewStart.cshtml
|
||||
├── Program.cs
|
||||
├── Properties
|
||||
│ └── launchSettings.json
|
||||
├── appsettings.Development.json
|
||||
├── appsettings.json
|
||||
├── myWebApp.csproj
|
||||
├── obj
|
||||
│ ├── myWebApp.csproj.nuget.dgspec.json
|
||||
│ ├── myWebApp.csproj.nuget.g.props
|
||||
│ ├── myWebApp.csproj.nuget.g.targets
|
||||
│ ├── project.assets.json
|
||||
│ └── project.nuget.cache
|
||||
└── wwwroot
|
||||
├── css
|
||||
├── favicon.ico
|
||||
├── js
|
||||
└── lib
|
||||
```
|
||||
|
||||
## Test the application
|
||||
|
||||
Let’s start our application and make sure it’s running properly. Open your terminal and navigate to the `src` directory and use the `dotnet run` command.
|
||||
|
||||
```console
|
||||
$ cd /path/to/dotnet-docker/src
|
||||
$ dotnet run --urls http://localhost:5000
|
||||
```
|
||||
|
||||
Output similar to the following appears.
|
||||
|
||||
```console
|
||||
Building...
|
||||
info: Microsoft.Hosting.Lifetime[0]
|
||||
Now listening on: http://localhost:5000
|
||||
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: C:\Users\username\dotnet-docker\src\
|
||||
```
|
||||
Read the output to verify how you can access the application. In the example above, `Now listening on: http://localhost:5000` indicates that you access the application at `http://localhost:5000`.
|
||||
|
||||
Open a web browser and access the application based on the URL in the output. The following page should appear.
|
||||
|
||||

|
||||
|
||||
Press Ctrl+C in the terminal window to stop the application.
|
||||
|
||||
## Create a Dockerfile
|
||||
|
||||
In the `dotnet-docker` directory, create a file named `Dockerfile`.
|
||||
|
||||
Next, we need to add a line in our Dockerfile that tells Docker what image
|
||||
we would like to use to build our application. Open the `Dockerfile` in an IDE or a text editor, and add the following instructions.
|
||||
|
||||
```dockerfile
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0 as build-env
|
||||
```
|
||||
|
||||
Docker images can be inherited from other images. Therefore, instead of creating our own image, we’ll use the official .NET SDK image that already has all the tools and packages that we need to build a .NET application.
|
||||
|
||||
We will use a multi-stage build and define a stage for building the application.We define a `build-env` stage in our Dockerfile using `as`.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> To learn more about multi-stage builds, see [Multi-stage builds](../../build/building/multi-stage.md).
|
||||
|
||||
To make things easier when running the rest of our commands, let’s create a working directory for our source files. This instructs Docker to use this path as the default location for all subsequent commands. By doing this, we do not have to type out full file paths but can use relative paths based on the working directory.
|
||||
|
||||
```dockerfile
|
||||
WORKDIR /src
|
||||
```
|
||||
|
||||
Although not necessary, the commands below will copy only the csproj files and then run `dotnet restore`. Each command creates a new container layer. To speed the building of containers, Docker caches these layers. Since these files won't change often, we can take advantage of the caching by copying these files and running restore as separate commands.
|
||||
|
||||
|
||||
```dockerfile
|
||||
COPY src/*.csproj .
|
||||
RUN dotnet restore
|
||||
```
|
||||
|
||||
Next, you'll need to copy the rest of your source files into the image. The line below will copy the files from the `src` directory on your local machine to a directory called `src` in the image.
|
||||
|
||||
```dockerfile
|
||||
COPY src .
|
||||
```
|
||||
|
||||
Next, you'll need to run the `dotnet publish` command to build the project.
|
||||
|
||||
```dockerfile
|
||||
RUN dotnet publish -c Release -o /publish
|
||||
```
|
||||
|
||||
Next, you'll specify the image that you'll use to run the application, and define it as the `runtime` stage.
|
||||
|
||||
```dockerfile
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0 as runtime
|
||||
```
|
||||
|
||||
Next, specify the working directory for this stage.
|
||||
|
||||
```dockerfile
|
||||
WORKDIR /publish
|
||||
```
|
||||
|
||||
Next, copy the /publish directory from the build-env stage into the runtime image.
|
||||
|
||||
```dockerfile
|
||||
COPY --from=build-env /publish .
|
||||
```
|
||||
|
||||
Expose port 80 to incoming requests.
|
||||
|
||||
```dockerfile
|
||||
EXPOSE 80
|
||||
```
|
||||
|
||||
Now, all we have to do is to tell Docker what command we want to run when our image is executed inside a container. We do this using the ENTRYPOINT command.
|
||||
|
||||
```dockerfile
|
||||
ENTRYPOINT ["dotnet", "myWebApp.dll"]
|
||||
```
|
||||
|
||||
Here's the complete Dockerfile.
|
||||
|
||||
```dockerfile
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0 as build-env
|
||||
WORKDIR /src
|
||||
COPY src/*.csproj .
|
||||
RUN dotnet restore
|
||||
COPY src .
|
||||
RUN dotnet publish -c Release -o /publish
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0 as runtime
|
||||
WORKDIR /publish
|
||||
COPY --from=build-env /publish .
|
||||
EXPOSE 80
|
||||
ENTRYPOINT ["dotnet", "myWebApp.dll"]
|
||||
```
|
||||
|
||||
## .dockerignore file
|
||||
|
||||
To make your [build context](../../build/building/context.md) as small as
|
||||
possible, add a [`.dockerignore` file](../../engine/reference/builder.md#dockerignore-file)
|
||||
to your `dotnet-docker` folder and copy the following into it.
|
||||
|
||||
```shell
|
||||
**/bin/
|
||||
**/obj/
|
||||
```
|
||||
|
||||
### Directory structure
|
||||
|
||||
Just to recap, we created a directory in our local machine called `dotnet-docker` and created a simple .NET application in the `src` folder. We also created a Dockerfile containing the commands to build an image as well as a .dockerignore file. The `dotnet-docker` directory structure should now look like:
|
||||
|
||||
```shell
|
||||
├── dotnet-docker
|
||||
│ ├── src/
|
||||
│ ├── Dockerfile
|
||||
│ ├── .dockerignore
|
||||
```
|
||||
|
||||
## Build an image
|
||||
|
||||
Now that we’ve created our Dockerfile, let’s build our image. To do this, we use the `docker build` command. The `docker build` command builds Docker images from a Dockerfile and a “context”. A build’s context is the set of files located in the specified PATH or URL. The Docker build process can access any of the files located in this context.
|
||||
|
||||
The build command optionally takes a `--tag` flag. The tag is used to set the name of the image and an optional tag in the format `name:tag`. We’ll leave off the optional `tag` for now to help simplify things. If you do not pass a tag, Docker uses “latest” as its default tag.
|
||||
|
||||
Let’s build our first Docker image. Change directory to the `dotnet-docker` directory and run `docker build`.
|
||||
|
||||
```console
|
||||
$ cd /path/to/dotnet-docker
|
||||
$ docker build --tag dotnet-docker .
|
||||
```
|
||||
|
||||
## View local images
|
||||
|
||||
To see a list of images we have on our local machine, we have two options. One is to use the CLI and the other is to use [Docker Desktop](../../desktop/use-desktop/images.md). As we are currently working in the terminal let’s take a look at listing images using the CLI.
|
||||
|
||||
To list images, simply run the `docker images` command.
|
||||
|
||||
```console
|
||||
$ docker images
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
dotnet-docker latest 8cae92a8fbd6 3 minutes ago 216MB
|
||||
```
|
||||
|
||||
You should see at least one image listed, the image we just built `dotnet-docker:latest`.
|
||||
|
||||
## Tag images
|
||||
|
||||
As mentioned earlier, an image name is made up of slash-separated name components. Name components may contain lowercase letters, digits and separators. A separator is defined as a period, one or two underscores, or one or more dashes. A name component may not start or end with a separator.
|
||||
|
||||
An image is made up of a manifest and a list of layers. Do not worry too much about manifests and layers at this point other than a “tag” points to a combination of these artifacts. You can have multiple tags for an image. Let’s create a second tag for the image we built and take a look at its layers.
|
||||
|
||||
To create a new tag for the image we’ve built above, run the following command.
|
||||
|
||||
```console
|
||||
$ docker tag dotnet-docker:latest dotnet-docker:v1.0.0
|
||||
```
|
||||
|
||||
The `docker tag` command creates a new tag for an image. It does not create a new image. The tag points to the same image and is just another way to reference the image.
|
||||
|
||||
Now, run the `docker images` command to see a list of our local images.
|
||||
|
||||
```console
|
||||
$ docker images
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
dotnet-docker latest 8cae92a8fbd6 4 minutes ago 216MB
|
||||
dotnet-docker v1.0.0 8cae92a8fbd6 4 minutes ago 216MB
|
||||
```
|
||||
|
||||
You can see that we have two images that start with `dotnet-docker`. We know they are the same image because if you take a look at the `IMAGE ID` column, you can see that the values are the same for the two images.
|
||||
|
||||
Let’s remove the tag that we just created. To do this, we’ll use the `rmi` command. The `rmi` command stands for remove image.
|
||||
|
||||
```console
|
||||
$ docker rmi dotnet-docker:v1.0.0
|
||||
Untagged: dotnet-docker:v1.0.0
|
||||
```
|
||||
|
||||
Note that the response from Docker tells us that the image has not been removed but only “untagged”. You can check this by running the `docker images` command.
|
||||
|
||||
```console
|
||||
$ docker images
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
dotnet-docker latest 8cae92a8fbd6 6 minutes ago 216MB
|
||||
```
|
||||
|
||||
Our image that was tagged with `:v1.0.0` has been removed, but we still have the `dotnet-docker:latest` tag available on our machine.
|
||||
|
||||
## Next steps
|
||||
|
||||
In this module, we took a look at setting up our example .NET application that we will use for the rest of the tutorial. We also created a Dockerfile that we used to build our Docker image. Then, we took a look at tagging our images and removing images. In the next module we’ll take a look at how to:
|
||||
|
||||
{{< button text="Run your image as a container" url="run-containers.md" >}}
|
||||
|
||||
## Feedback
|
||||
|
||||
Help us improve this topic by providing your feedback. Let us know what you think by creating an issue in the [Docker Docs](https://github.com/docker/docker.github.io/issues/new?title=[dotnet%20docs%20feedback]) GitHub repository. Alternatively, [create a PR](https://github.com/docker/docker.github.io/pulls) to suggest updates.
|
|
@ -1,21 +1,149 @@
|
|||
---
|
||||
title: Configure CI/CD for your application
|
||||
keywords: .net, CI/CD, local, development
|
||||
description: Learn how to Configure CI/CD for your application
|
||||
title: Configure CI/CD for your .NET application
|
||||
keywords: .net, CI/CD
|
||||
description: Learn how to Configure CI/CD for your .NET application
|
||||
---
|
||||
|
||||
## Get started with GitHub Actions
|
||||
## Prerequisites
|
||||
|
||||
{{< include "gha-tutorial.md" >}}
|
||||
Complete all the previous sections of this guide, starting with [Containerize a .NET application](containerize.md). You must have a [GitHub](https://github.com/signup) account and a [Docker](https://hub.docker.com/signup) account to complete this section.
|
||||
|
||||
## Overview
|
||||
|
||||
In this section, you'll learn how to set up and use GitHub Actions to build and test your Docker image as well as push it to Docker Hub. You will complete the following steps:
|
||||
|
||||
1. Create a new repository on GitHub.
|
||||
2. Define the GitHub Actions workflow.
|
||||
3. Run the workflow.
|
||||
|
||||
## Step one: Create the repository
|
||||
|
||||
Create a GitHub repository, configure the Docker Hub secrets, and push your source code.
|
||||
|
||||
1. [Create a new repository](https://github.com/new) on GitHub.
|
||||
|
||||
2. Open the repository **Settings**, and go to **Secrets and variables** >
|
||||
**Actions**.
|
||||
|
||||
3. Create a new secret named `DOCKER_USERNAME` and your Docker ID as value.
|
||||
|
||||
4. Create a new [Personal Access Token
|
||||
(PAT)](/docker-hub/access-tokens/#create-an-access-token) for Docker Hub. You
|
||||
can name this token `tutorial-docker`.
|
||||
|
||||
5. Add the PAT as a second secret in your GitHub repository, with the name
|
||||
`DOCKERHUB_TOKEN`.
|
||||
|
||||
6. In your local repository on your machine, run the following command to change
|
||||
the origin to the repository you just created. Make sure you change
|
||||
`your-username` to your GitHub username and `your-repository` to the name of
|
||||
the repository you created.
|
||||
|
||||
```console
|
||||
$ git remote set-url origin https://github.com/your-username/your-repository.git
|
||||
```
|
||||
|
||||
7. In your local repository on your machine, run the following command to rename
|
||||
the branch to main.
|
||||
|
||||
```console
|
||||
$ git branch -M main
|
||||
```
|
||||
|
||||
8. Run the following commands to stage, commit, and then push your local
|
||||
repository to GitHub.
|
||||
|
||||
```console
|
||||
$ git add -A
|
||||
$ git commit -m "my first commit"
|
||||
$ git push -u origin main
|
||||
```
|
||||
|
||||
## Step two: Set up the workflow
|
||||
|
||||
Set up your GitHub Actions workflow for building, testing, and pushing the image
|
||||
to Docker Hub.
|
||||
|
||||
1. Go to your repository on GitHub and then select the **Actions** tab.
|
||||
|
||||
2. Select **set up a workflow yourself**.
|
||||
|
||||
This takes you to a page for creating a new GitHub actions workflow file in
|
||||
your repository, under `.github/workflows/main.yml` by default.
|
||||
|
||||
3. In the editor window, copy and paste the following YAML configuration.
|
||||
|
||||
```yaml
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build and test
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
target: build
|
||||
load: true
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
target: final
|
||||
tags: ${{ secrets.DOCKER_USERNAME }}/${{ github.event.repository.name }}:latest
|
||||
```
|
||||
|
||||
For more information about the YAML syntax used here, see [Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions).
|
||||
|
||||
## Step three: Run the workflow
|
||||
|
||||
Save the workflow file and run the job.
|
||||
|
||||
1. Select **Commit changes...** and push the changes to the `main` branch.
|
||||
|
||||
After pushing the commit, the workflow starts automatically.
|
||||
|
||||
2. Go to the **Actions** tab. It displays the workflow.
|
||||
|
||||
Selecting the workflow shows you the breakdown of all the steps.
|
||||
|
||||
3. When the workflow is complete, go to your
|
||||
[repositories on Docker Hub](https://hub.docker.com/repositories).
|
||||
|
||||
If you see the new repository in that list, it means the GitHub Actions
|
||||
successfully pushed the image to Docker Hub.
|
||||
|
||||
## Summary
|
||||
|
||||
In this section, you learned how to set up a GitHub Actions workflow for your application.
|
||||
|
||||
Related information:
|
||||
- [Introduction to GitHub Actions](../../build/ci/github-actions/index.md)
|
||||
- [Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions)
|
||||
|
||||
## Next steps
|
||||
|
||||
In this module, you have learnt how to set up GitHub Actions workflow to an existing Docker project, optimize your workflow to improve build times and reduce the number of pull requests, and finally, we learnt how to push only specific versions to Docker Hub. You can also set up nightly tests against the latest tag, test each PR, or do something more elegant with the tags we are using and make use of the Git tag for the same tag in our image.
|
||||
Next, learn how you can deploy your application.
|
||||
|
||||
You can also consider deploying your application. For detailed instructions, see:
|
||||
|
||||
{{< button text="Deploy your app" url="deploy.md" >}}
|
||||
|
||||
## Feedback
|
||||
|
||||
Help us improve this topic by providing your feedback. Let us know what you think by creating an issue in the [Docker Docs](https://github.com/docker/docker.github.io/issues/new?title=[dotnet%20docs%20feedback]) GitHub repository. Alternatively, [create a PR](https://github.com/docker/docker.github.io/pulls) to suggest updates.
|
||||
{{< button text="Deploy your app" url="./deploy.md" >}}
|
|
@ -0,0 +1,148 @@
|
|||
---
|
||||
title: Containerize a .NET application
|
||||
keywords: .net, containerize, initialize
|
||||
description: Learn how to containerize an ASP.NET application.
|
||||
aliases:
|
||||
- /langauage/dotnet/build-images/
|
||||
- /langauage/dotnet/run-containers/
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* You have installed the latest version of [Docker
|
||||
Desktop](../../get-docker.md).
|
||||
* You have a [git client](https://git-scm.com/downloads). The examples in this
|
||||
section use a command-line based git client, but you can use any client.
|
||||
|
||||
## Overview
|
||||
|
||||
This section walks you through containerizing and running a .NET
|
||||
application.
|
||||
|
||||
## Get the sample applications
|
||||
|
||||
In this guide, you will use a pre-built .NET application. The application is
|
||||
similar to the application built in the Docker Blog article, [Building a
|
||||
Multi-Container .NET App Using Docker
|
||||
Desktop](https://www.docker.com/blog/building-multi-container-net-app-using-docker-desktop/).
|
||||
|
||||
Open a terminal, change directory to a directory that you want to work in, and
|
||||
run the following command to clone the repository.
|
||||
|
||||
```console
|
||||
$ git clone https://github.com/docker/docker-dotnet-sample
|
||||
```
|
||||
|
||||
## Test the simple application without Docker (optional)
|
||||
|
||||
You can test the sample application locally without Docker before you continue
|
||||
building and running the application with Docker. This section requires you to
|
||||
have the .NET SDK version 6.0 installed on your machine. Download and install
|
||||
[.NET 6.0 SDK](https://dotnet.microsoft.com/download).
|
||||
|
||||
Open a terminal, change directory to the `docker-dotnet-sample/src` directory,
|
||||
and run the following command.
|
||||
|
||||
```console
|
||||
$ dotnet run --urls http://localhost:5000
|
||||
```
|
||||
|
||||
Open a browser and view the application at [http://localhost:5000](http://localhost:5000). You should see a simple web application.
|
||||
|
||||
In the terminal, press `ctrl`+`c` to stop the application.
|
||||
|
||||
## Initialize Docker assets
|
||||
|
||||
Now that you have an application, you can use `docker init` to create the
|
||||
necessary Docker assets to containerize your application. Inside the
|
||||
`docker-dotnet-sample` directory, run the `docker init` command in a terminal.
|
||||
Refer to the following example to answer the prompts from `docker init`.
|
||||
|
||||
```console
|
||||
$ docker init
|
||||
Welcome to the Docker Init CLI!
|
||||
|
||||
This utility will walk you through creating the following files with sensible defaults for your project:
|
||||
- .dockerignore
|
||||
- Dockerfile
|
||||
- compose.yaml
|
||||
- README.Docker.md
|
||||
|
||||
Let's get started!
|
||||
|
||||
? What application platform does your project use? ASP.NET
|
||||
? What's the name of your solution's main project? myWebApp
|
||||
? What version of .NET do you want to use? 6.0
|
||||
? What local port do you want to use to access your server? 8080
|
||||
```
|
||||
|
||||
You should now have the following contents in your `docker-dotnet-sample`
|
||||
directory.
|
||||
|
||||
```
|
||||
├── docker-dotnet-sample/
|
||||
│ ├── .git/
|
||||
│ ├── src/
|
||||
│ ├── .dockerignore
|
||||
│ ├── compose.yaml
|
||||
│ ├── Dockerfile
|
||||
│ ├── README.Docker.md
|
||||
│ └── README.md
|
||||
```
|
||||
|
||||
To learn more about the files that `docker init` added, see the following:
|
||||
- [Dockerfile](../../engine/reference/builder.md)
|
||||
- [.dockerignore](../../engine/reference/builder.md#dockerignore-file)
|
||||
- [compose.yaml](../../compose/compose-file/_index.md)
|
||||
|
||||
## Run the application
|
||||
|
||||
Inside the `docker-dotnet-sample` directory, run the following command in a
|
||||
terminal.
|
||||
|
||||
```console
|
||||
$ docker compose up --build
|
||||
```
|
||||
|
||||
Open a browser and view the application at [http://localhost:8080](http://localhost:8080). You should see a simple web application.
|
||||
|
||||
In the terminal, press `ctrl`+`c` to stop the application.
|
||||
|
||||
### Run the application in the background
|
||||
|
||||
You can run the application detached from the terminal by adding the `-d`
|
||||
option. Inside the `docker-dotnet-sample` directory, run the following command
|
||||
in a terminal.
|
||||
|
||||
```console
|
||||
$ docker compose up --build -d
|
||||
```
|
||||
|
||||
Open a browser and view the application at [http://localhost:8080](http://localhost:8080). You should see a simple web application.
|
||||
|
||||
In the terminal, run the following command to stop the application.
|
||||
|
||||
```console
|
||||
$ docker compose down
|
||||
```
|
||||
|
||||
For more information about Compose commands, see the [Compose CLI
|
||||
reference](../../compose/reference/_index.md).
|
||||
|
||||
## Summary
|
||||
|
||||
In this section, you learned how you can containerize and run your .NET
|
||||
application using Docker.
|
||||
|
||||
Related information:
|
||||
- [Dockerfile reference](../../engine/reference/builder.md)
|
||||
- [Build with Docker guide](../../build/guide/index.md)
|
||||
- [.dockerignore file reference](../../engine/reference/builder.md#dockerignore-file)
|
||||
- [Docker Compose overview](../../compose/_index.md)
|
||||
|
||||
## Next steps
|
||||
|
||||
In the next section, you'll learn how you can develop your application using
|
||||
Docker containers.
|
||||
|
||||
{{< button text="Develop your application" url="develop.md" >}}
|
|
@ -1,458 +1,380 @@
|
|||
---
|
||||
title: Use containers for development
|
||||
keywords: .net, local, development, run,
|
||||
description: Learn how to develop your application locally.
|
||||
title: Use containers for .NET development
|
||||
keywords: .net, development
|
||||
description: Learn how to develop your .NET application locally using containers.
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Work through the steps to build an image and run it as a containerized application in [Run your image as a container](run-containers.md).
|
||||
Complete [Containerize a .NET application](containerize.md).
|
||||
|
||||
## Introduction
|
||||
## Overview
|
||||
|
||||
In this module, we’ll walk through setting up a local development environment for the application we built in the previous modules. We’ll use Docker to build our images and Docker Compose to make everything a whole lot easier.
|
||||
In this section, you'll learn how to set up a development environment for your containerized application. This includes:
|
||||
- Adding a local database and persisting data
|
||||
- Configuring Compose to automatically update your running Compose services as you edit and save your code
|
||||
- Creating a development container that contains the .NET Core SDK tools and dependencies
|
||||
|
||||
## Run a database in a container
|
||||
## Update the application
|
||||
|
||||
First, we’ll take a look at running a database in a container and how we use volumes and networking to persist our data and allow our application to talk with the database. Then we’ll pull everything together into a Compose file which allows us to setup and run a local development environment with one command.
|
||||
This section uses a different branch of the `docker-dotnet-sample` repository
|
||||
that contains an updated .NET application. The updated application is on the
|
||||
`add-db` branch of the repository you cloned in [Containerize a .NET
|
||||
application](containerize.md).
|
||||
|
||||
Instead of downloading PostgreSQL, installing, configuring, and then running the PostgreSQL database as a service, we can use the Docker Official Image for PostgreSQL and run it in a container.
|
||||
To get the updated code, you need to checkout the `add-db` branch. For the changes you made in [Containerize a .NET application](containerize.md), for this section, you can stash them. In a terminal, run the following commands in the `docker-dotnet-sample` directory.
|
||||
|
||||
Before we run PostgreSQL in a container, we'll create a volume that Docker can manage to store our persistent data. Let’s use the managed volumes feature that Docker provides instead of using bind mounts. You can read all about [Using volumes](../../storage/volumes.md) in our documentation.
|
||||
|
||||
Let’s create our data volume now.
|
||||
Stash any previous changes.
|
||||
|
||||
```console
|
||||
$ docker volume create postgres-data
|
||||
$ git stash -u
|
||||
```
|
||||
|
||||
Now we’ll create a network that our application and database will use to talk to each other. The network is called a user-defined bridge network and gives us a nice DNS lookup service which we can use when creating our connection string.
|
||||
Check out the new branch with the updated application.
|
||||
|
||||
```console
|
||||
$ docker network create postgres-net
|
||||
$ git checkout add-db
|
||||
```
|
||||
|
||||
Now we can run PostgreSQL in a container and attach to the volume and network we created above. Docker pulls the image from Hub and runs it for you locally.
|
||||
In the following command, option `-v` is for starting the container with the volume. For more information, see [Docker volumes](../../storage/volumes.md).
|
||||
In the `add-db` branch, only the .NET application has been updated. None of the Docker assets have been updated yet.
|
||||
|
||||
```console
|
||||
$ docker run --rm -d -v postgres-data:/var/lib/postgresql/data \
|
||||
--network postgres-net \
|
||||
--name db \
|
||||
-e POSTGRES_USER=postgres \
|
||||
-e POSTGRES_PASSWORD=example \
|
||||
postgres
|
||||
You should now have the following in your `docker-dotnet-sample` directory.
|
||||
|
||||
```
|
||||
├── docker-dotnet-sample/
|
||||
│ ├── .git/
|
||||
│ ├── src/
|
||||
│ │ ├── Data/
|
||||
│ │ ├── Models/
|
||||
│ │ ├── Pages/
|
||||
│ │ ├── Properties/
|
||||
│ │ ├── wwwroot/
|
||||
│ │ ├── appsettings.Development.json
|
||||
│ │ ├── appsettings.json
|
||||
│ │ ├── myWebApp.csproj
|
||||
│ │ └── Program.cs
|
||||
│ ├── tests/
|
||||
│ │ ├── tests.csproj
|
||||
│ │ ├── UnitTest1.cs
|
||||
│ │ └── Usings.cs
|
||||
│ ├── .dockerignore
|
||||
│ ├── .gitignore
|
||||
│ ├── compose.yaml
|
||||
│ ├── Dockerfile
|
||||
│ ├── README.Docker.md
|
||||
│ └── README.md
|
||||
```
|
||||
|
||||
Now, let’s make sure that our PostgreSQL database is running and that we can connect to it. Connect to the running PostgreSQL database inside the container using the following command:
|
||||
|
||||
```console
|
||||
$ docker exec -ti db psql -U postgres
|
||||
psql (14.5 (Debian 14.5-1.pgdg110+1))
|
||||
Type "help" for help.
|
||||
## Add a local database and persist data
|
||||
|
||||
postgres=#
|
||||
```
|
||||
You can use containers to set up local services, like a database. In this section, you'll update the `compose.yaml` file to define a database service and a volume to persist data.
|
||||
|
||||
Press CTRL-D to exit the interactive terminal.
|
||||
Open the `compose.yaml` file in an IDE or text editor. You'll notice it
|
||||
already contains commented-out instructions for a PostgreSQL database and volume.
|
||||
|
||||
Open `docker-dotnet-sample/src/appsettings.json` in an IDE or text editor. You'll
|
||||
notice the connection string with all the database information. The
|
||||
`compose.yaml` already contains this information, but it's commented out.
|
||||
Uncomment the database instructions in the `compose.yaml` file.
|
||||
|
||||
## Update the application to connect to the database
|
||||
|
||||
In the above command, we logged in to the PostgreSQL database by passing the `psql` command to the `db` container.
|
||||
|
||||
Next, we'll update the sample application we created in the [Build images](build-images.md#sample-application) module.
|
||||
|
||||
Let's add a package to allow the app to talk to a database and update the source files. On your local machine, open a terminal, change directory to the `src` directory and run the following command:
|
||||
|
||||
```console
|
||||
$ cd /path/to/dotnet-docker/src
|
||||
$ dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
|
||||
```
|
||||
|
||||
In the `src` directory, create a `Models` folder. Inside the `Models` folder create a file named `Student.cs` and add the following code to `Student.cs`:
|
||||
|
||||
```c#
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
namespace myWebApp.Models
|
||||
{
|
||||
public class Student
|
||||
{
|
||||
public int ID { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public string FirstMidName { get; set; }
|
||||
public DateTime EnrollmentDate { get; set; }
|
||||
}
|
||||
}
|
||||
```
|
||||
Save and close the `Student.cs` file.
|
||||
|
||||
In the `src` directory, create a `Data` folder. Inside the `Data` folder create a file named `SchoolContext.cs` and add the following code to `SchoolContext.cs`:
|
||||
|
||||
```c#
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
namespace myWebApp.Data
|
||||
{
|
||||
public class SchoolContext : DbContext
|
||||
{
|
||||
public SchoolContext(DbContextOptions<SchoolContext> options) : base(options) { }
|
||||
public DbSet<Models.Student>? Students { get; set; }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Save and close the `SchoolContext.cs` file.
|
||||
|
||||
In the `Program.cs` file located in the `src` directory, replace the contents with the following code:
|
||||
|
||||
```c#
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using myWebApp.Models;
|
||||
using myWebApp.Data;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Services.AddRazorPages();
|
||||
// Add services to the container.
|
||||
builder.Services.AddDbContext<SchoolContext>(options =>
|
||||
options.UseNpgsql(builder.Configuration.GetConnectionString("SchoolContext")));
|
||||
|
||||
var app = builder.Build();
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var services = scope.ServiceProvider;
|
||||
try
|
||||
{
|
||||
// add 10 seconds delay to ensure the db server is up to accept connections
|
||||
// this won't be needed in real world application
|
||||
System.Threading.Thread.Sleep(10000);
|
||||
var context = services.GetRequiredService<SchoolContext>();
|
||||
var created = context.Database.EnsureCreated();
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var logger = services.GetRequiredService<ILogger<Program>>();
|
||||
logger.LogError(ex, "An error occurred creating the DB.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseExceptionHandler("/Error");
|
||||
}
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapRazorPages();
|
||||
|
||||
app.Run();
|
||||
```
|
||||
|
||||
Save and close the `Program.cs` file.
|
||||
|
||||
|
||||
In the `appsettings.json` file located in the `src` directory, replace the contents with the following code:
|
||||
|
||||
```json
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"ConnectionStrings": {
|
||||
"SchoolContext": "Host=db;Database=my_db;Username=postgres;Password=example"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Save and close the `appsettings.json` file.
|
||||
|
||||
In the `Index.cshtml` file located in the `src\Pages` directory, replace the contents with the following code:
|
||||
|
||||
```html
|
||||
@page
|
||||
@model IndexModel
|
||||
@{
|
||||
ViewData["Title"] = "Home page";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Welcome</h1>
|
||||
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
|
||||
</div>
|
||||
|
||||
<div class="row mb-auto">
|
||||
<p>Student Name is @Model.StudentName</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
Save and close the `Index.cshtml` file.
|
||||
|
||||
In the `Index.cshtml.cs` file located in the `src\Pages` directory, replace the contents with the following code:
|
||||
|
||||
```c#
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace myWebApp.Pages;
|
||||
|
||||
public class IndexModel : PageModel
|
||||
{
|
||||
public string StudentName { get; private set; } = "PageModel in C#";
|
||||
private readonly ILogger<IndexModel> _logger;
|
||||
private readonly myWebApp.Data.SchoolContext _context;
|
||||
|
||||
public IndexModel(ILogger<IndexModel> logger, myWebApp.Data.SchoolContext context)
|
||||
{
|
||||
_logger = logger;
|
||||
_context= context;
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
var s =_context.Students?.Where(d=>d.ID==1).FirstOrDefault();
|
||||
this.StudentName = $"{s?.FirstMidName} {s?.LastName}";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Save and close the `Index.cshtml.cs` file.
|
||||
|
||||
Now we can rebuild our image. Open a terminal, change directory to the `dotnet-docker` directory and run the following command:
|
||||
|
||||
```console
|
||||
$ docker build --tag dotnet-docker .
|
||||
```
|
||||
|
||||
List your running containers.
|
||||
|
||||
```console
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
146e1cb76e71 postgres "docker-entrypoint.s…" 25 minutes ago Up 25 minutes 5432/tcp postgresqldb
|
||||
72bef28b1cd4 dotnet-docker "./myWebApp" 40 minutes ago Up 40 minutes 0.0.0.0:5000->80/tcp dotnet-app
|
||||
```
|
||||
Inspect the image column and stop any container that is using the `dotnet-docker` image.
|
||||
|
||||
```console
|
||||
$ docker stop dotnet-app
|
||||
dotnet-app
|
||||
```
|
||||
|
||||
Now, let’s run our container on the same network as the database. This allows us to access the database by its container name.
|
||||
|
||||
```console
|
||||
$ docker run \
|
||||
--rm -d \
|
||||
--network postgres-net \
|
||||
--name dotnet-app \
|
||||
-p 5000:80 \
|
||||
dotnet-docker
|
||||
```
|
||||
|
||||
Let's test that the application works and is connecting to the database. Using a web browser, access `http://localhost:5000`. A page similar to the following image appears.
|
||||
|
||||

|
||||
|
||||
|
||||
## Connect Adminer and populate the database
|
||||
|
||||
You now have an application accessing the database. Although the application created a database and table, it did not create any entries. Let's connect Adminer to manage our database and create a database entry.
|
||||
|
||||
```console
|
||||
$ docker run \
|
||||
--rm -d \
|
||||
--network postgres-net \
|
||||
--name db-admin \
|
||||
-p 8080:8080 \
|
||||
adminer
|
||||
```
|
||||
|
||||
Using a web browser, access `http://localhost:8080`.
|
||||
|
||||
The Adminer login page appears.
|
||||
|
||||

|
||||
|
||||
|
||||
Specify the following in the login page and then click **Login**:
|
||||
* System: PostgreSQL
|
||||
* Server: db
|
||||
* Username: postgres
|
||||
* Password: example
|
||||
* Database: my_db
|
||||
|
||||
The `Schema: public` page appears.
|
||||
|
||||

|
||||
|
||||
In `Tables and views`, click `Students`. The `Table: Students` page appears.
|
||||
|
||||

|
||||
|
||||
Click `New item`. The `Insert: Students` page appears.
|
||||
|
||||

|
||||
|
||||
Specify a `LastName`, `FirstMidName`, and `EnrollmentDate`. Click `Save`.
|
||||
|
||||
Verify that the student name appears in the application. Use a web browser to access `http://localhost:5000`.
|
||||
|
||||

|
||||
|
||||
List and then stop the application, database, and Adminer containers.
|
||||
|
||||
```console
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
b76346800b6d adminer "entrypoint.sh docke…" 30 minutes ago Up 30 minutes 0.0.0.0:8080->8080/tcp db-admin
|
||||
4ae70ac948a1 dotnet-docker "./myWebApp" 45 minutes ago Up 45 minutes 0.0.0.0:5000->80/tcp dotnet-app
|
||||
75554c7694d8 postgres "docker-entrypoint.s…" 46 minutes ago Up 46 minutes 5432/tcp db
|
||||
```
|
||||
|
||||
```console
|
||||
$ docker stop db-admin dotnet-app db
|
||||
db-admin
|
||||
dotnet-app
|
||||
db
|
||||
```
|
||||
|
||||
## Better productivity with Docker Compose
|
||||
|
||||
In this section, we’ll create a [Compose file](../../compose/index.md) to start our dotnet-docker app, Adminer, and the PostgreSQL database using a single command.
|
||||
|
||||
Open the `dotnet-docker` directory in your IDE or a text editor and create a new file named `docker-compose.yml`. Copy and paste the following contents into the file.
|
||||
The following is the updated `compose.yaml` file.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
server:
|
||||
build:
|
||||
context: .
|
||||
target: final
|
||||
ports:
|
||||
- 8080:80
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
db:
|
||||
image: postgres
|
||||
restart: always
|
||||
user: postgres
|
||||
secrets:
|
||||
- db-password
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_PASSWORD: example
|
||||
- POSTGRES_DB=example
|
||||
- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
|
||||
expose:
|
||||
- 5432
|
||||
healthcheck:
|
||||
test: [ "CMD", "pg_isready" ]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
adminer:
|
||||
image: adminer
|
||||
restart: always
|
||||
ports:
|
||||
- 8080:8080
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
ports:
|
||||
- 5000:80
|
||||
depends_on:
|
||||
- db
|
||||
volumes:
|
||||
postgres-data:
|
||||
db-data:
|
||||
secrets:
|
||||
db-password:
|
||||
file: db/password.txt
|
||||
```
|
||||
|
||||
Save and close the `docker-compose.yml` file.
|
||||
> **Note**
|
||||
>
|
||||
> To learn more about the instructions in the Compose file, see [Compose file
|
||||
> reference](/compose/compose-file/).
|
||||
|
||||
The `dotnet-docker` directory structure should now look like:
|
||||
Before you run the application using Compose, notice that this Compose file uses
|
||||
`secrets` and specifies a `password.txt` file to hold the database's password.
|
||||
You must create this file as it's not included in the source repository.
|
||||
|
||||
```shell
|
||||
├── dotnet-docker
|
||||
In the `docker-dotnet-sample` directory, create a new directory named `db` and
|
||||
inside that directory create a file named `password.txt`. Open `password.txt` in an IDE or text editor and add the following password. The password must be on a single line, with no additional lines in the file.
|
||||
|
||||
```
|
||||
example
|
||||
```
|
||||
|
||||
Save and close the `password.txt` file.
|
||||
|
||||
You should now have the following in your `docker-dotnet-sample` directory.
|
||||
|
||||
```
|
||||
├── docker-dotnet-sample/
|
||||
│ ├── .git/
|
||||
│ ├── db/
|
||||
│ │ └── password.txt
|
||||
│ ├── src/
|
||||
│ ├── Dockerfile
|
||||
│ ├── tests/
|
||||
│ ├── .dockerignore
|
||||
│ ├── docker-compose.yml
|
||||
│ ├── .gitignore
|
||||
│ ├── compose.yaml
|
||||
│ ├── Dockerfile
|
||||
│ ├── README.Docker.md
|
||||
│ └── README.md
|
||||
```
|
||||
|
||||
This Compose file is super convenient as we do not have to type all the parameters to pass to the `docker run` command. We can declaratively do that using a Compose file.
|
||||
|
||||
We expose the ports so that we can reach the web server and Adminer inside the containers. We also map our local source code into the running container to make changes in our text editor and have those changes picked up in the container.
|
||||
|
||||
Another really cool feature of using a Compose file is that we have service resolution set up to use the service names. Therefore, we are now able to use “db” in our connection string. The reason we use “db” is because that is what we've named our PostgreSQL service as in the Compose file.
|
||||
|
||||
Now, to start our application and to confirm that it is running properly, run the following command:
|
||||
Run the following command to start your application.
|
||||
|
||||
```console
|
||||
$ docker compose up --build
|
||||
```
|
||||
|
||||
We pass the `--build` flag so Docker will compile our image and then start the containers.
|
||||
Open a browser and view the application at [http://localhost:8080](http://localhost:8080). You should see a simple web application with the text `Student name is`.
|
||||
|
||||
Now let’s test our application. Using a web browser, access `http://localhost:5000` to view the page.
|
||||
The application doesn't display a name because the database is empty. For this application, you need to access the database and then add records.
|
||||
|
||||
## Shutting down
|
||||
## Add records to the database
|
||||
|
||||
To stop the containers started by Docker Compose, press Ctrl+C in the terminal where we ran `docker compose up`. To remove those containers after they have been stopped, run `docker compose down`.
|
||||
For the sample application, you must access the database directly to create sample records.
|
||||
|
||||
## Detached mode
|
||||
|
||||
You can run containers started by the `docker compose` command in detached mode, just as you would with the docker command, by using the `-d` flag.
|
||||
|
||||
To start the stack, defined by the Compose file in detached mode, run:
|
||||
You can run commands inside the database container using the `docker exec`
|
||||
command. Before running that command, you must get the ID of the database
|
||||
container. Open a new terminal window and run the following command to list all
|
||||
your running containers.
|
||||
|
||||
```console
|
||||
docker compose up --build -d
|
||||
$ docker container ls
|
||||
```
|
||||
|
||||
Then, you can use `docker compose stop` to stop the containers and `docker compose down` to remove them.
|
||||
You should see output like the following.
|
||||
|
||||
## The `.env` file
|
||||
|
||||
Docker Compose will automatically read environment variables from a `.env` file if it is available. Since our Compose file requires `POSTGRES_PASSWORD` to be set, we create an `.env` file and add the following content:
|
||||
|
||||
```shell
|
||||
POSTGRES_PASSWORD=example
|
||||
```console
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
cb36e310aa7e docker-dotnet-server "dotnet myWebApp.dll" About a minute ago Up About a minute 0.0.0.0:8080->80/tcp docker-dotnet-server-1
|
||||
39fdcf0aff7b postgres "docker-entrypoint.s…" About a minute ago Up About a minute (healthy) 5432/tcp docker-dotnet-db-1
|
||||
```
|
||||
|
||||
Now, update the compose file to use this variable.
|
||||
In the previous example, the container ID is `39fdcf0aff7b`. Run the following command to run the `psql` command inside your database container and insert a record into the database. Replace the container ID with your own container ID.
|
||||
|
||||
```console
|
||||
$ docker exec 39fdcf0aff7b psql -d example -U postgres -c "INSERT INTO \"Students\" (\"ID\", \"LastName\", \"FirstMidName\", \"EnrollmentDate\") VALUES (DEFAULT, 'Whale', 'Moby', '2013-03-20');"
|
||||
```
|
||||
You should see output like the following.
|
||||
|
||||
```console
|
||||
INSERT 0 1
|
||||
```
|
||||
|
||||
## Verify that data persists in the database
|
||||
|
||||
Open a browser and view the application at [http://localhost:8080](http://localhost:8080). You should see a simple web application with the text `Student name is Whale Moby`.
|
||||
|
||||
Press `ctrl+c` in the terminal to stop your application.
|
||||
|
||||
In the terminal, run `docker compose rm` to remove your containers and then run `docker compose up` to run your application again.
|
||||
|
||||
```console
|
||||
$ docker compose rm
|
||||
$ docker compose up --build
|
||||
```
|
||||
|
||||
Refresh [http://localhost:8080](http://localhost:8080) in your browser and verify that the student name persisted, even after the containers were removed and ran again.
|
||||
|
||||
Press `ctrl+c` in the terminal to stop your application.
|
||||
|
||||
## Automatically update services
|
||||
|
||||
Use Compose Watch to automatically update your running Compose services as you edit and save your code. For more details about Compose Watch, see [Use Compose Watch](../../compose/file-watch.md).
|
||||
|
||||
Open your `compose.yaml` file in an IDE or text editor and then add the Compose Watch instructions. The following is the updated `compose.yaml` file.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
server:
|
||||
build:
|
||||
context: .
|
||||
target: final
|
||||
ports:
|
||||
- 8080:80
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
develop:
|
||||
watch:
|
||||
- action: rebuild
|
||||
path: .
|
||||
db:
|
||||
image: postgres
|
||||
restart: always
|
||||
user: postgres
|
||||
secrets:
|
||||
- db-password
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?database password not set}
|
||||
- POSTGRES_DB=example
|
||||
- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
|
||||
expose:
|
||||
- 5432
|
||||
healthcheck:
|
||||
test: [ "CMD", "pg_isready" ]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
adminer:
|
||||
image: adminer
|
||||
restart: always
|
||||
ports:
|
||||
- 8080:8080
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
ports:
|
||||
- 5000:80
|
||||
depends_on:
|
||||
- db
|
||||
volumes:
|
||||
postgres-data:
|
||||
db-data:
|
||||
secrets:
|
||||
db-password:
|
||||
file: db/password.txt
|
||||
```
|
||||
|
||||
`POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?database password not set}` means that if the environment variable `POSTGRES_PASSWORD` is not set on the host, Docker Compose will display an error. This is OK, because we don’t want to hard-code default values for the password. We set the password value in the `.env` file, which is local to our machine. It is always a good idea to add `.env` to `.gitignore` to prevent the secrets being checked into the version control.
|
||||
|
||||
Build and run your application to confirm the changes are applied properly.
|
||||
Run the following command to run your application with Compose Watch.
|
||||
|
||||
```console
|
||||
$ docker compose up --build -d
|
||||
$ docker compose watch
|
||||
```
|
||||
|
||||
Now let’s test our application. Using a web browser, access `http://localhost:5000` to view the page.
|
||||
Open a browser and verify that the application is running at [http://localhost:8080](http://localhost:8080).
|
||||
|
||||
Any changes to the application's source files on your local machine will now be
|
||||
immediately reflected in the running container.
|
||||
|
||||
Open `docker-dotnet-sample/src/Pages/Index.cshtml` in an IDE or text editor and update the student name text on line 13 from `Student name is` to `Student name:`.
|
||||
|
||||
```diff
|
||||
- <p>Student Name is @Model.StudentName</p>
|
||||
+ <p>Student name: @Model.StudentName</p>
|
||||
```
|
||||
|
||||
Save the changes to `Index.cshmtl` and then wait a few seconds for the application to rebuild. Refresh [http://localhost:8080](http://localhost:8080) in your browser and verify that the updated text appears.
|
||||
|
||||
Press `ctrl+c` in the terminal to stop your application.
|
||||
|
||||
## Create a development container
|
||||
|
||||
At this point, when you run your containerized application, it's using the .NET runtime image. While this small image is good for production, it lacks the SDK tools and dependencies you may need when developing. Also, during development, you may not need to run `dotnet publish`. You can use multi-stage builds to build stages for both development and production in the same Dockerfile. For more details, see [Multi-stage builds](../../build/building/multi-stage.md).
|
||||
|
||||
Add a new development stage to your Dockerfile and update your `compose.yaml` file to use this stage for local development.
|
||||
|
||||
The following is the updated Dockerfile.
|
||||
|
||||
```Dockerfile
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build
|
||||
ARG TARGETARCH
|
||||
COPY . /source
|
||||
WORKDIR /source/src
|
||||
RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \
|
||||
dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS development
|
||||
COPY . /source
|
||||
WORKDIR /source/src
|
||||
CMD dotnet run --no-launch-profile
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine AS final
|
||||
WORKDIR /app
|
||||
COPY --from=build /app .
|
||||
ARG UID=10001
|
||||
RUN adduser \
|
||||
--disabled-password \
|
||||
--gecos "" \
|
||||
--home "/nonexistent" \
|
||||
--shell "/sbin/nologin" \
|
||||
--no-create-home \
|
||||
--uid "${UID}" \
|
||||
appuser
|
||||
USER appuser
|
||||
ENTRYPOINT ["dotnet", "myWebApp.dll"]
|
||||
```
|
||||
|
||||
The following is the updated `compose.yaml` file.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
server:
|
||||
build:
|
||||
context: .
|
||||
target: development
|
||||
ports:
|
||||
- 8080:80
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
develop:
|
||||
watch:
|
||||
- action: rebuild
|
||||
path: .
|
||||
environment:
|
||||
- ASPNETCORE_ENVIRONMENT=Development
|
||||
- ASPNETCORE_URLS=http://+:80'
|
||||
db:
|
||||
image: postgres
|
||||
restart: always
|
||||
user: postgres
|
||||
secrets:
|
||||
- db-password
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
environment:
|
||||
- POSTGRES_DB=example
|
||||
- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
|
||||
expose:
|
||||
- 5432
|
||||
healthcheck:
|
||||
test: [ "CMD", "pg_isready" ]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
volumes:
|
||||
db-data:
|
||||
secrets:
|
||||
db-password:
|
||||
file: db/password.txt
|
||||
```
|
||||
|
||||
Your containerized application will now use the `mcr.microsoft.com/dotnet/sdk:6.0-alpine` image, which includes development tools like `dotnet test`. Continue to the next section to learn how you can run `dotnet test`.
|
||||
|
||||
## Summary
|
||||
|
||||
In this section, you took a look at setting up your Compose file to add a local
|
||||
database and persist data. You also learned how to use Compose Watch to automatically rebuild and run your container when you update your code. And finally, you learned how to create a development container that contains the SDK tools and dependencies needed for development.
|
||||
|
||||
Related information:
|
||||
- [Compose file reference](/compose/compose-file/)
|
||||
- [Compose file watch](../../compose/file-watch.md)
|
||||
- [Multi-stage builds](../../build/building/multi-stage.md)
|
||||
|
||||
## Next steps
|
||||
|
||||
In the next module, we’ll take a look at how to write containerized tests in Docker. See:
|
||||
In the next section, you'll learn how to run unit tests using Docker.
|
||||
|
||||
{{< button text="Run your tests" url="run-tests.md" >}}
|
||||
|
||||
## Feedback
|
||||
|
||||
Help us improve this topic by providing your feedback. Let us know what you think by creating an issue in the [Docker Docs](https://github.com/docker/docker.github.io/issues/new?title=[dotnet%20docs%20feedback]) GitHub repository. Alternatively, [create a PR](https://github.com/docker/docker.github.io/pulls) to suggest updates.
|
||||
|
|
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 18 KiB |
|
@ -1,168 +0,0 @@
|
|||
---
|
||||
title: Run your image as a container
|
||||
keywords: .net, run, image, container,
|
||||
description: Learn how to run the image as a container.
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Work through the steps to build a .NET image in [Build your .NET image](build-images.md).
|
||||
|
||||
## Overview
|
||||
|
||||
In the previous module, we created our sample application and then we created a Dockerfile that we used to produce an image. We created our image using the docker command docker build. Now that we have an image, we can run that image and see if our application is running correctly.
|
||||
|
||||
A container is a normal operating system process except that this process is isolated in that it has its own file system, its own networking, and its own isolated process tree separate from the host.
|
||||
|
||||
To run an image inside of a container, we use the `docker run` command. The `docker run` command requires one parameter which is the name of the image. Let’s start our image and make sure it is running correctly. Run the following command in your terminal.
|
||||
|
||||
```console
|
||||
$ docker run dotnet-docker
|
||||
```
|
||||
|
||||
After running this command, you’ll notice that you were not returned to the command prompt. This is because our application is a server and runs in a loop waiting for incoming requests without returning control back to the OS until we stop the container.
|
||||
|
||||
Let’s open a new browser and access `http://localhost:80`.
|
||||
|
||||
As you'll see, the connection to our server was refused. This means, we were not able to connect to the localhost on port 80. This is expected because our container is running in isolation which includes networking. Let’s stop the container and restart with port 5000 published on our local network.
|
||||
|
||||
To stop the container, press Ctrl-C. This will return you to the terminal prompt.
|
||||
|
||||
To publish a port for our container, we’ll use the `--publish` flag (`-p` for short) on the `docker run` command. The format of the `--publish` command is `[host port]:[container port]`. So, if we wanted to expose port 80 inside the container to port 5000 outside the container, we would pass `5000:80` to the `--publish` flag. Run the container using the following command:
|
||||
|
||||
```console
|
||||
$ docker run --publish 5000:80 dotnet-docker
|
||||
```
|
||||
|
||||
Now, let's access `http://localhost:5000` in a browser. You should see a page similar to the following image.
|
||||
|
||||

|
||||
|
||||
Success! We were able to connect to the application running inside of our container on port 80.
|
||||
|
||||
Press Ctrl-C to stop the container.
|
||||
|
||||
## Run in detached mode
|
||||
|
||||
This is great so far, but our sample application is a web server and we don't have to be connected to the container. Docker can run your container in detached mode or in the background. To do this, we can use the `--detach` or `-d` for short. Docker starts your container the same as before but this time will “detach” from the container and return you to the terminal prompt.
|
||||
|
||||
```console
|
||||
$ docker run -d -p 5000:80 dotnet-docker
|
||||
ce02b3179f0f10085db9edfccd731101868f58631bdf918ca490ff6fd223a93b
|
||||
```
|
||||
|
||||
Docker started our container in the background and printed the Container ID on the terminal.
|
||||
|
||||
Again, let’s make sure that our container is running properly. In a web browser, access `http://localhost:5000`. You should see a page similar to the following image.
|
||||
|
||||

|
||||
|
||||
## List containers
|
||||
|
||||
Since we ran our container in the background, how do we know if our container is running or what other containers are running on our machine? Well, we can run the `docker ps` command. Just like on Linux to see a list of processes on your machine, we would run the `ps` command. In the same spirit, we can run the `docker ps` command which displays a list of containers running on our machine.
|
||||
|
||||
```console
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
ce02b3179f0f dotnet-docker "./myWebApp" 6 minutes ago Up 6 minutes 0.0.0.0:5000->80/tcp wonderful_kalam
|
||||
```
|
||||
|
||||
The `docker ps` command provides a bunch of information about our running containers. We can see the container ID, The image running inside the container, the command that was used to start the container, when it was created, the status, ports that exposed and the name of the container.
|
||||
|
||||
You are probably wondering where the name of our container is coming from. Since we didn’t provide a name for the container when we started it, Docker generated a random name. We’ll fix this in a minute, but first we need to stop the container. To stop the container, run the `docker stop` command which does just that, stops the container. You need to pass the name of the container or you can use the container ID.
|
||||
|
||||
```console
|
||||
$ docker stop wonderful_kalam
|
||||
wonderful_kalam
|
||||
```
|
||||
|
||||
Now, rerun the `docker ps` command to see a list of running containers.
|
||||
|
||||
```console
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
```
|
||||
|
||||
## Stop, start, and name containers
|
||||
|
||||
You can start, stop, and restart Docker containers. When we stop a container, it is not removed, but the status is changed to stopped and the process inside the container is stopped. When we ran the `docker ps` command in the previous module, the default output only shows running containers. When we pass the `--all` or `-a` for short, we see all containers on our machine, irrespective of their start or stop status.
|
||||
|
||||
```console
|
||||
$ docker ps -a
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
ce02b3179f0f dotnet-docker "./myWebApp" 16 minutes ago Exited (0) 5 minutes ago wonderful_kalam
|
||||
ec45285c456d dotnet-docker "./myWebApp" 28 minutes ago Exited (0) 20 minutes ago agitated_moser
|
||||
fb7a41809e5d dotnet-docker "./myWebApp" 37 minutes ago Exited (0) 36 minutes ago goofy_khayyam
|
||||
```
|
||||
|
||||
You should now see several containers listed. These are containers that we started and stopped but have not been removed.
|
||||
|
||||
Let’s restart the container that we just stopped. Locate the name of the container we just stopped and replace the name of the container below in the restart command.
|
||||
|
||||
```console
|
||||
$ docker restart wonderful_kalam
|
||||
```
|
||||
|
||||
Now list all the containers again using the `docker ps` command.
|
||||
|
||||
```console
|
||||
$ docker ps --all
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
ce02b3179f0f dotnet-docker "./myWebApp" 19 minutes ago Up 8 seconds 0.0.0.0:5000->80/tcp wonderful_kalam
|
||||
ec45285c456d dotnet-docker "./myWebApp" 31 minutes ago Exited (0) 23 minutes ago agitated_moser
|
||||
fb7a41809e5d dotnet-docker "./myWebApp" 40 minutes ago Exited (0) 39 minutes ago goofy_khayyam
|
||||
```
|
||||
|
||||
Notice that the container we just restarted has been started in detached mode and has port 80 exposed. Also, observe the status of the container is `Up X seconds`. When you restart a container, it starts with the same flags or commands that it was originally started with.
|
||||
|
||||
Now, let’s stop and remove all of our containers and take a look at fixing the random naming issue. Stop the container we just started. Find the name of your running container and replace the name in the command below with the name of the container on your system.
|
||||
|
||||
```console
|
||||
$ docker stop wonderful_kalam
|
||||
wonderful_kalam
|
||||
```
|
||||
|
||||
Now that all of our containers are stopped, let’s remove them. When you remove a container, it is no longer running, nor it is in the stopped status, but the process inside the container has been stopped and the metadata for the container has been removed.
|
||||
|
||||
```console
|
||||
$ docker ps --all
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
ce02b3179f0f dotnet-docker "./myWebApp" 19 minutes ago Exited (0) 2 minutes ago wonderful_kalam
|
||||
ec45285c456d dotnet-docker "./myWebApp" 31 minutes ago Exited (0) 23 minutes ago agitated_moser
|
||||
fb7a41809e5d dotnet-docker "./myWebApp" 40 minutes ago Exited (0) 39 minutes ago goofy_khayyam
|
||||
```
|
||||
|
||||
To remove a container, simple run the `docker rm` command passing the container name. You can pass multiple container names to the command using a single command. Again, replace the container names in the following command with the container names from your system.
|
||||
|
||||
```console
|
||||
$ docker rm wonderful_kalam agitated_moser goofy_khayyam
|
||||
wonderful_kalam
|
||||
agitated_moser
|
||||
goofy_khayyam
|
||||
```
|
||||
|
||||
Run the `docker ps --all` command again to see that all containers are removed.
|
||||
|
||||
Now, let’s address the random naming issue. Standard practice is to name your containers for the simple reason that it is easier to identify what is running in the container and what application or service it is associated with.
|
||||
|
||||
To name a container, we just need to pass the `--name` flag to the `docker run` command.
|
||||
|
||||
```console
|
||||
$ docker run -d -p 5000:80 --name dotnet-app dotnet-docker
|
||||
1aa5d46418a68705c81782a58456a4ccdb56a309cb5e6bd399478d01eaa5cdda
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
1aa5d46418a6 dotnet-docker "./myWebApp" 3 seconds ago Up 3 seconds 0.0.0.0:5000->80/tcp dotnet-app
|
||||
```
|
||||
|
||||
That’s better! We can now easily identify our container based on the name.
|
||||
|
||||
## Next steps
|
||||
|
||||
In this module, we took a look at running containers, publishing ports, and running containers in detached mode. We also took a look at managing containers by starting, stopping, and, restarting them. We also looked at naming our containers so they are more easily identifiable. In the next module, we’ll learn how to run a database in a container and connect it to our application. See:
|
||||
|
||||
{{< button text="How to develop your application" url="develop.md" >}}
|
||||
|
||||
## Feedback
|
||||
|
||||
Help us improve this topic by providing your feedback. Let us know what you think by creating an issue in the [Docker Docs](https://github.com/docker/docker.github.io/issues/new?title=[dotnet%20docs%20feedback]) GitHub repository. Alternatively, [create a PR](https://github.com/docker/docker.github.io/pulls) to suggest updates.
|
|
@ -1,142 +1,117 @@
|
|||
---
|
||||
title: Run your tests
|
||||
keywords: .NET, build, test
|
||||
description: How to build and run your tests
|
||||
title: Run .NET tests in a container
|
||||
keywords: .NET, test
|
||||
description: Learn how to run your .NET tests in a container.
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Work through the steps to build an image and run it as a containerized application in [Use containers for development](develop.md).
|
||||
Complete all the previous sections of this guide, starting with [Containerize a .NET application](containerize.md).
|
||||
|
||||
## Introduction
|
||||
## Overview
|
||||
|
||||
Testing is an essential part of modern software development. In combination with 3rd party frameworks or services, Docker helps to test applications without mocks or complicated environment configurations fast and reliably.
|
||||
Testing is an essential part of modern software development. Testing can mean a
|
||||
lot of things to different development teams. There are unit tests, integration
|
||||
tests and end-to-end testing. In this guide you take a look at running your unit
|
||||
tests in Docker when developing and when building.
|
||||
|
||||
## Add .NET test project
|
||||
## Run tests when developing locally
|
||||
|
||||
To test our sample application, we create a standalone test project from a template using the .NET CLI. On your local machine, open a terminal, change the directory to the `dotnet-docker` directory and run the following command:
|
||||
The sample application already has an xUnit test inside the `tests` directory. When developing locally, you can use Compose to run your tests.
|
||||
|
||||
Run the following command in the `docker-dotnet-sample` directory to run the tests inside a container.
|
||||
|
||||
```console
|
||||
$ cd /path/to/dotnet-docker
|
||||
$ dotnet new xunit -n myWebApp.Tests -o tests
|
||||
$ docker compose run --build --rm server dotnet test /source/tests
|
||||
```
|
||||
|
||||
Next, we'll update the test project and add the Testcontainers for .NET package that allows us to run tests against Docker resources (PostgreSQL container). Switch to the `tests` directory and run the following command:
|
||||
|
||||
```console
|
||||
$ dotnet add package Testcontainers --version 2.3.0
|
||||
```
|
||||
|
||||
## Add a test
|
||||
|
||||
Open the test project in your favorite IDE and replace the contents of `UnitTest1.cs` with the following code:
|
||||
|
||||
```c#
|
||||
using System.Net;
|
||||
using DotNet.Testcontainers.Builders;
|
||||
using DotNet.Testcontainers.Containers;
|
||||
using DotNet.Testcontainers.Networks;
|
||||
|
||||
public sealed class UnitTest1 : IAsyncLifetime, IDisposable
|
||||
{
|
||||
private const ushort HttpPort = 80;
|
||||
|
||||
private readonly CancellationTokenSource _cts = new(TimeSpan.FromMinutes(1));
|
||||
|
||||
private readonly IDockerNetwork _network;
|
||||
|
||||
private readonly IDockerContainer _dbContainer;
|
||||
|
||||
private readonly IDockerContainer _appContainer;
|
||||
|
||||
public UnitTest1()
|
||||
{
|
||||
_network = new TestcontainersNetworkBuilder()
|
||||
.WithName(Guid.NewGuid().ToString("D"))
|
||||
.Build();
|
||||
|
||||
_dbContainer = new TestcontainersBuilder<TestcontainersContainer>()
|
||||
.WithImage("postgres")
|
||||
.WithNetwork(_network)
|
||||
.WithNetworkAliases("db")
|
||||
.WithVolumeMount("postgres-data", "/var/lib/postgresql/data")
|
||||
.Build();
|
||||
|
||||
_appContainer = new TestcontainersBuilder<TestcontainersContainer>()
|
||||
.WithImage("dotnet-docker")
|
||||
.WithNetwork(_network)
|
||||
.WithPortBinding(HttpPort, true)
|
||||
.WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(HttpPort))
|
||||
.Build();
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
await _network.CreateAsync(_cts.Token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await _dbContainer.StartAsync(_cts.Token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await _appContainer.StartAsync(_cts.Token)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_cts.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Test1()
|
||||
{
|
||||
using var httpClient = new HttpClient();
|
||||
httpClient.BaseAddress = new UriBuilder("http", _appContainer.Hostname, _appContainer.GetMappedPublicPort(HttpPort)).Uri;
|
||||
|
||||
var httpResponseMessage = await httpClient.GetAsync(string.Empty)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var body = await httpResponseMessage.Content.ReadAsStringAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, httpResponseMessage.StatusCode);
|
||||
Assert.Contains("Welcome", body);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The test class picks up the configurations and lessons we learned in the previous steps. It connects our application and database through a custom Docker network and runs an HTTP request against our application. As you can see, running containerized tests allows us to test applications without mocks or complicated environment configurations. The tests run on any Docker-API compatible environments including CI.
|
||||
|
||||
## Run the test
|
||||
|
||||
Before you run the test, [stop](run-containers.md#stop-start-and-name-containers) any running containers from the previous sections.
|
||||
|
||||
To run the test, change directory to the `dotnet-docker` directory and run the following `dotnet test` command:
|
||||
|
||||
```console
|
||||
$ dotnet test tests
|
||||
```
|
||||
|
||||
You should see output like the following:
|
||||
You should see output that contains the following.
|
||||
|
||||
```console
|
||||
Starting test execution, please wait...
|
||||
A total of 1 test files matched the specified pattern.
|
||||
|
||||
Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: < 1 ms - myWebApp.Tests.dll (net6.0)
|
||||
Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: < 1 ms - /source/tests/bin/Debug/net6.0/tests.dll (net6.0)
|
||||
```
|
||||
|
||||
To learn more about the command, see [docker compose run](/engine/reference/commandline/compose_run/).
|
||||
|
||||
## Run tests when building
|
||||
|
||||
To run your tests when building, you need to update your Dockerfile. You can create a new test stage that runs the tests, or run the tests in the existing build stage. For this guide, update the Dockerfile to run the tests in the build stage.
|
||||
|
||||
The following is the updated Dockerfile.
|
||||
|
||||
```dockerfile
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build
|
||||
ARG TARGETARCH
|
||||
COPY . /source
|
||||
WORKDIR /source/src
|
||||
RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \
|
||||
dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app
|
||||
RUN dotnet test /source/tests
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS development
|
||||
COPY . /source
|
||||
WORKDIR /source/src
|
||||
CMD dotnet run --no-launch-profile
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine AS final
|
||||
WORKDIR /app
|
||||
COPY --from=build /app .
|
||||
ARG UID=10001
|
||||
RUN adduser \
|
||||
--disabled-password \
|
||||
--gecos "" \
|
||||
--home "/nonexistent" \
|
||||
--shell "/sbin/nologin" \
|
||||
--no-create-home \
|
||||
--uid "${UID}" \
|
||||
appuser
|
||||
USER appuser
|
||||
ENTRYPOINT ["dotnet", "myWebApp.dll"]
|
||||
```
|
||||
|
||||
Run the following command to build an image using the build stage as the target and view the test results. Include `--progress=plain` to view the build output, `--no-cache` to ensure the tests always run, and `--target build` to target the build stage.
|
||||
|
||||
```console
|
||||
$ docker build -t dotnet-docker-image-test --progress=plain --no-cache --target build .
|
||||
```
|
||||
|
||||
You should see output containing the following.
|
||||
|
||||
```console
|
||||
#11 [build 5/5] RUN dotnet test /source/tests
|
||||
#11 1.564 Determining projects to restore...
|
||||
#11 3.421 Restored /source/src/myWebApp.csproj (in 1.02 sec).
|
||||
#11 19.42 Restored /source/tests/tests.csproj (in 17.05 sec).
|
||||
#11 27.91 myWebApp -> /source/src/bin/Debug/net6.0/myWebApp.dll
|
||||
#11 28.47 tests -> /source/tests/bin/Debug/net6.0/tests.dll
|
||||
#11 28.49 Test run for /source/tests/bin/Debug/net6.0/tests.dll (.NETCoreApp,Version=v6.0)
|
||||
#11 28.67 Microsoft (R) Test Execution Command Line Tool Version 17.3.3 (x64)
|
||||
#11 28.67 Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
#11 28.68
|
||||
#11 28.97 Starting test execution, please wait...
|
||||
#11 29.03 A total of 1 test files matched the specified pattern.
|
||||
#11 32.07
|
||||
#11 32.08 Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: < 1 ms - /source/tests/bin/Debug/net6.0/tests.dll (net6.0)
|
||||
#11 DONE 32.2s
|
||||
```
|
||||
|
||||
To learn more about building and running tests, see the [Build with Docker guide](../../build/guide/_index.md).
|
||||
|
||||
## Summary
|
||||
|
||||
In this section, you learned how to run tests when developing locally using Compose and how to run tests when building your image.
|
||||
|
||||
Related information:
|
||||
- [docker compose run](/engine/reference/commandline/compose_run/)
|
||||
- [Build with Docker guide](../../build/guide/index.md)
|
||||
|
||||
## Next steps
|
||||
|
||||
In the next module, we’ll take a look at how to set up a CI/CD pipeline using GitHub Actions. See:
|
||||
Next, you’ll learn how to set up a CI/CD pipeline using GitHub Actions.
|
||||
|
||||
{{< button text="Configure CI/CD" url="configure-ci-cd.md" >}}
|
||||
|
||||
## Feedback
|
||||
|
||||
Help us improve this topic by providing your feedback. Let us know what you think by creating an issue in the [Docker Docs](https://github.com/docker/docker.github.io/issues/new?title=[dotnet%20docs%20feedback]) GitHub repository. Alternatively, [create a PR](https://github.com/docker/docker.github.io/pulls) to suggest updates.
|
||||
|
|
|
@ -93,10 +93,8 @@ Guides:
|
|||
section:
|
||||
- title: "Overview"
|
||||
path: /language/dotnet/
|
||||
- title: "Build images"
|
||||
path: /language/dotnet/build-images/
|
||||
- title: "Run containers"
|
||||
path: /language/dotnet/run-containers/
|
||||
- title: "Containerize your app"
|
||||
path: /language/dotnet/containerize/
|
||||
- title: "Develop your app"
|
||||
path: /language/dotnet/develop/
|
||||
- title: "Run your tests"
|
||||
|
|