Skip to content

Commit 3ee91e2

Browse files
authored
#64 : Support for Scheduler Service (#65)
* #64 : Support for Scheduler Service
1 parent 9a8e9f4 commit 3ee91e2

37 files changed

+1235
-14
lines changed

all.sln

+21
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PlacementSample", "Placemen
101101
EndProject
102102
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlacementSample", "samples\AspNetCore\PlacementSample\PlacementSample\PlacementSample.csproj", "{2FC86574-6A81-4E2B-A0D4-78D46528A917}"
103103
EndProject
104+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SchedulerSample", "SchedulerSample", "{8FD8AFF0-A56A-4BDC-B40E-F498AA147790}"
105+
ProjectSection(SolutionItems) = preProject
106+
samples\AspNetCore\SchedulerSample\README.md = samples\AspNetCore\SchedulerSample\README.md
107+
EndProjectSection
108+
EndProject
109+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SchedulerSample", "samples\AspNetCore\SchedulerSample\SchedulerSample\SchedulerSample.csproj", "{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}"
110+
EndProject
104111
Global
105112
GlobalSection(SolutionConfigurationPlatforms) = preSolution
106113
Debug|Any CPU = Debug|Any CPU
@@ -315,6 +322,18 @@ Global
315322
{2FC86574-6A81-4E2B-A0D4-78D46528A917}.Release|x64.Build.0 = Release|Any CPU
316323
{2FC86574-6A81-4E2B-A0D4-78D46528A917}.Release|x86.ActiveCfg = Release|Any CPU
317324
{2FC86574-6A81-4E2B-A0D4-78D46528A917}.Release|x86.Build.0 = Release|Any CPU
325+
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
326+
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Debug|Any CPU.Build.0 = Debug|Any CPU
327+
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Debug|x64.ActiveCfg = Debug|Any CPU
328+
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Debug|x64.Build.0 = Debug|Any CPU
329+
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Debug|x86.ActiveCfg = Debug|Any CPU
330+
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Debug|x86.Build.0 = Debug|Any CPU
331+
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Release|Any CPU.ActiveCfg = Release|Any CPU
332+
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Release|Any CPU.Build.0 = Release|Any CPU
333+
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Release|x64.ActiveCfg = Release|Any CPU
334+
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Release|x64.Build.0 = Release|Any CPU
335+
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Release|x86.ActiveCfg = Release|Any CPU
336+
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Release|x86.Build.0 = Release|Any CPU
318337
EndGlobalSection
319338
GlobalSection(SolutionProperties) = preSolution
320339
HideSolutionNode = FALSE
@@ -348,6 +367,8 @@ Global
348367
{9F4DA8E9-F253-4312-A0BB-E2873A21C41A} = {7F6A5D8C-9780-4824-8284-CC3149683C70}
349368
{AE430C04-78BD-4CAE-86D7-EBC599774D9C} = {DA3D8137-F2DD-465D-81AA-3CA5C75087D2}
350369
{2FC86574-6A81-4E2B-A0D4-78D46528A917} = {AE430C04-78BD-4CAE-86D7-EBC599774D9C}
370+
{8FD8AFF0-A56A-4BDC-B40E-F498AA147790} = {DA3D8137-F2DD-465D-81AA-3CA5C75087D2}
371+
{C0B2943F-3EA8-43A9-8714-7D6B84AD788E} = {8FD8AFF0-A56A-4BDC-B40E-F498AA147790}
351372
EndGlobalSection
352373
GlobalSection(ExtensibilityGlobals) = postSolution
353374
SolutionGuid = {E906E97D-7D56-4E02-A13F-1C48AEB47A88}

all.v3.ncrunchsolution

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<AutoEnable>True</AutoEnable>
88
<CurrentEngineMode>Run Solution Unit Tests automatically [SnapshotShared]</CurrentEngineMode>
99
<InstrumentationMode>Optimised</InstrumentationMode>
10+
<RdiConfigured>True</RdiConfigured>
1011
<SolutionConfigured>True</SolutionConfigured>
1112
</Settings>
1213
<EngineModes>
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
{
2-
"Logging": {
3-
"LogLevel": {
4-
"Default": "Information",
5-
"Microsoft": "Warning",
6-
"Microsoft.Hosting.Lifetime": "Information"
7-
}
2+
"Serilog": {
3+
"MinimumLevel": "Debug"
84
},
9-
"AllowedHosts": "*"
10-
}
5+
"Logging": {
6+
"LogLevel": {
7+
"Default": "Debug",
8+
"Microsoft": "Warning",
9+
"Microsoft.Hosting.Lifetime": "Information"
10+
}
11+
},
12+
"AllowedHosts": "*"
13+
}

samples/AspNetCore/PlacementSample/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ builder.Services.AddDaprSidekick(builder.Configuration)
1414
.AddPlacement();
1515
```
1616

17-
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
17+
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
1818
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:
1919

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

3131
```json5
3232
"Sidecar": {
33-
"PlacementHostAddress": "remote-host-1:6050,remote-host-2:6050,remote-host-3:6050"
33+
"PlacementHostAddress": "remote-host-1:50005,remote-host-2:50005,remote-host-3:50005"
3434
}
3535
```
3636

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Scheduler example
2+
3+
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
4+
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.
5+
As with the Sidecar, the Scheduler service will be continually monitored and maintained for the lifetime of the application.
6+
7+
## How Dapr Sidekick was added
8+
9+
This is an ASP.NET Core minimal Web API project. Dapr Sidekick was added to the [Program.cs](SchedulerSample\Program.cs) file as follows:
10+
11+
```csharp
12+
// Add Dapr Sidekick with Scheduler
13+
builder.Services.AddDaprSidekick(builder.Configuration)
14+
.AddScheduler();
15+
```
16+
17+
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
18+
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:
19+
20+
```json5
21+
"Scheduler": {
22+
"RuntimeDirectory": "scheduler",
23+
"Id": "dapr-scheduler-server-0", // Optional unique identifier when used in a cluster
24+
"Port": 6061 // To avoid conflicts with local Dapr Scheduler container. Sidecar will use this automatically as well.
25+
}
26+
```
27+
28+
By default the Sidecar that is launched alongside the Scheduler service will look for the Scheduler service locally on this custom port,
29+
unless a specific remote address is defined. For example the following specifies a three-host remote Scheduler cluster:
30+
31+
```json5
32+
"Sidecar": {
33+
"SchedulerHostAddress": "remote-host-1:50006,remote-host-2:50006,remote-host-3:50006"
34+
}
35+
```
36+
37+
## Running the sample
38+
39+
To run the sample simply set `SchedulerSample` as the startup project and run it in Visual Studio,
40+
it will launch first the Scheduler service then the Dapr sidecar, then open a browser and display
41+
the configured launch options for both.
42+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using Man.Dapr.Sidekick;
2+
using Serilog;
3+
4+
// Add Serilog for enhanced console logging.
5+
Log.Logger = new LoggerConfiguration()
6+
.Enrich.FromLogContext()
7+
.WriteTo.Console()
8+
.CreateLogger();
9+
10+
var builder = WebApplication.CreateBuilder(args);
11+
12+
builder.Services.AddControllers();
13+
14+
// Add Dapr Sidekick with Scheduler
15+
builder.Services.AddDaprSidekick(builder.Configuration)
16+
.AddScheduler();
17+
18+
builder.Host.UseSerilog();
19+
20+
var app = builder.Build();
21+
22+
app.MapGet("/status", (IDaprSidecarHost sidecarHost, IDaprSchedulerHost schedulerHost) => Results.Ok(new
23+
{
24+
sidecar = new
25+
{
26+
process = sidecarHost.GetProcessInfo(), // Information about the sidecar process such as if it is running
27+
options = sidecarHost.GetProcessOptions() // The sidecar options if running, including ports and locations
28+
},
29+
scheduler = new
30+
{
31+
process = schedulerHost.GetProcessInfo(), // Information about the sentry process such as if it is running
32+
options = schedulerHost.GetProcessOptions() // The sentry options if running, including ports and locations
33+
},
34+
}));
35+
36+
// For Dapr
37+
app.MapHealthChecks("/health");
38+
39+
app.Run();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"profiles": {
3+
"SchedulerSample": {
4+
"commandName": "Project",
5+
"launchBrowser": true,
6+
"launchUrl": "status",
7+
"environmentVariables": {
8+
"ASPNETCORE_ENVIRONMENT": "Development"
9+
},
10+
"applicationUrl": "http://localhost:5000"
11+
}
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
11+
</ItemGroup>
12+
13+
<ItemGroup>
14+
<ProjectReference Include="..\..\..\..\src\Man.Dapr.Sidekick.AspNetCore\Man.Dapr.Sidekick.AspNetCore.csproj" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<None Update="dapr\**\*.*">
19+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
20+
</None>
21+
<None Update="scheduler\**\*.*">
22+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
23+
</None>
24+
</ItemGroup>
25+
26+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"DaprSidekick": {
3+
"Sidecar": {
4+
"RuntimeDirectory": "dapr"
5+
},
6+
"Scheduler": {
7+
"RuntimeDirectory": "scheduler",
8+
"Id": "dapr-scheduler-server-0", // Optional unique identifier when used in a cluster
9+
"Port": 6061 // To avoid conflicts with local Dapr Scheduler container. Sidecar will use this automatically as well.
10+
}
11+
},
12+
"Logging": {
13+
"LogLevel": {
14+
"Default": "Information",
15+
"Microsoft.AspNetCore": "Warning"
16+
}
17+
},
18+
"AllowedHosts": "*"
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: dapr.io/v1alpha1
2+
kind: Configuration
3+
metadata:
4+
name: daprsystem
5+
namespace: default
6+
spec:
7+
mtls:
8+
enabled: true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
apiVersion: dapr.io/v1alpha1
2+
kind: Configuration
3+
metadata:
4+
name: daprsystem
5+
namespace: default
6+
spec:

src/Man.Dapr.Sidekick.AspNetCore/DaprSidekickBuilder.cs

+17
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Linq;
22
using Man.Dapr.Sidekick.AspNetCore.Metrics;
33
using Man.Dapr.Sidekick.AspNetCore.Placement;
4+
using Man.Dapr.Sidekick.AspNetCore.Scheduler;
45
using Man.Dapr.Sidekick.AspNetCore.Sentry;
56
using Microsoft.Extensions.DependencyInjection;
67
using Microsoft.Extensions.DependencyInjection.Extensions;
@@ -33,6 +34,22 @@ public IDaprSidekickBuilder AddPlacement()
3334
return this;
3435
}
3536

37+
public IDaprSidekickBuilder AddScheduler()
38+
{
39+
// Add the scheduler host
40+
_services.TryAddSingleton<IDaprSchedulerHost, DaprSchedulerHost>();
41+
_services.TryAddHostedService<DaprSchedulerHostedService>();
42+
43+
// Add the health checks and metrics
44+
_services.AddHealthChecks().AddDaprScheduler();
45+
_services.AddSingleton<IPrometheusCollector, DaprSchedulerMetricsCollector>();
46+
47+
// Override the default sidecar hosted service, to one that only starts when the Scheduler service is available.
48+
ReplaceSidecarHostedService<DaprSchedulerSidecarHostedService>();
49+
50+
return this;
51+
}
52+
3653
public IDaprSidekickBuilder AddSentry()
3754
{
3855
// Add the Sentry host

src/Man.Dapr.Sidekick.AspNetCore/IDaprSidekickBuilder.cs

+6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ public interface IDaprSidekickBuilder
1111
/// <returns>This instance to allow calls to be chained.</returns>
1212
public IDaprSidekickBuilder AddPlacement();
1313

14+
/// <summary>
15+
/// Adds the Dapr Scheduler service.
16+
/// </summary>
17+
/// <returns>This instance to allow calls to be chained.</returns>
18+
public IDaprSidekickBuilder AddScheduler();
19+
1420
/// <summary>
1521
/// Adds the Dapr Sentry service.
1622
/// </summary>

src/Man.Dapr.Sidekick.AspNetCore/Metrics/DaprMetricsConstants.cs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public static class DaprMetricsConstants
1313

1414
public static readonly string DaprSidecarLabel = "dapr-sidecar";
1515
public static readonly string DaprPlacementLabel = "dapr-placement";
16+
public static readonly string DaprSchedulerLabel = "dapr-scheduler";
1617
public static readonly string DaprSentryLabel = "dapr-sentry";
1718

1819
public static readonly string ExporterContentType = "text/plain; charset=utf-8";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace Man.Dapr.Sidekick.AspNetCore.Scheduler
2+
{
3+
public class DaprSchedulerHealthCheck : DaprProcessHealthCheck
4+
{
5+
public DaprSchedulerHealthCheck(
6+
IDaprSchedulerHost daprSchedulerHost)
7+
: base(daprSchedulerHost)
8+
{
9+
}
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System.Collections.Generic;
2+
using Man.Dapr.Sidekick.AspNetCore.Metrics;
3+
using Man.Dapr.Sidekick.AspNetCore.Scheduler;
4+
using Microsoft.Extensions.Diagnostics.HealthChecks;
5+
6+
namespace Microsoft.Extensions.DependencyInjection
7+
{
8+
public static class DaprSchedulerHealthCheckBuilderExtensions
9+
{
10+
/// <summary>
11+
/// Add a health check for the Dapr Scheduler.
12+
/// </summary>
13+
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
14+
/// <param name="name">The health check name. Optional. If <c>null</c> the type name 'dapr_Scheduler' will be used for the name.</param>
15+
/// <param name="failureStatus">
16+
/// The <see cref="HealthStatus"/> that should be reported when the health check fails. Optional. If <c>null</c> then
17+
/// the default status of <see cref="HealthStatus.Unhealthy"/> will be reported.
18+
/// </param>
19+
/// <param name="tags">A list of tags that can be used to filter sets of health checks. Optional.</param>
20+
/// <returns>The <see cref="IHealthChecksBuilder"/> to allow calls to be chained.</returns>
21+
public static IHealthChecksBuilder AddDaprScheduler(
22+
this IHealthChecksBuilder builder,
23+
string name = default,
24+
HealthStatus? failureStatus = default,
25+
IEnumerable<string> tags = default)
26+
{
27+
builder.AddCheck<DaprSchedulerHealthCheck>(name ?? DaprMetricsConstants.DaprSchedulerLabel, failureStatus, tags);
28+
return builder;
29+
}
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System.Threading;
2+
using Man.Dapr.Sidekick.AspNetCore.Metrics;
3+
using Microsoft.Extensions.Options;
4+
5+
namespace Man.Dapr.Sidekick.AspNetCore.Scheduler
6+
{
7+
public class DaprSchedulerHostedService : DaprHostedService<IDaprSchedulerHost, DaprSchedulerOptions>
8+
{
9+
public DaprSchedulerHostedService(
10+
IDaprSchedulerHost daprSchedulerHost,
11+
IOptionsMonitor<DaprOptions> optionsAccessor)
12+
: base(daprSchedulerHost, optionsAccessor)
13+
{
14+
}
15+
16+
protected override void OnStarting(DaprOptions options, CancellationToken cancellationToken)
17+
{
18+
// Assign metrics
19+
options.Scheduler ??= new DaprSchedulerOptions();
20+
options.Scheduler.Metrics ??= new DaprMetricsOptions();
21+
options.Scheduler.Metrics.SetLabel(DaprMetricsConstants.ServiceLabelName, options.Sidecar?.AppId);
22+
options.Scheduler.Metrics.SetLabel(DaprMetricsConstants.AppLabelName, DaprMetricsConstants.DaprSchedulerLabel);
23+
}
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Man.Dapr.Sidekick.AspNetCore.Metrics;
2+
3+
namespace Man.Dapr.Sidekick.AspNetCore.Scheduler
4+
{
5+
public class DaprSchedulerMetricsCollector : DaprProcessHostPrometheusCollector
6+
{
7+
public DaprSchedulerMetricsCollector(IDaprSchedulerHost daprSchedulerHost)
8+
: base(daprSchedulerHost)
9+
{
10+
}
11+
}
12+
}

0 commit comments

Comments
 (0)