Skip to content

Commit 578955b

Browse files
authored
Add basic HTTP health check (#4352)
1 parent 51f5be8 commit 578955b

File tree

6 files changed

+197
-32
lines changed

6 files changed

+197
-32
lines changed

README.samples.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,31 +61,31 @@ See [Hosting ASP.NET Core Images with Docker over HTTPS](https://github.com/dotn
6161
Tags | Dockerfile | OS Version
6262
-----------| -------------| -------------
6363
dotnetapp-alpine-slim-amd64, dotnetapp, latest | [Dockerfile](https://github.com/dotnet/dotnet-docker/blob/main/samples/dotnetapp/Dockerfile.alpine-x64-slim) | Alpine
64-
aspnetapp-alpine-slim-amd64, aspnetapp | [Dockerfile](https://github.com/dotnet/dotnet-docker/blob/main/samples/aspnetapp/Dockerfile.alpine-x64-slim) | Alpine
64+
aspnetapp-alpine-slim-amd64, aspnetapp | [Dockerfile](https://github.com/dotnet/dotnet-docker/blob/main/samples/aspnetapp/Dockerfile.alpine-slim) | Alpine
6565

6666
## Linux arm32 Tags
6767
Tags | Dockerfile | OS Version
6868
-----------| -------------| -------------
6969
dotnetapp-alpine-slim-arm32v7, dotnetapp, latest | [Dockerfile](https://github.com/dotnet/dotnet-docker/blob/main/samples/dotnetapp/Dockerfile.alpine-arm32-slim) | Alpine
70-
aspnetapp-alpine-slim-arm32v7, aspnetapp | [Dockerfile](https://github.com/dotnet/dotnet-docker/blob/main/samples/aspnetapp/Dockerfile.alpine-arm32-slim) | Alpine
70+
aspnetapp-alpine-slim-arm32v7, aspnetapp | [Dockerfile](https://github.com/dotnet/dotnet-docker/blob/main/samples/aspnetapp/Dockerfile.alpine-slim) | Alpine
7171

7272
## Linux arm64 Tags
7373
Tags | Dockerfile | OS Version
7474
-----------| -------------| -------------
7575
dotnetapp-alpine-slim-arm64v8, dotnetapp, latest | [Dockerfile](https://github.com/dotnet/dotnet-docker/blob/main/samples/dotnetapp/Dockerfile.alpine-arm64-slim) | Alpine
76-
aspnetapp-alpine-slim-arm64v8, aspnetapp | [Dockerfile](https://github.com/dotnet/dotnet-docker/blob/main/samples/aspnetapp/Dockerfile.alpine-arm64-slim) | Alpine
76+
aspnetapp-alpine-slim-arm64v8, aspnetapp | [Dockerfile](https://github.com/dotnet/dotnet-docker/blob/main/samples/aspnetapp/Dockerfile.alpine-slim) | Alpine
7777

7878
## Nano Server 2022 amd64 Tags
7979
Tag | Dockerfile
8080
---------| ---------------
8181
dotnetapp-nanoserver-ltsc2022, dotnetapp, latest | [Dockerfile](https://github.com/dotnet/dotnet-docker/blob/main/samples/dotnetapp/Dockerfile)
82-
aspnetapp-nanoserver-ltsc2022, aspnetapp | [Dockerfile](https://github.com/dotnet/dotnet-docker/blob/main/samples/aspnetapp/Dockerfile)
82+
aspnetapp-nanoserver-ltsc2022, aspnetapp | [Dockerfile](https://github.com/dotnet/dotnet-docker/blob/main/samples/aspnetapp/Dockerfile.nanoserver-slim)
8383

8484
## Nano Server, version 1809 amd64 Tags
8585
Tag | Dockerfile
8686
---------| ---------------
8787
dotnetapp-nanoserver-1809, dotnetapp, latest | [Dockerfile](https://github.com/dotnet/dotnet-docker/blob/main/samples/dotnetapp/Dockerfile)
88-
aspnetapp-nanoserver-1809, aspnetapp | [Dockerfile](https://github.com/dotnet/dotnet-docker/blob/main/samples/aspnetapp/Dockerfile)
88+
aspnetapp-nanoserver-1809, aspnetapp | [Dockerfile](https://github.com/dotnet/dotnet-docker/blob/main/samples/aspnetapp/Dockerfile.nanoserver-slim)
8989

9090
You can retrieve a list of all available tags for dotnet/samples at https://mcr.microsoft.com/v2/dotnet/samples/tags/list.
9191
<!--End of generated tags-->

manifest.samples.json

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
},
9191
"platforms": [
9292
{
93-
"dockerfile": "samples/aspnetapp/Dockerfile.alpine-x64-slim",
93+
"dockerfile": "samples/aspnetapp/Dockerfile.alpine-slim",
9494
"os": "linux",
9595
"osVersion": "alpine",
9696
"tags": {
@@ -108,7 +108,7 @@
108108
},
109109
{
110110
"architecture": "arm",
111-
"dockerfile": "samples/aspnetapp/Dockerfile.alpine-arm32-slim",
111+
"dockerfile": "samples/aspnetapp/Dockerfile.alpine-slim",
112112
"os": "linux",
113113
"osVersion": "alpine",
114114
"tags": {
@@ -127,7 +127,7 @@
127127
},
128128
{
129129
"architecture": "arm64",
130-
"dockerfile": "samples/aspnetapp/Dockerfile.alpine-arm64-slim",
130+
"dockerfile": "samples/aspnetapp/Dockerfile.alpine-slim",
131131
"os": "linux",
132132
"osVersion": "alpine",
133133
"tags": {
@@ -145,7 +145,10 @@
145145
]
146146
},
147147
{
148-
"dockerfile": "samples/aspnetapp",
148+
"buildArgs": {
149+
"TAG": "1809"
150+
},
151+
"dockerfile": "samples/aspnetapp/Dockerfile.nanoserver-slim",
149152
"os": "windows",
150153
"osVersion": "nanoserver-1809",
151154
"tags": {
@@ -166,7 +169,10 @@
166169
]
167170
},
168171
{
169-
"dockerfile": "samples/aspnetapp",
172+
"buildArgs": {
173+
"TAG": "ltsc2022"
174+
},
175+
"dockerfile": "samples/aspnetapp/Dockerfile.nanoserver-slim",
170176
"os": "windows",
171177
"osVersion": "nanoserver-ltsc2022",
172178
"tags": {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# https://hub.docker.com/_/microsoft-dotnet
2+
FROM mcr.microsoft.com/dotnet/sdk:7.0-alpine AS build
3+
WORKDIR /source
4+
5+
# copy csproj and restore as distinct layers
6+
COPY aspnetapp/*.csproj .
7+
RUN dotnet restore --use-current-runtime /p:PublishReadyToRun=true
8+
9+
# copy everything else and build app
10+
COPY aspnetapp/. .
11+
RUN dotnet publish -c Release -o /app --use-current-runtime --no-restore /p:PublishTrimmed=true /p:PublishReadyToRun=true /p:PublishSingleFile=true
12+
13+
# final stage/image
14+
FROM mcr.microsoft.com/dotnet/runtime-deps:7.0-alpine
15+
WORKDIR /app
16+
COPY --from=build /app .
17+
18+
# This port needs to match the port being used
19+
HEALTHCHECK CMD wget -qO- -t1 http://localhost:80/healthz || exit 1
20+
ENTRYPOINT ["./aspnetapp"]
21+
22+
# See: https://github.com/dotnet/announcements/issues/20
23+
# Uncomment to enable globalization APIs (or delete)
24+
# ENV \
25+
# DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false \
26+
# LC_ALL=en_US.UTF-8 \
27+
# LANG=en_US.UTF-8
28+
# RUN apk add --no-cache \
29+
# icu-data-full \
30+
# icu-libs
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# escape=`
2+
3+
ARG TAG=ltsc2022
4+
# https://hub.docker.com/_/microsoft-dotnet
5+
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
6+
WORKDIR /source
7+
8+
# copy csproj and restore as distinct layers
9+
COPY aspnetapp/*.csproj .
10+
RUN dotnet restore --use-current-runtime /p:PublishReadyToRun=true
11+
12+
# copy everything else and build app
13+
COPY aspnetapp/. .
14+
RUN dotnet publish -c Release -o /app --use-current-runtime --no-restore /p:PublishTrimmed=true /p:PublishReadyToRun=true /p:PublishSingleFile=true
15+
16+
# final stage/image
17+
FROM mcr.microsoft.com/windows/nanoserver:$TAG
18+
WORKDIR /app
19+
COPY --from=build /app .
20+
HEALTHCHECK CMD curl -sf --show-error http://localhost:80/healthz || exit 1
21+
ENV `
22+
# Configure web servers to bind to port 80 when present
23+
ASPNETCORE_URLS=http://+:80 `
24+
# Enable detection of running in a container
25+
DOTNET_RUNNING_IN_CONTAINER=true
26+
27+
ENTRYPOINT ["aspnetapp"]

samples/aspnetapp/README.md

Lines changed: 99 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,26 @@ If want to skip ahead, you can try a pre-built version with the following comman
1414
docker run --rm -it -p 8000:80 mcr.microsoft.com/dotnet/samples:aspnetapp
1515
```
1616

17+
You can also call an endpoint that the app exposes:
18+
19+
```bash
20+
$ curl http://localhost:8000/Environment
21+
{"runtimeVersion":".NET 7.0.2","osVersion":"Linux 5.15.79.1-microsoft-standard-WSL2 #1 SMP Wed Nov 23 01:01:46 UTC 2022","osArchitecture":"X64","user":"root","processorCount":16,"totalAvailableMemoryBytes":67430023168,"memoryLimit":9223372036854771712,"memoryUsage":100577280}
22+
```
23+
24+
You can see the app running via `docker ps`.
25+
26+
```bash
27+
$ docker ps
28+
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
29+
d79edc6bfcb6 mcr.microsoft.com/dotnet/samples:aspnetapp "./aspnetapp" 35 seconds ago Up 34 seconds (healthy) 0.0.0.0:8080->80/tcp nice_curran
30+
```
31+
32+
You may notice that the sample includes a [health check](https://docs.docker.com/engine/reference/builder/#healthcheck), which is indicated in the "STATUS" column.
33+
1734
## Build an ASP.NET Core image
1835

19-
You can build and run a .NET-based container image using the following instructions:
36+
You can build and run an image using the following instructions:
2037

2138
```console
2239
docker build --pull -t aspnetapp .
@@ -33,10 +50,10 @@ Now listening on: http://[::]:80
3350
Application started. Press Ctrl+C to shut down.
3451
```
3552

36-
After the application starts, navigate to `http://localhost:8000` in your web browser.
37-
3853
> Note: The `-p` argument maps port 8000 on your local machine to port 80 in the container (the form of the port mapping is `host:container`). See the [Docker run reference](https://docs.docker.com/engine/reference/commandline/run/) for more information on command-line parameters. In some cases, you might see an error because the host port you select is already in use. Choose a different port in that case.
3954
55+
After the application starts, navigate to `http://localhost:8000` in your web browser.
56+
4057
You can also view the ASP.NET Core site running in the container on another machine. This is particularly useful if you are wanting to view an application running on an ARM device like a Raspberry Pi on your network. In that scenario, you might view the site at a local IP address such as `http://192.168.1.18:8000`.
4158

4259
In production, you will typically start your container with `docker run -d`. This argument starts the container as a service, without any console interaction. You then interact with it through other Docker commands or APIs exposed by the containerized application.
@@ -45,35 +62,64 @@ We recommend that you do not use `--rm` in production. It cleans up container re
4562

4663
> Note: See [Establishing docker environment](../establishing-docker-environment.md) for more information on correctly configuring Dockerfiles and `docker build` commands.
4764
48-
## Build an image for Windows Nano Server
65+
## Build an image with `HEALTHCHECK`
4966

50-
The following example demonstrates targeting Windows Nano Server (x64) explicitly (you must have [Windows containers enabled](https://docs.docker.com/docker-for-windows/#switch-between-windows-and-linux-containers)):
67+
The sample uses [ASP.NET Core Health Check middleware](https://learn.microsoft.com/aspnet/core/host-and-deploy/health-checks). You can direct Docker, Kubernetes, or other systems to use the ASP.NET Core `healthz` endpoint.
5168

52-
```console
53-
docker build --pull -t aspnetapp:nanoserver -f Dockerfile.nanoserver-x64 .
54-
docker run --rm -it -p 8000:80 aspnetapp:nanoserver
55-
```
69+
The [`HEALTHCHECK`](https://docs.docker.com/engine/reference/builder/#healthcheck) directive is implemented in the [`Dockerfile.alpine-slim`](Dockerfile.alpine-slim) and [`Dockerfile.nanoserver`](Dockerfile.nanoserver-slim). You can build those via the same pattern.
5670

57-
You can view in the app in your browser in the same way as demonstrated earlier.
71+
```bash
72+
$ docker build --pull -t aspnetapp -f Dockerfile.alpine-slim .
73+
$ docker run --rm -it -p 8000:80 aspnetapp
74+
```
5875

59-
You can use `docker images` to see the images you've built:
76+
In another terminal:
6077

61-
```console
62-
> docker images aspnetapp
63-
REPOSITORY TAG IMAGE ID CREATED SIZE
64-
aspnetapp latest b2f0ecb7bdf9 About an hour ago 353MB
65-
aspnetapp nanoserver d4b7586827f2 About an hour ago 353MB
78+
```bash
79+
$ docker ps
80+
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
81+
b143cf4ac0d1 aspnetapp "./aspnetapp" 8 seconds ago Up 7 seconds (health: starting) 0.0.0.0:8000->80/tcp fervent_lichterman
6682
```
6783

68-
## Build an image for Windows Server Core
84+
After 30s, the status should transition to "healthy" from "health: starting".
85+
86+
You can also look at health status with `docker inspect`. The following pattern uses `jq`, which makes it much easier to drill in on the interesting data.
87+
88+
```bash
89+
$ docker inspect b143cf4ac0d1 | jq .[-1].State.Health
90+
{
91+
"Status": "healthy",
92+
"FailingStreak": 0,
93+
"Log": [
94+
{
95+
"Start": "2023-01-26T23:39:06.424631566Z",
96+
"End": "2023-01-26T23:39:06.589344994Z",
97+
"ExitCode": 0,
98+
"Output": "Healthy"
99+
},
100+
{
101+
"Start": "2023-01-26T23:39:36.597795818Z",
102+
"End": "2023-01-26T23:39:36.70857373Z",
103+
"ExitCode": 0,
104+
"Output": "Healthy"
105+
}
106+
]
107+
}
108+
```
69109

70-
The instructions for Windows Server Core are very similar to Windows Nano Server. There are three different sample Dockerfile files provided for Windows Server Core, which can all be used with the same approach as the Nano Server ones.
110+
The same thing can be accomplished with PowerShell.
71111

72-
In addition, one of the samples enables using IIS as the Web Server instead of Kestrel. The following example demonstrates using that Dockerfile.
112+
```powershell
113+
> $healthLog = docker inspect 92648775bce8 | ConvertFrom-Json
114+
> $healthLog[0].State.Health.Log
73115
74-
```console
75-
docker build -t aspnetapp -f .\Dockerfile.windowsservercore-iis-x64 .
76-
docker run --rm -it -p:8080:80 aspnetapp
116+
Start End ExitCode Output
117+
----- --- -------- ------
118+
2023-01-28T10:14:54.589686-08:00 2023-01-28T10:14:54.6137922-08:00 0 Healthy
119+
2023-01-28T10:15:24.6264335-08:00 2023-01-28T10:15:24.6602762-08:00 0 Healthy
120+
2023-01-28T10:15:54.6766598-08:00 2023-01-28T10:15:54.703489-08:00 0 Healthy
121+
2023-01-28T10:16:24.7192354-08:00 2023-01-28T10:16:24.74409-08:00 0 Healthy
122+
2023-01-28T10:16:54.7499988-08:00 2023-01-28T10:16:54.7750448-08:00 0 Healthy
77123
```
78124

79125
## Build an image for Alpine, Debian or Ubuntu
@@ -113,6 +159,37 @@ aspnetapp latest 8c5d1952e3b7 10 hours ago
113159

114160
You can run these images in the same way as is done above, with Alpine.
115161

162+
## Build an image for Windows Nano Server
163+
164+
The following example demonstrates targeting Windows Nano Server (x64) explicitly (you must have [Windows containers enabled](https://docs.docker.com/docker-for-windows/#switch-between-windows-and-linux-containers)):
165+
166+
```console
167+
docker build --pull -t aspnetapp:nanoserver -f Dockerfile.nanoserver-x64 .
168+
docker run --rm -it -p 8000:80 aspnetapp:nanoserver
169+
```
170+
171+
You can view in the app in your browser in the same way as demonstrated earlier.
172+
173+
You can use `docker images` to see the images you've built:
174+
175+
```console
176+
> docker images aspnetapp
177+
REPOSITORY TAG IMAGE ID CREATED SIZE
178+
aspnetapp latest b2f0ecb7bdf9 About an hour ago 353MB
179+
aspnetapp nanoserver d4b7586827f2 About an hour ago 353MB
180+
```
181+
182+
## Build an image for Windows Server Core
183+
184+
The instructions for Windows Server Core are very similar to Windows Nano Server. There are three different sample Dockerfile files provided for Windows Server Core, which can all be used with the same approach as the Nano Server ones.
185+
186+
In addition, one of the samples enables using IIS as the Web Server instead of Kestrel. The following example demonstrates using that Dockerfile.
187+
188+
```console
189+
docker build -t aspnetapp -f .\Dockerfile.windowsservercore-iis-x64 .
190+
docker run --rm -it -p:8080:80 aspnetapp
191+
```
192+
116193
## Build an image for ARM32 and ARM64
117194

118195
By default, distro-specific .NET tags target x64, such as `6.0-alpine` or `6.0-focal`. You need to use an architecture-specific tag if you want to target ARM. Note that .NET is only supported on Alpine on ARM64 and x64, and not ARM32.

samples/aspnetapp/aspnetapp/Program.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
// Add services to the container.
44
builder.Services.AddControllersWithViews();
5+
builder.Services.AddHealthChecks();
56

67
var app = builder.Build();
8+
app.MapHealthChecks("/healthz");
79

810
// Configure the HTTP request pipeline.
911
if (!app.Environment.IsDevelopment())
@@ -24,9 +26,32 @@
2426
name: "default",
2527
pattern: "{controller=Home}/{action=Index}/{id?}");
2628

29+
30+
CancellationTokenSource cancellation = new();
31+
app.Lifetime.ApplicationStopping.Register( () =>
32+
{
33+
cancellation.Cancel();
34+
});
35+
2736
app.MapGet("/Environment", () =>
2837
{
2938
return new EnvironmentInfo();
3039
});
3140

41+
// This API demonstrates how to use task cancellation
42+
// to support graceful container shutdown via SIGTERM.
43+
// The method itself is an example and not useful.
44+
app.MapGet("/Delay/{value}", async (int value) =>
45+
{
46+
try
47+
{
48+
await Task.Delay(value, cancellation.Token);
49+
}
50+
catch(TaskCanceledException)
51+
{
52+
}
53+
54+
return new {Delay = value};
55+
});
56+
3257
app.Run();

0 commit comments

Comments
 (0)