Expose Your Application on a Shared Gateway
In this guide, we'll expose an app using a shared gateway. This means that all apps and teams will share one physical set of Envoy gateways (probably deployed by the platform team), rather than each having their own which they manage themselves.
To use this article, first
- Install Tetrate Enterprise Gateway for Envoy (TEG)
- Familiarize yourself with the dedicated vs shared gateway architectures
- Read and understand the dedicated gateway deployment
For the purposes of this article it's assumed that your cluster follows the standard multi-tenancy pattern: each team of people has their own namespace(s). At first glance, a shared gateway should be no different to dedicated gateways -- each team deploys an HTTPRoute for their app(s) and attaches them to the single, shared Gateway resource. Indeed to a first approximation this is true, but the Gateway API (the spec that defines types like Gateway and HTTPRoute) was designed with multi-tenancy security in mind, and enforces a permissions system. This article will use the following representative cluster layout:
- namespace
ingress
: contains the single, shared Gateway resource, which results in a single, shared Deployment of Envoys. In a production cluster this would be managed by the platform team, but in this guide we'll deploy everything ourselves. - namespace
team1
: the namespace for engineering team 1, who wish to offer a service. In a production cluster this is likely all the people on team 1 will have k8s RBAC permissions to, but again in this guide we will omnipotently deploy everything. - namespace
team2
: engineering team 2 are also deploying some apps.
Let's get set up by making those namespaces for this how-to:
kubectl create namespace ingress
kubectl create namespace team1
kubectl label namespace team1 purpose=workloads
kubectl create namespace team2
kubectl label namespace team2 purpose=workloads
Since everything will be in different namespaces, we will have to add fields and resources to our normal objects, in order to explicitly allow such cross-namespace configuration, as you'll see in this guide.
Deploy Shared Gateway Instance
First, we deploy a Gateway resource, which will result in a set of running Envoy proxies to handle incoming requests.
shared-gateway.yamlapiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: shared-gateway
namespace: ingress
spec:
gatewayClassName: teg
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
purpose: workloadsNotice this is the same as the Gateway from the previous article, but with the addition of the
allowedRoutes
section. This allows HTTPRoutes in different namespaces, specifically any with the labelpurpose=workloads
to attach themselves to this Gateway; without this they would be denied.Route Traffic to Team 2
By way of a slightly contrived example, imagine that team 1 wants to offer a service, but that by luck they don't need to write any code, because team 2 has already deployed some Pods doing exactly what they want. So, we deploy the HTTPRoute below. This lives in team 1's namespace (as it's them offering the service at this location), but attaches to the previously-deployed Gateway
ingress
in a different namespace:ingress
. Any traffic matched by this route gets sent out-of-namespace, to Service httpbin in namespaceteam2
.team1-httpbin-httproute.yamlapiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
namespace: team1
name: httpbin
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
namespace: ingress
name: ingress
rules:
- matches:
- path:
type: PathPrefix
value: /httpbin
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- group: ""
kind: Service
namespace: team2
name: httpbin
port: 8000Accept Traffic from Team 1
To complete our test setup, let's quickly deploy a copy of httpbin to team 2's namespace.
kubectl apply -n team2 -f https://raw.githubusercontent.com/istio/istio/master/samples/httpbin/httpbin.yaml
This includes a Service also called
httpbin
, and we already have the HTTPRoute pointing at it. So the only thing that remains is to give that HTTPRoute permission to send traffic over here. This is done with a ReferenceGrant resource, which lives in the target namespace, with the Service which is allowing access to itself. The ReferenceGrant below allows access to any Service in its namespace, from any HTTPRoute in team 1's namespace.team2-referencegrant.yamlapiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
namespace: team2
name: httpbin-route-service
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: team1
to:
- group: ""
kind: ServiceTest Connectivity
See the instructions in the dedicated gateway article for how to call httpbin.
noteIn this case, the shared gateway's Service has the following labels:
kubectl get service -n envoy-gateway-system -l gateway.envoyproxy.io/owning-gateway-namespace=ingress,gateway.envoyproxy.io/owning-gateway-name=shared-gateway