-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add gRPC support for VirtualServer #1135
Conversation
Deploy request for nginx-kubernetes-ingress rejected. Rejected with commit 45cf1391d6819ae40effabf17bdb8b6645f3d8a2 https://docs.netlify.com/configure-builds/environment-variables/#sensitive-variable-policy |
45cf139
to
bd560aa
Compare
Looks like CI failed due to insufficient permission |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @CatTail
Thanks for the PR! Looks like most of the work is done.
I left a few comments and suggestions.
Additionally:
Could you add a warning(s) to VirtualServer or VirtualServerRoute if gRPC is enabled when HTTP/2 and TLS termination are both disabled? You could do that in GenerateVirtualServerConfig
when you loop over VS or VSRs upstreams. To add a warning, you can do something like below:
// owner is either VS or VSR
vsc.addWarningf(owner, "gRPC will not be enabled for upstream %s. gRPC requires enabled HTTP/2 and TLS termination", u.Name)
As a result, you will see that warning in the status of VS or VSR and the events. This will provide a short feedback looks for users.
Looks like CI failed due to insufficient permission
We recently migrated to GitHub actions and haven't checked how it works for PRs from forks. We should fix it shortly.
docs-web/configuration/virtualserver-and-virtualserverroute-resources.md
Outdated
Show resolved
Hide resolved
internal/configs/virtualserver.go
Outdated
} | ||
|
||
if internal { | ||
return fmt.Sprintf("%v$request_uri", proxyPass) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unfortunately, this will not work for gRPC. You can check it if you deploy a VS like this:
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
name: grpc-example
spec:
host: grpc.example.com
tls:
secret: grpc-secret
redirect:
enable: true
upstreams:
- name: grpc-app
service: grpc-svc
port: 50051
# grpc: true
- name: grpc-app-2
service: grpc-svc
port: 50051
# grpc: true
routes:
- path: /
splits:
- weight: 50
action:
pass: grpc-app
grpc: true
- weight: 50
action:
pass: grpc-app-2
grpc: true
In the NGINX logs you will see something simliar, depending on your app:
2020/09/11 02:56:07 [error] 71#71: *62 invalid host in upstream "vs_default_grpc-example_grpc-app-2/helloworld.Greeter/SayHello", client: 127.0.0.1, server: grpc.example.com, request: "POST /helloworld.
Greeter/SayHello HTTP/2.0", host: "grpc.example.com:8443"
127.0.0.1 - - [11/Sep/2020:02:56:07 +0000] "POST /helloworld.Greeter/SayHello HTTP/2.0" 500 1 "-" "grpc-go/1.29.0-dev" "-"
There is a solution to fix that: instead of doing grpc_pass grpc://vs_default_grpc-example_grpc-app-2$request_uri;
in the Config, do rewrite ^ $request_uri break;
. That will work :)
To do that, please update generateRewrites
function, so that it inserts rewrite ^ $request_uri break;
if the location is internal and gPRC is enabled for the upstream. Note that the function might add additional configured rewrites, so in that case rewrite ^ $request_uri break;
will not have break
in the end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the detailed explanation, it seems we already have rewrite rule for internal, correct me if I'm wrong, is it means we don't need additional rewrite rule?
Hi @CatTail Just checking if you're still planning to finish the implementation of this feature |
Hi @pleshakov, sorry for the late response, yes I still plan to implement this feature, however, I'm a little bit busy these days so I might need to come back a later to finish it. |
bd560aa
to
9621d2f
Compare
Hi @pleshakov I know it's been a while since I update this PR, when you have time can you help to take a look, thanks! |
Hi @CatTail Thanks for sending the update! Hopefully, I review it by the end of the week. We've been busy wrapping up things for the next release. |
Hi @CatTail Sorry for the delay. Thanks for all the work so far! However, we decided re-evaluate approach in the implementation to make sure it addressed the gRPC use case sufficiently, in particular user-facing configuration and error responses . We'll do an investigation and shall get back to you in about 2 weeks time. |
Hi Pleshakov, thanks for your help anyway, take your time. |
Hi @pleshakov, just checking if you have come to a conclusion that how you are going to implement gRPC for VirtualServer? |
Hi @CatTail Apologies! we got delayed because of the other priorities. I will provide an update next week. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @CatTail
We reevaluated approach in the implementation to make sure it addresses the gRPC use case sufficiently.
It covers the basic implementation of this feature well 👍
I left a number of comments as part of the review. Once those are resolved, we could merge this PR!
Your PR covers 2/3 of it - which is great! After the PR is merged, we can address the remaining ones - those would be supporting error pages and active health checks (for NGINX Plus template).
There is one more comment - about supporting rewrites. This is related to this discussion -- #1135 (comment)
We need to add a rewrite to support splits like below:
- path: /
splits:
- weight: 50
action:
pass: grpc-app
grpc: true
- weight: 50
action:
pass: grpc-app-2
grpc: true
There is a solution to fix that - adding in the Config rewrite ^ $request_uri break;
when we have an internal location (such as the one in the case with splits) and gRPC is enabled.
To do that, could you add something like below to generateRewrites, at the top of the function:
if grpcEnabled && internal {
return []string{"rewrite ^ $request_uri break;"}
}
That should solve the issue.
In that case, we will always ignore any rewrite, if specified by proxy.RewritePath
. However, that is ok for this implementation.
@@ -254,15 +254,20 @@ server { | |||
proxy_pass {{ $l.InternalProxyPass }}; | |||
{{ end }} | |||
|
|||
{{ if $l.ProxyPass }} | |||
{{ if or $l.ProxyPass $l.GRPCPass}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you fix the indentation here and in the other template? Please also note about the space between pass
and }
.
{{ if or $l.ProxyPass $l.GRPCPass }}
{{ $proxyOrGRPC := "proxy" }}
{{ if $l.GRPCPass }}
{{ $proxyOrGRPC = "grpc" }}
{{ end }}
{{ $proxyOrGRPC }}_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||
{{ $proxyOrGRPC }}_set_header X-Forwarded-Host $host; | ||
{{ $proxyOrGRPC }}_set_header X-Forwarded-Port $server_port; | ||
{{ $proxyOrGRPC }}_set_header X-Forwarded-Proto {{ with $s.TLSRedirect }}{{ .BasedOn }}{{ else }}$scheme{{ end }}; | ||
{{ range $h := $l.ProxySetHeaders }} | ||
proxy_set_header {{ $h.Name }} "{{ $h.Value }}"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you update the remaining headers to use either grpc or proxy prefixes? Below is the full list:
* proxy_ssl_* -> grpc_ssl _*
* proxy_set_header -> grpc_set_header (except Upgrade and Connection)
* proxy_intercept_errors -> grpc_intercept_errors
* proxy_hide_header -> grpc_hide_header
* proxy_pass_header -> grpc_pass_header
* proxy_ignore_headers -> grpc_ignore_headers
* proxy_next_upstream_* -> grpc_next_upstream_*
@@ -77,6 +77,7 @@ type Upstream struct { | |||
SlowStart string `json:"slow-start"` | |||
Queue *UpstreamQueue `json:"queue"` | |||
SessionCookie *SessionCookie `json:"sessionCookie"` | |||
GRPC bool `json:"grpc"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After we reconsidered gRPC feature, we decided to go with the type field, like below:
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
name: cafe
spec:
host: cafe.example.com
tls:
secret: cafe-secret
upstreams:
- name: tea
service: tea-svc
type: grpc # new field
port: 50051
routes:
- path: /tea
action:
pass: tea
For now, the field will support two values - http
(default) and grpc
. Could you add the type field and remove the grpc
field?
it will be important to add validation for that field in validateUpstreams here https://github.com/nginxinc/kubernetes-ingress/blob/1ee2bcdfa033278a9ad2ba534072cd167b8b8a1f/pkg/apis/configuration/validation/virtualserver.go#L397 . A new function like validateUpstreamType
could do the job.
For the documentation of the new field in the docs, I think something like below would work:
The type of the upstream. Supported values are ``http`` and ``grpc``. The default is ``http``. For gRPC, it is necessary to enable HTTP/2 in the `ConfigMap </nginx-ingress-controller/configuration/global-configuration/configmap-resource/#listeners)>`_ and configure TLS termination in the VirtualServer.
@@ -1216,6 +1224,23 @@ func generateProxyPassProtocol(enableTLS bool) string { | |||
return "http" | |||
} | |||
|
|||
func generateGRPCPass(grpcEnabled bool, http2Enabled bool, tlsEnabled bool, upstreamName string) string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is ok to generate gRPC config, even if http2
is not enabled - for the user, it will just not work. which is the same if we generate http config. Could we remove http2Enabled bool
here?
@@ -284,6 +284,10 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig(vsEx *VirtualS | |||
|
|||
// generate upstreams for VirtualServer | |||
for _, u := range vsEx.VirtualServer.Spec.Upstreams { | |||
if (vsEx.VirtualServer.Spec.TLS != nil || !vsc.cfgParams.HTTP2) && u.GRPC { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is a bug here - it should be vsEx.VirtualServer.Spec.TLS == nil
also, there are more cases to determine TLS termination than vsEx.VirtualServer.Spec.TLS == nil
. Those are covered in generateSSLConfig
. Because of that, we should use sslConfig == nil
here
if (vsEx.VirtualServer.Spec.TLS != nil || !vsc.cfgParams.HTTP2) && u.GRPC { | |
if (sslConfig == nil || !vsc.cfgParams.HTTP2) && u.GRPC { |
same for the VSR upstreams
|
||
result := generateLocationForProxying(path, upstreamName, conf_v1.Upstream{GRPC: true}, &cfgParams, nil, false, 0, "", nil, "", vsLocSnippets, false, "", "") | ||
if !reflect.DeepEqual(result, expected) { | ||
t.Errorf("TestGenerateLocationForGrpcProxying() returned \n%v but expected \n%v", result, expected) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for comparing big structs, we recently started using this approach -- https://github.com/nginxinc/kubernetes-ingress/blob/901dc72c98e8c3ced5d9efc7f3c3e0deaeb6ca58/internal/configs/virtualserver_test.go#L645
. I suggest using that here.
This PR is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days. |
Sorry for the late response but FYI I'm still interested in merge this PR, I will get back to it as soon as I have chance to do that. |
Hi @CatTail Thank you for your contributions! Expanding protocol support aligns with our current road map. We understand everyone's time is valuable and don't expect contributors to be bound by our timelines. We'd like to use your efforts as a foundation and build on them to meet broader product requirements; all contributions are attributed accordingly. We'd like to accept this work and allocate resources to see its completion. |
Hi Is there anyway to use gRPC with VirtualServer until this merge request is merged? I am currently stuck and need to use this feature. |
@Binsabbar I see that you have a support ticket open. |
@CatTail thanks for your idea and contribution, it was very helpful and made it possible for us to add gRPC support for VirtualServer building on this PR. The feature is released in https://github.com/nginxinc/kubernetes-ingress/releases/tag/v2.1.0 so I'm closing this PR, thanks again! |
Proposed changes
Add gRPC support for VirtualServer CRD. For example, grpc proxy traffic for specific route,
Fix #908
Note I'm not an expert in Go/Nginx/Kubernetes, so feel free to suggest changes so we can have a solid implementation for VirtualServer gRPC support.
Checklist
Before creating a PR, run through this checklist and mark each as complete.