Search Results for

    Show / Hide Table of Contents

    Phobos 1.x Quickstart Tutorial

    This tutorial will help you understand how to configure and install Phobos 1.x inside an Akka.NET v1.4+ application.

    Important

    Don't start any new Phobos applications with Phobos 1.x - use Phobos 2.x instead.

    Note

    For this quickstart tutorial, we're going to clone the Petabridge.Phobos.Web repository from Github and run it. Make sure you git checkout 1.x in order to run the 1.x version of this sample.

    This project is a ready-made solution for testing Phobos in a real production environment using the following technologies:

    • .NET Core 3.1
    • ASP.NET Core 3.1
    • Akka.Cluster
    • Prometheus
    • Jaeger Tracing
    • Grafana
    • Docker
    • and finally, Kubernetes

    Video Overview

    If you want to get a gist of how the installation process works, you can follow the video from our Phobos Installation Tutorial below.

    For further reading, please also see "Phobos Configuration".

    Note

    The Phobos QuickStart Tutorial also uses the "Phobos + Prometheus Akka.Cluster Dashboard for Grafana," which can immediately visualize your Phobos data inside Grafana when you're using Prometheus (which this demo does.) You should check it out along with the other Phobos dashboards.

    Build and Local Deployment

    Start by cloning this repository to your local system.

    Next - to build this solution you will need to purchase a Phobos license key. They cost $4,000 per year per organization with no node count or seat limitations and comes with a 30 day money-back guarantee.

    Once you purchase a Phobos NuGet keys for your organization, you're going to want to open NuGet.config and add your key:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <solution>
        <add key="disableSourceControlIntegration" value="true" />
      </solution>
      <packageSources>
        <clear />
        <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
        <add key="phobos" value="{your key here}" />
      </packageSources>
    </configuration>
    

    From there, run the following command on the prompt:

    PS> build.cmd Docker
    

    This will create the Docker images the solution needs to run inside Kubernetes: petabridge.phobos.web:0.1.0.

    Deploying the K8s Cluster (with Telemetry Installed)

    From there, everything you need to run the solution in Kubernetes is already defined inside the k8s/ - just run the following command to launch the Phobos-enabled application inside Kubernetes:

    PS> ./k8s/deployAll.cmd
    

    This will spin up a separate Kubernetes namespace, phobos-web, and you can view which services are deployed by running the following command:

    PS> kubectl get all -n phobos-web
    

    You should see the following or similar output:

    NAME                                        READY   STATUS    RESTARTS   AGE
    pod/grafana-5f54fd5bf4-wvdgw                1/1     Running   0          11m
    pod/jaeger-578558d6f9-2xzdv                 1/1     Running   0          11m
    pod/phobos-web-0                            1/1     Running   3          11m
    pod/phobos-web-1                            1/1     Running   2          10m
    pod/phobos-web-2                            1/1     Running   0          9m54s
    pod/prometheus-deployment-c6d99b8b9-28tmq   1/1     Running   0          11m
    
    NAME                            TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                               AGE
    service/grafana-ip-service      LoadBalancer   10.105.46.6      localhost     3000:31641/TCP                        11m
    service/jaeger-agent            ClusterIP      None             <none>        5775/UDP,6831/UDP,6832/UDP,5778/TCP   11m
    service/jaeger-collector        ClusterIP      10.109.248.20    <none>        14267/TCP,14268/TCP,9411/TCP          11m
    service/jaeger-query            LoadBalancer   10.109.204.203   localhost     16686:30911/TCP                       11m
    service/phobos-web              ClusterIP      None             <none>        4055/TCP                              11m
    service/phobos-webapi           LoadBalancer   10.103.247.68    localhost     1880:30424/TCP                        11m
    service/prometheus-ip-service   LoadBalancer   10.101.119.120   localhost     9090:31698/TCP                        11m
    service/zipkin                  ClusterIP      None             <none>        9411/TCP                              11m
    
    NAME                                    READY   UP-TO-DATE   AVAILABLE   AGE
    deployment.apps/grafana                 1/1     1            1           11m
    deployment.apps/jaeger                  1/1     1            1           11m
    deployment.apps/prometheus-deployment   1/1     1            1           11m
    
    NAME                                              DESIRED   CURRENT   READY   AGE
    replicaset.apps/grafana-5f54fd5bf4                1         1         1       11m
    replicaset.apps/jaeger-578558d6f9                 1         1         1       11m
    replicaset.apps/prometheus-deployment-c6d99b8b9   1         1         1       11m
    
    Note

    The restarts from the phobos-web-* pods come from calling Dns.GetHostName() prior to the local K8s service allocating its hostnames. Nothing to worry about - it'll resolve itself in a few moments.

    Once the cluster is fully up and running you can explore the application and its associated telemetry via the following Urls:

    • http://localhost:1880 - generates traffic across the Akka.NET cluster inside the phobos-web service.
    • http://localhost:16686/ - Jaeger tracing UI. Allows to explore the traces that are distributed across the different nodes in the cluster.
    • http://localhost:9090/ - Prometheus query UI.
    • http://localhost:3000/ - Grafana metrics. Log in using the username admin and the password admin. It includes some ready-made dashboards you can use to explore Phobos + App.Metrics metrics:
      • Akka.NET Metrics
      • ASP.NET Core Metrics
      • Kubernetes Cluster Metrics

    There's many more metrics exported by Phobos that you can use to create your own dashboards or extend the existing ones - you can view all of them by going to http://localhost:1880/metrics

    Looking at the Code

    So we can poke around and play with the solution now, but where's the real code?

    It's all located inside the Startup.cs class.

    public class Startup
    {
        /// <summary>
        ///     Name of the <see cref="Environment" /> variable used to direct Phobos' Jaeger
        ///     output.
        ///     See https://github.com/jaegertracing/jaeger-client-csharp for details.
        /// </summary>
        public const string JaegerAgentHostEnvironmentVar = "JAEGER_AGENT_HOST";
    
        public const string JaegerEndpointEnvironmentVar = "JAEGER_ENDPOINT";
    
        public const string JaegerAgentPortEnvironmentVar = "JAEGER_AGENT_PORT";
    
        public const int DefaultJaegerAgentPort = 6832;
    
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            // enables OpenTracing for ASP.NET Core
            services.AddOpenTracing(o =>
            {
                o.ConfigureAspNetCore(a =>
                    a.Hosting.OperationNameResolver = context => $"{context.Request.Method} {context.Request.Path}");
                o.AddCoreFx();
            });
    
            // sets up Prometheus + ASP.NET Core metrics
            ConfigureAppMetrics(services);
    
            // sets up Jaeger tracing
            ConfigureJaegerTracing(services);
    
            // sets up Akka.NET
            ConfigureAkka(services);
        }
    
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment()) app.UseDeveloperExceptionPage();
    
            app.UseRouting();
    
            // enable App.Metrics routes
            app.UseMetricsAllMiddleware();
            app.UseMetricsAllEndpoints();
    
            app.UseEndpoints(endpoints =>
            {
                var actors = endpoints.ServiceProvider.GetService<AkkaActors>();
                var tracer = endpoints.ServiceProvider.GetService<ITracer>();
                endpoints.MapGet("/", async context =>
                {
                    using (var s = tracer.BuildSpan("Cluster.Ask").StartActive())
                    {
                        // router actor will deliver message randomly to someone in cluster
                        var resp = await actors.RouterForwarderActor
                            .Ask<string>($"hit from {context.TraceIdentifier}",
                            TimeSpan.FromSeconds(5));
                        await context.Response.WriteAsync(resp);
                    }
                });
            });
        }
    }
    

    For the sake of brevity, the Startup methods that configure tracing, metrics, and Phobos aren't shown yet. This is to set the stage.

    Configuring ASP.NET + Phobos Tracing

    We call install the OpenTracing.Contrib.NetCore NuGet package so we can get built-in ASP.NET, HttpClient, and .NET Core tracing in our solution.

    PS> Install-Package OpenTracing.Contrib.NetCore
    

    With that package installed, we can call the following method during service setup:

    // enables OpenTracing for ASP.NET Core
    services.AddOpenTracing(o =>
    {
        o.ConfigureAspNetCore(a =>
            a.Hosting.OperationNameResolver = context 
                => $"{context.Request.Method} {context.Request.Path}");
        o.AddCoreFx();
    });
    

    The services.AddOpenTracing call injects the OpenTracing instrumentation into the ASP.NET Core middleware, and the a.Hosting.OperationNameResolver is used to help us customize the operation names that appear in Jaeger for ASP.NET HTTP calls.

    Next, we need to create a Jaeger ITracer that we can actually consume inside ASP.NET, Phobos, and other services.

    PS> Install-Package Jaeger
    

    We need to install the Jaeger NuGet package into our solution and add a method for configuring our ITracer.

    public static void ConfigureJaegerTracing(IServiceCollection services)
    {
        static ISender BuildSender()
        {
            if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(JaegerEndpointEnvironmentVar)))
            {
                if (!int.TryParse(Environment.GetEnvironmentVariable(JaegerAgentPortEnvironmentVar),
                    out var udpPort))
                    udpPort = DefaultJaegerAgentPort;
                return new UdpSender(
                    Environment.GetEnvironmentVariable(JaegerAgentHostEnvironmentVar) ?? "localhost",
                    udpPort, 0);
            }
    
            return new HttpSender(Environment.GetEnvironmentVariable(JaegerEndpointEnvironmentVar));
        }
    
        services.AddSingleton<ITracer>(sp =>
        {
            var loggerFactory = sp.GetRequiredService<ILoggerFactory>();
    
            var builder = BuildSender();
            var logReporter = new LoggingReporter(loggerFactory);
    
            var remoteReporter = new RemoteReporter.Builder()
                .WithLoggerFactory(loggerFactory) // optional, defaults to no logging
                .WithMaxQueueSize(100) // optional, defaults to 100
                .WithFlushInterval(TimeSpan.FromSeconds(1)) // optional, defaults to TimeSpan.FromSeconds(1)
                .WithSender(builder) // optional, defaults to UdpSender("localhost", 6831, 0)
                .Build();
    
            var sampler = new ConstSampler(true); // keep sampling disabled
    
            // name the service after the executing assembly
            var tracer = new Tracer.Builder(typeof(Startup).Assembly.GetName().Name)
                .WithReporter(new CompositeReporter(remoteReporter, logReporter))
                .WithSampler(sampler)
                .WithScopeManager(
                    new ActorScopeManager()); // IMPORTANT: ActorScopeManager needed to properly correlate trace inside Akka.NET
    
            return tracer.Build();
        });
    }
    

    Jaeger supports multiple reporting modes - and in this case we're using some environment variables to choose between reporting directly to the Jaeger aggregation service or an individual Jaeger agent. You can read more about that from the official Jaeger C# driver documentation.

    But once Jaeger is setup, we need to call services.AddSingleton<ITracer> so we can re-use this configured tracer inside both ASP.NET and Phobos.

    Configuring App.Metrics, Prometheus, and ASP.NET

    The App.Metrics base packages are already pre-installed into Phobos.Actor via the Phobos.Monitoring NuGet package, so we don't need to install those.

    However, we do need to install the packages needed for ASP.NET integration and reporting to Prometheus:

    PS> Install-Package App.Metrics.AspNetCore
    PS> Install-Package App.Metrics.AspNetCore.Tracking
    PS> Install-Package App.Metrics.Formatters.Prometheus
    

    Once that's done, we need to configure everything inside the IServiceCollection:

    public static void ConfigureAppMetrics(IServiceCollection services)
    {
        services.AddMetricsTrackingMiddleware();
        services.AddMetrics(b =>
        {
            var metrics = b.Configuration.Configure(o =>
                {
                    o.GlobalTags.Add("host", Dns.GetHostName());
                    o.DefaultContextLabel = "akka.net";
                    o.Enabled = true;
                    o.ReportingEnabled = true;
                })
                .OutputMetrics.AsPrometheusPlainText()
                .Build();
    
            services.AddMetricsEndpoints(ep =>
            {
                ep.MetricsTextEndpointOutputFormatter = metrics.OutputMetricsFormatters
                    .OfType<MetricsPrometheusTextOutputFormatter>().First();
                ep.MetricsEndpointOutputFormatter = metrics.OutputMetricsFormatters
                    .OfType<MetricsPrometheusTextOutputFormatter>().First();
            });
        });
        services.AddMetricsReportingHostedService();
    }
    

    The services.AddMetricsTrackingMiddleware() call adds the middleware we need for recording metrics from ASP.NET HTTP traffic.

    The services.AddMetrics() call configures the IMetricsRoot that Phobos and ASP.NET Core depend on for aggregating metrics.

    services.AddMetricsEndpoints(ep =>
    {
        ep.MetricsTextEndpointOutputFormatter = metrics.OutputMetricsFormatters
            .OfType<MetricsPrometheusTextOutputFormatter>().First();
        ep.MetricsEndpointOutputFormatter = metrics.OutputMetricsFormatters
            .OfType<MetricsPrometheusTextOutputFormatter>().First();
    });
    

    The above call ensures that all of the metrics will be available in Prometheus plain text format on the HTTP routes GET /metrics or GET /metrics-text. The Prometheus instance we spin up inside the K8s cluster is configured to periodically scrape the metrics data from this url on each instance of the Petabridge.Phobos.Web node.

    Configuring Phobos and Starting the ActorSystem

    Now that we've configured our ITracer , our IMetricsRoot, and registered both of these with the IServiceCollection - we can now configure Akka.NET and Phobos to consume them.

    Important

    We need to have the Phobos.Actor.Cluster NuGet package installed in order to complete instrumentation of this Akka.NET application.

    public static void ConfigureAkka(IServiceCollection services)
    {
        services.AddSingleton(sp =>
        {
            var metrics = sp.GetRequiredService<IMetricsRoot>();
            var tracer = sp.GetRequiredService<ITracer>();
    
            var config = ConfigurationFactory.ParseString(File.ReadAllText("app.conf")).BootstrapFromDocker();
    
            var phobosSetup = PhobosSetup.Create(new PhobosConfigBuilder()
                    .WithMetrics(m =>
                        m.SetMetricsRoot(metrics)) // binds Phobos to same IMetricsRoot as ASP.NET Core
                    .WithTracing(t => t.SetTracer(tracer))) // binds Phobos to same tracer as ASP.NET Core
                .WithSetup(BootstrapSetup.Create()
                    .WithConfig(config) // passes in the HOCON for Akka.NET to the ActorSystem
                    .WithActorRefProvider(PhobosProviderSelection
                        .Cluster)); // last line activates Phobos inside Akka.NET
    
            var sys = ActorSystem.Create("ClusterSys", phobosSetup);
    
            // create actor "container" and bind it to DI, so it can be used by ASP.NET Core
            return new AkkaActors(sys);
        });
    
        // this will manage Akka.NET lifecycle
        services.AddHostedService<AkkaService>();
    }
    

    The key to setting up an ActorSystem with Phobos is to use the PhobosSetup class, which is derived from the Setup class in Akka.NET.

    var phobosSetup = PhobosSetup.Create(new PhobosConfigBuilder()
                    .WithMetrics(m =>
                        m.SetMetricsRoot(metrics)) // binds Phobos to same IMetricsRoot as ASP.NET Core
                    .WithTracing(t => t.SetTracer(tracer))) // binds Phobos to same tracer as ASP.NET Core
                .WithSetup(BootstrapSetup.Create()
                    .WithConfig(config) // passes in the HOCON for Akka.NET to the ActorSystem
                    .WithActorRefProvider(PhobosProviderSelection
                        .Cluster)); // last line activates Phobos inside Akka.NET
    

    This allows us to take the same tracer and metrics that will be used by ASP.NET and pass it into Akka.NET - thus we can correlate activity that begins inside ASP.NET Core and uses the underlying Akka.NET cluster!

    Important

    Please note we call WithSetup and pass in an Akka.Actor.BootstrapSetup - this allows us to make sure that the HOCON Config gets passed into the ActorSystem as well. Additionally, we also need to pass in the WithActorRefProvider.PhobosProviderSelection.Cluster) method because that's what actually enables Phobos inside the ActorSystem.

    That's all we need to capture the automatically collected Phobos data, but if we want to manually capture data there's more we can do below.

    Calling Phobos APIs

    If you want to call the Phobos APIs directly to capture additional data, this is something you can do by calling Context.GetInstrumentation() inside any actor:

    public sealed class ConsoleActor : ReceiveActor
    {
        private readonly ILoggingAdapter _log = Context.GetLogger();
    
        public ConsoleActor()
        {
            Receive<string>(_ =>
            {
                // use the local metrics handle to record a timer duration for how long this block of code takes to execute
                Context.GetInstrumentation().Monitor.Timer.Time(new TimerOptions {Name = "ProcessingTime"}, () =>
                {
                    // start another span programmatically inside actor
                    using (var newSpan = Context.GetInstrumentation().Tracer.BuildSpan("SecondOp").StartActive())
                    {
                        _log.Info("Received: {0}", _);
                        Sender.Tell(_);
                    }
                });
            });
        }
    }
    

    When we call Context.GetInstrumentation().Monitor, we are accessing the IMeasureMetrics instance used by the application - and we can create custom metrics, such as a Timing in this case - and these will all be picked up by Prometheus automatically.

    Alternatively, when we call Context.GetInstrumentation().Tracer.BuildSpan("SecondOp").StartActive() - this is creating a new span operation that is a child span of the current one the actor created when it began processing the message, and logs that are recorded inside that using block will automatically be appended to the currently active ISpan.

    Tearing Down the Cluster

    When you're done exploring this sample, you can tear down the cluster by running the following command:

    PS> ./k8s/destroyAll.cmd
    

    This will delete the phobos-web namespace and all of the resources inside it.

    Further Reading

    For further reading, please see:

    • Phobos Configuration;
    • Phobos Tutorials; and
    • Phobos Support
    In This Article
    Back to top Generated by DocFX