Loading...
Loading...
Akka.Management for cluster bootstrapping, service discovery (Kubernetes, Azure, Config), health checks, and dynamic cluster formation without static seed nodes.
npx skill4agent add aaronontheweb/dotnet-skills akka-net-management| Approach | Pros | Cons |
|---|---|---|
| Static Seed Nodes | Simple, no dependencies | Doesn't scale, requires known IPs |
| Akka.Management | Dynamic discovery, scales to N nodes | More configuration, external dependencies |
┌─────────────────────────────────────────────────────────────┐
│ Cluster Bootstrap │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Node 1 │ │ Node 2 │ │ Node 3 │ │
│ │ │ │ │ │ │ │
│ │ Management │◄──►│ Management │◄──►│ Management │ │
│ │ HTTP :8558 │ │ HTTP :8558 │ │ HTTP :8558 │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └──────────────────┼──────────────────┘ │
│ │ │
│ ┌───────▼───────┐ │
│ │ Discovery │ │
│ │ Provider │ │
│ └───────────────┘ │
│ │ │
└────────────────────────────┼────────────────────────────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌─────▼─────┐ ┌──────▼─────┐ ┌─────▼──────┐
│ Kubernetes│ │ Azure │ │ Config │
│ API │ │ Tables │ │ (HOCON) │
└───────────┘ └────────────┘ └────────────┘<ItemGroup>
<!-- Core management -->
<PackageReference Include="Akka.Management" />
<PackageReference Include="Akka.Management.Cluster.Bootstrap" />
<!-- Choose ONE discovery provider -->
<PackageReference Include="Akka.Discovery.KubernetesApi" /> <!-- For Kubernetes -->
<PackageReference Include="Akka.Discovery.Azure" /> <!-- For Azure -->
<PackageReference Include="Akka.Discovery.Config.Hosting" /> <!-- For static config -->
</ItemGroup>public static class AkkaConfiguration
{
public static IServiceCollection ConfigureAkka(
this IServiceCollection services,
Action<AkkaConfigurationBuilder, IServiceProvider>? additionalConfig = null)
{
services.AddOptions<AkkaSettings>()
.BindConfiguration("AkkaSettings")
.ValidateDataAnnotations()
.ValidateOnStart();
return services.AddAkka("MySystem", (builder, sp) =>
{
var settings = sp.GetRequiredService<IOptions<AkkaSettings>>().Value;
var configuration = sp.GetRequiredService<IConfiguration>();
ConfigureNetwork(builder, settings, configuration);
ConfigureHealthChecks(builder);
additionalConfig?.Invoke(builder, sp);
});
}
private static void ConfigureNetwork(
AkkaConfigurationBuilder builder,
AkkaSettings settings,
IConfiguration configuration)
{
if (settings.ExecutionMode == AkkaExecutionMode.LocalTest)
return;
builder.WithRemoting(settings.RemoteOptions);
if (settings.ClusterBootstrapOptions.Enabled)
ConfigureAkkaManagement(builder, settings, configuration);
else
builder.WithClustering(settings.ClusterOptions);
}
}private static void ConfigureAkkaManagement(
AkkaConfigurationBuilder builder,
AkkaSettings settings,
IConfiguration configuration)
{
var mgmtOptions = settings.AkkaManagementOptions;
var bootstrapOptions = settings.ClusterBootstrapOptions;
// IMPORTANT: Clear seed nodes when using Akka.Management
settings.ClusterOptions.SeedNodes = [];
builder
.WithClustering(settings.ClusterOptions)
.WithAkkaManagement(setup =>
{
setup.Http.HostName = mgmtOptions.HostName;
setup.Http.Port = mgmtOptions.Port;
setup.Http.BindHostName = "0.0.0.0";
setup.Http.BindPort = mgmtOptions.Port;
})
.WithClusterBootstrap(options =>
{
options.ContactPointDiscovery.ServiceName = bootstrapOptions.ServiceName;
options.ContactPointDiscovery.PortName = bootstrapOptions.PortName;
options.ContactPointDiscovery.RequiredContactPointsNr = bootstrapOptions.RequiredContactPointsNr;
options.ContactPointDiscovery.Interval = bootstrapOptions.ContactPointProbingInterval;
options.ContactPointDiscovery.StableMargin = bootstrapOptions.StableMargin;
options.ContactPointDiscovery.ContactWithAllContactPoints = bootstrapOptions.ContactWithAllContactPoints;
options.ContactPoint.FilterOnFallbackPort = bootstrapOptions.FilterOnFallbackPort;
options.ContactPoint.ProbeInterval = bootstrapOptions.BootstrapperDiscoveryPingInterval;
});
// Configure the discovery provider
ConfigureDiscovery(builder, settings, configuration);
}| Endpoint | Purpose | Returns 200 When |
|---|---|---|
| Liveness | ActorSystem is running |
| Readiness | Cluster member is Up |
| Debug | Returns cluster membership |
// Register Akka health checks
builder.Services.AddHealthChecks();
// In Akka configuration
builder
.WithActorSystemLivenessCheck() // Adds "akka-liveness" health check
.WithAkkaClusterReadinessCheck(); // Adds "akka-cluster-readiness" health check
// Map endpoints
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("liveness")
});
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("readiness")
});ServiceNameRequiredContactPointsNrContactWithAllContactPoints = trueStableMarginFilterOnFallbackPort = falseFilterOnFallbackPort = trueServiceNameakka-net-aspire-configuration// In AppHost
appBuilder
.WithEndpoint(name: "remote", protocol: ProtocolType.Tcp,
env: "AkkaSettings__RemoteOptions__Port")
.WithEndpoint(name: "management", protocol: ProtocolType.Tcp,
env: "AkkaSettings__AkkaManagementOptions__Port")
.WithEnvironment("AkkaSettings__ClusterBootstrapOptions__Enabled", "true")
.WithEnvironment("AkkaSettings__ClusterBootstrapOptions__DiscoveryMethod", "AzureTableStorage")
.WithEnvironment("AkkaSettings__ClusterBootstrapOptions__FilterOnFallbackPort", "false");| Scenario | Discovery Method | FilterOnFallbackPort |
|---|---|---|
| Local development (single node) | None (use seed nodes) | N/A |
| Aspire multi-node | AzureTableStorage | |
| Kubernetes | Kubernetes | |
| Azure VMs/VMSS | AzureTableStorage | |
| Fixed infrastructure | Config | |
| AWS ECS/EC2 | AWS discovery plugins | |