Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaomi7732 committed Mar 16, 2018
2 parents dab55fb + 578f20e commit 2b36203
Show file tree
Hide file tree
Showing 17 changed files with 269 additions and 45 deletions.
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
Microsoft Application Insights for Kubernetes
==
This repository has code for Application Insights for Kubernetes, which works on .NET Core applications within the containers, managed by Kubernetes, on Azure Container Service.
This page is subject to be updated once more info is available.

**Note:** This library enhanced the [Microsoft Application Insights](https://github.com/Microsoft/ApplicationInsights-aspnetcore) but is **not required** to run applicaiton insights in Kubernetes. When you choose to use Microsoft Application Insights for Kubernetes, you will see Kubernetes related properties like *Pod-Name, Deployment ...* on all your telemetry entries. Rich features like Appliation Map to show the multiple micro service on your application map will also be provided.

### Continous Integration Status
|Rolling Build | Nightly Build |
Expand All @@ -15,9 +16,15 @@ This page is subject to be updated once more info is available.
* [Kubernetes](https://kubernetes.io/)

### Walkthrough
For ASP.NET Core Application: Refer [Getting Started](https://github.com/Microsoft/ApplicationInsights-Kubernetes/wiki/Getting-Started-for-ASP.NET-Core-Applications) for a simple walkthrough.
We support **ASP.NET Core** application as well as **.NET Core** application.

* For **ASP.NET Core** Application: Refer [Getting Started](https://github.com/Microsoft/ApplicationInsights-Kubernetes/wiki/Getting-Started-for-ASP.NET-Core-Applications) for a simple walkthrough.

* For **.NET Core** Application: Refer [Getting Started](https://github.com/Microsoft/ApplicationInsights-Kubernetes/wiki/Getting-Started-for-.NET-Core-Applications) for a simple walkthrough.

For .NET Core Application: Refer [Getting Started](https://github.com/Microsoft/ApplicationInsights-Kubernetes/wiki/Getting-Started-for-.NET-Core-Applications) for a simple walkthrough.
### Learn more
* Read the [Wikis](https://github.com/Microsoft/ApplicationInsights-Kubernetes/wiki).
* To enable Application Insights for Kubernetes by environement variable instead of code, please refer [Hosting startup for ApplicationInsights.Kubernetes](https://github.com/Microsoft/ApplicationInsights-Kubernetes/wiki/Hosting-startup-for-ApplicationInsights.Kubernetes).

## Contributing
### Report issues
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.0.1" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ public class K8sInjection : IHostingStartup
{
public void Configure(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
services.EnableKubernetes();
});
builder.UseApplicationInsights()
.ConfigureServices(services =>
{
services.EnableKubernetes();
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights.Kubernetes.Entities;

namespace Microsoft.ApplicationInsights.Kubernetes.Debugging
{
internal class K8sDebuggingEnvironmentFactory : IK8sEnvironmentFactory
{
public Task<K8sEnvironment> CreateAsync(TimeSpan timeout)
{
return Task.FromResult(new K8sEnvironment()
{
ContainerID = KubeHttpDebuggingClientSettings.FakeContainerId,
myContainerStatus = new ContainerStatus()
{
ContainerID = KubeHttpDebuggingClientSettings.FakeContainerId,
Image = nameof(ContainerStatus.Image),
ImageID = nameof(ContainerStatus.ImageID),
Name = nameof(ContainerStatus.Name),
Ready = true,
},
myDeployment = new K8sDeployment()
{
Metadata = new K8sDeploymentMetadata()
{
Labels = new Dictionary<string, string>() { { "app", "stub" } },
Name = nameof(K8sDeploymentMetadata.Name),
Uid = nameof(K8sDeploymentMetadata.Uid),
},
Spec = new K8sDeploymentSpec()
{
Selector = new Selector()
{
MatchLabels = new Dictionary<string, string>() { { "app", "stub" } },
},
},
},
myNode = new K8sNode()
{
Metadata = new K8sNodeMetadata()
{
Labels = new Dictionary<string, string>() { { "app", "stub" } },
Name = nameof(K8sNodeMetadata.Name),
Uid = nameof(K8sNodeMetadata.Uid),
},
Status = new K8sNodeStatus()
{
},
},
myPod = new K8sPod()
{
Metadata = new K8sPodMetadata()
{
Uid = "StubPodId",
Name = "StubPodName",
Labels = new Dictionary<string, string>() { { "app", "stub" } },
}
},
myReplicaSet = new K8sReplicaSet()
{
Metadata = new K8sReplicaSetMetadata()
{
Name = "StubReplicaName",
}
}
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using System.Net.Http;

namespace Microsoft.ApplicationInsights.Kubernetes.Debugging
{
internal class KubeHttpDebuggingClientSettings : IKubeHttpClientSettingsProvider
{
public const string FakeContainerId = "F8E1C6FF-2217-4962-90FF-0D9195AF0785";

public string ContainerId => FakeContainerId;

public string QueryNamespace => "063A30B8-6A62-4519-8BFE-0DE144B009A1";

public Uri ServiceBaseAddress => new Uri("http://localhost/stub");

public HttpMessageHandler CreateMessageHandler()
{
return null;
}

public string GetToken()
{
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.ApplicationInsights.Kubernetes.Debugging
{
public sealed class KubernetesDebuggingServiceCollectionBuilder : KubernetesServiceCollectionBuilder
{
#region Singleton
private KubernetesDebuggingServiceCollectionBuilder() { }
private static KubernetesDebuggingServiceCollectionBuilder _instance = new KubernetesDebuggingServiceCollectionBuilder();

[Obsolete("This instance is used only for debugging. Never use this in production!", false)]
public static KubernetesDebuggingServiceCollectionBuilder Instance => _instance;
#endregion

protected override void InjectChangableServices(IServiceCollection serviceCollection)
{
serviceCollection.AddSingleton<IKubeHttpClientSettingsProvider, KubeHttpDebuggingClientSettings>();
serviceCollection.AddSingleton<IK8sEnvironmentFactory, K8sDebuggingEnvironmentFactory>();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
namespace Microsoft.Extensions.DependencyInjection
using System;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.Kubernetes;

namespace Microsoft.Extensions.DependencyInjection
{
using System;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.Kubernetes;
using Microsoft.Extensions.Logging;

/// <summary>
/// Extnesion method to inject Kubernetes Telemtry Initializer.
/// </summary>
public static class ApplicationInsightsExtensions
{
public static IServiceCollection EnableKubernetes(this IServiceCollection services, TimeSpan? timeout = null)
public static IServiceCollection EnableKubernetes(this IServiceCollection services, TimeSpan? timeout = null,
IKubernetesServiceCollectionBuilder kubernetesServiceCollectionBuilder = null)
{
// Dispatch this on a differnet thread to avoid blocking the main thread.
// Mainly used with K8s Readness Probe enabled, where communicating with Server will temperory be blocked.
// TODO: Instead of query the server on the start, we should depend on watch services to provide dynamic realtime data.
Task.Run(() =>
{
KubernetesModule.EnableKubernetes(services, TelemetryConfiguration.Active, timeout);
KubernetesModule.EnableKubernetes(services, TelemetryConfiguration.Active, timeout, kubernetesServiceCollectionBuilder);
});

return services;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Microsoft.Extensions.DependencyInjection
{
public interface IKubernetesServiceCollectionBuilder
{
IServiceCollection InjectServices(IServiceCollection serviceCollection);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Microsoft.ApplicationInsights.Kubernetes;
using Microsoft.Extensions.Logging;

namespace Microsoft.Extensions.DependencyInjection
{
public class KubernetesServiceCollectionBuilder : IKubernetesServiceCollectionBuilder
{
/// <summary>
/// Inject Kubernetes related service into the service collection.
/// </summary>
/// <param name="serviceCollection"></param>
/// <returns></returns>
public IServiceCollection InjectServices(IServiceCollection serviceCollection)
{
IServiceCollection services = serviceCollection ?? new ServiceCollection();
InjectCommonServices(services);

InjectChangableServices(services);

return services;
}

private static void InjectCommonServices(IServiceCollection serviceCollection)
{
// According to the code, adding logging will not overwrite existing logging classes
// https://github.com/aspnet/Logging/blob/c821494678a30c323174bea8056f43b93a3ca6f4/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs
// Becuase it uses 'TryAdd()' extenion method on service collection.
serviceCollection.AddLogging();

serviceCollection.AddSingleton<KubeHttpClientFactory>();
serviceCollection.AddSingleton<K8sQueryClientFactory>();
}

protected virtual void InjectChangableServices(IServiceCollection serviceCollection)
{
serviceCollection.AddSingleton<IKubeHttpClientSettingsProvider>(p => new KubeHttpClientSettingsProvider(logger: p.GetService<ILogger<KubeHttpClientSettingsProvider>>()));
serviceCollection.AddSingleton<IK8sEnvironmentFactory, K8sEnvironmentFactory>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;
using System.Threading.Tasks;

namespace Microsoft.ApplicationInsights.Kubernetes
{
internal interface IK8sEnvironmentFactory
{
Task<K8sEnvironment> CreateAsync(TimeSpan timeout);
}
}
3 changes: 2 additions & 1 deletion src/ApplicationInsights.Kubernetes/K8sEnvironmentFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace Microsoft.ApplicationInsights.Kubernetes
{
internal class K8sEnvironmentFactory
internal class K8sEnvironmentFactory : IK8sEnvironmentFactory
{
private readonly ILogger _logger;
private readonly IKubeHttpClientSettingsProvider _httpClientSettings;
Expand All @@ -37,6 +37,7 @@ public async Task<K8sEnvironment> CreateAsync(TimeSpan timeout)
{
K8sEnvironment instance = null;
ILogger<K8sEnvironment> logger = null;

try
{
using (IKubeHttpClient httpClient = _httpClientFactory.Create(_httpClientSettings))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.Kubernetes.Utilities;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

Expand Down Expand Up @@ -47,12 +48,15 @@ public static void Initialize(TelemetryConfiguration configuration, TimeSpan? ti
/// </summary>
/// <param name="loggerFactory"></param>
/// <param name="timeout"></param>
public static void EnableKubernetes(IServiceCollection serviceCollection, TelemetryConfiguration configuration, TimeSpan? timeout = null)
public static void EnableKubernetes(IServiceCollection serviceCollection,
TelemetryConfiguration configuration,
TimeSpan? timeout = null,
IKubernetesServiceCollectionBuilder kubernetesServiceCollectionBuilder = null)
{
// 2 minutes maximum to spin up the container.
timeout = timeout ?? TimeSpan.FromMinutes(2);

serviceCollection = BuildK8sServiceCollection(serviceCollection);
serviceCollection = BuildK8sServiceCollection(serviceCollection, kubernetesServiceCollectionBuilder);
IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
ILogger logger = serviceProvider.GetService<ILogger<KubernetesModule>>();

Expand All @@ -71,11 +75,13 @@ public static void EnableKubernetes(IServiceCollection serviceCollection, Teleme

try
{
K8sEnvironment k8sEnv = serviceProvider.GetRequiredService<K8sEnvironmentFactory>().CreateAsync(timeout.Value).ConfigureAwait(false).GetAwaiter().GetResult();
K8sEnvironment k8sEnv = serviceProvider.GetRequiredService<IK8sEnvironmentFactory>().CreateAsync(timeout.Value).ConfigureAwait(false).GetAwaiter().GetResult();
if (k8sEnv != null)
{
// Inject the telemetry initializer.
ITelemetryInitializer initializer = new KubernetesTelemetryInitializer(k8sEnv, serviceProvider.GetService<ILogger<KubernetesTelemetryInitializer>>());
ITelemetryInitializer initializer = new KubernetesTelemetryInitializer(k8sEnv,
SDKVersionUtils.Instance,
serviceProvider.GetService<ILogger<KubernetesTelemetryInitializer>>());
configuration.TelemetryInitializers.Add(initializer);
logger?.LogDebug("Application Insights Kubernetes injected the service successfully.");
}
Expand All @@ -91,23 +97,10 @@ public static void EnableKubernetes(IServiceCollection serviceCollection, Teleme
}
}

internal static IServiceCollection BuildK8sServiceCollection(IServiceCollection original)
internal static IServiceCollection BuildK8sServiceCollection(IServiceCollection services, IKubernetesServiceCollectionBuilder kubernetesServiceCollectionBuilder = null)
{
if (Services == null || Services != original)
{
Services = original ?? new ServiceCollection();
// According github code, adding logging will not overwrite existing logging classes
// https://github.com/aspnet/Logging/blob/c821494678a30c323174bea8056f43b93a3ca6f4/src/Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs
// Becuase it uses 'TryAdd()' extenion method on service collection.
Services.AddLogging();

Services.AddSingleton<IKubeHttpClientSettingsProvider>(p => new KubeHttpClientSettingsProvider(logger: p.GetService<ILogger<KubeHttpClientSettingsProvider>>()));
Services.AddSingleton<KubeHttpClientFactory>();
Services.AddSingleton<K8sQueryClientFactory>();

Services.AddSingleton<K8sEnvironmentFactory>();
}

kubernetesServiceCollectionBuilder = kubernetesServiceCollectionBuilder ?? new KubernetesServiceCollectionBuilder();
Services = kubernetesServiceCollectionBuilder.InjectServices(services);
return Services;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.Extensibility.Implementation;
using Microsoft.ApplicationInsights.Kubernetes.Utilities;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

Expand Down Expand Up @@ -29,14 +31,17 @@ internal class KubernetesTelemetryInitializer : ITelemetryInitializer
public const string CPU = "CPU";
public const string Memory = "Memory";

private ILogger _logger;
private readonly ILogger _logger;
private readonly SDKVersionUtils _sdkVersionUtils;
internal IK8sEnvironment K8sEnvironment { get; private set; }

public KubernetesTelemetryInitializer(
IK8sEnvironment k8sEnv,
SDKVersionUtils sdkVersionUtils,
ILogger<KubernetesTelemetryInitializer> logger)
{
_logger = logger;
_sdkVersionUtils = Arguments.IsNotNull(sdkVersionUtils, nameof(sdkVersionUtils));
this.K8sEnvironment = Arguments.IsNotNull(k8sEnv, nameof(k8sEnv));
}

Expand All @@ -59,13 +64,13 @@ public void Initialize(ITelemetry telemetry)
#else
SetCustomDimensions(telemetry);
#endif

_logger.LogTrace(JsonConvert.SerializeObject(telemetry));
}
else
{
_logger.LogError("K8s Environemnt is null.");
}
telemetry.Context.GetInternalContext().SdkVersion = _sdkVersionUtils.CurrentSDKVersion;
}

private void SetCustomDimensions(ITelemetry telemetry)
Expand Down
Loading

0 comments on commit 2b36203

Please sign in to comment.