.Net 5 Kestrel 背后的 Kubernetes 负载均衡器无法识别的 HTTP 版本
.Net 5 Kestrel behind Kubernetes Load Balancer Unrecognized HTTP Version
我有一个 .NET 5 API 应用 运行 在 Docker 容器中安装了一个 Kestrel 服务器。我可以在 Docker 容器内 运行 本地应用程序,没有任何问题。当我部署到 Kubernetes 时会出现问题,而 Kubernetes 的 LoadBalancer 似乎会导致问题。
我反复收到以下错误:
←[40m←[37mdbug←[39m←[22m←[49m: Microsoft.AspNetCore.Server.Kestrel[17]
Connection id "0HMAMHC4BPDGN" bad request data: "Unrecognized HTTP version: '10.98.6.196 10.123.90.100 62168 443'"
Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Unrecognized HTTP version: '10.98.6.196 10.123.90.100 62168 443'
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpParser`1.RejectUnknownVersion(Int32 offset, ReadOnlySpan`1 requestLine)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpParser`1.ParseRequestLine(TRequestHandler handler, ReadOnlySpan`1 requestLine)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpParser`1.ParseRequestLine(TRequestHandler handler, SequenceReader`1& reader)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1Connection.ParseRequest(SequenceReader`1& reader)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1Connection.TryParseRequest(ReadResult result, Boolean& endConnection)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequestsAsync[TContext](IHttpApplication`1 application)
←[40m←[37mdbug←[39m←[22m←[49m: Microsoft.AspNetCore.Server.Kestrel[10]
Connection id "0HMAMHC4BPDGN" disconnecting.
←[40m←[37mdbug←[39m←[22m←[49m: Microsoft.AspNetCore.Server.Kestrel[2]
Connection id "0HMAMHC4BPDGN" stopped.
←[40m←[37mdbug←[39m←[22m←[49m: Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[7]
Connection id "0HMAMHC4BPDGN" sending FIN because: "The Socket transport's send loop completed gracefully."
我的 kubernetes 基础设施部署。它正在将所有 443 个请求映射到 运行ning Docker 容器的端口 80。
kind: ConfigMap
apiVersion: v1
metadata:
name: api-base
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-base
spec:
replicas: 1
selector:
matchLabels:
app: api-base
template:
metadata:
labels:
app: api-base
spec:
containers:
- name: api-base
image: /path/to/image
ports:
- containerPort: 80
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: api-base
labels:
app: api-base
annotations:
service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0
service.beta.kubernetes.io/aws-load-balancer-ssl-cert: mycert/path
service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"
spec:
selector:
app: api-base
ports:
- port: 443
targetPort: 80
protocol: TCP
name: https
type: LoadBalancer
Docker文件(缩写)暴露80端口,配置.net core到80端口运行。
FROM mcr.microsoft.com/dotnet/aspnet:5.0
WORKDIR /app
EXPOSE 80
ENV ASPNETCORE_URLS http://*:80
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "API.dll"]
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
private const string CorsOriginName = "AllowedOrigins";
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: CorsOriginName,
builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "API", Version = "v1" });
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "API v1"));
app.UseCors(CorsOriginName);
app.UseForwardedHeaders();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureAppConfiguration((hostContext, builder) =>
{
// Add other providers for JSON, etc.
if (hostContext.HostingEnvironment.IsDevelopment())
{
builder.AddUserSecrets<Program>();
}
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseKestrel().UseStartup<Startup>();
});
}
如何配置 Kestrel 以接受来自 LoadBalancer 的请求?
更新:
为此,我终止了 https 流量并使用 http
与容器通信
apiVersion: v1
kind: Service
metadata:
name: api-base
annotations:
# Note that the backend talks over HTTP.
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
# TODO: Fill in with the ARN of your certificate.
service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:{region}:{user id}:certificate/{id}
# Only run SSL on the port named "https" below.
service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https"
spec:
selector:
app: api-base
ports:
- name: http
port: 80
targetPort: 80
- name: https
port: 443
targetPort: 80
type: LoadBalancer
https://aws.amazon.com/premiumsupport/knowledge-center/terminate-https-traffic-eks-acm/
您需要决定是否要在您的应用程序中或在此之前终止 https。我建议您使用 Ingress 而不是 LB 类型的服务来将您的服务暴露给外部世界。这样你就可以在它自己的入口上配置 https 终止,并且应用程序将保持基于 http,无需在它自己的应用程序上配置证书、加密等,尽管你仍然需要正确配置入口。
我有一个 .NET 5 API 应用 运行 在 Docker 容器中安装了一个 Kestrel 服务器。我可以在 Docker 容器内 运行 本地应用程序,没有任何问题。当我部署到 Kubernetes 时会出现问题,而 Kubernetes 的 LoadBalancer 似乎会导致问题。
我反复收到以下错误:
←[40m←[37mdbug←[39m←[22m←[49m: Microsoft.AspNetCore.Server.Kestrel[17]
Connection id "0HMAMHC4BPDGN" bad request data: "Unrecognized HTTP version: '10.98.6.196 10.123.90.100 62168 443'"
Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Unrecognized HTTP version: '10.98.6.196 10.123.90.100 62168 443'
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpParser`1.RejectUnknownVersion(Int32 offset, ReadOnlySpan`1 requestLine)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpParser`1.ParseRequestLine(TRequestHandler handler, ReadOnlySpan`1 requestLine)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpParser`1.ParseRequestLine(TRequestHandler handler, SequenceReader`1& reader)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1Connection.ParseRequest(SequenceReader`1& reader)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1Connection.TryParseRequest(ReadResult result, Boolean& endConnection)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequestsAsync[TContext](IHttpApplication`1 application)
←[40m←[37mdbug←[39m←[22m←[49m: Microsoft.AspNetCore.Server.Kestrel[10]
Connection id "0HMAMHC4BPDGN" disconnecting.
←[40m←[37mdbug←[39m←[22m←[49m: Microsoft.AspNetCore.Server.Kestrel[2]
Connection id "0HMAMHC4BPDGN" stopped.
←[40m←[37mdbug←[39m←[22m←[49m: Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[7]
Connection id "0HMAMHC4BPDGN" sending FIN because: "The Socket transport's send loop completed gracefully."
我的 kubernetes 基础设施部署。它正在将所有 443 个请求映射到 运行ning Docker 容器的端口 80。
kind: ConfigMap
apiVersion: v1
metadata:
name: api-base
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-base
spec:
replicas: 1
selector:
matchLabels:
app: api-base
template:
metadata:
labels:
app: api-base
spec:
containers:
- name: api-base
image: /path/to/image
ports:
- containerPort: 80
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: api-base
labels:
app: api-base
annotations:
service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0
service.beta.kubernetes.io/aws-load-balancer-ssl-cert: mycert/path
service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"
spec:
selector:
app: api-base
ports:
- port: 443
targetPort: 80
protocol: TCP
name: https
type: LoadBalancer
Docker文件(缩写)暴露80端口,配置.net core到80端口运行。
FROM mcr.microsoft.com/dotnet/aspnet:5.0
WORKDIR /app
EXPOSE 80
ENV ASPNETCORE_URLS http://*:80
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "API.dll"]
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
private const string CorsOriginName = "AllowedOrigins";
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: CorsOriginName,
builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "API", Version = "v1" });
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "API v1"));
app.UseCors(CorsOriginName);
app.UseForwardedHeaders();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureAppConfiguration((hostContext, builder) =>
{
// Add other providers for JSON, etc.
if (hostContext.HostingEnvironment.IsDevelopment())
{
builder.AddUserSecrets<Program>();
}
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseKestrel().UseStartup<Startup>();
});
}
如何配置 Kestrel 以接受来自 LoadBalancer 的请求?
更新:
为此,我终止了 https 流量并使用 http
与容器通信apiVersion: v1
kind: Service
metadata:
name: api-base
annotations:
# Note that the backend talks over HTTP.
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
# TODO: Fill in with the ARN of your certificate.
service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:{region}:{user id}:certificate/{id}
# Only run SSL on the port named "https" below.
service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https"
spec:
selector:
app: api-base
ports:
- name: http
port: 80
targetPort: 80
- name: https
port: 443
targetPort: 80
type: LoadBalancer
https://aws.amazon.com/premiumsupport/knowledge-center/terminate-https-traffic-eks-acm/
您需要决定是否要在您的应用程序中或在此之前终止 https。我建议您使用 Ingress 而不是 LB 类型的服务来将您的服务暴露给外部世界。这样你就可以在它自己的入口上配置 https 终止,并且应用程序将保持基于 http,无需在它自己的应用程序上配置证书、加密等,尽管你仍然需要正确配置入口。