|
| 1 | +# Routing to Applications Using HTTP Matching Conditions |
| 2 | + |
| 3 | +In this guide we will configure advanced routing rules for multiple applications. These rules will showcase request |
| 4 | +matching by path, headers, query parameters, and method. For an introduction to exposing your application, it is |
| 5 | +recommended to go through the [basic guide](/docs/guides/routing-traffic-to-your-app.md) first. |
| 6 | + |
| 7 | +The following image shows the traffic flow that we will be creating with these rules. |
| 8 | + |
| 9 | + |
| 10 | + |
| 11 | +The goal is to create a set of rules that will result in client requests being sent to specific backends based on |
| 12 | +the request attributes. In this diagram, we have two versions of the `coffee` service. Traffic for v1 needs to be |
| 13 | +directed to the old application, while traffic for v2 needs to be directed towards the new application. We also |
| 14 | +have two `tea` services, one that handles GET operations and one that handles POST operations. Both the `tea` |
| 15 | +and `coffee` applications share the same Gateway. |
| 16 | + |
| 17 | +## Prerequisites |
| 18 | + |
| 19 | +- [Install](/docs/installation.md) NGINX Kubernetes Gateway. |
| 20 | +- [Expose NGINX Kubernetes Gateway](/docs/installation.md#expose-nginx-kubernetes-gateway) and save the public IP |
| 21 | + address and port of NGINX Kubernetes Gateway into shell variables: |
| 22 | + |
| 23 | + ```text |
| 24 | + GW_IP=XXX.YYY.ZZZ.III |
| 25 | + GW_PORT=<port number> |
| 26 | + ``` |
| 27 | + |
| 28 | +> **Note** |
| 29 | +> In a production environment, you should have a DNS record for the external IP address that is exposed, |
| 30 | +> and it should refer to the hostname that the gateway will forward for. |
| 31 | +
|
| 32 | +## Coffee Applications |
| 33 | + |
| 34 | +### Deploy the Coffee Applications |
| 35 | + |
| 36 | +Begin by deploying the `coffee-v1` and `coffee-v2` applications: |
| 37 | + |
| 38 | +```shell |
| 39 | +kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-kubernetes-gateway/main/examples/advanced-routing/coffee.yaml |
| 40 | +``` |
| 41 | + |
| 42 | +### Deploy the Gateway API Resources for the Coffee Applications |
| 43 | + |
| 44 | +The [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/) resource is typically deployed by the |
| 45 | +[cluster operator][roles-and-personas]. To deploy the Gateway: |
| 46 | + |
| 47 | +```yaml |
| 48 | +kubectl apply -f - <<EOF |
| 49 | +apiVersion: gateway.networking.k8s.io/v1beta1 |
| 50 | +kind: Gateway |
| 51 | +metadata: |
| 52 | + name: cafe |
| 53 | +spec: |
| 54 | + gatewayClassName: nginx |
| 55 | + listeners: |
| 56 | + - name: http |
| 57 | + port: 80 |
| 58 | + protocol: HTTP |
| 59 | +EOF |
| 60 | +``` |
| 61 | + |
| 62 | +This Gateway defines a single listener on port 80. Since no hostname is specified, this listener matches on all hostnames. |
| 63 | + |
| 64 | +The [HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/) is typically deployed by the |
| 65 | +[application developer][roles-and-personas]. To deploy the `coffee` HTTPRoute: |
| 66 | + |
| 67 | +```yaml |
| 68 | +kubectl apply -f - <<EOF |
| 69 | +apiVersion: gateway.networking.k8s.io/v1beta1 |
| 70 | +kind: HTTPRoute |
| 71 | +metadata: |
| 72 | + name: coffee |
| 73 | +spec: |
| 74 | + parentRefs: |
| 75 | + - name: cafe |
| 76 | + sectionName: http |
| 77 | + hostnames: |
| 78 | + - cafe.example.com |
| 79 | + rules: |
| 80 | + - matches: |
| 81 | + - path: |
| 82 | + type: PathPrefix |
| 83 | + value: /coffee |
| 84 | + backendRefs: |
| 85 | + - name: coffee-v1-svc |
| 86 | + port: 80 |
| 87 | + - matches: |
| 88 | + - path: |
| 89 | + type: PathPrefix |
| 90 | + value: /coffee |
| 91 | + headers: |
| 92 | + - name: version |
| 93 | + value: v2 |
| 94 | + - path: |
| 95 | + type: PathPrefix |
| 96 | + value: /coffee |
| 97 | + queryParams: |
| 98 | + - name: TEST |
| 99 | + value: v2 |
| 100 | + backendRefs: |
| 101 | + - name: coffee-v2-svc |
| 102 | + port: 80 |
| 103 | +EOF |
| 104 | +``` |
| 105 | + |
| 106 | +This HTTPRoute has a few important properties: |
| 107 | + |
| 108 | +- The `parentRefs` references the Gateway resource that we created, and specifically defines the `http` listener |
| 109 | +to attach to, via the `sectionName` field. |
| 110 | +- `cafe.example.com` is the hostname that is matched for all requests to the backends defined in this HTTPRoute. |
| 111 | +- The first rule defines that all requests with the path prefix `/coffee` and no other matching conditions are sent |
| 112 | +to the `coffee-v1` Service. |
| 113 | +- The second rule defines two matching conditions. If *either* of these conditions match, requests are forwarded to |
| 114 | +the `coffee-v2` Service: |
| 115 | + - Request with the path prefix `/coffee` and header `version=v2` |
| 116 | + - Request with the path prefix `/coffee` and the query parameter `TEST=v2` |
| 117 | + If you want both conditions to be required, you can define headers and queryParams in the same match object. |
| 118 | + |
| 119 | +[roles-and-personas]: https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1 |
| 120 | + |
| 121 | +### Send Traffic to Coffee |
| 122 | + |
| 123 | +Using the external IP address and port for NGINX Kubernetes Gateway, we can send traffic to our coffee |
| 124 | +applications. |
| 125 | + |
| 126 | +> **Note** |
| 127 | +> If you have a DNS record allocated for `cafe.example.com`, you can send the request directly to that |
| 128 | +> hostname, without needing to resolve. |
| 129 | +
|
| 130 | +```shell |
| 131 | +curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee |
| 132 | +``` |
| 133 | + |
| 134 | +This request should receive a response from the `coffee-v1` Pod. |
| 135 | + |
| 136 | +```text |
| 137 | +Server address: 10.244.0.9:8080 |
| 138 | +Server name: coffee-v2-68bd55f798-s9z5q |
| 139 | +``` |
| 140 | + |
| 141 | +If we want our request to be routed to `coffee-v2`, then we need to meet the defined conditions. We can include |
| 142 | +a header: |
| 143 | + |
| 144 | +```shell |
| 145 | +curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee -H "version:v2" |
| 146 | +``` |
| 147 | + |
| 148 | +or include a query parameter: |
| 149 | + |
| 150 | +```shell |
| 151 | +curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/coffee?TEST=v2 |
| 152 | +``` |
| 153 | + |
| 154 | +Either request should result in a response from the `coffee-v2` Pod. |
| 155 | + |
| 156 | +```text |
| 157 | +Server address: 10.244.0.9:8080 |
| 158 | +Server name: coffee-v2-68bd55f798-s9z5q |
| 159 | +``` |
| 160 | + |
| 161 | +## Tea Applications |
| 162 | + |
| 163 | +Let's deploy a different set of applications now called `tea` and `tea-post`. These applications will |
| 164 | +have their own set of rules, but will still attach to the same Gateway listener as the `coffee` apps. |
| 165 | + |
| 166 | +### Deploy the Tea Applications |
| 167 | + |
| 168 | +```shell |
| 169 | +kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-kubernetes-gateway/main/examples/advanced-routing/tea.yaml |
| 170 | +``` |
| 171 | + |
| 172 | +### Deploy the HTTPRoute for the Tea Services |
| 173 | + |
| 174 | +We are reusing the previous Gateway for these applications, so all we need to create is the HTTPRoute. |
| 175 | + |
| 176 | +```yaml |
| 177 | +kubectl apply -f - <<EOF |
| 178 | +apiVersion: gateway.networking.k8s.io/v1beta1 |
| 179 | +kind: HTTPRoute |
| 180 | +metadata: |
| 181 | + name: tea |
| 182 | +spec: |
| 183 | + parentRefs: |
| 184 | + - name: cafe |
| 185 | + hostnames: |
| 186 | + - cafe.example.com |
| 187 | + rules: |
| 188 | + - matches: |
| 189 | + - path: |
| 190 | + type: PathPrefix |
| 191 | + value: /tea |
| 192 | + method: POST |
| 193 | + backendRefs: |
| 194 | + - name: tea-post-svc |
| 195 | + port: 80 |
| 196 | + - matches: |
| 197 | + - path: |
| 198 | + type: PathPrefix |
| 199 | + value: /tea |
| 200 | + method: GET |
| 201 | + backendRefs: |
| 202 | + - name: tea-svc |
| 203 | + port: 80 |
| 204 | +EOF |
| 205 | +``` |
| 206 | + |
| 207 | +The properties of this HTTPRoute include: |
| 208 | + |
| 209 | +- The same Gateway is referenced as before. |
| 210 | +- The same hostname is used as with the `coffee` apps. |
| 211 | +- The first rule defines that a POST request to the `/tea` path is routed to the `tea-post` Service. |
| 212 | +- The second rule defines that a GET request to the `/tea` path is routed to the `tea` Service. |
| 213 | + |
| 214 | +### Send Traffic to Tea |
| 215 | + |
| 216 | +Using the external IP address and port for NGINX Kubernetes Gateway, we can send traffic to our tea |
| 217 | +applications. |
| 218 | + |
| 219 | +> **Note** |
| 220 | +> If you have a DNS record allocated for `cafe.example.com`, you can send the request directly to that |
| 221 | +> hostname, without needing to resolve. |
| 222 | +
|
| 223 | +```shell |
| 224 | +curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/tea |
| 225 | +``` |
| 226 | + |
| 227 | +This GET request should receive a response from the `tea` Pod. |
| 228 | + |
| 229 | +```text |
| 230 | +Server address: 10.244.0.10:8080 |
| 231 | +Server name: tea-df5655878-5fmfg |
| 232 | +``` |
| 233 | + |
| 234 | +If we want our request to be routed to `tea-post`, then we need to send a POST request: |
| 235 | + |
| 236 | +```shell |
| 237 | +curl --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/tea -X POST |
| 238 | +``` |
| 239 | + |
| 240 | +```text |
| 241 | +Server address: 10.244.0.7:8080 |
| 242 | +Server name: tea-post-b59b8596b-g586r |
| 243 | +``` |
| 244 | + |
| 245 | +This request should receive a response from the `tea-post` Pod. Any other type of method, such as PATCH, will |
| 246 | +result in a `404 Not Found` response. |
| 247 | + |
| 248 | + |
| 249 | +## Troubleshooting |
| 250 | + |
| 251 | +If you have any issues while sending traffic, try the following to debug your configuration and setup: |
| 252 | + |
| 253 | +- Make sure you set the shell variables $GW_IP and $GW_PORT to the public IP and port of the NGINX Kubernetes Gateway |
| 254 | + Service. Instructions for finding those values are [here](/docs/installation.md#expose-nginx-kubernetes-gateway). |
| 255 | + |
| 256 | +- Check the status of the Gateway: |
| 257 | + |
| 258 | + ```shell |
| 259 | + kubectl describe gateway cafe |
| 260 | + ``` |
| 261 | + |
| 262 | + The Gateway status should look like this: |
| 263 | + |
| 264 | + ```text |
| 265 | + Status: |
| 266 | + Addresses: |
| 267 | + Type: IPAddress |
| 268 | + Value: 10.244.0.85 |
| 269 | + Conditions: |
| 270 | + Last Transition Time: 2023-08-15T20:57:21Z |
| 271 | + Message: Gateway is accepted |
| 272 | + Observed Generation: 1 |
| 273 | + Reason: Accepted |
| 274 | + Status: True |
| 275 | + Type: Accepted |
| 276 | + Last Transition Time: 2023-08-15T20:57:21Z |
| 277 | + Message: Gateway is programmed |
| 278 | + Observed Generation: 1 |
| 279 | + Reason: Programmed |
| 280 | + Status: True |
| 281 | + Type: Programmed |
| 282 | + Listeners: |
| 283 | + Attached Routes: 2 |
| 284 | + Conditions: |
| 285 | + Last Transition Time: 2023-08-15T20:57:21Z |
| 286 | + Message: Listener is accepted |
| 287 | + Observed Generation: 1 |
| 288 | + Reason: Accepted |
| 289 | + Status: True |
| 290 | + Type: Accepted |
| 291 | + Last Transition Time: 2023-08-15T20:57:21Z |
| 292 | + Message: Listener is programmed |
| 293 | + Observed Generation: 1 |
| 294 | + Reason: Programmed |
| 295 | + Status: True |
| 296 | + Type: Programmed |
| 297 | + Last Transition Time: 2023-08-15T20:57:21Z |
| 298 | + Message: All references are resolved |
| 299 | + Observed Generation: 1 |
| 300 | + Reason: ResolvedRefs |
| 301 | + Status: True |
| 302 | + Type: ResolvedRefs |
| 303 | + Last Transition Time: 2023-08-15T20:57:21Z |
| 304 | + Message: No conflicts |
| 305 | + Observed Generation: 1 |
| 306 | + Reason: NoConflicts |
| 307 | + Status: False |
| 308 | + Type: Conflicted |
| 309 | + Name: http |
| 310 | + ``` |
| 311 | + |
| 312 | + Check that the conditions match and that the attached routes for the `http` listener equals 2. If it is less than |
| 313 | + 2, there may be an issue with the routes. |
| 314 | + |
| 315 | +- Check the status of the HTTPRoutes: |
| 316 | + |
| 317 | + ```shell |
| 318 | + kubectl describe httproute coffee |
| 319 | + ``` |
| 320 | + |
| 321 | + ```shell |
| 322 | + kubectl describe httproute tea |
| 323 | + ``` |
| 324 | + |
| 325 | + Each HTTPRoute status should look like this: |
| 326 | + |
| 327 | + ```text |
| 328 | + Status: |
| 329 | + Parents: |
| 330 | + Conditions: |
| 331 | + Last Transition Time: 2023-08-15T20:57:21Z |
| 332 | + Message: The route is accepted |
| 333 | + Observed Generation: 1 |
| 334 | + Reason: Accepted |
| 335 | + Status: True |
| 336 | + Type: Accepted |
| 337 | + Last Transition Time: 2023-08-15T20:57:21Z |
| 338 | + Message: All references are resolved |
| 339 | + Observed Generation: 1 |
| 340 | + Reason: ResolvedRefs |
| 341 | + Status: True |
| 342 | + Type: ResolvedRefs |
| 343 | + Controller Name: gateway.nginx.org/nginx-gateway-controller |
| 344 | + Parent Ref: |
| 345 | + Group: gateway.networking.k8s.io |
| 346 | + Kind: Gateway |
| 347 | + Name: cafe |
| 348 | + Namespace: default |
| 349 | + ``` |
| 350 | + |
| 351 | + Check for any error messages in the conditions. |
| 352 | + |
| 353 | +## Further Reading |
| 354 | + |
| 355 | +To learn more about the Gateway API and the resources we created in this guide, check out the following resources: |
| 356 | + |
| 357 | +- [Gateway API Overview](https://gateway-api.sigs.k8s.io/concepts/api-overview/) |
| 358 | +- [Deploying a simple Gateway](https://gateway-api.sigs.k8s.io/guides/simple-gateway/) |
| 359 | +- [HTTP Routing](https://gateway-api.sigs.k8s.io/guides/http-routing/) |
0 commit comments