Pulling a Rabbit Out of My Cluster, Part 1: Make Kubernetes Vanish for Envoy with Gateway API and a Touch of Gloo
We at Solo are big believers in Envoy proxy. It has been a fundamental element of our products since we shipped our first open-source products back in 2018. Envoy is widely recognized as the next-generation foundation of proxy technology. It is open-source, cloud-native and highly scalable. This superb video documents its motivations and emergence over the past few years.
We’re also big believers in the new Kubernetes Gateway API. Its 2023 GA release is an important standards milestone for the Kubernetes community. It represents an evolution in capabilities from the earlier Kubernetes Ingress API. This is evidenced by the many vendors and open-source communities within the API gateway and service mesh ecosystems moving aggressively to adopt it.
Not surprisingly then, Solo and its Gloo Gateway product have been among the first movers in adopting both Envoy and the Kube Gateway API.
You might also reasonably conclude that Kubernetes is a requirement for adopting Gloo technology. After all, most of the products in the Envoy community target Kubernetes deployments exclusively.
But what if I told you that Gloo Gateway could make that Kubernetes requirement disappear?
But why? If container orchestration via Kubernetes represents the future, then why does this flexibility matter? The answer is simple: Legacy Systems.
It’s easy to criticize Legacy Systems. You didn’t build them. You don’t want to maintain them. But you can’t just retire them.
Why? Because they’re delivering real value for your organization. Some of them likely pre-date Kubernetes, back when clouds were just puffy cotton balls in the sky.
And when you need to deploy an API gateway to safely expose them to the outside world, adding a Kubernetes dependency is just too complex. Your legacy gateway may be a bloated, overpriced, monolithic beast. But Kubernetes is a bridge too far in those scenarios. Even when container orchestration is a strategic direction for the enterprise.
What if you could enjoy the best of both worlds? A modern API gateway with a cloud-native architecture that delivers the flexibility of Kube, hybrid, and even No-Kube VM deployments.
That’s the promise of the latest versions of Gloo Gateway.
Gloo Gateway Deployment Models
In this blog, we’re going to take a look at three deployment models for Gloo Gateway.
- Conventional Model: All Kube, All the Time
- Hybrid Model: Some Kube, All the Time
- VM-Only Model: No Kube, Any of the Time
We’ll discuss the conventional model here, to set the cloud-native foundation that underlies all Solo technology. We’ll briefly touch on the hybrid model, but give it a more complete treatment in Part 2 of this blog. Finally, we’ll dig into the VM-only model in this post, including a step-by-step example where you can follow along.
Conventional Model: All Kube, All the Time
This diagram depicts the conventional model that Gloo Gateway users have employed.
Gloo configurations are specified using Kubernetes Gateway API CRDs. This is important for several reasons. First, the Gateway API is being quickly adopted across modern gateways, yielding significant reusability benefits for core capabilities. Second, because these APIs are expressed as CRDs, they support declarative configuration with modern GitOps platforms like ArgoCD and Flux. Additionally, you can use standard Kubernetes CLI tools like kubectl
.
Deploying the Gloo control plane in Kubernetes has other benefits as well, notably the ability to use the native etcd
storage system and other Kubernetes facilities. That’s why, unlike many alternatives in the marketplace, Gloo technology doesn’t require you to spin up separate database services just to support artifact storage. Fewer moving parts translate into simpler architectures and a better user experience.
Envoy is the heart of the request data path. In this model, the Envoy proxy is deployed as a LoadBalancer service. It accepts requests from outside the cluster and processes each of them to comply with policies specified by the Gloo control plane. The control plane can be viewed as a giant translation engine. It accepts simple GW-API CRDs as input and produces complex Envoy configurations as output. The Envoy proxy fleet consumes this configuration via a protocol called xDS, but this process is independent of its high-volume request processing.
This design allows Envoy to remain lightweight and process requests remarkably fast. It can be scaled independently from the control plane. This delivers vast improvements in cost and scalability over prior generations of gateways that are deployable only in large monolithic chunks.
Modern API gateways do much more than basic TLS termination and traffic routing. They often deliver advanced capabilities like external authorization and rate limiting. These critical and resource-intensive features impact the data path of managed requests. For example, ExtAuth often requires sophisticated policy evaluation steps and even delegation to external identity provider platforms. The Envoy proxy decrypts outside requests, evaluates the relevant policy configurations, and then delegates to the ExtAuth and rate limiter as required before routing traffic to upstream application services.
Historically, Gloo users needed to scale all these components separately. The processing load due to core Envoy request processing can vary significantly from the requirements of ExtAuth and rate limiting. So in the conventional model, these services are deployed as separately scalable sets of Kubernetes pods, as shown in the diagram above.
Hybrid Model: Some Kube, All the Time
Solo now supports a hybrid deployment model, where Envoy proxies and data path components like ExtAuth and rate limiting are hosted in a VM. But the Gloo control plane is hosted in a Kubernetes cluster. We’ll preview just the architectural diagram for now, and save the complete discussion for Part 2 of this blog.
VM-Only Model: No Kube, Any of the Time
Finally, let’s consider an even more drastic departure from Gloo’s original deployment models. In this case, we’ll place not only the Envoy proxy and related data path components in a non-Kubernetes VM but also the control plane.
Note that while multiple VMs are depicted below, single-VM deployments are fully supported by Solo, too.
This model introduces significant changes. Neither the data plane nor control plane components can be provisioned using Kubernetes facilities. Since the Gloo service itself will be VM-deployed, this has implications for both the storage layer and tooling. When the control plane was Kubernetes-deployed, it could use the native etcd service for storing its CRD configuration.
This is no longer possible, so Gloo Gateway supports a couple of storage alternatives. The simplest, default option is to enable file-based storage for the Gloo component. This is fine for development and basic deployments. However, if high availability (HA) is a user requirement, something more robust is needed. Gloo supports independent shared storage mechanisms like Postgres for such cases.
Common developer tooling like kubectl
is impacted as well. It is commonly used to manage and inspect CRDs like those published as part of the Kubernetes Gateway API. You can see its usage in a conventional GW-API scenario in tutorials like this. But we require an alternative since there is no Kube cluster on the receiving end of this tooling. Solo has responded with a pluggable alternative called glooapi
. This tool offers the same interface for reading and writing GW-API components as before, but it interacts directly with the Gloo control plane, which in turn manages object storage and translation into Envoy configurations.
Interestingly, the use of the GW-API is not affected by any of these changes. Even without a Kubernetes cluster, the Gloo control plane still understands these CRDs and can translate them into Envoy proxy configuration.
The magic of employing a declarative API like GW-API in both Kube and No-Kube contexts is the secret elixir in Gloo Gateway.
An Observability Note
As we develop these models that cross between Kube and VM environments, we need to pause and consider observability. This kind of complexity breeds major operational challenges when trying to manage deployments and debug issues.
Standards like OpenTelemetry are the keystone of an enterprise observability strategy. Gloo Gateway data path components support OTel across any deployment environment. That goes a long way toward forestalling operational issues. For further automated OTel support across your application networks, consider service mesh technologies like Istio and its commercial counterpart Gloo Mesh.
Step-by-Step with No-Kube Deployments
How exactly do these models work? We’ll explore how to bring one of these models to life with a real-world example. We’ll demonstrate that No-Kube deployments require no sleight-of-hand, even when using the Kube GW-API.
In addition to showing basic installation of these new packages, we’ll establish that the same declarative APIs are used to configure Gloo’s most popular gateway features:
- Traffic routing;
- External authorization;
- Rate limiting; and
- TLS termination.
Prerequisites
We’ll carry out this example scenario using two vanilla VMs, one for the control plane and another for the data plane. You can spin these up in your environment using either a Debian-based Linux distro or a RHEL-based distro.
We’re using AWS for the test process outlined in this document, although a properly resourced workstation running these VMs with a modern hypervisor should be fine, too. We’re using a stock AWS Ubuntu image at version 24.04 LTS. It’s deployed on two t2.medium
EC2 instances in the us-east-2
region, running with 2 vCPUs, 4GB RAM, and 8 GB of disk.
t2.medium
. We encountered resource issues trying to use a micro
instance.If running with multiple VMs, you’ll need to ensure that the network configuration on your VMs is sufficiently open to allow traffic to flow between the control and data planes. Since we’re building this strictly for test purposes, we shared a single AWS security group across our two EC2s. Our SG allowed all traffic to flow both inbound and outbound across our VMs. You would of course never use such an unsecured configuration in a production environment.
Note that this VM support is an enterprise feature of Gloo Gateway. To work through this example, you’ll require a license key as well as the Debian or RPM packages that support VM installation. Navigate to this link to start a free trial.
Package Structure
Before we start exploring gateway features, we first consider the package structure for installing in a No-Kube environment. This incarnation of Gloo Gateway is available in popular Linux formats, like Debian and RPM, and also as a single-stack AWS AMI.
The packaging comes in multiple flavors, to facilitate mix-and-match of architectural components across multiple VMs:
gloo-control
– Envoy control plane services- Gloo control plane
glooapi
CLI, deliverskubectl
-like services in VM environments
gloo-gateway
– Envoy proxy data planegloo-extensions
– Optional data plane services- data plane component that enforces external authorization policies, like API keys, Open Policy Agent, and OpenID Connect
- rate limiter component
Install Control Plane Packages
Copy either the gloo-control.deb
or gloo-control.rpm
package to the control-plane VM. Install it using your distro’s package manager.
For Debian-based distros:
sudo dpkg -i gloo-control.deb
For RPM-based distros:
yum -y install gloo-control.rpm
Edit /etc/gloo/gloo-controller.env
and specify the license key you obtained from Solo as the value for GLOO_LICENSE_KEY
.
Enable and start the Gloo API and controller services:
sudo systemctl enable gloo-apiserver
sudo systemctl enable gloo-controller
sudo systemctl restart gloo-apiserver
sudo systemctl restart gloo-controller
Confirm the services are running:
sudo systemctl status gloo-apiserver
sudo systemctl status gloo-controller
Confirm the glooapi
service responds to queries:
glooapi get gatewayclass
The GatewayClass
that you should see from the previous command is a component of the Gateway API standard that Gloo creates to declare what types of Gateway
objects the user can create.
Now we’ll create a Gateway
object to define a listener that will be opened on the Envoy proxy to be configured on the data plane VM.
apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: http-gateway spec: gatewayClassName: gloo-gateway listeners: - protocol: HTTP port: 8080 name: http allowedRoutes: namespaces: from: Same
In this case, we declare an HTTP listener to be opened on the proxy at port 8080. We’ll apply that on our control plane VM using the glooapi
utility. In a Kubernetes cluster, you would use kubectl
to apply this Gateway
object to the Gloo control plane. But since no such cluster is being used, we’ll use the drop-in replacement glooapi
CLI instead.
glooapi apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/no-kube-deployment/01-gateway.yaml
You can also use glooapi
to confirm that the Gateway
has been accepted by the control plane. Note the Accepted
entries in the status stanza:
glooapi get gateway http-gateway -o yaml
apiVersion: gateway.networking.k8s.io/v1 kind: Gateway ... status: conditions: - lastTransitionTime: "2024-07-02T21:22:09Z" message: "" observedGeneration: 1 reason: Accepted status: "True" type: Accepted ...
Install Data Plane Packages
Copy either the gloo-gateway.deb
or gloo-gateway.rpm
package to the data plane VM depending on the distro being used.
Install it using the distro’s package manager.
For Debian-based distros:
sudo dpkg -i gloo-gateway.deb
For RPM-based distros:
yum -y install gloo-gateway.rpm
Add an entry to the /etc/hosts
file to resolve the Gloo controller’s hostname to its IP address. This IP address should match the VM where your control plane is deployed. The DNS entry may appear as follows:
172.31.6.132 gloo.gloo-system.svc.cluster.local
The hostname gloo.gloo-system.svc.cluster.local
is the default location of the gloo
control plane deployment when Gloo Gateway is deployed on Kubernetes. Specifying this DNS entry makes it easier to complete this configuration with no problems.
Edit /etc/gloo/gloo-gateway.env
and update the GATEWAY_NAME
to a name corresponding to the Gateway
resource created previously (http-gateway
). The completed gloo-gateway.env
file on the data plane VM might look something like this:
GATEWAY_NAME=http-gateway
GATEWAY_NAMESPACE=default
CONTROLLER_HOST=gloo.gloo-system.svc.cluster.local
LOG_LEVEL=info
Enable and start the Gloo API and controller services:
sudo systemctl enable gloo-gateway
sudo systemctl restart gloo-gateway
Confirm the services are running:
sudo systemctl status gloo-gateway
Set the GATEWAY_HOST
environment variable to the address of the gateway VM. If you’re executing your curl
invocations from the same VM where the gateway is deployed, you can simple use localhost
as the value here.
export GATEWAY_HOST=<gateway-address>
Confirm Access to Upstream Service
Before we proceed with the gateway configuration, let’s first confirm that we have access from the data plane VM to the upstream httpbin service:
curl -si http://httpbin.org:80/get
If network connectivity is in order, you should see something similar to this with an HTTP 200 response code:
HTTP/1.1 200 OK Date: Tue, 02 Jul 2024 21:27:25 GMT Content-Type: application/json Content-Length: 249 Connection: keep-alive Server: gunicorn/19.9.0 Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true { "args": {}, "headers": { "Accept": "*/*", "Host": "httpbin.org", "User-Agent": "curl/8.5.0", "X-Amzn-Trace-Id": "Root=1-668470bd-316d5b880bc29dd81be77a63" }, "origin": "3.17.74.2", "url": "http://httpbin.org/get" }
Traffic Routing
On the control plane VM, create an Upstream
component that points to the httpbin.org service. Then configure an HTTPRoute
object that routes traffic to this service:
apiVersion: gloo.solo.io/v1 kind: Upstream metadata: name: httpbin spec: static: hosts: - addr: httpbin.org port: 80 --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: test spec: parentRefs: - name: http-gateway hostnames: - "api.example.com" rules: - backendRefs: - name: httpbin port: 80 kind: Upstream group: gloo.solo.io
Use the glooapi
utility to apply these two objects to the Gloo control plane. It will then translate these objects into Envoy configuration and update the proxy configuration automatically via the xDS protocol.
glooapi apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/no-kube-deployment/02-route-upstream.yaml
You can confirm traffic the gateway has accepted this new configuration by sending a request to the gateway on port 8080:
curl -si $GATEWAY_HOST:8080/get -H 'Host: api.example.com'
HTTP/1.1 200 OK date: Tue, 02 Jul 2024 21:28:55 GMT content-type: application/json content-length: 305 server: envoy access-control-allow-origin: * access-control-allow-credentials: true x-envoy-upstream-service-time: 26 { "args": {}, "headers": { "Accept": "*/*", "Host": "api.example.com", "User-Agent": "curl/8.5.0", "X-Amzn-Trace-Id": "Root=1-66847117-18992db741cc69c74fd033ea", "X-Envoy-Expected-Rq-Timeout-Ms": "15000" }, "origin": "3.17.74.2", "url": "http://api.example.com/get" }
As you can see above, the response returned from httpbin indicates that our traffic routing works as expected.
If the request doesn’t succeed, first confirm the status of the HTTPRoute
is in the Accepted
state on the control plane. If you don’t see a response that includes a status stanza similar to the one below, then try debugging by deleting and re-applying all the configuration steps above.
$ glooapi get httproute test -o yaml
apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute ... status: parents: - conditions: - lastTransitionTime: "2024-06-28T19:44:44Z" message: "" observedGeneration: 10 reason: Accepted status: "True" type: Accepted ...
Another debugging tip is to check the status of the gloo-gateway
using systemctl
, then restart and recheck the status if there’s a problem.
External Authorization
In this section we’ll configure the Gloo external auth components on our gateway VM, then declare and test an HTTP basic auth strategy on the gateway.
Install Gloo Gateway ExtAuth Components
The ExtAuth service is provided by the gloo-extensions
package. Copy either the gloo-extensions.deb
or gloo-extensions.rpm
package to the gateway VM depending on the distro being used.
Install it using the distro’s package manager.
For Debian-based distros:
sudo dpkg -i gloo-extensions.deb
For RPM-based distros:
yum -y install gloo-extensions.rpm
Enable and start the Gloo API and controller services:
sudo systemctl enable gloo-extauth
sudo systemctl restart gloo-extauth
Confirm the service is running:
sudo systemctl status gloo-extauth
Configure HTTP Basic Auth
Refer to the product documentation to learn about how Gloo Gateway enables HTTP basic auth using declarative configuration — and many other authorization strategies, too! — at the gateway layer.
In this exercise, we use the username test
and password test123
. The username and password have been hashed using the Apache htpasswd
utility to produce the following:
- salt:
n28wuo5p
- hashed password:
NLFTUNkXaht0g/2GJjUM51
On the control VM, we’ll create a corresponding AuthConfig
resource. See the Gloo Gateway docs for a full list of supported request authorization strategies.
apiVersion: enterprise.gloo.solo.io/v1 kind: AuthConfig metadata: name: basic-auth spec: configs: - basicAuth: apr: users: test: hashedPassword: NLFTUNkXaht0g/2GJjUM51 salt: n28wuo5p realm: gloo
Use glooapi
to apply this object to the control plane:
glooapi apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/no-kube-deployment/04-auth-config.yaml
The gateway proxy will communicate with the ExtAuth service over a Unix Domain Socket. An Upstream
resource is automatically created by Gloo Gateway when the default setting of ENABLE_GATEWAY_LOCAL_EXTAUTH=true
is present in the /etc/gloo/gloo-controller.env
file on the control plane VM. You can inspect this generated Upstream
as shown below:
$ glooapi get upstream extauth -n gloo-system -o yaml
apiVersion: gloo.solo.io/v1
kind: Upstream
...
metadata:
name: extauth
namespace: gloo-system
labels:
app: gloo
gloo: extauth
spec:
useHttp2: true
pipe:
path: "/var/lib/gloo/.sock"
status:
statuses:
gloo-system:
reportedBy: gloo
state: Accepted
We’ll then add a new configuration object here, the RouteOption
, to point at our HTTP basic auth config. We’ll then modify our routing behavior by updating our existing HTTPRoute
to refer to that RouteOption
:
apiVersion: gateway.solo.io/v1 kind: RouteOption metadata: name: basic-auth namespace: default spec: options: extauth: configRef: name: basic-auth namespace: default --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: test namespace: default spec: parentRefs: - name: http-gateway hostnames: - "api.example.com" rules: - filters: # Extend our existing route using our new AuthConfig - type: ExtensionRef extensionRef: group: gateway.solo.io kind: RouteOption name: basic-auth backendRefs: - name: httpbin port: 80 kind: Upstream group: gloo.solo.io
Apply this new configuration on the control plane VM using glooapi
:
glooapi apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/no-kube-deployment/05-route-option-ext.yaml
On the data plane VM, confirm that a request without an Authorization
header fails as expected:
$ curl -si $GATEWAY_HOST:8080/get -H 'Host: api.example.com'
HTTP/1.1 401 Unauthorized www-authenticate: Basic realm="gloo" date: Tue, 02 Jul 2024 21:35:51 GMT server: envoy content-length: 0
Confirm a request with the base64 encoded user:password set in the Authorization
header succeeds:
$ echo -n "test:test123" | base64 dGVzdDp0ZXN0MTIz $ curl -si $GATEWAY_HOST:8080/get -H 'Host: api.example.com' -H "Authorization: basic dGVzdDp0ZXN0MTIz" HTTP/1.1 200 OK date: Tue, 02 Jul 2024 21:37:38 GMT content-type: application/json content-length: 384 server: envoy access-control-allow-origin: * access-control-allow-credentials: true x-envoy-upstream-service-time: 75 { "args": {}, "headers": { "Accept": "*/*", "Authorization": "basic dGVzdDp0ZXN0MTIz", "Host": "api.example.com", "User-Agent": "curl/8.5.0", "X-Amzn-Trace-Id": "Root=1-66847321-72876a3e65ef23100fd55f55", "X-Envoy-Expected-Rq-Timeout-Ms": "15000", "X-User-Id": "gloo;test" }, "origin": "3.17.74.2", "url": "http://api.example.com/get" }
See the Gloo Gateway docs for more information on how to create declarative, API-driven authorization, along with a complete list of supported protocols.
Rate Limiting
In this section we’ll configure the Gloo rate limiting components on our gateway VM, then declare and test a global one-request-per-minute strategy on the gateway.
Install Gloo Gateway Rate Limiting Components
The Gloo Gateway rate limiting service is included in the gloo-extensions
package. It can be installed on either the gateway or control VM, but since the gloo-extensions
package is already installed on the gateway VM, we will use it there. As a general rule, you want the rate limiting service to be installed “near” the Envoy proxy gateway, which delegates to the rate-limit service as required to evaluate whether to accept a given request.
This example uses a simple one-per-minute global rate limiting config. You can be much more discriminating in your rate limiting than this, including rate limiting for particular users or classes of service, or even sets of request attributes. Learn more about the flexibility of Gloo rate limiting in the product documentation.
Rate limiting requires a persistence mechanism to track the counters of the various scenarios that are being rate-limited. Redis is the default choice for this storage, although other options are supported by Gloo as well.
In our case, we’ll deploy a simple free Redis instance. You can of course use a more robust enterprise Redis service, too.
Install the Redis service on the gateway VM. In our example, we follow this guide for a basic installation on our Ubuntu EC2 instance.
$ sudo apt update $ sudo apt install redis-server $ sudo systemctl restart redis-server $ sudo systemctl status redis-server ubuntu@ip-172-31-14-244:~$ sudo systemctl status redis-server ● redis-server.service - Advanced key-value store Loaded: loaded (/usr/lib/systemd/system/redis-server.service; enabled; preset: enabled) Active: active (running) since Fri 2024-06-28 21:27:20 UTC; 1min 19s ago Docs: http://redis.io/documentation, man:redis-server(1) Main PID: 6219 (redis-server) Status: "Ready to accept connections" Tasks: 5 (limit: 4676) Memory: 3.3M (peak: 3.8M) CPU: 205ms CGroup: /system.slice/redis-server.service └─6219 "/usr/bin/redis-server 127.0.0.1:6379" Jun 28 21:27:20 ip-172-31-14-244 systemd[1]: Starting redis-server.service - Advanced key-value store... Jun 28 21:27:20 ip-172-31-14-244 systemd[1]: Started redis-server.service - Advanced key-value store.
We will use redis123
as the password to access the keys on the server. Add the following to the bottom of the /etc/redis/redis.conf
file:
user default +@all allkeys on >redis123
Apply this config to the Redis config and restart the service:
sudo echo 'user default +@all allkeys on >redis123' >> /etc/redis/redis.conf
sudo systemctl restart redis-server
Update /etc/gloo/gloo-ratelimiter.env
by setting REDIS_PASSWORD=redis123
.
Your gloo-ratelimiter.env
should now look similar to this:
GLOO_ADDRESS=gloo.gloo-system.svc.cluster.local:9977 REDIS_URL=127.0.0.1:6379 REDIS_DB=0 REDIS_SOCKET_TYPE=tcp REDIS_CLUSTERED_MODE=false REDIS_PASSWORD=redis123 READY_PORT_HTTP=18080 READY_PATH_HTTP=/ready ALIVE_PATH_HTTP=/alive POD_NAMESPACE=gloo-system
Note in particular that the REDIS_PASSWORD
field matches our new global password.
Enable and start the gloo-ratelimiter service on the data plane VM:
sudo systemctl enable gloo-ratelimiter
sudo systemctl restart gloo-ratelimiter
An Upstream
resource is automatically created by Gloo to access the rate-limit service when this default configuration is set ENABLE_GATEWAY_LOCAL_RATE_LIMITER=true
. Confirm that this Upstream
is already in place by using glooapi
on the control plane VM:
glooapi get upstream rate-limit -n gloo-system -o yaml
apiVersion: gloo.solo.io/v1 kind: Upstream ... metadata: name: rate-limit namespace: gloo-system labels: app: gloo gloo: rate-limit spec: healthChecks: - timeout: 5s interval: 10s noTrafficInterval: 10s unhealthyThreshold: 3 healthyThreshold: 3 grpcHealthCheck: serviceName: ratelimit static: hosts: - addr: 127.0.0.1 port: 18081 serviceSpec: grpc: {} status: statuses: gloo-system: reportedBy: gloo state: Accepted
Note that the rate limiter’s Upstream
indicates an Accepted
status.
Configure Rate Limiting
Apply a global rate limit config to limit requests to one per minute:
apiVersion: ratelimit.solo.io/v1alpha1 kind: RateLimitConfig metadata: name: ratelimit-config namespace: gloo-system spec: raw: descriptors: - key: generic_key value: counter rateLimit: requestsPerUnit: 1 unit: MINUTE rateLimits: - actions: - genericKey: descriptorValue: counter
Apply this new rate limit specification on the control plane VM using glooapi
:
glooapi apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/no-kube-deployment/06-rate-limit.yaml
Now we’ll add this to our gateway’s configuration by creating a new RouteOption
object that points at our RateLimitConfig
, and we’ll modify our existing HTTPRoute
to reference it.
apiVersion: gateway.solo.io/v1 kind: RouteOption metadata: name: ratelimit namespace: default spec: options: rateLimitConfigs: refs: - name: ratelimit-config namespace: gloo-system --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: test namespace: default spec: parentRefs: - name: http-gateway hostnames: - "api.example.com" rules: - filters: # Extend our existing route using our new RateLimitConfig - type: ExtensionRef extensionRef: group: gateway.solo.io kind: RouteOption name: ratelimit backendRefs: - name: httpbin port: 80 kind: Upstream group: gloo.solo.io
Apply the new RouteOption
and the modified HTTPRoute
using glooapi
:
glooapi apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/no-kube-deployment/07-rl-route-option-ext.yaml
Confirm requests are being rate limited by issuing the same request to the gateway twice in succession. The first should be processed with no problem. The second should be rejected at the gateway layer with an HTTP 429 Too Many Requests error.
curl -si $GATEWAY_HOST:8080/get -H 'Host: api.example.com'
HTTP/1.1 200 OK date: Tue, 02 Jul 2024 21:43:31 GMT content-type: application/json content-length: 305 server: envoy ... $ curl -si $GATEWAY_HOST:8080/get -H 'Host: api.example.com' HTTP/1.1 429 Too Many Requests x-envoy-ratelimited: true date: Tue, 02 Jul 2024 21:43:33 GMT server: envoy content-length: 0
TLS Termination
Create certs on the control plane VM:
mkdir certs
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=any domain/CN=*' -keyout certs/root.key -out certs/root.crt
openssl req -out certs/gateway.csr -newkey rsa:2048 -nodes -keyout certs/gateway.key -subj "/CN=*/O=any domain"
openssl x509 -req -sha256 -days 365 -CA certs/root.crt -CAkey certs/root.key -set_serial 0 -in certs/gateway.csr -out certs/gateway.crt
glooapi create secret tls gateway-tls --key certs/gateway.key --cert certs/gateway.crt
glooapi label secret gateway-tls gateway=http-gateway
Modify the Gateway
component to add an HTTPS listener on the proxy listening on port 8443.
apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: http-gateway spec: gatewayClassName: gloo-gateway listeners: - protocol: HTTP port: 8080 name: http allowedRoutes: namespaces: from: Same # New Gateway listener to handle HTTPS traffic on port 8443 using certs # created earlier - protocol: HTTPS port: 8443 name: https allowedRoutes: namespaces: from: Same tls: mode: Terminate certificateRefs: - name: gateway-tls kind: Secret
Apply this new Gateway
config on the control plane VM:
glooapi apply -f https://raw.githubusercontent.com/solo-io/gloo-gateway-use-cases/main/no-kube-deployment/08-https-gateway.yaml
Back on the data plane VM, confirm that the HTTPS request succeeds:
curl -ik https://$GATEWAY_HOST:8443/get -H "Host: api.example.com"
HTTP/2 200 date: Tue, 02 Jul 2024 21:45:43 GMT content-type: application/json content-length: 305 server: envoy access-control-allow-origin: * access-control-allow-credentials: true x-envoy-upstream-service-time: 22 { "args": {}, "headers": { "Accept": "*/*", "Host": "api.example.com", "User-Agent": "curl/8.5.0", "X-Amzn-Trace-Id": "Root=1-66847507-5e386fa3116eba785aa2da7f", "X-Envoy-Expected-Rq-Timeout-Ms": "15000" }, "origin": "3.17.74.2", "url": "http://api.example.com/get" }
Since the rate limiting configuration is still in place, you should also be able to re-issue the same request within a minute and see the expect HTTP 429 Too Many Requests error:
curl -ik https://$GATEWAY_HOST:8443/get -H "Host: api.example.com"
HTTP/2 429 x-envoy-ratelimited: true date: Tue, 02 Jul 2024 21:45:46 GMT server: envoy
Cleanup VM Resources
Gateway / Data Plane VM
sudo systemctl stop gloo-gateway
sudo systemctl stop gloo-extauth
sudo systemctl stop gloo-ratelimiter
On Debian-based distros:
sudo dpkg --purge gloo-gateway
sudo dpkg --purge gloo-extensions
sudo rm -rf /var/lib/gloo/ /etc/gloo
On RHEL-based distros:
sudo yum -y remove gloo-gateway
sudo yum -y remove gloo-extensions
sudo rm -rf /var/lib/gloo/ /etc/gloo
Control Plane VM
sudo systemctl stop gloo-apiserver
sudo systemctl stop gloo-controller
On Debian-based distros:
sudo dpkg --purge gloo-control
sudo rm -rf /var/lib/gloo/ /etc/gloo
On RHEL-based distros:
sudo yum -y remove gloo-control
sudo rm -rf /var/lib/gloo/ /etc/gloo
What About High Availability?
We’ve worked through a fairly simple example showing how to deploy an Envoy proxy, the Gloo Gateway control plane, and some related components on VMs with nary a Kube cluster in sight.
Many who are familiar with Kubernetes infrastructure for managing deployments with built-in high availability might be asking: Is HA is possible in a no-Kube scenario like this?
The answer is yes, with some explanation required. Gloo Gateway does not aspire to duplicate Kube infrastructure, so static provisioning of components on VM infrastructure is a management task that the Gloo user would take on.
But in terms of HA of the Gloo infrastructure, Solo provides some important capabilities. For example, we earlier discussed storage of configuration artifacts at runtime. This is managed in Kube deployments using the standard etcd
storage framework. Alas, there is no standard object framework on VMs. Gloo addresses this by supporting file-based storage for simple deployments like ours. But it also supports shared storage platforms like Postgres for environments where HA is a priority.
Recap and Next Steps
If you’ve followed along to this point, congratulations! We’ve deployed the entire Gloo Gateway infrastructure in a No-Kube, VM-only environment. We’ve established TLS termination for all services in our gateway layer. Plus, we’ve configured basic traffic routing and some advanced features like external authorization and rate limiting. And we did this all with a Kubernetes-standard, declarative API.
Gloo Gateway equips its users with a unified control plane and delivers many operational benefits across both Kubernetes and non-Kube platforms:
- Simplified Management: Benefit from a single interface for managing both API gateway and service mesh components, reducing the burden of overseeing multiple systems.
- Consistent Policies: Enforce consistent security, traffic management, observability, and compliance policies across Kube and non-Kube infrastructure, ensuring uniform adherence to best practices.
- Streamlined Operations: Isolate and resolve operational issues quickly by leveraging the centralized monitoring, logging, and troubleshooting capabilities provided by the unified control plane.
- Enhanced Observability: Enjoy heightened visibility into traffic patterns, performance metrics, and error rates across different types of infrastructure.
To experience the Gloo Gateway magic across multiple platforms for yourself, request a live demo here or a trial of the enterprise product here.
If you’d like to explore the more conventional Kube-only deployment approach with the Gateway API, check out this tutorial blog.
Would you like to learn more about the alternative model we mentioned earlier, with the Envoy-based data plane on a VM but the Gloo control plane hidden away in a Kube cluster? If so, check out the second part of this blog series here.
Explore all the configuration used in this exercise is available in GitHub here.
If you have questions or would like to stay closer to Solo’s streams of innovation, subscribe to our YouTube channel or join our Slack community.
Acknowledgements
One of the great things about working with Solo is that there are lot of people smarter than I am to provide advice and reviews. Many thanks to Shashank Ram for doing much of the core engineering work and supporting me throughout this process. And also thank you to Ram Vennam and Christian Posta for their insights that greatly enhanced this blog.