我遇到了一个问题,无法让 nginx 与 gRPC 一起正常工作。我正在使用 .Net core 3.1 来提供支持 REST 和 gRPC 的 API。
我正在使用下面的 docker 镜像:
- .Net Core 3.1(aspnet:3.1-alpine)
- Nginx(nginx:最新)
客户端在本地运行,因为我只是通过 nginx 连接到 docker 容器(端口 8080 和 443 映射到主机)
我已经在 docker 容器中构建了 API 映像,并使用 docker compose 来启动一切。
当涉及到 gRPC 时,我的 API 相当简单:
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<CartService>();
endpoints.MapControllers();
});
我在 API 前面使用 nginx 作为反向代理,下面是我的 nginx 配置。但 rpc 调用不起作用。我无法通过客户端连接到 gRPC 服务,它返回 502 请求。 我得到了2020/06/29 18:33:30 [error] 27#27: *3 upstream sent too large http2 frame: 4740180 while reading response header from upstream, client: 172.20.0.1
.罢工>。添加单独的 kestral 端点后(请参阅下面的 Edit1),我收到*1 upstream prematurely closed connection while reading response header from upstream
当我查看 Nginx 日志时。
服务器甚至从未收到实际的请求,因为当我查看 Docker 日志时,服务器端没有记录任何内容。
关于如何通过 .Net 上的 docker 支持 gRPC 的文档很少甚至没有,因此不确定如何继续。为了使其正常工作,除了我必须做的之外,还需要配置/启用什么?请注意,API 的 REST 部分工作正常,没有任何问题。不确定 SSL 是否需要一直传送到上游服务器(即 API 级别的 SSL)。
我在 Nginx for gRPC 上看到的文档与我下面的内容完全相同。 http_v2_module 在 Nginx 中启用,我可以通过响应协议验证它是否适用于 API 的非 gRPC 部分。
http {
upstream api {
server apiserver:5001;
}
upstream function {
server funcserver:5002;
}
# redirect all http requests to https
server {
listen 80 default_server;
listen [::]:80 default_server;
return 301 https://$host$request_uri;
}
server {
server_name api.localhost;
listen 443 http2 ssl ipv6only=on;
ssl_certificate /etc/certs/api.crt;
ssl_certificate_key /etc/certs/api.key;
location /CartCheckoutService/ValidateCartCheckout {
grpc_pass grpc://api;
proxy_buffer_size 512k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 512k;
grpc_set_header Upgrade $http_upgrade;
grpc_set_header Connection "Upgrade";
grpc_set_header Connection keep-alive;
grpc_set_header Host $host:$server_port;
grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
grpc_set_header X-Forwarded-Proto $scheme;
}
location / {
proxy_pass http://api;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Connection keep-alive;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
server {
server_name func.localhost;
listen 443 ssl;
ssl_certificate /etc/certs/func.crt;
ssl_certificate_key /etc/certs/func.key;
location / {
proxy_pass http://function;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
gzip on;
gzip_vary on;
gzip_proxied no-cache no-store private expired auth;
gzip_types text/plain text/css application/json application/xml;
}
Edit1:我还尝试为 REST/gRPC 启动单独的端点。由此根据文档,当不安全的请求进来时,它会自动假定为 Http1 请求。我手动将 kestrel 配置为具有 2 个独立的端点、两个端口 - 一个用于 http1+http2,另一个用于 http2 请求。
services.Configure<KestrelServerOptions>(y =>
{
y.ListenAnyIP(5010, o =>
{
o.Protocols = HttpProtocols.Http2;
//o.UseHttps("./certs/backend.pfx", "password1");
});
y.ListenAnyIP(5001, o =>
{
o.Protocols = HttpProtocols.Http1AndHttp2;
});
});
在 Nginx 中,我还创建了一个单独的条目:
upstream api {
server apiserver:5001;
}
upstream grpcservice {
server apiserver:5010;
}
upstream function {
server funcserver:5002;
}
这也行不通。我什至尝试通过使 htt2 端点仅接受 ssl 连接而不接受骰子来尝试上游 SSL。
Edit2
我也尝试过以下:
- Nginx 中的上游 SSL - 即后端和反向代理之间的 SSL
- 使用基于 Debian/Ubuntu 的镜像而不是 Alpine
它们都不起作用。
Edit 3:我终于能够完成这项工作:
location /CartCheckoutService/ValidateCartCheckout {
grpc_pass grpc://api;
}
无论出于何种原因,nginx 唯一有效的配置是使用grpc_pass仅有的。它与代理通行证不同,并且不需要其他配置。我终于能够让它工作,而无需执行上游 SSL,只需使用代理,就像我的意思一样 - 在代理处终止 SSL。
我仍在寻找正式的解释,否则我会将我自己的解决方案标记为答案,因为我已经成功测试了它。