Azure App Service removing Jaeger HTTP headers from inter-service requests - c#

I'm setting up a proof of concept featuring two ASP.NET Core applications that are both instrumented with Jaeger to demonstrate how it can propagate a trace between services over the wire. Both applications are being deployed to Azure App Services.
I'm using the OpenTracing Contrib package to automatically inject the Jaeger trace context into my inter-service traffic in the form of HTTP Headers (the package is hardcoded to use that form of transmission). But it appears that those headers are going missing along the way, as the receiving application is unable to resume the tracing context.
Before deploying to Azure, I'm testing the applications locally with Docker Compose, and with that setup the context propagation works fine. It's only once the apps are in Azure that things break.
The applications communicate over HTTPS and I've disabled HSTS and HTTPS redirection in case that might be causing Azure to drop the headers, based on the answer in this previous thread.
I've also tried running both applications in Azure Container Instances, and that seems to be a non-starter - it doesn't fix the context propagation and seems to introduce more bugs around span relationships.
The two applications are nearly identical in their setup, and differ only in the API endpoints they serve.
My CreateWebHostBuild from program.cs:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureServices(services =>
{
// Registers and starts Jaeger (see Shared.JaegerServiceCollectionExtensions)
services.AddJaeger(CheckoutConfiguration.JaegerSettings.Host);
// Enables OpenTracing instrumentation for ASP.NET Core, CoreFx, EF Core
services.AddOpenTracing();
});
The contents of the AddJaeger extension method which is largely borrowed from the Contrib sample:
public static IServiceCollection AddJaeger(this IServiceCollection services, string jaegerHost = "localhost")
{
if (services == null)
throw new ArgumentNullException(nameof(services));
services.AddSingleton<ITracer>(serviceProvider =>
{
string serviceName = Assembly.GetEntryAssembly().GetName().Name;
ILoggerFactory loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
ISampler sampler = new ConstSampler(sample: true);
var reporter = new RemoteReporter.Builder()
.WithSender(new UdpSender(jaegerHost, 6831, 0))
.Build();
ITracer tracer = new Tracer.Builder(serviceName)
.WithLoggerFactory(loggerFactory)
.WithReporter(reporter)
.WithSampler(sampler)
.Build();
GlobalTracer.Register(tracer);
return tracer;
});
var jaegerUri = new Uri($"http://{jaegerHost}:14268/api/traces");
// Prevent endless loops when OpenTracing is tracking HTTP requests to Jaeger.
services.Configure<HttpHandlerDiagnosticOptions>(options =>
{
options.IgnorePatterns.Add(request => jaegerUri.IsBaseOf(request.RequestUri));
// We don't need to track Prometheus scraping requests
});
services.Configure<AspNetCoreDiagnosticOptions>(options => {
// We don't need to trace Prometheus scraping requests
options.Hosting.IgnorePatterns.Add(context => context.Request.Path.Equals("/metrics", StringComparison.OrdinalIgnoreCase));
});
return services;
}
My startup.cs configure method to show I'm not doing anything weird with the headers (the metrics extensions are for prometheus-net)
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseHttpMetrics();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// Do release exception handling
}
app.UseMetricServer();
app.UseMvc();
}
I expect any calls from one application to the other to propagate the active Jaeger trace context. Instead, the two applications log their traces separately and no link can be discerned between them in the Jaeger UI.
Here's a screenshot of a trace that should have spanned both services, but instead only shows spans from the first service:

Maybe you should check whether the application services which you set up in a hurry are both in the same azure resource group as the VM running the Jaeger all-in-one instance, otherwise the second application might not be able to communicate with the Jaeger instance at all.

Related

Do I really need CORS here with Swagger?

I developed an API to give public access to some company data, which is of no value to hackers. It's public data that can be sourced from other websites.
I developed the project using default Visual Studio ASP.Net Core API templates. One thing I noticed it's using CORS and I don't know how it got in here to be honest.
However, the default Policy is set as any domain, any method and any header:
builder.Services.AddCors(p =>
p.AddPolicy("corsapp", builder =>
{
builder.WithOrigins("*").AllowAnyMethod().AllowAnyHeader();
}));
Do I even need CORS, if I am only allowing the API to run from a single origin, which already has HTTPS?
What benefit, when the API works as is?
Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
//do I really need this!?!
builder.Services.AddCors(p => p.AddPolicy("corsapp", builder =>
{
builder.WithOrigins("*").AllowAnyMethod().AllowAnyHeader();
}));
var startup = new Startup(builder.Configuration);
startup.ConfigureServices(builder.Services);
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
startup.Configure(app);
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
//why!?!
app.UseCors("corsapp");
app.UseAuthorization();
app.MapControllers();
app.Run();
UPDATE
I've re-worded my OP based on Rahul's excellent video about CORS from Chen's answer below. However, I am still confused if I should configure CORS here to give a specific single origin as I am more worried someone after me may not configure this Policy correctly if the API is required in more than two places.
Thanks
You would need to know if your program provides API responses to client applications loaded from other domains to determine if you need to enable CORS.
For example, there are two cases where no action is needed for CORS support:
Swagger UI is hosted on the same server as the application itself (same host and port).
The application is located behind a proxy that enables the required CORS headers. This may already be covered within your organization.
You can get a better understanding and use of CORS through this link and this official documentation.

Windows authentication for SignalR service hosted in Kestrel (AspNet Core 5.0)

I have a SignalR (AspNet Core 5.0) hosted in a console app using Kestrel as the web host.
I want to access the user Identity of any request in a Hub implementation, when accessing the following the Identity values are NULL.
I've looked at the available documentation on MSDN and made the following changes, but not getting the Identity populated as I expected, also not finding any examples for AspNet Core 5.0 anywhere.
Any ideas what I am doing wrong?
public class ExampleHub : Hub
{
public Task Foo()
{
*// why is name NULL?*
var name = Context.User.Identity.Name;
return Task.Completed;
}
}
I have added the following line when configuring the services as StartUp:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNeogtiate();
services.AddSignalR(options => options.EnableDetailedErrors = true);
...
}
public void Configure(IApplicationBuuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseAuthentication();
app.UseEndpoints(endpoints => endpoints.MapHub<ExampleHub>("/Example"); });
}
Managed to get this working by changing to HttpSys instead of Kestrel as the host. The Context.User.Identity is now populated as WindowsIdentity.
In this case using HttpSys is preferred - an internal (corporate) network hosted in a Windows Service instead of a web server (IIS). Note IIS also uses HttpSys internally.

Limit concurrent requests in .net core

I am trying to configure my .net core API in order to limit the requests.
To achieve this i modify the program.cs class like
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(serverOptions =>
{
serverOptions.Limits.MaxConcurrentConnections = 2;
})
//.UseContentRoot(Directory.GetCurrentDirectory())
//.UseIISIntegration()
.UseStartup<Startup>();
});
}
but the problem is when I call my API with a console app using more threads than 2 I get the responses from all threads-calls. The API I have deployed-published it in my local IIS pc.
I consider that I must get only 2 responses and then for the other calls I will get 503 services unavailable.
what is wrong with my code?
EDIT
I have read this article How to configure concurrency in .NET Core Web API?, the problem is when I add the web.config
<configuration>
<system.web>
<applicationPool
maxConcurrentRequestsPerCPU="5000"
maxConcurrentThreadsPerCPU="0"
requestQueueLimit="5000" />
</system.web>
</configuration>
i have the warning
the element system. web has invalid child element applicationpool
and i cannot publish the api on iis or run it in iis express
Since the API will host in the IIS, so, the configuration for the Kestrel will not be used.
To set the max concurrency connections in IIS, you could Open IIS manager window. Select the site from the server node. Then, select Advance setting from the action pane. You can see the Limits under the Behavior section. Set Maximum Concurrent Connection value based on your requirement. Like this:
[Note] This setting is for all Sites.
Besides, you could also check this sample and create a custom middleware to limit the request.
Option serverOptions.Limits.MaxConcurrentConnections limits maximum number of tcp connections to Kestrel, not the number of concurrent http requests. Through each tcp connection there still might be multiple concurrent http requests. That's why in your test you can see no errors, even though there are more than 2 concurrent http requests.
In order to limit the number of concurrent http requests in .NET Core (or .NET
5+), you can use ConcurrencyLimiterMiddleware (package Microsoft.AspNetCore.ConcurrencyLimiter). Here is an example:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddStackPolicy(options =>
{
options.MaxConcurrentRequests = 2;
options.RequestQueueLimit = 25;
});
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
app.UseConcurrencyLimiter();
}
}

How to implement an signalr CLIENT in c# System.Web.Http.ApiController

We have an application running different services (c#, .NET Core) LOCAL on a Windows PC.
I now need some kind of mechanism to inform all interested services if data changed in one service (some kind of observer pattern for microservices, or some kind of MQTT (pub/sub) mechanism of c# and .NET Core microservices locally running on a windows pc).
First I want to use Sockets but the Windows documentation says use Signalr instead.
So here is what I have so far:
public class Startup
{
public Startup()
{
// empty
}
public void ConfigureServices(IServiceCollection services)
{
// Add services.
//Test bidirectional communication (pub / sub Pattern over SignalR groups)
services.AddSignalR();
// Add the localization services to the services container.
services.AddLocalization(options => options.ResourcesPath = "Properties");
services.AddMvc()
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseStaticFiles();
// Use sessions
// The order of middleware is important.
// An InvalidOperationException exception will occur when UseSession is invoked after UseMvc.
app.UseSession();
//Test bidirectional communication (pub / sub Pattern over SignalR groups)
//The SignalR Hubs API enables you to call methods on connected clients from the server.
//In the server code, you define methods that are called by client. In the client code, you define methods that are called from the server.
app.UseSignalR(routes =>
{
routes.MapHub<SignalRHub>("/SignalRHub");
});
app.UseMvc(
routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
For the .NET CORE Service
But I now need a client for the c# System.Web.Http.ApiController and can not find an example.
Seems some are confused by our "beautiful" architecture ;-)
I hope the following picture makes it clearer:
So, if Application 1 changes data in Microservice 2, than Application 2 has to be informed.
And again, this is all running local on a Windows PC, no clouds are involved.
Probably missing something from your description.
SignalR is fine if there are clients to report relevant information to.
In your scenario, however, it would seem that the clients are the APIs themselves and this makes little sense to me.
Maybe there's a piece missing in the middle that does the work you're saying.
In any case, you may find relevant technical information about SignalR starting from the official website.
https://learn.microsoft.com/en-us/aspnet/core/signalr/dotnet-client?view=aspnetcore-3.1&tabs=visual-studio

How to access an API from the same hostname with Angular 4 frontend and DotNet Core backend?

I have an Angular 4 frontend using a .NET Core-backend in the same solution.
As far as I can tell you can't access the window object from Angular the same way, so there's no good way to decode get the current location/environment.
With a .NET Core-backend I also don't have an angular.cli.json file so that I can configure an environments/environment.prod.ts environments/environment.local.ts kind of setup.
What I'm wondering is why I can't access say my backend API without specificing the hostname (which I have trouble accessing) without entering it specifically?
On a localhost it's: http://localhost:port/api// for example but I have to specify http://localhost:port when I want to be able to access it with '/api//' directly.
So when working locally I have one environment and deployed another one. So I can't exactly deploy it with localhost URL.
What is the proper way for Angular's HTTP client to use the same hostname in requests?
I have set up environments like I said, but during release build it does not choose the environment.prod.ts file for example. I guess this has to do with the .NET Core solution rather than Angular.
Any suggestions?
Either by a configuration setup for production/development/local or to get the environmental settings working in a .NET Core solution with Angular 4.
You must change the Startup.cs. AnyWay, if you use Visual Studio 2017 you can use the angular template
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true
});
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseDefaultFiles();
app.UseStaticFiles();
app.Use(async (context, next) =>
{
if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value) && !context.Request.Path.Value.StartsWith("api"))
{
context.Request.Path = "/Index.cshtml";
context.Response.StatusCode = 200;
await next();
}
});
}
So, a http call can be like
this._http.get('/api/controller/action')

Categories