How to deploy Asp.Net Core apps on Azure Service Fabric using subpaths sharing same port on the cluster - azure

The Service Fabric samples like wordcount the web app listen on a port in a subpath like this:
http://localhost:8081/wordcount
The code for this configuration is: (See the file on GitHub https://github.com/Azure-Samples/service-fabric-dotnet-getting-started/blob/master/Services/WordCount/WordCount.WebService/WordCountWebService.cs)
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new[]
{
new ServiceInstanceListener(initParams => new OwinCommunicationListener("wordcount", new Startup(), initParams))
};
}
With this configuration we can deploy other web apps on the same cluster using the same port (8081)
http://localhost:8081/wordcount
http://localhost:8081/app1
http://localhost:8081/app2
And so on.
But the Asp.Net Core project template is different and I don't know how to add the subpath on listener configuration.
The code below is what we have in the project template (Program.cs class WebHostingService):
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new[] { new ServiceInstanceListener(_ => this) };
}
Task<string> ICommunicationListener.OpenAsync(CancellationToken cancellationToken)
{
var endpoint = FabricRuntime.GetActivationContext().GetEndpoint(_endpointName);
string serverUrl = $"{endpoint.Protocol}://{FabricRuntime.GetNodeContext().IPAddressOrFQDN}:{endpoint.Port}";
_webHost = new WebHostBuilder().UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseUrls(serverUrl)
.Build();
_webHost.Start();
return Task.FromResult(serverUrl);
}
The semantic is a bit different, but all ends up in the same point.
The problems is that even I add the subpath at the end of serverUrl it does't work and the web apps always responds on the root http://localhost:8081/
See how I've tried in the code snippet below:
string serverUrl = $"{endpoint.Protocol}://{FabricRuntime.GetNodeContext().IPAddressOrFQDN}:{endpoint.Port}/app1";
How to achieve the same result as "classic" web app using asp.net core?
The goal is to publish on azure on port 80 to let users with a better experience like:
http://mywebsite.com/app1
http://mywebsite.com/app2
Thank you a lot!

As #Vaclav said is necessary to change UseKestrel by UseWebListener.
But the problem is that WebListener binding to the address is different.
Look this thread to more details https://github.com/aspnet/Hosting/issues/749
Is necessary to use + instead of localhost or other machine names on the serverUrl.
So, change de template code from:
Task<string> ICommunicationListener.OpenAsync(CancellationToken cancellationToken)
{
var endpoint = FabricRuntime.GetActivationContext().GetEndpoint(_endpointName);
string serverUrl = $"{endpoint.Protocol}://{FabricRuntime.GetNodeContext().IPAddressOrFQDN}:{endpoint.Port}/service1";
_webHost = new WebHostBuilder().UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseUrls(serverUrl)
.Build();
_webHost.Start();
return Task.FromResult(serverUrl);
}
To
Task<string> ICommunicationListener.OpenAsync(CancellationToken cancellationToken)
{
var endpoint = FabricRuntime.GetActivationContext().GetEndpoint(_endpointName);
string serverUrl = $"{endpoint.Protocol}://+:{endpoint.Port}/service1";
_webHost = new WebHostBuilder().UseWebListener()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseUrls(serverUrl)
.Build();
_webHost.Start();
return Task.FromResult(serverUrl);
}
And it workd very well.

Kestrel doesn't support URL prefixes or port sharing between multiple applications. You have to use WebListener instead:
using Microsoft.AspNetCore.Hosting
...
_webHost = new WebHostBuilder().UseWebListener()

I've not done this yet, but is this GitHub repository useful?
https://github.com/weidazhao/Hosting
About The Sample
This sample demonstrates:
1.How ASP.NET Core can be used in a communication listener of stateless/stateful services. Today the scenario we've enabled is to host ASP.NET Core web application as a stateless service with Service Fabric. We wanted to light up the scenarios that people also can use ASP.NET Core as communication listeners in stateless services and stateful services, similar to what the OwinCommunicationListener does.
2.How to build an API gateway service to forward requests to multiple microservices behind it with the reusable and modular component. Service Fabric is a great platform for building microservices. The gateway middleware (Microsoft.ServiceFabric.AspNetCore.Gateway) is an attempt to provide a building block for people to easily implement the API gateway pattern of microservices on Service Fabric. There are a couple good articles elaborating the API gateway pattern, such as http://microservices.io/patterns/apigateway.html, http://www.infoq.com/articles/microservices-intro, etc. For more information about microservices, check out https://azure.microsoft.com/en-us/blog/microservices-an-application-revolution-powered-by-the-cloud/, http://martinfowler.com/articles/microservices.html.

#Nick Randell
With the sample approach is possible to run several Services on the same port using their names like:
http://localhost:20000/service1 <--- Svc in Application1
http://localhost:20000/service2 <--- Svc in Application1
This is possible because is there a Gateway service that maps the addresses service1 and service2 in the URI to the correct services.
But I couldn't find a way to have 2 different Applications running on the same port.
Is it possible?
http://localhost:20000/service1 <--- Svc in Application1
http://localhost:20000/service2 <--- Svc in Application2

Related

Asynchronous HttpClient calls not shown as dependency in Azure App insights for App service automatically

I am new to Azure app insights and want to know why are Asynchronous HttpClient calls not shown as dependency automatically in Azure App insights for App service.
Also, what configuration changes or code changes should I make in ASP.NET project for tracking http dependencies ?
When I refer documentation it says "A dependency is a component that is called by your application. It's typically a service called using HTTP, or a database, or a file system. Application Insights measures the duration of dependency calls, whether its failing or not, along with additional information like name of dependency and so on. You can investigate specific dependency calls, and correlate them to requests and exceptions."
You can use the below example of code to track http dependency automatically
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DependencyCollector;
using Microsoft.ApplicationInsights.Extensibility;
using System.Net.Http;
using System.Threading.Tasks;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
TelemetryConfiguration configuration = TelemetryConfiguration.CreateDefault();
configuration.InstrumentationKey = "removed";
configuration.TelemetryInitializers.Add(new HttpDependenciesParsingTelemetryInitializer());
var telemetryClient = new TelemetryClient(configuration);
using (InitializeDependencyTracking(configuration))
{
// run app...
telemetryClient.TrackTrace("Hello World!");
using (var httpClient = new HttpClient())
{
// Http dependency is automatically tracked!
httpClient.GetAsync("https://microsoft.com").Wait();
}
}
// before exit, flush the remaining data
telemetryClient.Flush();
// flush is not blocking when not using InMemoryChannel so wait a bit. There is an active issue regarding the need for `Sleep`/`Delay`
// which is tracked here: https://github.com/microsoft/ApplicationInsights-dotnet/issues/407
Task.Delay(5000).Wait();
}
static DependencyTrackingTelemetryModule InitializeDependencyTracking(TelemetryConfiguration configuration)
{
var module = new DependencyTrackingTelemetryModule();
// prevent Correlation Id to be sent to certain endpoints. You may add other domains as needed.
module.ExcludeComponentCorrelationHttpHeadersOnDomains.Add("core.windows.net");
module.ExcludeComponentCorrelationHttpHeadersOnDomains.Add("core.chinacloudapi.cn");
module.ExcludeComponentCorrelationHttpHeadersOnDomains.Add("core.cloudapi.de");
module.ExcludeComponentCorrelationHttpHeadersOnDomains.Add("core.usgovcloudapi.net");
module.ExcludeComponentCorrelationHttpHeadersOnDomains.Add("localhost");
module.ExcludeComponentCorrelationHttpHeadersOnDomains.Add("127.0.0.1");
// enable known dependency tracking, note that in future versions, we will extend this list.
// please check default settings in https://github.com/microsoft/ApplicationInsights-dotnet-server/blob/develop/WEB/Src/DependencyCollector/DependencyCollector/ApplicationInsights.config.install.xdt
module.IncludeDiagnosticSourceActivities.Add("Microsoft.Azure.ServiceBus");
module.IncludeDiagnosticSourceActivities.Add("Microsoft.Azure.EventHubs");
// initialize the module
module.Initialize(configuration);
return module;
}
}
}
NOTE: -
Based on the MSDOC Azure Monitor Application Insights Agent currently supports ASP.NET 4.x only.
For more information please refer the below links:
MS DOC: Application Insights for .NET console applications(Source code) & Automatically dependency tracking
GitHub : LINK1 & LINK2

Add application insights to stateless service fabric app

I'm trying to follow this documentation for starting to use application insights in a .net core application I deploy to service fabric.
My code is really simple
public FailedAuthorise(StatelessServiceContext context, IConfigManager config)
: base(context)
{
_worker = new Worker<PaymentFailedAuthorise>(config, FailedAuthoriseHandlerFactory.Create, "FailedAuthorise", "FailedAuthoriseError");
}
protected override async Task RunAsync(CancellationToken cancellationToken)
{
await _worker.RunAsync(cancellationToken);
}
Being the worker just a generic class that reads from some queues and processes the messages
But if I was to follow that document I would need to install some nuget packages (which actually are giving me problems to find and/or install, for instance no access to the using Microsoft.ApplicationInsights.ServiceFabric; or will need to change the instrumentation key on a configuration file modification on the pipeline) and start using "hosting" classes that I don't really need in my solution.
Is not a simple way of just adding application insights to what used to be a worker role in the cloud services with no need of the hosting bit?
Thanks,
You can just add this nuget package and create your own custom telemetry like this:
public class MyService
{
private readonly TelemetryClient telemetryClient;
public MyService()
{
telemetryClient = new TelemetryClient(configuration);
telemetryClient.Context.InstrumentationKey = "[Your Key Here]";
}
public FailedAuthorise(StatelessServiceContext context, IConfigManager config)
: base(context)
{
_worker = new Worker<PaymentFailedAuthorise>(config, FailedAuthoriseHandlerFactory.Create, "FailedAuthorise", "FailedAuthoriseError");
}
protected override async Task RunAsync(CancellationToken cancellationToken)
{
telemetryClient.TrackEvent("Worker started");
await _worker.RunAsync(cancellationToken);
}
}
There are several things you can track, like exceptions, traces, events, metrics and requests but if you are not using Asp.Net Core you will have to manually send those events as opposed to having some middleware sending the telemetry to App Insigths.
If your service calls other services you can add this package to automatically track communication to other services.

Configuring Azure ServiceFabric for development lifecycle - how to parameterize host name?

What's a good way to manage deploying code changes to Dev, Test, and Prod environments in Azure? The Azure / Service Fabric site provides an article for specifying port numbers using parameters under How-to guides - Manage application lifecycle (https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-how-to-specify-port-number-using-parameters), but I'm not sure how one manages host names - is there a host name related property that can be included in Publish Profile .xml file (e.g., Cloud.xml)?
Background: I'm migrating from a self hosted on premise WCF application running as a Windows Service and using WebHttpBinding with http and https endpoints (uses T4 config file templates to determine hostname and port number depending on the environment). I'm migrating this to an Azure ServiceFabric WcfCommunicationListener application (similar to the sample found here: https://github.com/loekd/ServiceFabric.WcfCalc)....
internal sealed class ServiceFabricWcfService : StatelessService
{
public ServiceFabricWcfService(StatelessServiceContext context) : base(context)
protected override IEnumerable<ServiceInstanceListener>
CreateServiceInstanceListeners()
{
yield return new ServiceInstanceListener(CreateRestListener);
}
private ICommunicationListener CreateRestListener(StatelessServiceContext context)
{
var host = context.NodeContext.IPAddressOrFQDN;
var endpointConfig = context.CodePackageActivationContext.GetEndpoint("ServiceEndpoint");
var port = endpointConfig.Port;
var scheme = endpointConfig.Protocol.ToString();
var uri = string.Format(CultureInfo.InvariantCulture, "{0}://{1}:{2}/webhost/", scheme, host, port);
var listener = new WcfCommunicationListener<IJsonService>(context, new JsonServicePerCall(), new WebHttpBinding(WebHttpSecurityMode.None), new EndpointAddress(uri));
var ep = listener.ServiceHost.Description.Endpoints.Last();
ep.Behaviors.Add(new WebHttpBehavior());
return listener;
}
}
As you can see, the host name is obtained from the StatelessServiceContext's NodeContext - is there a good way to set this up to target different host names for each environment? My clients need to be able to make http/https calls based on host name to determine which environment they connect to. Thanks!
I don't think that you can do that, since in the provided example host variable represents exact node on which service is running. You can reach it using cluster name if you open appropriate port, e.g. http://mycluster.eastus.cloudapp.azure.com:19081/MyApp/MyService

How to add EventSource to a web application

We finally got EventSource and ElasticSearch correctly configured in our service fabric cluster. Now that we have that we want to add EventSources to our web applications that interact with our service fabric applications so that we can view all events (application logs) in one location and filter / query via Kibana.
Our issue seems to be related to the differences between a service fabric app which is an exe and a .NET 4.6 (not .net CORE) web app which is stateless. In service Fabric we place the using statement that instantiates the pipeline in Program.cs and set an infinite sleep.
private static void Main()
{
try
{
using (var diagnosticsPipeline = ServiceFabricDiagnosticPipelineFactory.CreatePipeline("CacheApp-CacheAPI-DiagnosticsPipeline"))
{
ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(Endpoint).Name);
// Prevents this host process from terminating so services keeps running.
Thread.Sleep(Timeout.Infinite);
}
How do I do this in a web app? This is the pipeline code we are using for a non ServiceFabric implementation of the EventSource. This is what we are using:
using (var pipeline = DiagnosticPipelineFactory.CreatePipeline("eventFlowConfig.json"))
{
IEnumerable ie = System.Diagnostics.Tracing.EventSource.GetSources();
ServiceEventSource.Current.Message("initialize eventsource");
}
We are able to see the pipeline and send events to ElasticSearch from within the using statement but not outside of it. So the question is:
how/where do we place our pipeline using statement for a web app?
Do we need to instantiate and destroy the pipeline that every time we log or is there a way to reuse that pipeline across the stateless web events? It would seem like that would be very expensive and hurt performance. Maybe we can we cache a pipeline?
That’s the jist, let me know if you need clarification. I see lots of doco out there for client apps but not much for web apps.
Thanks,
Greg
UPDATE WITH SOLUTION CODE
DiagnosticPipeline pipeline;
protected void Application_Start(Object sender, EventArgs e)
{
try
{
pipeline = DiagnosticPipelineFactory.CreatePipeline("eventFlowConfig.json");
IEnumerable ie = System.Diagnostics.Tracing.EventSource.GetSources();
AppEventSource.Current.Message("initialize eventsource");
}
}
protected void Application_End(Object sender, EventArgs e)
{
pipeline.Dispose();
}
Assuming ASP.NET Core the simplest way to initialize EventFlow pipeline would be in the Program.cs Main() method, for example:
public static void Main(string[] args)
{
using (var pipeline = DiagnosticPipelineFactory.CreatePipeline("eventFlowConfig.json"))
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.UseApplicationInsights()
.Build();
host.Run();
}
}
This takes advantage of the fact that host.Run() will block until the server is shut down, and so the pipeline will exist during the time when requests are received and served.
Depending on the web framework you use things might vary. E.g. if the one you use offers "setup" and "cleanup" hooks, you could create a diagnostic pipeline during setup phase (and store a reference to it in some member variable), then dispose of it during cleanup phase. For example, in ASP.NET classic you'd put the code in global.asax.cs and leverage Application_OnStart and Application_OnEnd methods. See Application Instances, Application Events, and Application State in ASP.NET for details.
Creating a pipeline instance every time a request is served is quite inefficient, like you said. There is really no good reason to do that.

Web API Owin self host multiple applications

We are trying to setup an owin self host service for multiple applications (app1, app2).
We'd like to use the same behavior of IIS where one application pool uses one AppDomain per application.
Here is the code we use:
using (WebApp.Start<WebServerStartup>(url: "http://localhost:9000/")) { ... }
... and the WebServerStartup implementation:
internal class WebServerStartup
{
public void Configuration(IAppBuilder appBuilder)
{
// Configure Web API for self-host.
var config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
appBuilder.UseWebApi(config);
}
How can we use multiple AppDomains for each application? Is there a way to declare multiple webapps using Webapp.Start(...) in separate AppDomains that would share the same port? Or is there some way to route the requests to various appdomains somehow by defining a route like app/api/{controller}/{id} instead of api/{controller}/{id}?
Thanks
There's no built in support for running the apps in separate domains. You'd want to start each new AppDomain and then run WebApp.Start inside.

Resources