我正在尝试在本地 Kubernetes 集群中部署测试 Blazor 服务器端应用程序。我已将其配置为可以通过服务或 nginx-ingress 访问它。然而,这两种途径都有其自身的问题,导致应用程序在服务后无法正常运行。
通过服务提供时,它看起来像。我相信这就是它应该的样子。但是,有很多错误导致我根本无法与该页面交互。 错误:
blazor.server.js:1 WebSocket connection to 'ws://172.25.217.142:30500/_blazor?id=1qrCqjxomf15Rd6QTY11rw' failed:
(anonymous) @ blazor.server.js:1
blazor.server.js:1 [2024-01-23T20:20:31.036Z] Information: (WebSockets transport) There was an error with the transport.
blazor.server.js:1 [2024-01-23T20:20:31.036Z] Error: Failed to start the transport 'WebSockets': Error: WebSocket failed to connect. The connection could not be found on the server, either the endpoint may not be a SignalR endpoint, the connection ID is not present on the server, or there is a proxy blocking WebSockets. If you have multiple servers check that sticky sessions are enabled.
log @ blazor.server.js:1
blazor.server.js:1 [2024-01-23T20:20:31.107Z] Warning: Failed to connect via WebSockets, using the Long Polling fallback transport. This may be due to a VPN or proxy blocking the connection. To troubleshoot this, visit https://aka.ms/blazor-server-using-fallback-long-polling.
log @ blazor.server.js:1
:30500/_blazor?id=eC3fba0OdI6vu_KNZl9_Kg:1
Failed to load resource: the server responded with a status of 404 (Not Found)
blazor.server.js:1 Uncaught (in promise) Error: No Connection with that ID: Status code '404'
at ut.send (blazor.server.js:1:39443)
at async rt (blazor.server.js:1:36427)
at async _t._sendLoop (blazor.server.js:1:61855)
:30500/_blazor?id=eC3fba0OdI6vu_KNZl9_Kg:1
Failed to load resource: the server responded with a status of 404 (Not Found)
blazor.server.js:1 Uncaught (in promise) Error: No Connection with that ID: Status code '404'
at ut.send (blazor.server.js:1:39443)
at async rt (blazor.server.js:1:36427)
at async _t._sendLoop (blazor.server.js:1:61855)
:30500/_blazor?id=eC3fba0OdI6vu_KNZl9_Kg:1
Failed to load resource: the server responded with a status of 404 (Not Found)
blazor.server.js:1 Uncaught (in promise) Error: No Connection with that ID: Status code '404'
at ut.send (blazor.server.js:1:39443)
at async rt (blazor.server.js:1:36427)
at async _t._sendLoop (blazor.server.js:1:61855)
:30500/_blazor?id=eC3fba0OdI6vu_KNZl9_Kg:1
Failed to load resource: the server responded with a status of 404 (Not Found)
blazor.server.js:1 Uncaught (in promise) Error: No Connection with that ID: Status code '404'
at ut.send (blazor.server.js:1:39443)
at async rt (blazor.server.js:1:36427)
at async _t._sendLoop (blazor.server.js:1:61855)
WebSockets 似乎存在一些问题,但我不知道如何解决。
当在入口处供应时,它看起来像。这显然是不正确的,但我只收到一个错误:
blazor.server.js:2 Uncaught SyntaxError: Unexpected token '<'
我不知道那是什么文件,因为我无法从 DevTools Console 打开它,并且无法在 DevTools Source 窗格中找到它。它似乎也不在应用程序的代码库中。
我在 Windows Hyper-V 上运行 Minikube。该应用程序是 Visual Studio 2022 中的默认服务器端 Blazor 应用程序。不过,我对默认 Dockerfile 进行了一些更改:
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["WebApp.csproj", "WebApp/"]
RUN dotnet restore "./WebApp/./WebApp.csproj"
COPY . .
WORKDIR "/src/WebApp"
RUN dotnet build "./WebApp.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./WebApp.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "WebApp.dll"]
有什么线索可以解释导致这两个问题的原因吗?
编辑:这是我的 Kubernetes .yaml 文件:
apiVersion: apps/v1
kind: Deployment
metadata:
name: blazor
labels:
app: blazor
spec:
replicas: 3
selector:
matchLabels:
app: blazor
template:
metadata:
labels:
app: blazor
spec:
containers:
- name: blazor
image: registry/...
ports:
- containerPort: 80
imagePullSecrets:
- name: test-webapp
apiVersion: v1
kind: Service
metadata:
labels:
app: blazor
name: blazor
namespace: default
spec:
ports:
- nodePort: 30500
port: 80
protocol: TCP
targetPort: 80
selector:
app: blazor
type: NodePort
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: blazor-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
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"
spec:
rules:
- host: blazor.test
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: blazor
port:
number: 80
我们这里有 2 个问题
nginx.ingress.kubernetes.io/rewrite-target: /$1
然后重写有
$1
,大概意思是放在第一位capturing group
,但是它是什么?嗯,只有添加 nginx.ingress.kubernetes.io/use-regex
时才能使用它,所以它没有任何意义,当发送请求获取像 /styles.css
这样的文件时,它最终会在 Blazor 应用程序中得到类似 /$1styles.css
的内容从您的应用程序角度来看,其他任何内容都无效。
你可以举个例子
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: blazor-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
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"
spec:
rules:
- host: blazor.test
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: blazor
port:
number: 80
一切都应该有效,但是将
/
(path: /
) 重写为 /
(rewrite-target: /
) 以我们乞求的方式结束,因此删除带有 rewrite-target
的行具有完全相同的效果。
使用正则表达式的另一个示例
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: blazor-ingress
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$1
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"
spec:
rules:
- host: blazor.test
http:
paths:
- path: /(.*)
pathType: Prefix
backend:
service:
name: blazor
port:
number: 80
在这种情况下,在
path
中,我们有/(.*)
,还有一个capturing group
,什么是(.*)
(要使路径/
正常工作,你必须有*
而不是+
),然后rewrite
出现,它显示 /$1
,因为我们启用了正则表达式来代替 $1
,将使用存储在第一个捕获组中的值。
SignalR 与 Blazor 后端建立连接需要发送 2 个请求
第一个请求将在响应中发送
connectionToken
,然后该令牌将用于连接,因此,如果您有多个副本,则有可能(随着副本数量的增加)这两个请求将有两个不同一。为了解决这个问题,微软建议使用一些额外的注释来强制入口始终使用基于cookie的相同副本,因此Negotiate
和Connect
将始终命中相同的副本,并且不会出现问题(还有一些关于的GitHub问题)它.
要回答为什么它不能与
NodePort
一起使用的问题,因为我们已经知道它发送两个请求,它们必须命中同一个副本,但是服务 NodePort
没有像 nginx.ingress.kubernetes.io/affinity
这样的功能来强制将请求发送到始终使用相同的副本,并且没有解决方案期望仅使用一个副本(或者至少我不知道)。