I have a simple dashboard application that I wrote in .NET 8.0 Blazor. There are various backend classes using timers to update parts of the page, it works as expected run through Visual Studio as a container, and standalone.
When deployed to an Azure Kubernetes cluster the page loads and shows the expected data, but the timers are not updating the page.
I can see from kubernetes logs that the timers are running, and I am not seeing exceptions.
When run locally I can see the expected SignalR negotiation, and messages exchanging in the browser network tab. I also see console messages in the browser:
[2024-08-11T22:46:43.501Z] Information: Normalizing '_blazor' to 'https://localhost:32782/_blazor'.
blazor.web.js:1 [2024-08-11T22:46:43.543Z] Information: WebSocket connected to wss://localhost:32782/_blazor?id=uu8i0EXFbvgzUFqKBzgjug.
When deployed to kubernetes I cannot see any network tab activity related to SignalR, nor do I see any console messages.
The application is based heavily on the Blazor server side VS template, aside from the template code to hook up Blazor there is an MS EntraID based auth.
I’m looking for any insight into why the client JS script is not attempting to connect to the _blazor
SignalR hub.
.csproj
:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Dapper" Version="2.1.35" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.7" NoWarn="NU1605" />
<PackageReference Include="Microsoft.Identity.Web" Version="3.0.1" />
<PackageReference Include="Microsoft.Identity.Web.MicrosoftGraph" Version="3.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
<PackageReference Include="Npgsql" Version="8.0.3" />
<PackageReference Include="Seq.Api" Version="2024.3.0" />
</ItemGroup>
</Project>
Abridged program.cs
looks like this:
builder.Services
.AddRazorComponents()
.AddInteractiveServerComponents()
.AddHubOptions(ho => {
ho.EnableDetailedErrors = true;
});
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(options =>
{
builder.Configuration.Bind("AzureAd", options);
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.RedirectUri = context.ProtocolMessage.RedirectUri.Replace("http://", "https://");
return Task.CompletedTask;
}
};
});
...
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
app.UseHsts();
app.UseDeveloperExceptionPage();
}
app.UseForwardedHeaders();
app.UseAuthentication();
app.UseAuthorization();
app.UseStaticFiles();
app.UseAntiforgery();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();
Dockerfile
looks like:
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER app
WORKDIR /app
EXPOSE 8080
EXPOSE 8081
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["Dashboard/Dashboard.csproj", "Dashboard/"]
RUN dotnet restore "./Dashboard/Dashboard.csproj"
COPY . .
WORKDIR "/src/Dashboard"
RUN dotnet build "./Dashboard.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./Dashboard.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Dashboard.dll"]
Ingress currently looks like this:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: systems-dashboard-ingress
annotations:
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "affinity"
nginx.ingress.kubernetes.io/session-cookie-expires: "14400"
nginx.ingress.kubernetes.io/session-cookie-max-age: "14400"
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
labels:
app.kubernetes.io/managed-by: Helm
spec:
ingressClassName: nginx
tls:
- hosts:
- {mysite}
rules:
- host: {mysite}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: systems-dashboard
port:
number: 8080
I have added ingress annotations per this MS article:
https://learn.microsoft.com/en-us/aspnet/core/blazor/host-and-deploy/server?view=aspnetcore-8.0#kubernetes
and this SO article:
How to correctly deploy a Blazor Server app on Kubernetes
I have put a breakpoint in the blazor.web.js
source in the browser, and attempted to step through the issue. Function names are obfuscated so its rather hard to see what’s going on.
I sent post request to https://{mysite}/_blazor/negotiate?negotiateVersion=1
and got back
{
"negotiateVersion": 1,
"connectionId": "DdFgDoSqHeNzcYb_byCMkA",
"connectionToken": "TL4xt2O-Z4xIFOIQvNG6YA",
"availableTransports": [
{
"transport": "WebSockets",
"transferFormats": [
"Text",
"Binary"
]
},
{
"transport": "ServerSentEvents",
"transferFormats": [
"Text"
]
},
{
"transport": "LongPolling",
"transferFormats": [
"Text",
"Binary"
]
}
]
}
user1890407 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.