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",
|
||||
"configurations": [
|
||||
{
|
||||
"name": ".NET Core Launch (console)",
|
||||
"name": ".NET Core Launch - .NET Generic Host",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"preLaunchTask": "Build Example - .NET Generic Host",
|
||||
// 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": [],
|
||||
"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
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
|
|
|
@ -79,5 +79,18 @@
|
|||
"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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче