* Update the code

* Clean up the code a bit

* Add readme for the exmaple
This commit is contained in:
Saar Shen 2019-08-16 13:36:13 -07:00 коммит произвёл GitHub
Родитель 8ea6802acb
Коммит 8fee01b543
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 313 добавлений и 4 удалений

8
examples/.vscode/launch.json поставляемый
Просмотреть файл

@ -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

13
examples/.vscode/tasks.json поставляемый
Просмотреть файл

@ -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.

Двоичные данные
examples/GenericHost/media/result.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 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);
}
}
}
}