Skip to content

Commit

Permalink
#64 : Support for Scheduler Service (#65)
Browse files Browse the repository at this point in the history
* #64 : Support for Scheduler Service
  • Loading branch information
badgeratu authored Sep 11, 2024
1 parent 9a8e9f4 commit 3ee91e2
Show file tree
Hide file tree
Showing 37 changed files with 1,235 additions and 14 deletions.
21 changes: 21 additions & 0 deletions all.sln
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PlacementSample", "Placemen
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlacementSample", "samples\AspNetCore\PlacementSample\PlacementSample\PlacementSample.csproj", "{2FC86574-6A81-4E2B-A0D4-78D46528A917}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SchedulerSample", "SchedulerSample", "{8FD8AFF0-A56A-4BDC-B40E-F498AA147790}"
ProjectSection(SolutionItems) = preProject
samples\AspNetCore\SchedulerSample\README.md = samples\AspNetCore\SchedulerSample\README.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SchedulerSample", "samples\AspNetCore\SchedulerSample\SchedulerSample\SchedulerSample.csproj", "{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -315,6 +322,18 @@ Global
{2FC86574-6A81-4E2B-A0D4-78D46528A917}.Release|x64.Build.0 = Release|Any CPU
{2FC86574-6A81-4E2B-A0D4-78D46528A917}.Release|x86.ActiveCfg = Release|Any CPU
{2FC86574-6A81-4E2B-A0D4-78D46528A917}.Release|x86.Build.0 = Release|Any CPU
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Debug|x64.ActiveCfg = Debug|Any CPU
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Debug|x64.Build.0 = Debug|Any CPU
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Debug|x86.ActiveCfg = Debug|Any CPU
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Debug|x86.Build.0 = Debug|Any CPU
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Release|Any CPU.Build.0 = Release|Any CPU
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Release|x64.ActiveCfg = Release|Any CPU
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Release|x64.Build.0 = Release|Any CPU
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Release|x86.ActiveCfg = Release|Any CPU
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -348,6 +367,8 @@ Global
{9F4DA8E9-F253-4312-A0BB-E2873A21C41A} = {7F6A5D8C-9780-4824-8284-CC3149683C70}
{AE430C04-78BD-4CAE-86D7-EBC599774D9C} = {DA3D8137-F2DD-465D-81AA-3CA5C75087D2}
{2FC86574-6A81-4E2B-A0D4-78D46528A917} = {AE430C04-78BD-4CAE-86D7-EBC599774D9C}
{8FD8AFF0-A56A-4BDC-B40E-F498AA147790} = {DA3D8137-F2DD-465D-81AA-3CA5C75087D2}
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E} = {8FD8AFF0-A56A-4BDC-B40E-F498AA147790}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E906E97D-7D56-4E02-A13F-1C48AEB47A88}
Expand Down
1 change: 1 addition & 0 deletions all.v3.ncrunchsolution
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<AutoEnable>True</AutoEnable>
<CurrentEngineMode>Run Solution Unit Tests automatically [SnapshotShared]</CurrentEngineMode>
<InstrumentationMode>Optimised</InstrumentationMode>
<RdiConfigured>True</RdiConfigured>
<SolutionConfigured>True</SolutionConfigured>
</Settings>
<EngineModes>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
"Serilog": {
"MinimumLevel": "Debug"
},
"AllowedHosts": "*"
}
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
4 changes: 2 additions & 2 deletions samples/AspNetCore/PlacementSample/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ builder.Services.AddDaprSidekick(builder.Configuration)
.AddPlacement();
```

Typically when installing Dapr in self-hosted mode, a Placement service container is added to Docker exposing the default port 6500. If this samle is run
Typically when installing Dapr in self-hosted mode, a Placement service container is added to Docker exposing the default port 6050. If this same is run
while that container is up it will be unable to start due to a port conflict. Instead a different port 6501 is assigned to Placement in configuration:

```json5
Expand All @@ -30,7 +30,7 @@ unless a specific remote address is defined. For example the following specifies

```json5
"Sidecar": {
"PlacementHostAddress": "remote-host-1:6050,remote-host-2:6050,remote-host-3:6050"
"PlacementHostAddress": "remote-host-1:50005,remote-host-2:50005,remote-host-3:50005"
}
```

Expand Down
42 changes: 42 additions & 0 deletions samples/AspNetCore/SchedulerSample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Scheduler example

This sample shows how Dapr Sidekick can be used to host the Dapr Scheduler service alongside a Dapr Sidecar instance. Sidekick will launch the Scheduler service and wait for it
to enter a healthy running state, then will launch the Sidecar alongside. The Sidecar will be configured to use the launched Scheduler service by default.
As with the Sidecar, the Scheduler service will be continually monitored and maintained for the lifetime of the application.

## How Dapr Sidekick was added

This is an ASP.NET Core minimal Web API project. Dapr Sidekick was added to the [Program.cs](SchedulerSample\Program.cs) file as follows:

```csharp
// Add Dapr Sidekick with Scheduler
builder.Services.AddDaprSidekick(builder.Configuration)
.AddScheduler();
```

Typically when installing Dapr in self-hosted mode, a Scheduler service container is added to Docker exposing the default port 6060. If this same is run
while that container is up it will be unable to start due to a port conflict. Instead a different port 6061 is assigned to Scheduler in configuration:

```json5
"Scheduler": {
"RuntimeDirectory": "scheduler",
"Id": "dapr-scheduler-server-0", // Optional unique identifier when used in a cluster
"Port": 6061 // To avoid conflicts with local Dapr Scheduler container. Sidecar will use this automatically as well.
}
```

By default the Sidecar that is launched alongside the Scheduler service will look for the Scheduler service locally on this custom port,
unless a specific remote address is defined. For example the following specifies a three-host remote Scheduler cluster:

```json5
"Sidecar": {
"SchedulerHostAddress": "remote-host-1:50006,remote-host-2:50006,remote-host-3:50006"
}
```

## Running the sample

To run the sample simply set `SchedulerSample` as the startup project and run it in Visual Studio,
it will launch first the Scheduler service then the Dapr sidecar, then open a browser and display
the configured launch options for both.

39 changes: 39 additions & 0 deletions samples/AspNetCore/SchedulerSample/SchedulerSample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Man.Dapr.Sidekick;
using Serilog;

// Add Serilog for enhanced console logging.
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

// Add Dapr Sidekick with Scheduler
builder.Services.AddDaprSidekick(builder.Configuration)
.AddScheduler();

builder.Host.UseSerilog();

var app = builder.Build();

app.MapGet("/status", (IDaprSidecarHost sidecarHost, IDaprSchedulerHost schedulerHost) => Results.Ok(new
{
sidecar = new
{
process = sidecarHost.GetProcessInfo(), // Information about the sidecar process such as if it is running
options = sidecarHost.GetProcessOptions() // The sidecar options if running, including ports and locations
},
scheduler = new
{
process = schedulerHost.GetProcessInfo(), // Information about the sentry process such as if it is running
options = schedulerHost.GetProcessOptions() // The sentry options if running, including ports and locations
},
}));

// For Dapr
app.MapHealthChecks("/health");

app.Run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"profiles": {
"SchedulerSample": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "status",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:5000"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Man.Dapr.Sidekick.AspNetCore\Man.Dapr.Sidekick.AspNetCore.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="dapr\**\*.*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="scheduler\**\*.*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"DaprSidekick": {
"Sidecar": {
"RuntimeDirectory": "dapr"
},
"Scheduler": {
"RuntimeDirectory": "scheduler",
"Id": "dapr-scheduler-server-0", // Optional unique identifier when used in a cluster
"Port": 6061 // To avoid conflicts with local Dapr Scheduler container. Sidecar will use this automatically as well.
}
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
name: daprsystem
namespace: default
spec:
mtls:
enabled: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
name: daprsystem
namespace: default
spec:
17 changes: 17 additions & 0 deletions src/Man.Dapr.Sidekick.AspNetCore/DaprSidekickBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Linq;
using Man.Dapr.Sidekick.AspNetCore.Metrics;
using Man.Dapr.Sidekick.AspNetCore.Placement;
using Man.Dapr.Sidekick.AspNetCore.Scheduler;
using Man.Dapr.Sidekick.AspNetCore.Sentry;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
Expand Down Expand Up @@ -33,6 +34,22 @@ public IDaprSidekickBuilder AddPlacement()
return this;
}

public IDaprSidekickBuilder AddScheduler()
{
// Add the scheduler host
_services.TryAddSingleton<IDaprSchedulerHost, DaprSchedulerHost>();
_services.TryAddHostedService<DaprSchedulerHostedService>();

// Add the health checks and metrics
_services.AddHealthChecks().AddDaprScheduler();
_services.AddSingleton<IPrometheusCollector, DaprSchedulerMetricsCollector>();

// Override the default sidecar hosted service, to one that only starts when the Scheduler service is available.
ReplaceSidecarHostedService<DaprSchedulerSidecarHostedService>();

return this;
}

public IDaprSidekickBuilder AddSentry()
{
// Add the Sentry host
Expand Down
6 changes: 6 additions & 0 deletions src/Man.Dapr.Sidekick.AspNetCore/IDaprSidekickBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ public interface IDaprSidekickBuilder
/// <returns>This instance to allow calls to be chained.</returns>
public IDaprSidekickBuilder AddPlacement();

/// <summary>
/// Adds the Dapr Scheduler service.
/// </summary>
/// <returns>This instance to allow calls to be chained.</returns>
public IDaprSidekickBuilder AddScheduler();

/// <summary>
/// Adds the Dapr Sentry service.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public static class DaprMetricsConstants

public static readonly string DaprSidecarLabel = "dapr-sidecar";
public static readonly string DaprPlacementLabel = "dapr-placement";
public static readonly string DaprSchedulerLabel = "dapr-scheduler";
public static readonly string DaprSentryLabel = "dapr-sentry";

public static readonly string ExporterContentType = "text/plain; charset=utf-8";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Man.Dapr.Sidekick.AspNetCore.Scheduler
{
public class DaprSchedulerHealthCheck : DaprProcessHealthCheck
{
public DaprSchedulerHealthCheck(
IDaprSchedulerHost daprSchedulerHost)
: base(daprSchedulerHost)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Collections.Generic;
using Man.Dapr.Sidekick.AspNetCore.Metrics;
using Man.Dapr.Sidekick.AspNetCore.Scheduler;
using Microsoft.Extensions.Diagnostics.HealthChecks;

namespace Microsoft.Extensions.DependencyInjection
{
public static class DaprSchedulerHealthCheckBuilderExtensions
{
/// <summary>
/// Add a health check for the Dapr Scheduler.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The health check name. Optional. If <c>null</c> the type name 'dapr_Scheduler' will be used for the name.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check fails. Optional. If <c>null</c> then
/// the default status of <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used to filter sets of health checks. Optional.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/> to allow calls to be chained.</returns>
public static IHealthChecksBuilder AddDaprScheduler(
this IHealthChecksBuilder builder,
string name = default,
HealthStatus? failureStatus = default,
IEnumerable<string> tags = default)
{
builder.AddCheck<DaprSchedulerHealthCheck>(name ?? DaprMetricsConstants.DaprSchedulerLabel, failureStatus, tags);
return builder;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Threading;
using Man.Dapr.Sidekick.AspNetCore.Metrics;
using Microsoft.Extensions.Options;

namespace Man.Dapr.Sidekick.AspNetCore.Scheduler
{
public class DaprSchedulerHostedService : DaprHostedService<IDaprSchedulerHost, DaprSchedulerOptions>
{
public DaprSchedulerHostedService(
IDaprSchedulerHost daprSchedulerHost,
IOptionsMonitor<DaprOptions> optionsAccessor)
: base(daprSchedulerHost, optionsAccessor)
{
}

protected override void OnStarting(DaprOptions options, CancellationToken cancellationToken)
{
// Assign metrics
options.Scheduler ??= new DaprSchedulerOptions();
options.Scheduler.Metrics ??= new DaprMetricsOptions();
options.Scheduler.Metrics.SetLabel(DaprMetricsConstants.ServiceLabelName, options.Sidecar?.AppId);
options.Scheduler.Metrics.SetLabel(DaprMetricsConstants.AppLabelName, DaprMetricsConstants.DaprSchedulerLabel);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Man.Dapr.Sidekick.AspNetCore.Metrics;

namespace Man.Dapr.Sidekick.AspNetCore.Scheduler
{
public class DaprSchedulerMetricsCollector : DaprProcessHostPrometheusCollector
{
public DaprSchedulerMetricsCollector(IDaprSchedulerHost daprSchedulerHost)
: base(daprSchedulerHost)
{
}
}
}
Loading

0 comments on commit 3ee91e2

Please sign in to comment.