fix: update Platform packages to v1.0.17 and fix Gateway DB schema
Some checks failed
Build and Push Docker / build (push) Failing after 2m40s
Some checks failed
Build and Push Docker / build (push) Failing after 2m40s
- Update Fengling.Platform.Domain/Infrastructure from 1.0.14 to 1.0.17 - Add temporary EnsureDeletedAsync() to recreate Gateway DB tables - Fix bigint/character varying type mismatch in GwCluster.Id - Add Npgsql log level config to suppress GSSAPI warnings - Add DATABASE_SCHEMA_FIX.md documentation Refs: AGENTS.md Recent Changes 2026-03-08
This commit is contained in:
parent
5dc9dc5979
commit
2f8c35ef3e
@ -4,8 +4,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- Microsoft Packages -->
|
<!-- Microsoft Packages -->
|
||||||
<PackageVersion Include="Fengling.Platform.Domain" Version="1.0.14" />
|
<PackageVersion Include="Fengling.Platform.Domain" Version="1.0.17" />
|
||||||
<PackageVersion Include="Fengling.Platform.Infrastructure" Version="1.0.14" />
|
<PackageVersion Include="Fengling.Platform.Infrastructure" Version="1.0.17" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.3" />
|
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.3" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.3" />
|
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.3" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.2" />
|
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.2" />
|
||||||
|
|||||||
172
docs/DATABASE_SCHEMA_FIX.md
Normal file
172
docs/DATABASE_SCHEMA_FIX.md
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
# Gateway 数据库表结构修复文档
|
||||||
|
|
||||||
|
## 问题描述
|
||||||
|
|
||||||
|
### 错误现象
|
||||||
|
Gateway 服务启动时抛出异常:
|
||||||
|
```
|
||||||
|
Npgsql.PostgresException (0x80004005): 42883: operator does not exist: bigint = character varying
|
||||||
|
```
|
||||||
|
|
||||||
|
### 根本原因
|
||||||
|
1. `PlatformDbContext` 配置中 `GwCluster` 与 `GwDestination` 的关系外键类型不匹配
|
||||||
|
2. 旧版 `GwCluster.Id` 是 `long` (bigint),新版改为 `string` (varchar)
|
||||||
|
3. 数据库表结构是旧的(bigint),但代码已更新为新的实体定义(string)
|
||||||
|
|
||||||
|
### 影响范围
|
||||||
|
- fengling-gateway 服务无法加载路由配置
|
||||||
|
- YARP 代理功能无法正常工作
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 修复步骤
|
||||||
|
|
||||||
|
### 1. 更新 Platform 包版本
|
||||||
|
|
||||||
|
**文件**: `fengling-console/Directory.Packages.props`
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- 修改前 -->
|
||||||
|
<PackageVersion Include="Fengling.Platform.Domain" Version="1.0.14" />
|
||||||
|
<PackageVersion Include="Fengling.Platform.Infrastructure" Version="1.0.17" />
|
||||||
|
|
||||||
|
<!-- 修改后 -->
|
||||||
|
<PackageVersion Include="Fengling.Platform.Domain" Version="1.0.17" />
|
||||||
|
<PackageVersion Include="Fengling.Platform.Infrastructure" Version="1.0.17" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 临时添加 EnsureDeletedAsync
|
||||||
|
|
||||||
|
**文件**: `fengling-console/src/Program.cs`
|
||||||
|
|
||||||
|
在初始化 Gateway 数据库前添加删除旧表逻辑:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 2. 初始化 Gateway 数据库(创建 GwRoutes, GwClusters 等表)
|
||||||
|
var gatewayDb = scope.ServiceProvider.GetRequiredService<PlatformDbContext>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
app.Logger.LogInformation("Deleting old Gateway database schema...");
|
||||||
|
await gatewayDb.Database.EnsureDeletedAsync(); // 临时:删除旧表结构
|
||||||
|
app.Logger.LogInformation("Initializing Gateway database...");
|
||||||
|
await gatewayDb.Database.EnsureCreatedAsync();
|
||||||
|
app.Logger.LogInformation("Gateway database initialized successfully");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 构建并部署
|
||||||
|
|
||||||
|
在 K3s 服务器 (192.168.100.120) 上执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /root/fengling/Fengling.Refactory.Pack
|
||||||
|
|
||||||
|
# 构建并推送新镜像
|
||||||
|
cd fengling-console/src
|
||||||
|
docker build -f Dockerfile -t 192.168.100.120:8418/fengling/console:test .
|
||||||
|
docker push 192.168.100.120:8418/fengling/console:test
|
||||||
|
|
||||||
|
# 重启 Console Pod(触发 EnsureDeleted + EnsureCreated)
|
||||||
|
kubectl rollout restart deployment/fengling-console -n fengling-test
|
||||||
|
|
||||||
|
# 观察日志,确认数据库表重建成功
|
||||||
|
kubectl logs -n fengling-test deployment/fengling-console -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 验证修复
|
||||||
|
|
||||||
|
检查 Gateway 服务日志,确认路由加载成功:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 重启 Gateway 加载新表结构
|
||||||
|
kubectl rollout restart deployment/fengling-gateway -n fengling-test
|
||||||
|
|
||||||
|
# 验证路由加载
|
||||||
|
kubectl logs -n fengling-test deployment/fengling-gateway | grep -i "loaded"
|
||||||
|
```
|
||||||
|
|
||||||
|
预期输出:
|
||||||
|
```
|
||||||
|
Loaded X routes from database
|
||||||
|
Loaded X clusters from database
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 清理临时代码
|
||||||
|
|
||||||
|
修复成功后,移除 `EnsureDeletedAsync()` 避免数据丢失风险:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 恢复 Program.cs(删除 EnsureDeletedAsync 行)
|
||||||
|
cd /root/fengling/Fengling.Refactory.Pack
|
||||||
|
git checkout fengling-console/src/Program.cs
|
||||||
|
# 或手动编辑删除 EnsureDeletedAsync 那一行
|
||||||
|
|
||||||
|
# 重新构建部署(不带删除逻辑)
|
||||||
|
cd fengling-console/src
|
||||||
|
docker build -f Dockerfile -t 192.168.100.120:8418/fengling/console:test .
|
||||||
|
docker push 192.168.100.120:8418/fengling/console:test
|
||||||
|
kubectl rollout restart deployment/fengling-console -n fengling-test
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 架构说明
|
||||||
|
|
||||||
|
### 数据库结构
|
||||||
|
|
||||||
|
```
|
||||||
|
fengling_gateway 数据库
|
||||||
|
├── GwRoutes (路由配置表)
|
||||||
|
│ ├── Id (string, PK)
|
||||||
|
│ ├── ServiceName (string)
|
||||||
|
│ ├── ClusterId (string, FK to GwClusters.ClusterId)
|
||||||
|
│ └── Match (jsonb)
|
||||||
|
│
|
||||||
|
└── ServiceInstances (集群配置表)
|
||||||
|
├── Id (string, PK)
|
||||||
|
├── ClusterId (string, Unique)
|
||||||
|
├── Name (string)
|
||||||
|
├── Destinations (Owned Collection)
|
||||||
|
│ ├── DestinationId (string)
|
||||||
|
│ ├── Address (string)
|
||||||
|
│ ├── TenantCode (string) -- 用于多租户路由
|
||||||
|
│ └── ...
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 配置同步流程
|
||||||
|
|
||||||
|
```
|
||||||
|
K8s Service (Label: app-router-*)
|
||||||
|
↓
|
||||||
|
Console (K8sServiceWatchService)
|
||||||
|
↓
|
||||||
|
PendingConfigCache → User Confirm
|
||||||
|
↓
|
||||||
|
PostgreSQL (GwRoutes, ServiceInstances)
|
||||||
|
↓
|
||||||
|
PostgreSQL NOTIFY
|
||||||
|
↓
|
||||||
|
Gateway (PgSqlConfigChangeListener)
|
||||||
|
↓
|
||||||
|
YARP Route Reload
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **数据备份**: `EnsureDeletedAsync()` 会删除所有数据,生产环境务必先备份
|
||||||
|
2. **临时方案**: 此修复仅适用于测试环境,生产环境应使用 Migration
|
||||||
|
3. **多数据库**: Console 管理两个数据库:
|
||||||
|
- `fengling_console`: 身份认证、用户租户数据
|
||||||
|
- `fengling_gateway`: 网关路由配置(由 Console 写入,Gateway 读取)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 相关文件
|
||||||
|
|
||||||
|
- `fengling-console/src/Program.cs` - 数据库初始化逻辑
|
||||||
|
- `fengling-console/Directory.Packages.props` - Platform 包版本
|
||||||
|
- `fengling-platform/Fengling.Platform.Infrastructure/PlatformDbContext.cs` - 实体配置
|
||||||
|
- `fengling-platform/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/` - 实体定义
|
||||||
135
k8s/test/deployment.yaml
Normal file
135
k8s/test/deployment.yaml
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: fengling-console-config
|
||||||
|
namespace: fengling-test
|
||||||
|
labels:
|
||||||
|
app: fengling-console
|
||||||
|
data:
|
||||||
|
ASPNETCORE_ENVIRONMENT: "Production"
|
||||||
|
Kestrel__Endpoints__Http__Url: "http://0.0.0.0:8080"
|
||||||
|
Logging__LogLevel__Default: "Information"
|
||||||
|
Logging__LogLevel__Microsoft__AspNetCore: "Warning"
|
||||||
|
Logging__LogLevel__Npgsql: "Error" # 隐藏 GSSAPI 警告
|
||||||
|
ServiceDiscovery__UseInClusterConfig: "true"
|
||||||
|
ServiceDiscovery__Namespace: "fengling-test"
|
||||||
|
ServiceDiscovery__LabelSelector: "app-router-name"
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: fengling-console-secret
|
||||||
|
namespace: fengling-test
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
ConnectionStrings__DefaultConnection: "Host=81.68.223.70;Port=15432;Database=fengling_console;Username=movingsam;Password=sl52788542"
|
||||||
|
Jwt__Authority: "https://apigateway.shtao1.cn"
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: fengling-console
|
||||||
|
namespace: fengling-test
|
||||||
|
labels:
|
||||||
|
app: fengling-console
|
||||||
|
version: v2
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: fengling-console
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: fengling-console
|
||||||
|
version: v2
|
||||||
|
spec:
|
||||||
|
serviceAccountName: fengling-console-sa
|
||||||
|
containers:
|
||||||
|
- name: console
|
||||||
|
image: 192.168.100.120:8418/fengling/fengling-console:test
|
||||||
|
imagePullPolicy: Always
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
name: http
|
||||||
|
protocol: TCP
|
||||||
|
env:
|
||||||
|
- name: ASPNETCORE_ENVIRONMENT
|
||||||
|
value: "Production"
|
||||||
|
- name: ASPNETCORE_URLS
|
||||||
|
value: "http://+:8080"
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: fengling-console-config
|
||||||
|
- secretRef:
|
||||||
|
name: fengling-console-secret
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 5
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 3
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 200m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
cpu: 1000m
|
||||||
|
memory: 512Mi
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: fengling-console
|
||||||
|
namespace: fengling-test
|
||||||
|
labels:
|
||||||
|
app: fengling-console
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app: fengling-console
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 8080
|
||||||
|
name: http
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: fengling-console-sa
|
||||||
|
namespace: fengling-test
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: fengling-console-role
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["services"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["namespaces"]
|
||||||
|
verbs: ["get", "list"]
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: fengling-console-binding
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: fengling-console-sa
|
||||||
|
namespace: fengling-test
|
||||||
|
roleRef:
|
||||||
|
kind: ClusterRole
|
||||||
|
name: fengling-console-role
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
@ -30,6 +30,18 @@ builder.Services.AddDbContext<ConsoleDbContext>(options =>
|
|||||||
options.EnableDetailedErrors();
|
options.EnableDetailedErrors();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Gateway 数据库上下文(用于管理网关配置表)
|
||||||
|
builder.Services.AddDbContext<PlatformDbContext>(options =>
|
||||||
|
{
|
||||||
|
options.UseNpgsql(builder.Configuration.GetConnectionString("GatewayConnection") ??
|
||||||
|
builder.Configuration.GetConnectionString("DefaultConnection")?.Replace("fengling_console", "fengling_gateway"));
|
||||||
|
if (builder.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
options.EnableSensitiveDataLogging();
|
||||||
|
}
|
||||||
|
options.EnableDetailedErrors();
|
||||||
|
});
|
||||||
|
|
||||||
// Use Platform's identity
|
// Use Platform's identity
|
||||||
builder.Services.AddIdentity<ApplicationUser, ApplicationRole>()
|
builder.Services.AddIdentity<ApplicationUser, ApplicationRole>()
|
||||||
.AddEntityFrameworkStores<ConsoleDbContext>()
|
.AddEntityFrameworkStores<ConsoleDbContext>()
|
||||||
@ -114,6 +126,41 @@ app.UseCors("AllowAll");
|
|||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
// 执行数据库初始化(Console 负责数据库管理,测试环境使用 EnsureCreated)
|
||||||
|
using (var scope = app.Services.CreateScope())
|
||||||
|
{
|
||||||
|
// 1. 初始化 Console 数据库
|
||||||
|
var consoleDb = scope.ServiceProvider.GetRequiredService<ConsoleDbContext>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
app.Logger.LogInformation("Initializing Console database...");
|
||||||
|
await consoleDb.Database.EnsureCreatedAsync();
|
||||||
|
app.Logger.LogInformation("Console database initialized successfully");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
app.Logger.LogError(ex, "Failed to initialize Console database");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 初始化 Gateway 数据库(创建 GwRoutes, GwClusters 等表)
|
||||||
|
var gatewayDb = scope.ServiceProvider.GetRequiredService<PlatformDbContext>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
app.Logger.LogInformation("Deleting old Gateway database schema...");
|
||||||
|
await gatewayDb.Database.EnsureDeletedAsync(); // 临时:删除旧表结构
|
||||||
|
app.Logger.LogInformation("Initializing Gateway database...");
|
||||||
|
await gatewayDb.Database.EnsureCreatedAsync();
|
||||||
|
app.Logger.LogInformation("Gateway database initialized successfully");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
app.Logger.LogError(ex, "Failed to initialize Gateway database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
|
// 健康检查端点
|
||||||
|
app.MapGet("/health", () => Results.Ok(new { status = "healthy", timestamp = DateTime.UtcNow }));
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user