-
Notifications
You must be signed in to change notification settings - Fork 844
Add Kubernetes based Resource Monitoring #6748
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 19 commits
be94825
bb36737
6024134
785a2f0
95e1a3c
cfc4861
27fd482
632636f
4faae5b
d49ab90
727159a
02517e4
8057769
ced4716
66bf3ca
67c3553
87b81cf
cd8bf74
296f896
01f2f4d
fb94614
d9158c0
9e4eb2b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System; | ||
| using System.Globalization; | ||
|
|
||
| namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Kubernetes; | ||
|
|
||
| internal class KubernetesMetadata | ||
| { | ||
| /// <summary> | ||
| /// Gets or sets the resource memory limit the container is allowed to use in bytes. | ||
| /// </summary> | ||
| public ulong LimitsMemory { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the resource CPU limit the container is allowed to use in milicores. | ||
| /// </summary> | ||
| public ulong LimitsCpu { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the resource memory request the container is allowed to use in bytes. | ||
| /// </summary> | ||
| public ulong RequestsMemory { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the resource CPU request the container is allowed to use in milicores. | ||
amadeuszl marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| /// </summary> | ||
| public ulong RequestsCpu { get; set; } | ||
|
|
||
| private string _environmentVariablePrefix; | ||
|
|
||
| public KubernetesMetadata(string environmentVariablePrefix) | ||
| { | ||
| _environmentVariablePrefix = environmentVariablePrefix; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Fills the object with data loaded from environment variables. | ||
| /// </summary> | ||
| /// <returns>Self</returns> | ||
| public KubernetesMetadata Build() | ||
| { | ||
| LimitsMemory = GetEnvironmentVariableAsUInt64($"{_environmentVariablePrefix}LIMITS_MEMORY"); | ||
| LimitsCpu = GetEnvironmentVariableAsUInt64($"{_environmentVariablePrefix}LIMITS_CPU"); | ||
| RequestsMemory = GetEnvironmentVariableAsUInt64($"{_environmentVariablePrefix}REQUESTS_MEMORY"); | ||
| RequestsCpu = GetEnvironmentVariableAsUInt64($"{_environmentVariablePrefix}REQUESTS_CPU"); | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| private static ulong GetEnvironmentVariableAsUInt64(string variableName) | ||
| { | ||
| var value = Environment.GetEnvironmentVariable(variableName); | ||
| if (string.IsNullOrWhiteSpace(value)) | ||
| { | ||
| return 0; | ||
| } | ||
|
|
||
| if (!ulong.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out ulong result)) | ||
| { | ||
| throw new InvalidOperationException($"Environment variable '{variableName}' contains invalid value '{value}'. Expected a non-negative integer."); | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using Microsoft.Shared.Diagnostics; | ||
|
|
||
| namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Kubernetes; | ||
|
|
||
| internal class KubernetesResourceQuotaProvider : ResourceQuotaProvider | ||
| { | ||
| private const double MillicoresPerCore = 1000.0; | ||
| private KubernetesMetadata _kubernetesMetadata; | ||
|
|
||
| public KubernetesResourceQuotaProvider(KubernetesMetadata kubernetesMetadata) | ||
| { | ||
| _ = Throw.IfNull(kubernetesMetadata); | ||
| _kubernetesMetadata = kubernetesMetadata; | ||
evgenyfedorov2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| public override ResourceQuota GetResourceQuota() | ||
| { | ||
| ResourceQuota quota = new() | ||
| { | ||
| BaselineCpuInCores = ConvertMillicoreToCpuUnit(_kubernetesMetadata.RequestsCpu), | ||
| MaxCpuInCores = ConvertMillicoreToCpuUnit(_kubernetesMetadata.LimitsCpu), | ||
| BaselineMemoryInBytes = _kubernetesMetadata.RequestsMemory, | ||
| MaxMemoryInBytes = _kubernetesMetadata.LimitsMemory, | ||
| }; | ||
|
|
||
| if (quota.BaselineCpuInCores <= 0.0) | ||
| { | ||
| quota.BaselineCpuInCores = quota.MaxCpuInCores; | ||
| } | ||
|
|
||
| if (quota.BaselineMemoryInBytes == 0) | ||
| { | ||
| quota.BaselineMemoryInBytes = quota.MaxMemoryInBytes; | ||
| } | ||
|
|
||
| return quota; | ||
evgenyfedorov2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| private static double ConvertMillicoreToCpuUnit(ulong millicores) | ||
| { | ||
| return millicores / MillicoresPerCore; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using Microsoft.Extensions.DependencyInjection.Extensions; | ||
| using Microsoft.Extensions.Diagnostics.ResourceMonitoring; | ||
| using Microsoft.Extensions.Diagnostics.ResourceMonitoring.Kubernetes; | ||
|
|
||
| namespace Microsoft.Extensions.DependencyInjection; | ||
|
|
||
| /// <summary> | ||
| /// Lets you configure and register Kubernetes resource monitoring components. | ||
| /// </summary> | ||
| public static class KubernetesResourceQuotaServiceCollectionExtensions | ||
| { | ||
| /// <summary> | ||
| /// Configures and adds an Kubernetes resource monitoring components to a service collection alltoghter with necessary basic resource monitoring components. | ||
amadeuszl marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| /// </summary> | ||
| /// <param name="services">The dependency injection container to add the Kubernetes resource monitoring to.</param> | ||
| /// <param name="environmentVariablePrefix">Optional value of prefix used to read environment variables in the container.</param> | ||
| /// <returns>The value of <paramref name="services" />.</returns> | ||
| /// <remarks> | ||
| /// <para> | ||
| /// If you have configured your Kubernetes container with Downward API to add environment variable <c>MYCLUSTER_LIMITS_CPU</c> with CPU limits, | ||
| /// then you should pass <c>MYCLUSTER_</c> to <paramref name="environmentVariablePrefix"/> parameter. Environment variables will be read during DI Container resolution. | ||
| /// </para> | ||
| /// <para> | ||
| /// <strong>Important:</strong> Do not call <see cref="ResourceMonitoringServiceCollectionExtensions.AddResourceMonitoring(IServiceCollection)"/> | ||
| /// if you are using this method, as it already includes all necessary resource monitoring components and registers a Kubernetes-specific | ||
| /// <see cref="ResourceQuotaProvider"/> implementation. Calling both methods may result in conflicting service registrations. | ||
| /// </para> | ||
| /// </remarks> | ||
| public static IServiceCollection AddKubernetesResourceMonitoring( | ||
| this IServiceCollection services, | ||
| string? environmentVariablePrefix = default) | ||
| { | ||
| services.TryAddSingleton<KubernetesMetadata>(serviceProvider => | ||
evgenyfedorov2 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| var metadata = new KubernetesMetadata(environmentVariablePrefix ?? string.Empty); | ||
| return metadata.Build(); | ||
evgenyfedorov2 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }); | ||
evgenyfedorov2 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| services.TryAddSingleton<ResourceQuotaProvider, KubernetesResourceQuotaProvider>(); | ||
|
|
||
| _ = services.AddResourceMonitoring(); | ||
|
|
||
| return services; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,32 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| <PropertyGroup> | ||||
| <RootNamespace>Microsoft.Extensions.Diagnostics.ResourceMonitoring.Kubernetes</RootNamespace> | ||||
| <Description>Provides Kubernetes data for measurements of processor and memory usage.</Description> | ||||
| <Workstream>ResourceMonitoring</Workstream> | ||||
| <NoWarn Condition="'$(TargetFramework)' == 'net462'">$(NoWarn);CS0436</NoWarn> | ||||
| <EnablePackageValidation>false</EnablePackageValidation> | ||||
| </PropertyGroup> | ||||
|
|
||||
| <PropertyGroup> | ||||
| <InjectSharedInstruments>true</InjectSharedInstruments> | ||||
| </PropertyGroup> | ||||
|
|
||||
| <PropertyGroup> | ||||
| <Stage>normal</Stage> | ||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it should be extensions/eng/MSBuild/Packaging.targets Line 40 in 7ddf87e
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let me verify that. I would prefer to release it already in December as normal. |
||||
| <MinCodeCoverage>99</MinCodeCoverage> | ||||
| <MinMutationScore>90</MinMutationScore> | ||||
| </PropertyGroup> | ||||
|
|
||||
| <ItemGroup Condition="'$(TargetFramework)' == 'net462'"> | ||||
| <Compile Remove="Linux\**\*.cs" /> | ||||
| </ItemGroup> | ||||
amadeuszl marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||
|
|
||||
| <ItemGroup> | ||||
| <ProjectReference Include="..\Microsoft.Extensions.Diagnostics.ResourceMonitoring\Microsoft.Extensions.Diagnostics.ResourceMonitoring.csproj" /> | ||||
| </ItemGroup> | ||||
|
|
||||
| <ItemGroup> | ||||
| <InternalsVisibleToDynamicProxyGenAssembly2 Include="*" /> | ||||
| <InternalsVisibleToTest Include="$(AssemblyName).Tests" /> | ||||
| </ItemGroup> | ||||
| </Project> | ||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # Microsoft.Extensions.Diagnostics.ResourceMonitoring.Kubernetes | ||
|
|
||
| Registers `ResourceQuota` implementation specific to Kubernetes. | ||
|
|
||
| ## Install the package | ||
|
|
||
| From the command-line: | ||
|
|
||
| ```console | ||
| dotnet add package Microsoft.Extensions.Diagnostics.ResourceMonitoring.Kubernetes | ||
| ``` | ||
|
|
||
| Or directly in the C# project file: | ||
|
|
||
| ```xml | ||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.Extensions.Diagnostics.ResourceMonitoring.Kubernetes" Version="[CURRENTVERSION]" /> | ||
| </ItemGroup> | ||
| ``` | ||
|
|
||
|
|
||
| ## Feedback & Contributing | ||
|
|
||
| We welcome feedback and contributions in [our GitHub repo](https://github.com/dotnet/extensions). |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using Microsoft.Extensions.Options; | ||
|
|
||
| namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux; | ||
|
|
||
| internal class LinuxResourceQuotaProvider : ResourceQuotaProvider | ||
| { | ||
| private readonly ILinuxUtilizationParser _parser; | ||
| private bool _useLinuxCalculationV2; | ||
|
|
||
| public LinuxResourceQuotaProvider(ILinuxUtilizationParser parser, IOptions<ResourceMonitoringOptions> options) | ||
| { | ||
| _parser = parser; | ||
| _useLinuxCalculationV2 = options.Value.UseLinuxCalculationV2; | ||
| } | ||
|
|
||
| public override ResourceQuota GetResourceQuota() | ||
| { | ||
| var resourceQuota = new ResourceQuota(); | ||
| if (_useLinuxCalculationV2) | ||
| { | ||
| resourceQuota.MaxCpuInCores = _parser.GetCgroupLimitV2(); | ||
| resourceQuota.BaselineCpuInCores = _parser.GetCgroupRequestCpuV2(); | ||
| } | ||
| else | ||
| { | ||
| resourceQuota.MaxCpuInCores = _parser.GetCgroupLimitedCpus(); | ||
| resourceQuota.BaselineCpuInCores = _parser.GetCgroupRequestCpu(); | ||
| } | ||
|
|
||
| resourceQuota.MaxMemoryInBytes = _parser.GetAvailableMemoryInBytes(); | ||
| resourceQuota.BaselineMemoryInBytes = _parser.GetMinMemoryInBytes(); | ||
|
|
||
| if (resourceQuota.BaselineMemoryInBytes == 0) | ||
| { | ||
| resourceQuota.BaselineMemoryInBytes = resourceQuota.MaxMemoryInBytes; | ||
| } | ||
|
|
||
| return resourceQuota; | ||
| } | ||
| } | ||
|
|
Uh oh!
There was an error while loading. Please reload this page.