- Add a package reference to Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore version 2.2.0.
- Add a DbContext health check in
Startup.cs
by adding the following code toConfigureServices
:services.AddHealthChecks() .AddDbContextCheck<ApplicationDbContext>();
- Add the health checks middleware by modifying
Configure
to callapp.UseHealthChecks("/health");
. This will configure the health checks on the/health
end point. - Test the end point by navigating to
/health
, it should return the textHealthy
.
- Add a package reference to Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore version 2.2.0.
- Add a DbContext health check in
Startup.cs
by adding the following code toConfigureServices
:services.AddHealthChecks() .AddDbContextCheck<IdentityDbContext>();
- Add the health checks middleware by modifying
Configure
to callapp.UseHealthChecks("/health");
. This will configure the health checks on the/health
end point. - Test the end point by navigating to
/health
, it should return the textHealthy
.
- Add a
Task<bool> CheckHealthAsync()
method toIApiClient
. - Implement the
CheckHealthAsync
method inApiClient
by adding the following code:public async Task<bool> CheckHealthAsync() { try { var response = await _httpClient.GetStringAsync("/health"); return string.Equals(response, "Healthy", StringComparison.OrdinalIgnoreCase); } catch { return false; } }
- Create a
HealthChecks
folder under the root folder in the FrontEnd project. - Create a file called
BackendHealthCheck.cs
with the following implementation:using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using FrontEnd.Services; using Microsoft.Extensions.Diagnostics.HealthChecks; namespace FrontEnd.HealthChecks { public class BackendHealthCheck : IHealthCheck { private readonly IApiClient _client; public BackendHealthCheck(IApiClient client) { _client = client; } public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default(CancellationToken)) { if (await _client.CheckHealthAsync()) { return HealthCheckResult.Healthy(); } return HealthCheckResult.Unhealthy(); } } }
- Register the
BackendHealthCheck
inConfigureServices
:services.AddHealthChecks() .AddCheck<BackendHealthCheck>("backend") .AddDbContextCheck<IdentityDbContext>();
- Test the end point by navigating to
/health
, it should return the textHealthy
.
https://docs.microsoft.com/en-us/aspnet/core/publishing/#publish-to-a-folder
Prerequisites
Add Docker support to the BackEnd project by right clicking the project file and selecting Add > Docker Support
A new project is added to the solution docker-compose.dcproj containing the following files.
- .dockerignore
- docker-compose.yml
- docker-compose.override.yml
A Dockerfile is also added to the BackEnd project.
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
WORKDIR /src
COPY ConferencePlanner.sln ./
COPY BackEnd/BackEnd.csproj BackEnd/
COPY ConferenceDTO/ConferenceDTO.csproj ConferenceDTO/
RUN dotnet restore -nowarn:msb3202,nu1503
COPY . .
WORKDIR /src/BackEnd
RUN dotnet build -c Release -o /app
FROM build AS publish
RUN dotnet publish -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "BackEnd.dll"]
Repeat the same step for the FrontEnd project. The Dockerfile is added to the project for it.
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
WORKDIR /src
COPY ConferencePlanner.sln ./
COPY FrontEnd/FrontEnd.csproj FrontEnd/
COPY ConferenceDTO/ConferenceDTO.csproj ConferenceDTO/
RUN dotnet restore -nowarn:msb3202,nu1503
COPY . .
WORKDIR /src/FrontEnd
RUN dotnet build -c Release -o /app
FROM build AS publish
RUN dotnet publish -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "FrontEnd.dll"]
The docker-compose.yml file is updated to reflect that there are two projects to build images.
version: '3'
services:
backend:
image: backend
build:
context: .
dockerfile: BackEnd/Dockerfile
frontend:
image: frontend
build:
context: .
dockerfile: FrontEnd/Dockerfile
In the previous architecture, there were some setting and manual orchestration to start up and wire the applications together.
- BackEnd tested for OS type and set the application to use SQL Server or SQLite
- FrontEnd application had the API url set in the appsetting.json file.
- BackEnd hard coded the url:port -
http://localhost:56009
Using Docker, adding a container for SQL Server and linking the containers in the compose file simplifies this.
Open the docker-compose.yml file and add the following entry. Note the $ is doubled for escaping
db:
image: "microsoft/mssql-server-linux"
environment:
SA_PASSWORD: "ConferencePlanner1234$$"
ACCEPT_EULA: "Y
Since the BackEnd application must have connectivity and cannot start until the database container is ready. Add the depends_on entry to the backend definition in the compose file.
backend:
image: backend
build:
context: .
dockerfile: BackEnd/Dockerfile
depends_on:
- db
Finally, change the connection string for the database in the BackEnd\appsettings.json file.
"ConnectionStrings": {
"DefaultConnection": "Server=db;Initial Catalog=ConferencePlanner;User=sa;Password=ConferencePlanner1234$;MultipleActiveResultSets=true"
}
In the docker-compose.yml file, add the links section to the frontend definition. This sets up the host name in the Docker networking allowing for the web application to call the API by name. http://backend
frontend:
image: frontend
build:
context: .
dockerfile: FrontEnd/Dockerfile
links:
- backend
Change the value for the ServiceUrl in FrontEnd/appsetting.json
{
"ServiceUrl": "http://backend/",
Remove or comment out the .UseUrls(http://localhost:56009)
in BackEnd\Program.cs.
Finally open the docker-compose.override.yml file. Change the ports for the backend entry to 56009:80
and for the frontend, make it 5001:80
version: '3'
services:
backend:
environment:
- ASPNETCORE_ENVIRONMENT=Development
ports:
- "56009:80"
frontend:
environment:
- ASPNETCORE_ENVIRONMENT=Development
ports:
- "5001:80"
By changing these values, the API can still be hit via http://localhost:56009
, allowing for testing using the swagger UI. The FrontEnd web application will run on http://localhost:5001
, however communicates to the API via internal networking hostname backend.
Once the changes are complete, F5 to build start the application in Docker. Debugging is still available in all projects, but now the application is running in containers.
Changes can be made to Razor pages and seen immediately without rebuilds, however and *.cs file changes require rebuilds.
Create and add the following Dockerfile for the BackEnd application.
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
WORKDIR /src
COPY ConferencePlanner.sln ./
COPY BackEnd/BackEnd.csproj BackEnd/
COPY FrontEnd/FrontEnd.csproj FrontEnd/
COPY ConferenceDTO/ConferenceDTO.csproj ConferenceDTO/
RUN dotnet restore
COPY . .
WORKDIR /src/BackEnd
RUN dotnet build -c Release -o /app
FROM build AS publish
RUN dotnet publish -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "BackEnd.dll"]
Create and add the following Dockerfile for the BackEnd application.
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
WORKDIR /src
COPY ConferencePlanner.sln ./
COPY BackEnd/BackEnd.csproj BackEnd/
COPY FrontEnd/FrontEnd.csproj FrontEnd/
COPY ConferenceDTO/ConferenceDTO.csproj ConferenceDTO/
RUN dotnet restore
COPY . .
WORKDIR /src/FrontEnd
RUN dotnet build -c Release -o /app
FROM build AS publish
RUN dotnet publish -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "FrontEnd.dll"]
At the root of the ConferencePlanner solution, add the following docker-compose.yml file.
version: '3'
services:
backend:
image: backend
build:
context: .
dockerfile: BackEnd/Dockerfile
ports:
- "56009:80"
environment:
- ASPNETCORE_ENVIRONMENT=Development
depends_on:
- db
frontend:
image: frontend
build:
context: .
dockerfile: FrontEnd/Dockerfile
ports:
- "5002:80"
environment:
- ASPNETCORE_ENVIRONMENT=Development
links:
- backend
db:
image: "microsoft/mssql-server-linux"
environment:
SA_PASSWORD: "ConferencePlanner1234$$"
ACCEPT_EULA: "Y"
Change the connection string for the database in the BackEnd\appsettings.json file.
"ConnectionStrings": {
"DefaultConnection": "Server=db;Initial Catalog=ConferencePlanner;User=sa;Password=ConferencePlanner1234$;MultipleActiveResultSets=true"
}
Change the value for the ServiceUrl in FrontEnd/appsetting.json
{
"ServiceUrl": "http://backend/",
Remove or comment out the .UseUrls(http://localhost:56009)
in BackEnd\Program.cs.
- Build the Docker images
docker-compose build
- Run
docker-compose up -d
to start the application - Run
docker-compose down
to stop the application - Open the application at http://localhost:5002
A helm chart is located in /save-points/6-Deployment-docker/helm allowing for deployment to a Kubernetes cluster using helm install ./workshop
.
Here values for the service and deployment templates are defined. Docker images and tags should be updated to your repository name in the image
and tag
fields for each part of the application prior to deplyoyment.
db:
replicaCount: 1
name: db
internalPort: 55555
externalPort: 55555
image: microsoft/mssql-server-linux
tag: latest
pullPolicy: IfNotPresent
eula: ACCEPT_EULA
eulaValue: \Y
sa: SA_PASSWORD
saValue: ConferencePlanner1234$
restart: Always
frontend:
replicaCount: 1
name: frontend
internalPort: 80
externalPort: 80
image: frontend
tag: latest
environment: Production
pullPolicy: IfNotPresent
restart: Always
backend:
replicaCount: 1
name: backend
internalPort: 80
externalPort: 80
image: backend
tag: latest
environment: Production
pullPolicy: IfNotPresent
restart: Always
Next: Session #7 - Challenges | Previous: Session #5 - Add Agenda