/gg-portal-demo

Demo repo for Gloo Gateway Portal

Primary LanguageShell

gg-portal-demo

Demo repo for Gloo Gateway Portal

Installation

This demo comes with a number of installation scripts that allow you to easily deploy this demo to a Kubernetes cluster.

First, we need to to run the install-gg-dev.sh script. Note that this step requires you to have a valid Glue Gateway license key. This will install the Gloo Gateway onto your Kubernetes cluster. It will also download the meshctl command line interface.

cd install
export GLOO_GATEWAY_LICENSE_KEY={YOUR_GLUE_GATEWAY_LICENSE_KEY}
./install-gg-dev.sh

Next, run the init.sh script to pre-provision your environment with some authentication, rate-limit policies, etc. (see the init.sh file for details). This file needs to be executed from the repo's root directory.

cd ..
./install/init.sh

Access Gloo Mesh dashboard using the meshctl CLI:

./install/.gloo-mesh/bin/meshctl dashboard

.... or, via kubectl

kubectl port-forward -n gloo-mesh svc/gloo-mesh-ui 8090:8090
open http://localhost:8090

Story

Individual AppDev teams are developing and contributing services and their APIs (REST, gRPC, GraphQL, etc) and deploying these services and their APIs into Kubernetes.

These APIs are collected and bundled them into so called "API Products". These "API Products" are the APIs that will be exposed to end-customers and users. APIs can be bundled into API Producs in multiple ways.

  • There can be a 1 on 1 mapping between the API exposed by the service and the API exposed in the API Product.
  • The API product can be a composition of multiple APIs. E.g. a group of microservices that together form an API Product that I want to expose through the Gateway.

The primitive we use to create and API Product is RouteTable. These RouteTables are then exposed to the outside world through a gateway ... the VirtualGateway. We use the concept of delegated RouteTables to expose a single host/domain for the API and use delegated routing to route to individual APIs under that domain.

We can expose the Developer Portal on the same host as our APIs, but this can also be a different host. In this demo, the Developer Portal and APIs are exposed via different hosts:

  • developer.example.com for the Developer Poral
  • api.example.com for the APIs and API Products.

Our PortalResource selects the RouteTables (or API Products) to be added to the Developer Portal by label, which forms the configuration that is exposed through our Developer Portal REST API. The front-end Developer Portal UI communicates with the Developer Portal via its REST API server.

The above creates very flexible model in which we can easily implement concepts like segmented internal and external developer portals, or maybe something like a partner developer portal. In these models, multiple Portals can be deployed and mapped to different VirtualGateways. This allows you to map different VirtualGatways to different cloud-provider load-balancers to segment traffic and access. Another option would be to make both portals accessible via a public endpoint, and to use an OIDC provider to define different security policies for these different portals. There are many different options and combinations.


Architecture

In this demo, we will implement and provision the following architecture.

Alt text

The architecure shows a number of different microservices, APIs, Gateways and other components. A brief explanation:

  • The black boxes at the bottom of the diagram represent microservices (Tracks, Pets, Users and Store) that expose a RESTful API and its microservice implementation
  • The Track RT and Petstore RT represent Gloo RouteTables, and, as stated above, are the pimitives we use to create and expose API Products.
  • The Portal defines the Gloo Portal, being the Developer Portal that hosts our API Product definitions, in which we can apply API policies like security and rate limiting, and where API Keys can be generated to grant access to our APIs.
  • The api.example.com RouteTable routes traffic from the api.example.com domain to our services.
  • The developer.example.com RouteTable routes traffic from the developer.example.com domain to the Developer Portal.
  • The Virtual Gateway exposes our routes to the outside world.

Walkthrough

Tracks API

In a clean Kubernetes environment, we can show that there are no services in the default namespace except for the Kube API service:

kubectl get svc

We can also shwo that there are no apidoc resources in the cluster and that we're starting from an clean state:

kubectl get apidocs -A

We can now deploy our first service and API, the Tracks API. First we create the tracks namespace:

kubectl create ns tracks

Next we can deploy the actual microservice and API:

kubectl apply -f apis/tracks-api.yaml

We can see that the Tracks API consists of a Kubernetes deployment and service by inspecting its YAML file:

cat apis/tracks-api.yaml

The one thing that is unique about this YAML file is the gloo.solo.io/scrape-openapi-source annotation on the Service object, which tells the Gloo Platform from where to get the (Open)API specification of the given service. If developers don't want to annotate their services, that's fine too. The APIDoc resource can simply be added seperately and you don't need to use discovery.

We can inspect the Tracks service's swagger.json specification as follows:

kubectl -n tracks port-forward services/tracks-rest-api 5000:5000
open http://localhost:5000/swagger.json

Here we simply port-forward our local port 5000 to the Tracks api service's port 5000, allowing us to access its http endpoint.

Since we have annotated the Tracks service with our gloo.solo.io/scrape-openapi-source annotation, the service's swagger.json file has been discoverd and an Gloo APIDoc resource has been generated from it:

kubectl -n tracks get apidoc
kubectl -n tracks get apidoc tracks-rest-api-service -o yaml

The APIDoc defines both the API Definition, and the destination that serves that API, based on cluster,namespace, name selectors and a port.

Now that we have a service and API deployed, we can expose the service and API to the outside world. This is done by deploying a RouteTable. The tracks/tracks-api-rt.yaml file contains the definition of the RouteTable that exposes the Tracks service and its API:

cat track/tracks-api-rt.yaml

The portalMetadata fiels is a field in the RouteTable resource that allows us to specify additional metadata about our API. The metedata is returned via the Gloo Portal REST API, and from there, can be exposed in the Developer Portal UI. This metadata can for example be licesing information, title, description, service owner, data classification, etc.

The additional metadata will be inlined with the service's Swagger API definition.

Apart from the portalMetadata field, this is a standard RouteTable resource.

Note that routes can have labels. This mechanism is used to wire in various policies. For example, the tracks-api route has the label usagePlans: dev-portal. This usagePlan is defined in the following ExtAuthPolicy and defines:

cat policy/auth-policy.yaml

Observe that the applyToRoutes field of this resources states that the routes to which this policy should be applied are the routes that have the usagePlans: dev-portal label

Let's apply the RouteTable resource:

kubectl apply -f tracks/tracks-api-rt.yaml

We can now see the API in the Gloo Platform UI at http://localhost:8090/apis

open http://localhost:8090/apis/

NOTE: the API is only show in the Gloo Platform UI when it is exposed via a RouteTable.

Click on the API to view the details.

The final thing that we need to do to expose our API to the outside world is to connect it to the top-level domain. This can be done with labels and selectors, but in this demo we use a static configuration.

The api-example-com-rt.yaml file defines the RouteTable resource that expose the APIs on api.example.com host via the istio-ingressgateway.

cat api-example-com-rt.yaml

In the api-example-rt.yaml file, uncomment the /trackapi matcher and its delegate configuration. After you've saved these changes, apply the RouteTable resource:

kubectl apply -f api-example-com-rt.yaml

We should now be able to cURL that endpoint. Note that you might need to edit your /etc/hosts file to map the api.example.com domain to the ip-address of your Kubernetes cluster's ingress.

curl -v api.example.com/trackapi/tracks

Security

NOTE: For demo purposes, no security has been enabled on this endpoint/service yet. The API will be secured after we apply the ExtAuthPolicy:

kubectl apply -f policy/auth-policy.yaml

When we runt the cURL command again, 401 Unauthorized is returned"

curl -v api.example.com/trackapi/tracks
*   Trying 127.0.0.1:80...
* Connected to api.example.com (127.0.0.1) port 80 (#0)
> GET /trackapi/tracks HTTP/1.1
> Host: api.example.com
> User-Agent: curl/7.86.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 401 Unauthorized
< www-authenticate: API key is missing or invalid
< date: Wed, 29 Mar 2023 15:13:04 GMT
< server: istio-envoy
< content-length: 0
<

When we run the command with the correct API-key, we get correctly authenticated and authorized to access the service (Note the the API-Key can be found in policy/api-key.yaml):

curl -v -H "api-key:N2YwMDIxZTEtNGUzNS1jNzgzLTRkYjAtYjE2YzRkZGVmNjcy" api.example.com/trackapi/tracks

Rate Limiting

Another common API Management feature is "rate limiting". Gloo implements rate-limiting via so called Usage Plans, which specify the tier of access given to clients. To apply a rate limiting policy to our Tracks API, we apply the policy/rl-policy.yaml file. This policy uses labels to apply the policy to routes. In this demo, all routes with the label usagePlans: dev-portal will get the policy applies. This includes our Tracks API route.

kubectl apply -f policy/rl-policy.yaml

Note that our API Key has a the silver usage plan configured, as can be seen in the policy/api-key.yaml file:

cat policy/api-key.yaml

The silver usage plan is configured to allow 3 requests per minute, as can be seen in the rate limiting configuration file policy/rl-config.yaml:

cat policy/rl-config.yaml

When we now try to access the Tracks API multiple times per minute, we will see that we will get rate limited at the 3rd attempt, and get a 429 Too Many Requests error returned:

curl -v -H "api-key:N2YwMDIxZTEtNGUzNS1jNzgzLTRkYjAtYjE2YzRkZGVmNjcy" api.example.com/trackapi/tracks
*   Trying 127.0.0.1:80...
* Connected to api.example.com (127.0.0.1) port 80 (#0)
> GET /trackapi/tracks HTTP/1.1
> Host: api.example.com
> User-Agent: curl/7.86.0
> Accept: */*
> api-key:N2YwMDIxZTEtNGUzNS1jNzgzLTRkYjAtYjE2YzRkZGVmNjcy
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 429 Too Many Requests
< x-envoy-ratelimited: true
< date: Wed, 29 Mar 2023 14:53:39 GMT
< server: istio-envoy
< content-length: 0
<

Dev Portal

We have deployed our API's RouteTable, but we haven't defined the DevPortal yet. We can deploy the basic Portal definition via the dev-portal.yaml file. It uses a label to define which RouteTables are exposed via the DevPortal. In this case, the label is portal: dev-portal. This label can for example be found on the tracks-api-rt.yaml. In other words: the Portal does not statically define which APIs get exposed, but performs a dynamic selection using labels. Let's deploy the DevPortal:

kubectl apply -f dev-portal.yaml

We can inspect the status of the Portal resource using the following commands:

kubectl get Portal -A
kubectl -n gloo-mesh-addons get Portal developer-portal -o yaml

The Portal automatically creates the PortalConfig resources from the existing APIDoc resources. In our case this is the tracks-api doc:

kubectl get PortalConfig -A
kubectl -n gloo-mesh-addons get PortalConfig developer-portal-gloo-mesh-addons-gg-demo-single -o yaml

Note that the APIDoc that is referenced by the PortalConfig is a stitched API:

kubectl -n gloo-mesh-addons get apidoc tracks-rt-stitched-openapi-gg-demo-single-gloo-mesh-gateways-gg-demo-single -o yaml

This schema is stitched from all the APIDoc resources that are exposed via a given RouteTable. In the case of the Tracks API, this is only a single APIDoc.

We can now cURL the REST API definition from the developer portal:

curl -v developer.example.com/v1/apis/tracks-rt-gloo-mesh-gateways-gg-demo-single/schema

A list of all APIs exposed via this developer portal can be fetched with the following cURL command:

curl -v developer.example.com/v1/apis

TODO: Provide instructions how to run the developer portal UI application. See: https://github.com/solo-io/dev-portal-starter/ . Use the live-api branch instead of main for now.

With the Developer Portal UI running, we can access it on http://localhost:4000

open http://localhost:4000

In the Developer Portal UI we can view all the APIs that have been exposed via our Developer Portal and too which we have access (authentication and authorization flows to be added later). Click on the Tracks API to get the OpenAPI doc from which you can inspect all the RESTful resources and the operations that the API exposes.

NOTE: A login flow has not been implemented yet in this Developer Portal UI Starter application. Once authentication has been implemented, users have an identity and then can look the usage plans for the APIs and generate API keys.

Pet Store API

We can now deploy an additional API and its microservice to our environment. Like with the Tracks API, this Pets API YAML definition deploys a Kuberneres deployment and service.

kubectl apply -f apis/pets-api.yaml

Like with the Tracks API, we can fetch the OpenAPI Swagger definitiion by port-forwarding to the Pets API service and opening the swagger.json URL:

kubectl -n default port-forward services/pets-rest-api 5000:5000
open http://localhost:5000/swagger.json

The Gloo Portal discovery mechanism has discovered the swagger.json file via the annotation in the Pets API Service definition. and a newly generated APIDoc document has been added to the environment:

kubectl get apidocs -A

We now have 3 APIDocs, one for the Tracks api, one for the Pets api and one stitched api. Every API product (defined by a RouteTable resource) gets a stitched API. This becomes important when you have multiple APIs in a single API product.

NOTE: there is no stitched APIDoc for the Pets API yet, because we're not yet exposing the service via a RouteTable, and hence, it's not part of an API Product yet.

With the Pets API and service deployed, we can now add the required RouteTable to the platform, which defines the Petstore API Product:

kubectl apply -f petstore/petstore-rt.yaml

To make the Petstore API Product accessible to the outside world, we also need to connect it to the top-level domain. Open the api-example-com-rt.yaml file again and uncomment the /petstore matcher and its delegate configuration. After you've saved these changes, re-apply the RouteTable resource:

kubectl apply -f api-example-com-rt.yaml

Go back to the Dev Portal UI at http://localhost:4000 and observe that the Petstore API has been dynamically added to the Dev Portal.

We can now deploy 2 additional APIs, the User API and the Store API. Like with the other APIs, these are implemented as individual microservices. We will add these APIs to our Petstore API Product. Firs we need to deploy the APIs and services:

kubectl apply -f apis/users-api.yaml
kubectl apply -f apis/store-api.yaml

We can now add these 2 services to the petstore-rt.yaml RouteTable definition, which defines our Petstore API Product. Open the petstore-rt.yaml file and uncomment the user-api and store-api routes. Save the file and re-apply it:

kubectl apply - f petstore/petstore-rt.yaml

Go back to the Dev Portal UI at http://localhost:4000 and notice that our Petstore API Product now contains 2 additional RESTful Resources, /user and /store.