ARCHITECTING .NET SOLUTIONS
IN A DOCKER ECOSYSTEM
ALEX THISSEN
3
Agenda
A story on creating .NET applications
using container technology
Featuring Docker and Visual Studio
With highlights and some emphasis on .NET Core
4
Task: Fit your application into a container
5
Making progress. It will fit. It must…
6
There I fixed it!
7
Building and running .NET applications
8
Running containers on Linux
9
Containers on Windows
Better isolation from
hypervisor virtualization
Container
management
Docker
Engine
Compute
10
Docker and containers
Standardized tooling
Adopted for standalone
and cluster scenarios
11
Changes when switching to containers
Environments Configuration
Security
Networking
Storage
Composition
Lifecycle
Hosting
Monitoring
12
Application architecture less relevant
Featuring your favorite patterns
Microservices or Monolith
13
Container application lifecycle
14
Web APIWeb API
containercontainer
Leaderboard
Microservice
Identity
Microservice
ASP.NET Core
Web App
Client applications (browser)
Web
Page
Games
reporting
high-scores
15
Container workflow and lifecycle
Inner loop
Run
Code
Validate
Outer loop
16
Deploying .NET apps with containers
17
VS2019 container building
Multi-stage Dockerfile per project
Can be built without VS2019
FROM mcr.microsoft.com/dotnet/core/aspnet:3.0-buster-slim AS base
WORKDIR /app
EXPOSE 13995
EXPOSE 44369
…
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "Leaderboard.WebAPI.dll"]
18
mcr.microsoft.com/windows/servercore:1903
-or- debian:buster-slimdebian:buster-slim
Docker image layers .NET Core
mcr.microsoft.com/dotnet/core/runtime:3.0
mcr.microsoft.com/dotnet/core/runtime-deps
mcr.microsoft.com/dotnet/core/aspnet:3.0
Your application layer:
e.g. alexthissen/gamingwebapp:latest
19
Container image registries
Push and store your images to centralized location
Each image is in its own repository
20
Build pipeline in Azure DevOps
Contains steps to:
Docker images need to be
created on correct host OS
Produce artifacts to be used
during deployment
21
Release pipelines in Azure DevOps
Create deployment file to apply on cluster
Elaborate scenarios involve multiple environments
Demo: Lifecycle
A new development lifecycle
for Docker based solutions
Visual Studio 2019
Azure DevOps
23
From single-node host to multi-node clusters
24
Hosting using cluster orchestrators
Cluster FabricHigh Availability
Hyper-Scale
Hybrid Operations
High Density
Rolling Upgrades
Stateful services
Low Latency
Fast startup &
shutdown
Container Orchestration &
lifecycle management
Replication &
Failover
Simple
programming
models
Load balancing
Self-healingData Partitioning
Automated Rollback
Health
Monitoring
Placement
Constraints
Microservices
Mesos DC/OS Docker Swarm Google Kubernetes Azure Service Fabric
Largely cloud provider-agnostic
25
Many more Azure options for hosting
Composing applications
27
Container cluster
Examples of external resources
Solution composed of
multiple containers
Different compositions
per environment
Web
Frontend
Web API
Composing
.NET solutions
Backend service
28
Container cluster
Redis cache
External
resources
Web
Frontend
Web API Backend service
SQL
Server
on Linux RabbitMQ
Maximize isolation in development and testing
29
Composing container solutions
Docker-compose:
“Orchestration tool for container automation”
Single command: docker-compose
YAML files describe composition
30
services:
service-name:
docker-image
how to build
other services
key/value pairs
port mappings
networks:
network-name:
volumes:
volume-name:
31
Web API
Web App
SQL Server
on Linux
32
Composing docker-compose files
Order of files matters:
last one wins
docker-compose
-f "docker-compose.yml"
-f "docker-compose.override.yml"
-p composition up -d
33
Changing environments
34
Environment:
e.g. developer laptop,
staging or production cluster
Environments everywhere
Container images are immutable
Environments are not same
docker run –it –-env key=value alexthissen/gamingwebapp
Container image
{
"ConnectionString": "Server=tcp:…",
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
}
}
appsettings.json
35
Working with environments in .NET Core
Bootstrapping environment variables
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
ENV variablesSettings files
36
Docker-compose file
development.yml
production.yml
For .NET 4.7.1+ use similar strategy with new
ConfigurationBuilders
Environmental variables overrides
Non-container development
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:1337
- ConnectionString=Server=sql.data;…
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=DOCKERSECRETS_KEY
{
"ConnectionString": "Server=tcp:…",
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
}
} appsettings.json
37
Base compositions
• Services
• Images using registry names
• Dependencies between
services
• Networks
Composing for different environments
Environmental overrides:
• Port mappings
• Endpoint configurations
• Non-production services
SQL Server
on Linux
Redis
cache
Azure SQL
Database
Azure
Cache
39
public class HomeController : Controller
{
private readonly IOptionsSnapshot<WebAppSettings> settings;
// Inject snapshot of settings
public HomeController(IOptionsSnapshot<WebAppSettings> settings) {
this.settings = settings;
}
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.Configure<WebAppSettings>(Configuration);
services.AddMvc();
} public class WebAppSettings {
public string BaseURL …
public int MaxRetryCount …
}
40
Security and secrets
41
Everything in Docker ecosystem is inspectable
Secrets in configuration
42
Configuration
Dealing with secrets in configuration
Where would you leave sensitive data?
Production
appsettings.
development.json
Cluster secrets
docker-
compose.debug.yml
docker-
compose.production.yml
User Secrets Azure KeyVault
appsettings.
secrets.json
Connection
strings
Passwords
appsettings.
secrets.json
43
Managed by and distributed across cluster
echo "azurekeysecret" | docker secret create azurekeyvault_secret –
docker secret ls
Specific to cluster orchestrator
Other orchestrators have similar mechanisms
e.g. kubectl create secret
docker-compose.production.yml
var builder = new ConfigurationBuilder()
// ...
.AddEnvironmentVariables()
.AddDockerSecrets();
services:
leaderboardwebapi:
secrets:
- azurekeyvault_secret
secrets:
azurekeyvault_secret:
external: true
44
Reading secrets from Azure Key Vault
Available from IConfiguration in ASP.NET Core
Key and secret names
Connecting to KeyVault securely
if (env.IsProduction())
{
builder.AddAzureKeyVault(
$"https://{Configuration["azurekeyvault_vault"]}.vault.azure.net/",
Configuration["azurekeyvault_clientid"],
Configuration["azurekeyvault_clientsecret"]);
Configuration = builder.Build();
}
Demo: Secrets
Creating and reading secrets
in a Kubernetes cluster
Mounting secret settings in volume
Reading individual secrets
Read from Azure KeyVault
Building resilient applications
47
Resiliency: design for failure
Determine your strategy for resiliency
Focus on dependencies outside of your own container
Cloud and containers:
Transient errors
are a fact,
not a possibility
48
Dealing with transient faults
Containers will crash or be terminated
External dependencies might not be available
49
Built-in Retry mechanisms
Most Azure services and client
SDKs include retry mechanism
Patterns and Practices
TFH Application Block (.NET FX)
Roll your own for container
connectivity
services.AddDbContext<LeaderboardContext>(options => {
string connectionString =
Configuration.GetConnectionString("LeaderboardContext");
options.UseSqlServer(connectionString, sqlOptions =>
{
sqlOptions.EnableRetryOnFailure(
maxRetryCount: 5,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null);
});
});
50
Fault handling policies
Use policy configurations
Centralize policies in registry
Policy.Handle<HttpRequestException>().WaitAndRetryAsync(
6, // Number of retries
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
(exception, timeSpan, retryCount, context) => { // On retry
logger.LogWarning(
"Retry {Count} at {ExecutionKey} : {Exception}.",
retryCount, context.ExecutionKey, exception);
})
51
Creating resilient HTTP clients with Polly
Leverage HttpClientFactory
Built-in Polly support
Demo: Resiliency
Preparing for failures
Typed Clients
Polly support
53
Other options: service meshes
Provides several capabilities:
54
Challenges for cluster hosted apps
Keeping entire system running
Determine state of entire system and intervene
How to know health status of individual services?
Collecting/correlating performance and health data
Sentry.ioRaygun.io RunscopeNewRelic AlertSite DataDogAppMetrics Azure Monitor
55
ASP.NET Core application
/api/v1/… Middle
ware
Integrating health checks
New in .NET Core 2.2
Bootstrap health checks in
ASP.NET Core app
services.AddHealthChecks();
endpoints.MapHealthChecks("/health);
/health DefaultHealthCheckService
Microsoft.Extensions.Diagnostics.HealthChecks
.Abstractions
.EntityFramework
Microsoft.AspNetCore.Diagnostics.HealthChecks
56
Integrating health checks
services
.AddHealthChecks()
.AddCheck("sync", () => … )
.AddAsyncCheck("async", async () => … )
.AddCheck<SqlConnectionHealthCheck>("SQL")
.AddCheck<UrlHealthCheck>("URL");
ASP.NET Core application
/health
DefaultHealthCheckService
57
Probing containers to check for availability and health
Readiness and liveness
Kubernetes node
Kubernetes node
Kubernetes nodes
Containers
Readiness
Liveliness
58
Implementing readiness and liveliness
1. Add health checks with tags
2. Register multiple endpoints
with filter using
Options predicate
/api/v1/…
/health
/health/ready
/health/lively
app.UseHealthChecks("/health/ready",
new HealthCheckOptions() {
Predicate = reg => reg.Tags.Contains("ready")
});
services.AddHealthChecks()
.AddCheck<CircuitBreakerHealthCheck>(
"circuitbreakers",
tags: new string[] { "ready" });
app.UseHealthChecks("/health/lively",
new HealthCheckOptions() {
Predicate = _ => true
});
59
Zero downtime deployments
Original pods only taken offline after new healthy one is up
Allows roll forward upgrades: Never roll back to previous version
60
Networking
61
Connecting containerized services
62
Docker containers in same network are connected
Drivers determine network type
Docker networks
Bridge network 172.17.0.0/16
Host IP 172.31.1.10
63
Hosts may
span across
cloud
providers
Docker networks: overlay in Swarm
Overlay network 10.0.0.0/24
Host IP 172.31.1.10 Host IP 192.168.2.13
docker_gwbridge 172.18.0.0/16 docker_gwbridge
64
Exposing containers
Port publishing
Built-in DNS
65
Composing networks
By default single network is created
Create user-defined networks for
network isolation
Backend
network
Frontend network
Web API
Web App
SQL
Server
66
Backend
network
Frontend network
Web API
Web App
SQL
Server
67
Gear your architecture to work with containers
Environments Configuration
Security
Networking
Storage
Composition
Lifecycle
Hosting
Monitoring
Thanks!
Resources
https://dot.net/
https://www.visualstudio.com
https://github.com/microsoft/dotnet
https://xpirit.com/inspiration/#downloads

.NET Fest 2019. Alex Thissen. Architecting .NET solutions in a Docker ecosystem

  • 1.
    ARCHITECTING .NET SOLUTIONS INA DOCKER ECOSYSTEM ALEX THISSEN
  • 3.
    3 Agenda A story oncreating .NET applications using container technology Featuring Docker and Visual Studio With highlights and some emphasis on .NET Core
  • 4.
    4 Task: Fit yourapplication into a container
  • 5.
    5 Making progress. Itwill fit. It must…
  • 6.
  • 7.
    7 Building and running.NET applications
  • 8.
  • 9.
    9 Containers on Windows Betterisolation from hypervisor virtualization Container management Docker Engine Compute
  • 10.
    10 Docker and containers Standardizedtooling Adopted for standalone and cluster scenarios
  • 11.
    11 Changes when switchingto containers Environments Configuration Security Networking Storage Composition Lifecycle Hosting Monitoring
  • 12.
    12 Application architecture lessrelevant Featuring your favorite patterns Microservices or Monolith
  • 13.
  • 14.
    14 Web APIWeb API containercontainer Leaderboard Microservice Identity Microservice ASP.NETCore Web App Client applications (browser) Web Page Games reporting high-scores
  • 15.
    15 Container workflow andlifecycle Inner loop Run Code Validate Outer loop
  • 16.
    16 Deploying .NET appswith containers
  • 17.
    17 VS2019 container building Multi-stageDockerfile per project Can be built without VS2019 FROM mcr.microsoft.com/dotnet/core/aspnet:3.0-buster-slim AS base WORKDIR /app EXPOSE 13995 EXPOSE 44369 … FROM base AS final WORKDIR /app COPY --from=publish /app . ENTRYPOINT ["dotnet", "Leaderboard.WebAPI.dll"]
  • 18.
    18 mcr.microsoft.com/windows/servercore:1903 -or- debian:buster-slimdebian:buster-slim Docker imagelayers .NET Core mcr.microsoft.com/dotnet/core/runtime:3.0 mcr.microsoft.com/dotnet/core/runtime-deps mcr.microsoft.com/dotnet/core/aspnet:3.0 Your application layer: e.g. alexthissen/gamingwebapp:latest
  • 19.
    19 Container image registries Pushand store your images to centralized location Each image is in its own repository
  • 20.
    20 Build pipeline inAzure DevOps Contains steps to: Docker images need to be created on correct host OS Produce artifacts to be used during deployment
  • 21.
    21 Release pipelines inAzure DevOps Create deployment file to apply on cluster Elaborate scenarios involve multiple environments
  • 22.
    Demo: Lifecycle A newdevelopment lifecycle for Docker based solutions Visual Studio 2019 Azure DevOps
  • 23.
    23 From single-node hostto multi-node clusters
  • 24.
    24 Hosting using clusterorchestrators Cluster FabricHigh Availability Hyper-Scale Hybrid Operations High Density Rolling Upgrades Stateful services Low Latency Fast startup & shutdown Container Orchestration & lifecycle management Replication & Failover Simple programming models Load balancing Self-healingData Partitioning Automated Rollback Health Monitoring Placement Constraints Microservices Mesos DC/OS Docker Swarm Google Kubernetes Azure Service Fabric Largely cloud provider-agnostic
  • 25.
    25 Many more Azureoptions for hosting
  • 26.
  • 27.
    27 Container cluster Examples ofexternal resources Solution composed of multiple containers Different compositions per environment Web Frontend Web API Composing .NET solutions Backend service
  • 28.
    28 Container cluster Redis cache External resources Web Frontend WebAPI Backend service SQL Server on Linux RabbitMQ Maximize isolation in development and testing
  • 29.
    29 Composing container solutions Docker-compose: “Orchestrationtool for container automation” Single command: docker-compose YAML files describe composition
  • 30.
    30 services: service-name: docker-image how to build otherservices key/value pairs port mappings networks: network-name: volumes: volume-name:
  • 31.
    31 Web API Web App SQLServer on Linux
  • 32.
    32 Composing docker-compose files Orderof files matters: last one wins docker-compose -f "docker-compose.yml" -f "docker-compose.override.yml" -p composition up -d
  • 33.
  • 34.
    34 Environment: e.g. developer laptop, stagingor production cluster Environments everywhere Container images are immutable Environments are not same docker run –it –-env key=value alexthissen/gamingwebapp Container image { "ConnectionString": "Server=tcp:…", "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Warning" } } } appsettings.json
  • 35.
    35 Working with environmentsin .NET Core Bootstrapping environment variables public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build(); } ENV variablesSettings files
  • 36.
    36 Docker-compose file development.yml production.yml For .NET4.7.1+ use similar strategy with new ConfigurationBuilders Environmental variables overrides Non-container development environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=http://0.0.0.0:1337 - ConnectionString=Server=sql.data;… environment: - ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_URLS=http://0.0.0.0:80 - ConnectionString=DOCKERSECRETS_KEY { "ConnectionString": "Server=tcp:…", "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Warning" } } } appsettings.json
  • 37.
    37 Base compositions • Services •Images using registry names • Dependencies between services • Networks Composing for different environments Environmental overrides: • Port mappings • Endpoint configurations • Non-production services SQL Server on Linux Redis cache Azure SQL Database Azure Cache
  • 38.
    39 public class HomeController: Controller { private readonly IOptionsSnapshot<WebAppSettings> settings; // Inject snapshot of settings public HomeController(IOptionsSnapshot<WebAppSettings> settings) { this.settings = settings; } public void ConfigureServices(IServiceCollection services) { // Add framework services. services.Configure<WebAppSettings>(Configuration); services.AddMvc(); } public class WebAppSettings { public string BaseURL … public int MaxRetryCount … }
  • 39.
  • 40.
    41 Everything in Dockerecosystem is inspectable Secrets in configuration
  • 41.
    42 Configuration Dealing with secretsin configuration Where would you leave sensitive data? Production appsettings. development.json Cluster secrets docker- compose.debug.yml docker- compose.production.yml User Secrets Azure KeyVault appsettings. secrets.json Connection strings Passwords appsettings. secrets.json
  • 42.
    43 Managed by anddistributed across cluster echo "azurekeysecret" | docker secret create azurekeyvault_secret – docker secret ls Specific to cluster orchestrator Other orchestrators have similar mechanisms e.g. kubectl create secret docker-compose.production.yml var builder = new ConfigurationBuilder() // ... .AddEnvironmentVariables() .AddDockerSecrets(); services: leaderboardwebapi: secrets: - azurekeyvault_secret secrets: azurekeyvault_secret: external: true
  • 43.
    44 Reading secrets fromAzure Key Vault Available from IConfiguration in ASP.NET Core Key and secret names Connecting to KeyVault securely if (env.IsProduction()) { builder.AddAzureKeyVault( $"https://{Configuration["azurekeyvault_vault"]}.vault.azure.net/", Configuration["azurekeyvault_clientid"], Configuration["azurekeyvault_clientsecret"]); Configuration = builder.Build(); }
  • 44.
    Demo: Secrets Creating andreading secrets in a Kubernetes cluster Mounting secret settings in volume Reading individual secrets Read from Azure KeyVault
  • 45.
  • 46.
    47 Resiliency: design forfailure Determine your strategy for resiliency Focus on dependencies outside of your own container Cloud and containers: Transient errors are a fact, not a possibility
  • 47.
    48 Dealing with transientfaults Containers will crash or be terminated External dependencies might not be available
  • 48.
    49 Built-in Retry mechanisms MostAzure services and client SDKs include retry mechanism Patterns and Practices TFH Application Block (.NET FX) Roll your own for container connectivity services.AddDbContext<LeaderboardContext>(options => { string connectionString = Configuration.GetConnectionString("LeaderboardContext"); options.UseSqlServer(connectionString, sqlOptions => { sqlOptions.EnableRetryOnFailure( maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); }); });
  • 49.
    50 Fault handling policies Usepolicy configurations Centralize policies in registry Policy.Handle<HttpRequestException>().WaitAndRetryAsync( 6, // Number of retries retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (exception, timeSpan, retryCount, context) => { // On retry logger.LogWarning( "Retry {Count} at {ExecutionKey} : {Exception}.", retryCount, context.ExecutionKey, exception); })
  • 50.
    51 Creating resilient HTTPclients with Polly Leverage HttpClientFactory Built-in Polly support
  • 51.
    Demo: Resiliency Preparing forfailures Typed Clients Polly support
  • 52.
    53 Other options: servicemeshes Provides several capabilities:
  • 53.
    54 Challenges for clusterhosted apps Keeping entire system running Determine state of entire system and intervene How to know health status of individual services? Collecting/correlating performance and health data Sentry.ioRaygun.io RunscopeNewRelic AlertSite DataDogAppMetrics Azure Monitor
  • 54.
    55 ASP.NET Core application /api/v1/…Middle ware Integrating health checks New in .NET Core 2.2 Bootstrap health checks in ASP.NET Core app services.AddHealthChecks(); endpoints.MapHealthChecks("/health); /health DefaultHealthCheckService Microsoft.Extensions.Diagnostics.HealthChecks .Abstractions .EntityFramework Microsoft.AspNetCore.Diagnostics.HealthChecks
  • 55.
    56 Integrating health checks services .AddHealthChecks() .AddCheck("sync",() => … ) .AddAsyncCheck("async", async () => … ) .AddCheck<SqlConnectionHealthCheck>("SQL") .AddCheck<UrlHealthCheck>("URL"); ASP.NET Core application /health DefaultHealthCheckService
  • 56.
    57 Probing containers tocheck for availability and health Readiness and liveness Kubernetes node Kubernetes node Kubernetes nodes Containers Readiness Liveliness
  • 57.
    58 Implementing readiness andliveliness 1. Add health checks with tags 2. Register multiple endpoints with filter using Options predicate /api/v1/… /health /health/ready /health/lively app.UseHealthChecks("/health/ready", new HealthCheckOptions() { Predicate = reg => reg.Tags.Contains("ready") }); services.AddHealthChecks() .AddCheck<CircuitBreakerHealthCheck>( "circuitbreakers", tags: new string[] { "ready" }); app.UseHealthChecks("/health/lively", new HealthCheckOptions() { Predicate = _ => true });
  • 58.
    59 Zero downtime deployments Originalpods only taken offline after new healthy one is up Allows roll forward upgrades: Never roll back to previous version
  • 59.
  • 60.
  • 61.
    62 Docker containers insame network are connected Drivers determine network type Docker networks Bridge network 172.17.0.0/16 Host IP 172.31.1.10
  • 62.
    63 Hosts may span across cloud providers Dockernetworks: overlay in Swarm Overlay network 10.0.0.0/24 Host IP 172.31.1.10 Host IP 192.168.2.13 docker_gwbridge 172.18.0.0/16 docker_gwbridge
  • 63.
  • 64.
    65 Composing networks By defaultsingle network is created Create user-defined networks for network isolation Backend network Frontend network Web API Web App SQL Server
  • 65.
  • 66.
    67 Gear your architectureto work with containers Environments Configuration Security Networking Storage Composition Lifecycle Hosting Monitoring
  • 67.
  • 68.