英文:
How do I access static files saved within the service/pod of .NET Core REST API in Kubernetes
问题 {#heading}
我最近一直在学习Kubernetes,我已经将一个包含.NET Core 7 REST API的服务部署到了一个Pod中。这个Pod/服务配置了它的ClusterIP和NGINX端口。
路径将是"app>wwwroot>icons>svg>anyimagesname.svg"。
我的本地主机地址127.0.0.1已配置为具有www.chorebear.com的DNS,因此访问静态文件的完整URL将是http://chorebear.com/icons/svg/afghanistan_adobe_express.xml。
但每次我访问此URL时,它显示"NGINX 404 Not Found"。服务本身是否有任何问题?任何帮助将会有所帮助。谢谢!。
var builder = WebApplication.CreateBuilder(args);
// 将服务添加到容器中。
var sqlConnectionBuilder = new SqlConnectionStringBuilder();
if (builder.Environment.IsDevelopment())
{
sqlConnectionBuilder.ConnectionString = builder.Configuration.GetConnectionString("Chorebear_Connection");
sqlConnectionBuilder.UserID = builder.Configuration["UserId"];
sqlConnectionBuilder.Password = builder.Configuration["Password"];
} else if (builder.Environment.IsProduction())
{
sqlConnectionBuilder.ConnectionString = builder.Configuration.GetConnectionString("Chorebear_Connection");
// sqlConnectionBuilder.UserID = builder.Configuration["UserId"];
// sqlConnectionBuilder.Password = builder.Configuration["Password"];
}
builder.Services.AddControllers();
// 了解有关配置Swagger/OpenAPI的更多信息,请访问https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddDbContext<ChoreBearContext>(options => options.UseSqlServer(sqlConnectionBuilder.ConnectionString));
// 注册依赖注入的服务和类
builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
builder.Services.AddIdentity<User, UserRole>().AddDefaultTokenProviders();
builder.Services.Configure<IdentityOptions>(options =>
{
options.User.RequireUniqueEmail = true;
options.Password.RequiredLength = 8;
options.SignIn.RequireConfirmedEmail = true;
});
builder.Services.AddScoped<IUserStore<User>, UserStore>();
builder.Services.AddTransient<IRoleStore<UserRole>, RoleStore>();
builder.Services.AddScoped<ICategoryOfTaskRepo, CategoryOfTaskRepo>();
builder.Services.AddScoped<ITaskRepo, TaskRepo>();
builder.Services.AddScoped<IAccountRepo, AccountRepo>();
// 注册依赖注入的实用工具类
builder.Services.AddScoped<IEmailUtility, EmailUtility>();
builder.Services.AddScoped<IPasswordHasher<User>, PasswordHasher<User>>();
builder.Services.AddControllers();
// 了解有关配置Swagger/OpenAPI的更多信息,请访问https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// 配置HTTP请求管道。
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.UseStaticFiles();
PrepDb.PrepPopulation(app, builder.Environment.IsProduction());
app.Run();
英文:
I've recently being learning about Kubernetes and I have deployed a service which contains a .NET Core 7 REST API into a pod. This pod/service is configured with it's ClusterIP and with NGINX port as well.
Whenever I would want to access the static files which is stored under this path in this screenshot:
The path will be "app>wwwroot>icons>svg>anyimagesname.svg".
My localhost address for 127.0.0.1 has been configured to have a DNS of www.chorebear.com hence the complete URL to access the static file will be http://chorebear.com/icons/svg/afghanistan_adobe_express.xml
But everytime I hit this URL, it says "NGINX 404 Not Found". Is there any issue with the service itself? Any help would help in this. Thanks!.
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var sqlConnectionBuilder = new SqlConnectionStringBuilder();
if (builder.Environment.IsDevelopment())
{
sqlConnectionBuilder.ConnectionString = builder.Configuration.GetConnectionString(\"Chorebear_Connection\");
sqlConnectionBuilder.UserID = builder.Configuration\[\"UserId\"\];
sqlConnectionBuilder.Password = builder.Configuration\[\"Password\"\];
} else if (builder.Environment.IsProduction())
{
sqlConnectionBuilder.ConnectionString = builder.Configuration.GetConnectionString(\"Chorebear_Connection\");
// sqlConnectionBuilder.UserID = builder.Configuration\[\"UserId\"\];
// sqlConnectionBuilder.Password = builder.Configuration\[\"Password\"\];
}
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddDbContext\<ChoreBearContext\>(options =\> options.UseSqlServer(sqlConnectionBuilder.ConnectionString));
//Registering services and classes which are dependency injection
builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
builder.Services.AddIdentity\<User, UserRole\>().AddDefaultTokenProviders();
builder.Services.Configure\<IdentityOptions\>(options =\>
{
options.User.RequireUniqueEmail = true;
options.Password.RequiredLength = 8;
options.SignIn.RequireConfirmedEmail = true;
});
builder.Services.AddScoped\<IUserStore\<User\>, UserStore\>();
builder.Services.AddTransient\<IRoleStore\<UserRole\>, RoleStore\>();
builder.Services.AddScoped\<ICategoryOfTaskRepo, CategoryOfTaskRepo\>();
builder.Services.AddScoped\<ITaskRepo, TaskRepo\>();
builder.Services.AddScoped\<IAccountRepo, AccountRepo\>();
//Registering utility classes which are dependency injection
builder.Services.AddScoped\<IEmailUtility, EmailUtility\>();
builder.Services.AddScoped\<IPasswordHasher\<User\>, PasswordHasher\<User\>\>();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.UseStaticFiles();
PrepDb.PrepPopulation(app, builder.Environment.IsProduction());
`app.Run();
`
答案1 {#1}
得分: 1
中间件的顺序非常重要,您的 app.UseStaticFiles
太晚了,请将其移动到管道的更靠前位置:
// ...
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthorization();
app.MapControllers();
阅读更多:
英文:
Middleware order is very important, your app.UseStaticFiles
is too late move it closer to the start of the pipeline:
// ...
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthorization();
`app.MapControllers();
`
Read more:
答案2 {#2}
得分: 1
我已经自己解决了这个问题。让我逐步解释一下我是如何找到这个错误并解决它的。
目前,我的Kubernetes架构包括以下内容:
- 在一个Pod中的.NET Core REST API服务[通过ClusterIP服务暴露到端口80]
- 用于.NET Core REST API的Node Port服务[仅用于开发目的]
- Ingress NGINX服务,配置为连接或重写.NET Core REST API的ClusterIP,以便使用域名URL而不是"localhost" IP地址。[用作生产标准环境]
- MS SQL Server实例数据库服务[通过ClusterIP服务暴露到端口1433和通过LoadBalancer暴露到1433,以允许Microsoft SQL Server管理工作室访问该服务]
- 持久卷声明,用于将数据保存在MS SQL Server实例数据库服务中,直接保存到主机的硬件中(在这种情况下是我的个人电脑)
现在,我的.NET Core REST API出现了问题,首先是中间件位置,特别是名为app.UseStaticFiles()
的中间件。以前的位置是在app.Run()
之前,因此当向Kubernetes中的REST API服务发起HTTP请求时,我保存在www/icons/svg
文件夹下的静态文件被提供得相对较晚。因此,特别感谢@GuruStron,他发现了这个问题并提供了帮助我理解问题的文档链接。我将提供以下链接:
但这并不是解决问题的全部。问题出现在Ingress服务中,特别是在我的ingress-depl.yaml
文件中。让我展示一下以前的YAML文件和更新的文件。
以前的Ingress服务YAML文件
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-srv
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/use-regex: 'true'
spec:
rules:
- host: chorebear.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: chorebear-clusterip-srv
port:
number: 80
更新后的Ingress YAML文件
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-srv
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/use-regex: 'true'
spec:
rules:
- host: chorebear.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: chorebear-clusterip-srv
port:
number: 80
这两个文件之间的区别是path: /api
配置属性。该属性将.NET Core REST API的Pod容器的URL域名重写为"http://chorebear.com/api"。任何端点的HTTP请求都会变成这样 -> http://chorebear.com/api/[你在控制器中创建的任何端点]
现在,由于我的REST API中的控制器包含[Route("api/[controller]")]
,"Route"的 "api" 部分被Ingress的YAML文件中的 path: /api
重写。这使得我可以使用YAML文件中设置的域名URL调用控制器中创建的任何端点,但对于静态文件来说,它无法在相同的重写URL域上提供服务,因为app.UseStaticFiles
中间件需要HTTP请求中的"api"路径,而这个路径在中间件本身中没有配置/设置,因此最终会出现"404错误未找到NGINX"。
为了解决这个问题,我只需在我的Ingress服务YAML文件中将path: /api
重新输入为path: /
,然后它就能正常工作了。对于我在控制器中定义的端点,我仍然可以像往常一样调用,只需添加"api",就像 http://chorebear.com/api/[你在控制器中创建的任何端点] 一样,因为我的控制器都使用了[Route("api/[controller]")]
,其中包含"api"。所以现在,我的静态文件在我的Ingress服务YAML文件中设置的域名URL上提供并可访问 http://chorebear.com/icons/svg/afghanistan_adobe_express.xml
希望这可以帮助您!
1: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-7.0
2: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/static-files?view=aspnetcore-7.0
3: http://chorebear.com/api/[你在控制器中创建的任何端点]
4: http://chorebear.com/icons/svg/afghanistan_adobe_express.xml
5: https://i.stack.imgur.com/cctV4.png
6: http://static.51tbox.com/static/2024-11-29/col/51844905fe2d3f96da60902cf32b9ddf/003e05efc3e74c8aaca0b8fe5c4af945.png.jpg
英文:
I've solved the issue by my own. Let me explain step-by-step on how I found the bug and how I solve it.
Currently, my architecture of my Kubernetes contains the following:
- .NET Core REST API service [With a ClusterIP service exposed to port 80] in a pod
- Node Port Service for .NET Core REST API [only for development purposes]
- Ingress NGINX Service which is configured to connect or rewrite the ClusterIP of .NET Core REST API to a domain URL instead of "localhost" IP address. [Used as production standard environment]
- MS SQL Server Instance Database service [With a ClusterIP service exposed to port 1433 and LoadBalancer exposed to 1433 to allow the service to be accessed by Microsoft SQL Server Management Studio]
- Persistent Volume Claim to persist data saved in MS SQL Server Instance Database service directly into the hardware of the host (in this case my PC)
Now my .NET Core REST API had an issue, with first the middleware position especially which is the middleware called app.UseStaticFiles()
. The position previously was right before app.Run()
hence when a HTTP request is initiated to the REST API service in Kubernetes, the static files which I saved under www/icons/svg
folder was being served quite late. So quick shoutout to @GuruStron for spotting this and providing the links to the documentation which helped me understanding it. I'll provide the links below:
But it's not the full solution to the problem. The problem relies in the Ingress Service and in this case my ingress-depl.yaml
file. Let me show you the previous YAML file and the update file.
Previous Ingress Service YAML file
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-srv
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/use-regex: 'true'
spec:
rules:
- host: chorebear.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: chorebear-clusterip-srv
port:
number: 80
Updated Ingress YAML file
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-srv
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/use-regex: 'true'
spec:
rules:
- host: chorebear.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: chorebear-clusterip-srv
port:
number: 80
The difference between both files were the configuration property called path: /api
. This property rewrites the URL domain for the .NET Core REST API pod container to "http://chorebear.com/api". Any HTTP request to any endpoint will be like this --> http://chorebear.com/api/[any-endpoint-you-create-in-your-controller]
Now since my controllers in my REST API contains [Route("api/[controller]")]
, the "api" portion of the "Route" gets re-written by the path: /api
of the YAML file for Ingress. This then allows me to call any endpoint created in my controllers with the domain URL in my YAML file but for the static files, it cannot be served on that same re-written URL domain which is "chorebear.com/api" simply because the app.UseStaticFiles
middleware needs the "api" path from the HTTP request which is not configured/set in the middleware itself and hence the ultimate dead-end of "404 Error Not Found NGINX".
To solve it, I simple re-type the path: /api
to path: /
in my Ingress Service YAML File and it works. For the endpoints I define in my controllers, I can still call as usual with "api" addition like http://chorebear.com/api/[any-endpoint-you-create-in-your-controller] since my controllers are decorated with [Route("api/[controller]")]
which has the "api" on it. So now, my static files are served and accessible on my domain URL set in my Ingress Service YAML File http://chorebear.com/icons/svg/afghanistan_adobe_express.xml
I hope this helps!