diff --git a/examples/grpc-routing/README.md b/examples/grpc-routing/README.md new file mode 100644 index 000000000..1feffee6d --- /dev/null +++ b/examples/grpc-routing/README.md @@ -0,0 +1,224 @@ +# gRPC Routing Example + +In this example, we deploy NGINX Gateway Fabric, a simple gRPC web application, and then configure NGINX Gateway Fabric +to route traffic to that application using GRPCRoute resources. + +## Running the Example + +## 1. Deploy NGINX Gateway Fabric + +1. Follow the [installation instructions](https://docs.nginx.com/nginx-gateway-fabric/installation/) to deploy NGINX Gateway Fabric. + > **Important**: Ensure the Gateway APIs from the experimental channel are installed and that NGF is deployed with the Gateway experimental features enabled. + +1. Save the public IP address of NGINX Gateway Fabric into a shell variable: + + ```text + GW_IP=XXX.YYY.ZZZ.III + ``` + +1. Save the port of NGINX Gateway Fabric: + + ```text + GW_PORT= + ``` + +## 2. Deploy the Helloworld Application + +1. Create the two helloworld Deployments and Services: + + ```shell + kubectl apply -f helloworld.yaml + ``` + +1. Check that the Pods are running in the `default` Namespace: + + ```shell + kubectl -n default get pods + ``` + + ```text + NAME READY STATUS RESTARTS AGE + grpc-infra-backend-v1-766c7d6788-rg92p 1/1 Running 0 12s + grpc-infra-backend-v2-546f7c8d48-mjkkx 1/1 Running 0 12s + ``` + + Save these pod names into variables: + + ```text + POD_V1= + POD_V2= + ``` + +## 3. Configure Routing + +There are 3 options to configure gRPC routing. To access the application and test the routing rules, we will use [grpcurl](https://github.com/fullstorydev/grpcurl?tab=readme-ov-file#installation). + +### 3a. Configure exact method matching based routing + +1. Create the Gateway and GRPCRoute resources: + + ```shell + kubectl apply -f exact-method.yaml + ``` + +2. Test the Application: + + ```shell + grpcurl -plaintext -proto grpc.proto -authority bar.com -d '{"name": "exact"}' ${GW_IP}:${GW_PORT} helloworld.Greeter/SayHello + ``` + + ```text + { + "message": "Hello exact" + } + ``` + +3. Clean up the Gateway and GRPCRoute resources: + + ```shell + kubectl delete -f exact-method.yaml + ``` + +### 3b. Configure hostname based routing + +1. Create the Gateway and GRPCRoute resources: + + ```shell + kubectl apply -f hostname.yaml + ``` + +2. Test the Application: + + ```shell + grpcurl -plaintext -proto grpc.proto -authority bar.com -d '{"name": "bar server"}' ${GW_IP}:${GW_PORT} helloworld.Greeter/SayHello + ``` + + ```text + { + "message": "Hello bar server" + } + ``` + + To make sure this came from the correct server, we can check the application server logs: + + ```shell + kubectl logs ${POD_V1} + ``` + + ```text + 2024/04/29 09:26:54 server listening at [::]:50051 + 2024/04/29 09:28:54 Received: bar server + ``` + + Now we'll send a request to `foo.bar.com` + + ```shell + grpcurl -plaintext -proto grpc.proto -authority foo.bar.com -d '{"name": "foo bar server"}' ${GW_IP}:${GW_PORT} helloworld.Greeter/SayHello + ``` + + ```text + { + "message": "Hello foo bar server" + } + ``` + + This time, we'll check the POD_V2 logs: + + ```shell + kubectl logs ${POD_V2} + ``` + + ```text + 2024/04/29 09:26:55 server listening at [::]:50051 + 2024/04/29 09:29:46 Received: foo bar server + ``` + +3. Clean up the Gateway and GRPCRoute resources: + + ```shell + kubectl delete -f hostname.yaml + ``` + +### 3c. Configure headers based routing + +1. Create the Gateway and GRPCRoute resources: + + ```shell + kubectl apply -f headers.yaml + ``` + +2. Test the Application: + + ```shell + grpcurl -plaintext -proto grpc.proto -authority bar.com -d '{"name": "version one"}' -H 'version: one' ${GW_IP}:${GW_PORT} helloworld.Greeter/SayHello + ``` + + ```text + { + "message": "Hello version one" + } + ``` + + To make sure this came from the correct server, we can check the application server logs: + + ```shell + kubectl logs ${POD_V1} + ``` + + ```text + <...> + 2024/04/29 09:30:27 Received: version one + ``` + + Now we'll send a request with the header `version: two` + + ```shell + grpcurl -plaintext -proto grpc.proto -authority bar.com -d '{"name": "version two"}' -H 'version: two' ${GW_IP}:${GW_PORT} helloworld.Greeter/SayHello + ``` + + ```text + { + "message": "Hello version two" + } + ``` + + This time, we'll check the POD_V2 logs: + + ```shell + kubectl logs ${POD_V2} + ``` + + ```text + <...> + 2024/04/29 09:32:46 Received: version two + ``` + + Finally, we'll send a request with the headers `version: two` and `color: orange` + + ```shell + grpcurl -plaintext -proto grpc.proto -authority bar.com -d '{"name": "version two orange"}' -H 'version: two' -H 'color: orange' ${GW_IP}:${GW_PORT} helloworld.Greeter/SayHello + ``` + + ```text + { + "message": "Hello version two orange" + } + ``` + + Now check the POD_V1 logs again: + + ```shell + kubectl logs ${POD_V1} + ``` + + ```text + <...> + 2024/04/29 09:30:27 Received: version one + 2024/04/29 09:33:26 Received: version two orange + ``` + +3. Clean up the Gateway and GRPCRoute resources: + + ```shell + kubectl delete -f headers.yaml + ``` diff --git a/examples/grpc-routing/exact-method.yaml b/examples/grpc-routing/exact-method.yaml new file mode 100644 index 000000000..b47b0a801 --- /dev/null +++ b/examples/grpc-routing/exact-method.yaml @@ -0,0 +1,29 @@ +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: same-namespace +spec: + gatewayClassName: nginx + listeners: + - name: http + port: 80 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same +--- +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: GRPCRoute +metadata: + name: exact-matching +spec: + parentRefs: + - name: same-namespace + rules: + - matches: + - method: + service: helloworld.Greeter + method: SayHello + backendRefs: + - name: grpc-infra-backend-v1 + port: 8080 diff --git a/examples/grpc-routing/grpc.proto b/examples/grpc-routing/grpc.proto new file mode 100644 index 000000000..692ef9ded --- /dev/null +++ b/examples/grpc-routing/grpc.proto @@ -0,0 +1,38 @@ +// Copyright 2015 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +option go_package = "google.golang.org/grpc/examples/helloworld/helloworld"; +option java_multiple_files = true; +option java_package = "io.grpc.examples.helloworld"; +option java_outer_classname = "HelloWorldProto"; + +package helloworld; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} diff --git a/examples/grpc-routing/headers.yaml b/examples/grpc-routing/headers.yaml new file mode 100644 index 000000000..fcd3b256d --- /dev/null +++ b/examples/grpc-routing/headers.yaml @@ -0,0 +1,70 @@ +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: same-namespace +spec: + gatewayClassName: nginx + listeners: + - name: http + port: 80 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same +--- +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: GRPCRoute +metadata: + name: grpc-header-matching +spec: + parentRefs: + - name: same-namespace + rules: + # Matches "version: one" + - matches: + - headers: + - name: version + value: one + backendRefs: + - name: grpc-infra-backend-v1 + port: 8080 + # Matches "version: two" + - matches: + - headers: + - name: version + value: two + backendRefs: + - name: grpc-infra-backend-v2 + port: 8080 + # Matches "version: two" AND "color: orange" + - matches: + - headers: + - name: version + value: two + - name: color + value: orange + backendRefs: + - name: grpc-infra-backend-v1 + port: 8080 + # Matches "color: blue" OR "color: green" + - matches: + - headers: + - name: color + value: blue + - headers: + - name: color + value: green + backendRefs: + - name: grpc-infra-backend-v1 + port: 8080 + # Matches "color: red" OR "color: yellow" + - matches: + - headers: + - name: color + value: red + - headers: + - name: color + value: yellow + backendRefs: + - name: grpc-infra-backend-v2 + port: 8080 diff --git a/examples/grpc-routing/helloworld.yaml b/examples/grpc-routing/helloworld.yaml new file mode 100644 index 000000000..7a554c484 --- /dev/null +++ b/examples/grpc-routing/helloworld.yaml @@ -0,0 +1,79 @@ +apiVersion: v1 +kind: Service +metadata: + name: grpc-infra-backend-v1 +spec: + selector: + app: grpc-infra-backend-v1 + ports: + - protocol: TCP + port: 8080 + targetPort: 50051 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: grpc-infra-backend-v1 + labels: + app: grpc-infra-backend-v1 +spec: + replicas: 1 + selector: + matchLabels: + app: grpc-infra-backend-v1 + template: + metadata: + labels: + app: grpc-infra-backend-v1 + spec: + containers: + - name: grpc-infra-backend-v1 + image: ghcr.io/nginxinc/kic-test-grpc-server:0.2.1 + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + resources: + requests: + cpu: 10m +--- +apiVersion: v1 +kind: Service +metadata: + name: grpc-infra-backend-v2 +spec: + selector: + app: grpc-infra-backend-v2 + ports: + - protocol: TCP + port: 8080 + targetPort: 50051 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: grpc-infra-backend-v2 + labels: + app: grpc-infra-backend-v2 +spec: + replicas: 1 + selector: + matchLabels: + app: grpc-infra-backend-v2 + template: + metadata: + labels: + app: grpc-infra-backend-v2 + spec: + containers: + - name: grpc-infra-backend-v2 + image: ghcr.io/nginxinc/kic-test-grpc-server:edge + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + resources: + requests: + cpu: 10m diff --git a/examples/grpc-routing/hostname.yaml b/examples/grpc-routing/hostname.yaml new file mode 100644 index 000000000..4da094064 --- /dev/null +++ b/examples/grpc-routing/hostname.yaml @@ -0,0 +1,49 @@ +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: grpcroute-listener-hostname-matching +spec: + gatewayClassName: nginx + listeners: + - name: listener-1 + port: 80 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same + kinds: + - kind: GRPCRoute + hostname: bar.com + - name: listener-2 + port: 80 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same + hostname: foo.bar.com +--- +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: GRPCRoute +metadata: + name: backend-v1 +spec: + parentRefs: + - name: grpcroute-listener-hostname-matching + sectionName: listener-1 + rules: + - backendRefs: + - name: grpc-infra-backend-v1 + port: 8080 +--- +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: GRPCRoute +metadata: + name: backend-v2 +spec: + parentRefs: + - name: grpcroute-listener-hostname-matching + sectionName: listener-2 + rules: + - backendRefs: + - name: grpc-infra-backend-v2 + port: 8080