Examples/generic host (#194)
* Update the code * Clean up the code a bit * Add readme for the exmaple
This commit is contained in:
Родитель
8ea6802acb
Коммит
8fee01b543
|
@ -5,14 +5,14 @@
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": ".NET Core Launch (console)",
|
"name": ".NET Core Launch - .NET Generic Host",
|
||||||
"type": "coreclr",
|
"type": "coreclr",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"preLaunchTask": "build",
|
"preLaunchTask": "Build Example - .NET Generic Host",
|
||||||
// If you have changed target frameworks, make sure to update the program path.
|
// If you have changed target frameworks, make sure to update the program path.
|
||||||
"program": "${workspaceFolder}/BasicConsoleAppILogger/bin/Debug/netcoreapp2.2/BasicConsoleAppILogger.dll",
|
"program": "${workspaceFolder}/GenericHost/src/bin/Debug/netcoreapp2.2/AIK8sGenericHost.dll",
|
||||||
"args": [],
|
"args": [],
|
||||||
"cwd": "${workspaceFolder}/BasicConsoleAppILogger",
|
"cwd": "${workspaceFolder}/GenericHost/src",
|
||||||
// For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window
|
// For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window
|
||||||
"console": "internalConsole",
|
"console": "internalConsole",
|
||||||
"stopAtEntry": false
|
"stopAtEntry": false
|
||||||
|
|
|
@ -79,5 +79,18 @@
|
||||||
"cwd": "${workspaceFolder}/WindowsContainer/"
|
"cwd": "${workspaceFolder}/WindowsContainer/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "Build Example - .NET Generic Host",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "shell",
|
||||||
|
"group": "build",
|
||||||
|
"problemMatcher":"$msCompile",
|
||||||
|
"args": [
|
||||||
|
"build"
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/GenericHost/src/"
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
# Use Application Insights for Kubernetes in .NET Generic Host project
|
||||||
|
|
||||||
|
This is an example of enabling Application Insights for Kubernetes in .NET Generic Host project. If you are looking into enabling profiler on a plain Console Application, refer to [Enable Application Insights for Kubernetes in .NET Core Console Application](../BasicConsoleAppILogger/Readme.md).
|
||||||
|
|
||||||
|
## Why .NET Generic Host
|
||||||
|
|
||||||
|
According to [.NET Generic Host](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-2.2):
|
||||||
|
|
||||||
|
> "The purpose of Generic Host is to decouple the HTTP pipeline from the Web Host API to enable a wider array of host scenarios. Messaging, background tasks, and other non-HTTP workloads based on Generic Host benefit from cross-cutting capabilities, such as configuration, dependency injection (DI), and logging."
|
||||||
|
|
||||||
|
And also notice:
|
||||||
|
> Generic Host is new in ASP.NET Core 2.1 and isn't suitable for web hosting scenarios. For web hosting scenarios, use the Web Host. Generic Host will replace Web Host in a future release and act as the primary host API in both HTTP and non-HTTP scenarios.
|
||||||
|
|
||||||
|
Now that you know what the .NET Generic Host is used for, lets dive in.
|
||||||
|
|
||||||
|
## Walk-through
|
||||||
|
|
||||||
|
### Create an empty console app
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet new console -n AIK8sGenericHost
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding necessary packages
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet add package Microsoft.Extensions.Hosting
|
||||||
|
dotnet add package Microsoft.Extensions.DependencyInjection
|
||||||
|
dotnet add package Microsoft.Extensions.Logging.ApplicationInsights
|
||||||
|
dotnet add package Microsoft.Extensions.Logging.Console
|
||||||
|
```
|
||||||
|
|
||||||
|
Reference to [AIK8sGenericHost.csproj](./src/AIK8sGenericHost.csproj) for the final results.
|
||||||
|
|
||||||
|
### Set up a host
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
namespace AIK8sGenericHost
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
public static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
var host = new HostBuilder()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
await host.RunAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Enable Application Insights and its Kubernetes enricher
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var host = new HostBuilder()
|
||||||
|
.ConfigureServices((context, services) =>
|
||||||
|
{
|
||||||
|
var channel = new InMemoryChannel();
|
||||||
|
|
||||||
|
// Add application insights for Kubernetes. Making sure this is called before services.Configure<TelemetryConfiguration>().
|
||||||
|
services.AddApplicationInsightsKubernetesEnricher();
|
||||||
|
|
||||||
|
services.Configure<TelemetryConfiguration>(
|
||||||
|
(config) =>
|
||||||
|
{
|
||||||
|
config.TelemetryChannel = channel;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add the logging pipelines to use. We are using Application Insights only here.
|
||||||
|
services.AddLogging(builder =>
|
||||||
|
{
|
||||||
|
// Optional: Apply filters to configure LogLevel Trace or above is sent to
|
||||||
|
// Application Insights for all categories.
|
||||||
|
builder.AddFilter<Microsoft.Extensions.Logging.ApplicationInsights.ApplicationInsightsLoggerProvider>
|
||||||
|
("", LogLevel.Trace);
|
||||||
|
builder.AddApplicationInsights("--YourAIKeyHere--");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register your services that implemented IHostedService interface. For example, SendAIEventService. You will need to uncomment this later.
|
||||||
|
// services.AddHostedService<SendAIEventService>();
|
||||||
|
}).Build();
|
||||||
|
```
|
||||||
|
|
||||||
|
Largely, those implementations come from [ApplicationInsightsLoggerProvider for .NET Core ILogger logs](https://docs.microsoft.com/en-us/azure/azure-monitor/app/ilogger).
|
||||||
|
|
||||||
|
### Create a hosted service
|
||||||
|
|
||||||
|
Refer to [SendAIEventService](./src/SendAIEventService.cs) for the implementation.
|
||||||
|
|
||||||
|
Now that the service is created, it could be registered into the dependency injection container:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Register your services that implemented IHostedService interface. For example, SendAIEventService.
|
||||||
|
services.AddHostedService<SendAIEventService>();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Flush the channel with lifetime methods
|
||||||
|
|
||||||
|
Since the telemetry won't necessary be flushed, we should flush the channel at the end of the life cycle of the host.
|
||||||
|
One way of doing that is to register to the application lifetime:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
IApplicationLifetime lifetime = host.Services.GetRequiredService<IApplicationLifetime>();
|
||||||
|
lifetime.ApplicationStopping.Register(() =>
|
||||||
|
{
|
||||||
|
channel.Flush();
|
||||||
|
// Work around Application Insights issue:
|
||||||
|
// https://github.com/Microsoft/ApplicationInsights-dotnet/issues/407
|
||||||
|
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Containerize the application
|
||||||
|
|
||||||
|
* Create a docker file like [this](./src/Dockerfile).
|
||||||
|
* Build and push the images:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t dockeraccount/aik8sgenerichost .
|
||||||
|
docker push dockeraccount/aik8sgenerichost:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deploy it to Kubernetes
|
||||||
|
|
||||||
|
* Create a Kubernetes deployment file like [this](./src/K8s.yaml).
|
||||||
|
* Deploy it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl create -f K8s.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
For RBAC enabled Kubernetes cluster (AKS for example), you will need to have proper service account bindings. Refer to [here](../BasicUsage_clr21_RBAC/README.MD#setup-the-default-service-account-for-rbac-enabled-cluster) to see how to do it.
|
||||||
|
|
||||||
|
### Result
|
||||||
|
|
||||||
|
Go to the application insights and you will see traces with kubernetes properties.
|
||||||
|
|
||||||
|
![Traces in Application Insights](./media/result.png)
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
* [.NET Generic Host](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-2.2)
|
||||||
|
* [USING HOSTBUILDER AND THE GENERIC HOST IN .NET CORE MICROSERVICES](https://www.stevejgordon.co.uk/using-generic-host-in-dotnet-core-console-based-microservices)
|
||||||
|
* [ApplicationInsightsLoggerProvider for .NET Core ILogger logs](https://docs.microsoft.com/en-us/azure/azure-monitor/app/ilogger)
|
||||||
|
* Specifically, [Console application](https://docs.microsoft.com/en-us/azure/azure-monitor/app/ilogger#console-application) part.
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 41 KiB |
|
@ -0,0 +1,16 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.1.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.ApplicationInsights" Version="2.10.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,16 @@
|
||||||
|
FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build-env
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy csproj and restore as distinct layers
|
||||||
|
COPY *.csproj ./
|
||||||
|
RUN dotnet restore
|
||||||
|
|
||||||
|
# Copy everything else and build
|
||||||
|
COPY . ./
|
||||||
|
RUN dotnet publish -c Release -o out
|
||||||
|
|
||||||
|
# Build runtime image
|
||||||
|
FROM mcr.microsoft.com/dotnet/core/runtime:2.2
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build-env /app/out .
|
||||||
|
ENTRYPOINT ["dotnet", "AIK8sGenericHost.dll"]
|
|
@ -0,0 +1,14 @@
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: ai-k8s-generic-host
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: consoleapp
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: ai-k8s-generic-host-container
|
||||||
|
image: dockeraccount/aik8sgenerichost:latest
|
|
@ -0,0 +1,63 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.ApplicationInsights.Channel;
|
||||||
|
using Microsoft.ApplicationInsights.Extensibility;
|
||||||
|
using Microsoft.ApplicationInsights.Kubernetes.Debugging;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
namespace AIK8sGenericHost
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
public static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
// Channel is explicitly configured to do flush on it later.
|
||||||
|
var channel = new InMemoryChannel();
|
||||||
|
|
||||||
|
var host = new HostBuilder()
|
||||||
|
.ConfigureServices((context, services) =>
|
||||||
|
{
|
||||||
|
// Uncomment the following lines for debugging AI.K8s.
|
||||||
|
// Refer to https://github.com/microsoft/ApplicationInsights-Kubernetes/blob/develop/docs/SelfDiagnostics.MD for details.
|
||||||
|
// var observer = new ApplicationInsightsKubernetesDiagnosticObserver(DiagnosticLogLevel.Trace);
|
||||||
|
// ApplicationInsightsKubernetesDiagnosticSource.Instance.Observable.SubscribeWithAdapter(observer);
|
||||||
|
|
||||||
|
// Add application insights for Kubernetes. Making sure this is called before services.Configure<TelemetryConfiguration>().
|
||||||
|
services.AddApplicationInsightsKubernetesEnricher();
|
||||||
|
|
||||||
|
services.Configure<TelemetryConfiguration>(
|
||||||
|
(config) =>
|
||||||
|
{
|
||||||
|
config.TelemetryChannel = channel;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add the logging pipelines to use. We are using Application Insights only here.
|
||||||
|
services.AddLogging(builder =>
|
||||||
|
{
|
||||||
|
// Optional: Apply filters to configure LogLevel Trace or above is sent to
|
||||||
|
// Application Insights for all categories.
|
||||||
|
builder.AddFilter<Microsoft.Extensions.Logging.ApplicationInsights.ApplicationInsightsLoggerProvider>
|
||||||
|
("", LogLevel.Trace);
|
||||||
|
builder.AddApplicationInsights("----Your instrumentation key----");
|
||||||
|
builder.AddConsole();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register your services that implemented IHostedService interface. For example, SendAIEventService
|
||||||
|
services.AddHostedService<SendAIEventService>();
|
||||||
|
}).Build();
|
||||||
|
|
||||||
|
IApplicationLifetime lifetime = host.Services.GetRequiredService<IApplicationLifetime>();
|
||||||
|
lifetime.ApplicationStopping.Register(() =>
|
||||||
|
{
|
||||||
|
channel.Flush();
|
||||||
|
// Work around Application Insights issue:
|
||||||
|
// https://github.com/Microsoft/ApplicationInsights-dotnet/issues/407
|
||||||
|
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
|
||||||
|
});
|
||||||
|
await host.RunAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace AIK8sGenericHost
|
||||||
|
{
|
||||||
|
public class SendAIEventService : IHostedService
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
public SendAIEventService(ILogger<SendAIEventService> logger)
|
||||||
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Task _ = Task.Run(async () => await RunUntilCancelledAsync(cancellationToken));
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RunUntilCancelledAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Application Insights for Kubernetes is working . . .");
|
||||||
|
await Task.Delay(5000).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче