Using the njs Preload Object Directive with NGINX Gateway Fabric

by

in

This post shows how you can expand NGINX’s functionality with the njs scripting language, focusing specifically on the js_preload_object directive. By implementing the directive into NGINX Gateway Fabric, we’ll demonstrate how js_preload_object can be a powerful tool for reloading objects at runtime and managing key-value pairs.

The Basics of njs Modules and Directives

NGINX JavaScript (njs) is an ECMA 5.1-complaint subset of JavaScript designed for NGINX. It allows for pre-upstream access control and security verifications for requests, header response manipulation, and the creation of adaptable asynchronous content handlers and filters. (Keep these use cases in mind, as they inform how njs is implemented below!)

Within your NGINX configuration, njs modules are used to handle runtime tasks like incoming requests. When handling an HTTP request, NGINX reads any corresponding configuration.

njs directives are triggered based on which block the request encounters – server{}, http{}, or location{}. Then, they pass the request variable to the module function. The request variable (typically denoted as r) is an object containing HTTP request fields and values that can be modified by JavaScript before being passed to the server. This powerful feature highlights njs’s flexibility and enables NGINX to function as a highly versatile web server.

Workflow between NGINX Gateway Fabric, NGINX, and njs

How the js_preload_object Directive Works in NGINX Gateway Fabric

When building NGINX Gateway Fabric, we encountered challenges defining complex HTTP routes for the Gateway API. The routes contained numerous match conditions that were previously stored in a variable in the config. When these match conditions grew too large, they caused reload errors, because 4096 is the maximum number of characters allowed after a variable in NGINX. Exceeding this limit in the http_matches variable triggered these reload errors.

We decided to implement the js_preload_object directive because it allows for dynamic content loading on the cluster or system during runtime. It also provides flexibility to use the object based on contextual requirements, avoiding possible reload problems.

To demonstrate how this directive works, let’s dive into an HTTPRoute example with a large amount of matches using NGINX Gateway Fabric. This example illustrates how to add a HTTPRoute without the directive, then adjust it afterwards.

Setting Up the Environment

Prerequisites:

  1. Download Go v1.2 and above from the official Go website.
  2. Install NGINX Gateway Fabric v1.2 in your Kubernetes cluster.
  3. Install the latest release of Docker to manage application images.
  4. Install the latest release of kubectl, a command-line interface for Kubernetes that allows you to manage and inspect cluster resources.
  5. Install the latest release kind, which is short for Kubernetes in Docker, to run a local cluster using Docker.

Note: This example starts with NGINX Gateway Fabric v1.2, which doesn’t use the js_preload_object directive, to demonstrate the possible issues. Then we upgrade to a more recent version of NGINX Gateway Fabric to demonstrate how the directive resolves long matching conditions.

To verify your NGINX Gateway Fabric installation, run the following command with the namespace (nginx-gateway) used for installation:

Once NGINX Gateway Fabric is installed, install the Coffee application. This application is designed solely to return 200 ok for requests on the URI pathPrefix /. It creates the Coffee Service and a deployment with one Pod.

Run the following command to verify the resources were created:

Define an HTTPRoute for the Coffee application with rules to match the hostname. The YAML below has match conditions for each rule.

To verify if the HTTPRoute is configured successfully, check the status of the resource by running the following command:

The HTTPRoute will not configure successfully due to reload failures in NGINX, as the error describes. To investigate why the reload failed, you need to exec into the NGINX container and reload NGINX.

To get shell access to the nginx container and reload NGINX, run the following command:

The error indicates that the reload of nginx config has failed due to variable being too long.

Let’s look at the long variable in /etc/nginx/conf.d/http.conf:

Resolving the Error

To better understand this error, it is important to understand what location blocks do in the nginx.conf. A location block is a directive used within a server block to define how NGINX should process requests for specific URIs or patterns of URIs. Location blocks allow you to match and control requests based on their URL paths and can be used to route requests to different backends, serve static files, apply specific configurations, or enforce access controls.

Previously, we assigned multiple routes to the http_match variable within a specific location block. However, the match condition in this location block exceeds the character limit allowed for a variable in NGINX. As a result, NGINX fails to reload properly.

To resolve this issue, we switched to using the js_preload_object directive for specifying match conditions. This approach requires storing the match conditions as key-value pairs in a file and mounting it onto our cluster file system. In our setup, this file system must be mounted to nginx container of the NGINX Gateway Fabric pod. It is essential to provide the full path to the js_preload_object directive to ensure it can locate and read the necessary file.

Integrating the Directive into Your Configuration

To integrate js_preload_object into your NGINX configuration, import the http.js module.

Here are two examples:

The matches above indicates the object which stores the content loaded on to it during runtime from match.json. With our current HTTPRoute example, match.json file content will have one key defined for the location block with value as the HTTPMatches for that route. The matches object will store that information to find the winning match to determine where the cURL request will be directed.

Update NGINX Gateway Fabric to the latest version. Refer to this installation guide, to do so. We will configure the same Coffee application and HTTPRoute.

Check the status of the HTTPRoute:

The HTTPRoute is configured successfully. Look at match.json – it maps the key as id of the location block and match conditions as value.

The nginx.conf file uses this object as shown here:

Since you are now able to configure the HTTPRoute, you should be able to send cURL requests to the Coffee application.

Here is an example of what a successful cURL request to this HTTPRoute looks like:

Get Involved with NGINX Gateway Fabric

Whether you’re using NGINX as your web server, or simply looking to optimize your workflows, we hope the above guide helped provide practical insights into leveraging the js_preload_object directive in your nginx.conf file.

If you are interested in NGINX’s implementation of the Gateway API, check out the NGINX Gateway Fabric project on GitHub. You can get involved by:

  • Joining the project as a contributor
  • Trying the implementation in your lab
  • Testing and providing feedback

To learn more about njs, check out additional examples or read this blog.