The Ultra Performant API Gateway

Abdullah Fathi

Introduction

In today’s digital landscape, managing microservices highlights the need for efficient API Gateways. They facilitate communication between client applications and backend services, enhancing performance, security, and microservice orchestration.

What is                       ?

  • Extensible, declarative, high performance open-source API Gateway

  • Written in Go

  • Stateless Architecture

  • Needs no programming as it offers a declarative way to create the endpoints

  • KrakenD core engine has joined The Linux Foundation on 2021 with a codename Lura Project

Effortlessly adopt microservices

1.

 

Move from dealing with every particular microservice to talking to a single entry point that handles the interaction with all your microservices

2.

Secure by Default

 

Authorization, protection against scrapping, security best practices and abuse prevention implementation out of the box

3.

Scale Out Without Single Points of Failure

 

Stateless design provides true linear scalability. KrakenD does not require node coordinator or data centralization

KrakenD is more than a typical proxy that forwards clients to backend services, but a powerful engine that can transform, aggregate or remove data from your own or third party services

Performance

KrakenD supports more than 70K req/s on commodity software. They're the fastest and more performant solution in the market.

Low Operational Cost

Higher performance results in lower requirements in terms of resources, allowing you to save costs in your infrastructure

Stateless Design

The stateless design is a competitive advantage over other solutions. It provides a true linear scalability and no need of node coordination

True Linear Scalability

Every KrakenD node can operate independently without any coordination or centralized persistence of configuration needed

What makes KrakenD different from others?

Simple to Operate
and Run

Support from Creators

Rich Open-Source
Ecosystem

No Lock-In

Only need KrakenD binary and a JSON config file to get KrakenD up & running

Rather than "trained" or "sales engineers. you'll receive support and advice from the engineers that coded the software

Able to integrate with standard and proved open-source solutions for every specific task

Integrate with existing solutions in the technical stack, and easily replace them when needed. Compatible to both Community or Enterprise Edition

What makes KrakenD different from others?

What else?

  • A Feature-Rich Gateway: Supports various protocols and APIs with access controls, data transformation, integrations, and caching.
  • Customizable to Your Needs: Add features via plugin system, tailoring KrakenD to your requirements.
  • Innovative Content Aggregation: A pioneer in the Backend For Frontend pattern, KrakenD aggregates multiple APIs in one call, handling different encodings.
  • Uncompromised Security: Compliant with OWASP best practices, KrakenD offers top security without storing data, ensuring regulatory compliance.
  • Transparent, Predictable Pricing: The pricing model includes unlimited requests and services, with a free open-source version and an Enterprise Edition for premium features and support.

KrakenD Features

1) Installation

Running KrakenD (Dev)

Docker

docker run -p "8080:8080" -v $PWD:/etc/krakend/ devopsfaith/krakend:2.7.0 run -c krakend.json

# Healthcheck
http://localhost:8080/__health
{
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "version": 3
}

krakend.json

CLI

Running KrakenD (Dev)

Docker Compose

version: "3"
services:
  krakend:
    image: devopsfaith/krakend:watch
    volumes:
      - ".:/etc/krakend"
    ports:
      - "8080:8080"
    command: [ "run", "-dc", "krakend.json" ]
{
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "version": 3
}

krakend.json

docker-compose.yaml

Running KrakenD (Prod)

Docker Artifact

FROM devopsfaith/krakend:2.7 as builder
ARG ENV=prod

COPY krakend.tmpl .
COPY config .

## Save temporary file to /tmp to avoid permission errors
RUN FC_ENABLE=1 \
    FC_OUT=/tmp/krakend.json \
    FC_PARTIALS="/etc/krakend/partials" \
    FC_SETTINGS="/etc/krakend/settings/$ENV" \
    FC_TEMPLATES="/etc/krakend/templates" \
    krakend check -d -t -c krakend.tmpl --lint

FROM devopsfaith/krakend:2.7
# Keep operating system updated with security fixes between releases
RUN apk upgrade --no-cache --no-interactive

COPY --from=builder --chown=krakend:nogroup /tmp/krakend.json .
# Uncomment with Enterprise image:
# COPY LICENSE /etc/krakend/LICENSE

Dockerfile

docker build --build-arg ENV=prod -t mykrakend .

CLI

Running KrakenD (Prod)

Kubernetes

apiVersion: apps/v1
kind: Deployment
metadata:
  name: krakend-deployment
spec:
  selector:
    matchLabels:
      app: krakend
  replicas: 2
  template:
    metadata:
      labels:
        app: krakend
    spec:
      containers:
      - name: krakend
        image: YOUR-KRAKEND-IMAGE:1.0.0
        ports:
        - containerPort: 8080
        imagePullPolicy: Never
        command: [ "/usr/bin/krakend" ]
        args: [ "run", "-d", "-c", "/etc/krakend/krakend.json", "-p", "8080" ]
        securityContext:
          allowPrivilegeEscalation: false
          runAsNonRoot: true
          runAsUser: 1000
          readOnlyRootFilesystem: true
          capabilities:
            drop:
              - ALL
            add:
              - NET_BIND_SERVICE
        env:
        - name: KRAKEND_PORT
          value: "8080"

deployment.yaml

Running KrakenD (Prod)

Kubernetes

apiVersion: v1
kind: Service
metadata:
  name: krakend-service
spec:
  type: NodePort
  ports:
  - name: http
    port: 8000
    targetPort: 8080
    protocol: TCP
  selector:
    app: krakend

service.yaml

2) Configuration

Config File Structure

Flexible Configuration (Intro)

Benefit of Flexible Configuration

Flexible Configuration

Flexible Configuration

Flexible Configuration (File Structure)

.
├── krakend.tmpl
└── config
    ├── partials
    │   └── file.txt
    ├── templates
    │   ├── telemetry.tmpl
    │   └── endpoints.tmpl
    └── settings
        ├── prod
        |   └── urls.json
        └── dev
            └── urls.json

Template Introduction

  • Delimited by {{ }}
  • Multiple functions and options
{{/*
This
is
multiline
comment
*/}}

I had {{ .num_coffees}} coffees today.

{{ if gt .num_coffees 3 }}
This was too much
{{end}}


{{ if and (gt .num_coffees 3) (lt .num_coffees 10) }}
This was too much
{{end}}

Template Overview

KrakenD will set variable for your template automatically depending on where the template is used

Debug and print context as JSON structure using toJson

Create variable (using $)

Template Overview

Conditionals

Loops Array

Template Overview

  1. Enable it

  2. Define the location of input files

Working with Flexible Configuration

FC_ENABLE: Switch on the flexible configuration

FC_OUT: Writed the result

FC_TEMPLATES: Directory with the sub templates you will call from the master template

FC_SETTINGS: Directory where you store environment configuration and other settings

FC_PARTIALS: Directory where you store snippets of text that you can inject into templates without being evaluated

Flexible Configuration (Env Var)

{
	"$schema":"https://www.krakend.io/schema/krakend.json",
    "version": 3,
    "name": "A simple config"

}

Run Flexible Configuration (docker)

docker run --rm -it -v "$PWD:/etc/krakend" -p "8080:8080" -e "FC_ENABLE=1" -e "FC_OUT=out.json" devopsfaith/krakend:watch run -c krakend.tmpl
# Give a write permission to output file
sudo chown $USER out.json

krakend.json

cli

{
	"$schema":"https://www.krakend.io/schema/krakend.json",
    "version": 3,
    "name": "A simple config"

}

Run Flexible Configuration (docker compose)

version: "3"
services:
  krakend:
    image: devopsfaith/krakend
    volumes:
      - .:/etc/krakend
    ports:
      - "8080:8080"
    environment:
      - FC_ENABLE=1
      - FC_SETTINGS=config/settings/prod
      - FC_PARTIALS=config/partials
      - FC_TEMPLATES=config/templates
      - FC_OUT=out.json
      - SERVICE_NAME=KrakenD API Gateway
    command: [ "run", "-dc", "krakend.tmpl", "" ]

krakend.json

docker-compose.yaml

3) Routing and Forwarding

Declare endpoints and backends

{
	"$schema":"https://www.krakend.io/schema/v3.json",
    "version": 3,
    "host": ["http://api.github.com"],
    "endpoints": [
    	{
        	"endpoint": "/users/{id}",
        	"backend": [
            	{
                  "url_pattern": "/users/{id}",
                  "group": "fathi"
                },
                {
                  "url_pattern": "/users/hafizan",
                  "group": "hafizan"
                }
            ]
        }
    ]
}

Proxy-like features (no-op)

KrakenD is NOT Reverse Proxy

KrakenD is not designed as a reverse proxy but as an API Gateway that acts as a decoupling layer between the client and microservices

Proxy-like features (no-op)

No-op Encoding

Allows response from the backend to be returned directly to the client without any modification or parsing by the gateway. It behaves like a regular proxy, preserving the original status codes and headers

Proxy-like features (no-op)

No-op Limitation

  • Limited to single backend per endpoint
  • Does not support merging responses from multiple backends
  • Couples the client directly to the backend and max expose more information than necessary

Usage Scenarios

Useful in cases where direct interaction between the client and backend is required, such as setting cookies or performing write operations like file uploads

Proxy-like features (no-op)

{
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "version": 3,
    "host": [ "https://api.github.com" ],
    "endpoints":[
        {
            "endpoint": "/default",
            "@comment": "The body is parsed by KrakenD and headers are the ones from KrakenD",
            "backend": [{
                "url_pattern": "/status"
            }]
        },
        {
            "endpoint": "/no-op",
            "output_encoding": "no-op",
            "@comment": "Content and headers are the ones provided by the backend directly",
            "backend": [{
                "encoding": "no-op",
                "url_pattern": "/status"
            }]
        }
    ]
}

Sequential Proxy (chain reqs.)

Sequential Proxy (chain reqs.)

{
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "version": 3,
    "port": 8080,
    "host": ["http://backend:8080"],
    "endpoints": [{
        "endpoint": "/hotel/{id}",
        "extra_config": {
          "proxy": {
            "sequential": true
          }
        },
        "backend": [{
            "@comment": "The first call fields have its response under resp0_xxx",
            "url_pattern": "/hotels/{id}.json"
          },
          {
            "@comment": "Second call fields have its response under resp1_xxx, we could add more chained backends",
            "url_pattern": "/calendar/{resp0_calendar_id}.json"
          }
        ]
      },
      {
        "endpoint": "/hotel/failing/{id}",
        "extra_config": {
          "proxy": {
            "sequential": true
          }
        },
        "backend": [{
            "@comment": "The first call fields have its response under resp0_xxx",
            "url_pattern": "/hotels/{id}.json"
          },
          {
            "@comment": "The requested attribute does not exist, so we can't fetch the data",
            "url_pattern": "/calendar/{resp0_unexisting_field}.json"
          }
        ]
      }
    ]
  }

Sequential Proxy

4) Request and Response Manipulation

1) Encoding

Encoding is referring to data format you will find in your upstream responses and the format you want to deliver to API consumers

1) Encoding

{
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "version": 3,
    "host": [
        "http://107.155.65.148:5000"
    ],
    "endpoints": [
        {
            "endpoint": "/xml",
            "output_encoding": "xml",
            "@comment": "Backend is json but client receives XML",
            "backend": [
                {
                    "url_pattern": "/index.php",
                    "encoding": "json"
                }
            ]
        },
        {
            "endpoint": "/collection",
            "output_encoding": "json",
            "@comment": "The backend is a JSON collection [], not an object {}, and by default encapsulated in a 'collection' object",
            "backend": [
                {
                    "is_collection": true,
                    "url_pattern": "/index.php?collection=true",
                    "encoding": "json"
                }
            ]
        },
        {
            "endpoint": "/safe-collection",
            "output_encoding": "json",
            "@comment": "KrakenD looks if the 'is_collection' is needed or not.",
            "backend": [
                {
                    "url_pattern": "/index.php?collection=true",
                    "encoding": "safejson",
                    "mapping": {
                        "collection": "SOMETHING_ELSE"
                    }
                }
            ]
        },
        {
            "endpoint": "/collection-as-it-was",
            "output_encoding": "json-collection",
            "@comment": "The backend is a JSON collection [], not an object {}, and is returned as such",
            "backend": [
                {
                    "is_collection": true,
                    "url_pattern": "/index.php?collection=true",
                    "encoding": "json"
                }
            ]
        },
        {
            "endpoint": "/negotiate",
            "output_encoding": "negotiate",
            "backend": [
                {
                    "url_pattern": "/index.php",
                    "encoding": "json"
                }
            ]
        },
        {
            "endpoint": "/string",
            "output_encoding": "string",
            "backend": [
                {
                    "encoding": "string",
                    "host": [
                        "https://www.google.com"
                    ],
                    "url_pattern": "/"
                }
            ]
        },
        {
            "endpoint": "/string-in-object",
            "output_encoding": "json",
            "backend": [
                {
                    "encoding": "string",
                    "host": [
                        "https://www.google.com"
                    ],
                    "url_pattern": "/"
                }
            ]
        }
    ]
}

2) Status Codes, Headers and Errors

The client get all the status code, the headers and the error body

KrakenD return a 500 status code, no body and none of the response header

2) Status Codes, Headers and Errors

KrakenD do this by default because client must completely decoupled from their underlying services. When API is protected by API Gateway, we should not return back an implementation detail. In the same way we wouldn't return an implementation detail to the client from the application.

2) Status Codes, Headers and Errors

Status codes returned before contacting backends

404

  Requested endpoint does not exist

400

  Bad Request. i.e: failed JSON schema validation of the body 

401

  Unauthorized: Invalid JWT Token

403

  Forbidden access

429

  Too many requests. Reach the rate limit for the endpoint

503

  Service Unavailable. Reached global rate limit for the endpoint

2) Status Codes, Headers and Errors

Status codes returned to clients after reaching the backends

2) Status Codes, Headers and Errors

What happen with redirect status code such as 301 and 302?

2) Status Codes, Headers and Errors

Strategies

2) Status Codes, Headers and Errors

{
    "version": 3,
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "debug_endpoint": true,
    "endpoints": [
        {
            "endpoint": "/ok",
            "backend": [
                {
                    "host": ["https://api.github.com"],
                    "url_pattern": "/status"
                }
            ]
        },
        {
            "endpoint": "/epic-fail",
            "backend": [
                {
                    "@comment": "Request to a URL that will produce a 404",
                    "host": ["http://google.com"],
                    "url_pattern": "/this-url-likely-does-not-exist"
                }
            ]
        },
        {
            "endpoint": "/mixed",
            "backend": [
                {
                    "host": ["https://api.github.com"],
                    "url_pattern": "/status"
                },
                {
                    "@comment": "Request to a URL that will produce a 404",
                    "host": ["http://google.com"],
                    "url_pattern": "/this-url-likely-does-not-exist"
                }
            ]
        }
    ]
}

2) Status Codes, Headers and Errors

Interpretation of error

{
    "version": 3,
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "@comment": "Preserve the status code of a single backend",
    "extra_config": {
        "router": {
            "return_error_msg": true
        }
    },
    "endpoints": [
        {
            "endpoint": "/interpretation-bad-status",
            "backend": [
                {
                    "@comment": "Request to a URL that will produce a 404, which is not a returnable status code",
                    "host": ["http://google.com"],
                    "url_pattern": "/this-url-likely-does-not-exist"
                }
            ]
        },
        {
            "endpoint": "/interpretation-bad-encoding",
            "backend": [
                {
                    "@comment": "Request a valid URL, but with the wrong encoding parsing",
                    "host": ["http://google.com"],
                    "url_pattern": "/"
                }
            ]
        }
    ]
}

Show the interpretation of the error but not the error of the backend

2) Status Codes, Headers and Errors

{
    "version": 3,
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "@comment": "Preserve the status code of a single backend",
    "endpoints": [
        {
            "endpoint": "/details",
            "backend": [
                {
                    "extra_config": {
                        "backend/http": {
                            "return_error_details": "error_from_google"
                        }
                    },
                    "@comment": "Request to a URL that will produce a 404",
                    "host": ["http://google.com"],
                    "url_pattern": "/this-url-likely-does-not-exist"
                }
            ]
        }
    ]
}

Return the error in a key

Revealing error details to the client. Using return_error_details, all status codes returned to the client are 200. The client must parse the response for the presence of the error_backend_alias or any other key we have set to determine if there’s a problem

2) Status Codes, Headers and Errors

{
    "version": 3,
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "@comment": "Preserve the status code of a single backend",
    "endpoints": [
        {
            "endpoint": "/preserve-404",
            "backend": [
                {
                    "extra_config": {
                        "backend/http": {
                            "return_error_code": true
                        }
                    },
                    "@comment": "Request to a URL that will produce a 404",
                    "host": ["http://google.com"],
                    "url_pattern": "/this-url-likely-does-not-exist"
                }
            ]
        }
    ]
}

Preserve the status code of a single backend

When we have one backend only and use an encoding different than no-op

3) Parameter Forwarding

Deny by default

Zero-trust security

KrakenD is an API Gateway with a zero-trust security policy, and when it comes to forward query strings, cookies, and headers, we need to define what is allowed.

Part of the zero-trust policy implies that KrakenD does not forward any unexpected query string, headers, or cookies

3) Parameter Forwarding

Fine tuned parameter forwarding

input_query_strings

  Query strings reaching backends

input_headers

  Headers reaching backends

case-sensitive

case-insensitive

3) Parameter Forwarding

The received headers are always normalized. The normalization consist of converting any header to its canonical MIME format

3) Parameter Forwarding

3) Parameter Forwarding

{
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "version": 3,
    "echo_endpoint": true,
    "@comment": "All examples use as backend KrakenD itself",
    "host": [ "http://localhost:8080" ],
    "endpoints":[
        {
            "endpoint": "/default",
            "@comment": "The backend does not receive any header, query string or cookie from the client",
            "backend": [{
                "url_pattern": "/__echo/"
            }]
        },
        {
            "endpoint": "/known-params",
            "input_headers": ["Accept"],
            "input_query_strings": ["q","a[]" ],
            "@comment": "The backend receives a controlled set of parameters, but discards unknown sets",
            "backend": [{
                "url_pattern": "/__echo/"
            }]
        },
        {
            "endpoint": "/all",
            "input_headers": ["*"],
            "input_query_strings": ["*" ],
            "@comment": "The backend receives all parameters from client. NOT RECOMMENDED.",
            "backend": [{
                "url_pattern": "/__echo/"
            }]
        },
        {
            "endpoint": "/mandatory/{id}",
            "input_query_strings": ["optional" ],
            "@comment": "The backend receives always the mandatory {id} and the optional one only when sent.",
            "backend": [{
                "url_pattern": "/__echo/?id={id}"
            }]
        }
    ]
}

4) Basic Manipulation

Automatically merges all responses from all backends in a final response

Aggregation

Wrap response of a backend inside a new object

Grouping

Remove hierarchy by extracting subpath

Targetting

Rename attribute in JSON response

Mapping

4) Basic Manipulation

Rename attribute in JSON response

Mapping

Filter response data in JSON response. 

Filtering

Rename attribute in JSON response

Mapping

Allows us to extract, move, append and delete properties

Flatmap

4) Basic Manipulation

{
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "version": 3,
    "echo_endpoint": true,
    "debug_endpoint": true,
    "@comment": "All examples use as backend KrakenD itself",
    "host": [
        "http://107.155.65.148:5002"
    ],
    "endpoints": [
        {
            "endpoint": "/aggregation",
            "backend": [
                {
                    "url_pattern": "/users/alice.json"
                },
                {
                    "url_pattern": "/users/bob.json"
                }
            ]
        },
        {
            "endpoint": "/group",
            "backend": [
                {
                    "url_pattern": "/users/alice.json",
                    "group": "alice"
                },
                {
                    "url_pattern": "/users/bob.json",
                    "group": "bob"
                }
            ]
        },
        {
            "endpoint": "/target",
            "backend": [
                {
                    "url_pattern": "/users/alice.json",
                    "target": "data.user"
                }
            ]
        },
        {
            "endpoint": "/mapping",
            "backend": [
                {
                    "url_pattern": "/users/bob.json",
                    "mapping": {
                        "data": "my_response",
                        "status": "result"
                    }
                }
            ]
        },
        {
            "endpoint": "/deny",
            "backend": [
                {
                    "url_pattern": "/users/bob.json",
                    "deny": [
                        "data"
                    ]
                }
            ]
        },
        {
            "endpoint": "/allow",
            "backend": [
                {
                    "url_pattern": "/users/bob.json",
                    "allow": [
                        "data.user.name",
                        "data.user.city"
                    ]
                }
            ]
        },
        {
            "endpoint": "/flatmap/object",
            "backend": [
                {
                    "url_pattern": "/users/alice.json",
                    "extra_config": {
                        "proxy": {
                            "flatmap_filter": [
                                { "type": "move", "args": ["data.user","details"] },
                                { "type": "move", "args": ["details.name","name"] },
                                { "type": "del","args": ["status", "data"] }
                            ]
                        }
                    }
                }
            ]
        },
        {
            "endpoint": "/flatmap/collection",
            "backend": [
                {
                    "url_pattern": "/numbers.json",
                    "extra_config": {
                        "proxy": {
                            "flatmap_filter": [
                                {
                                    "type": "del",
                                    "args": ["numerals.0"]
                                },
                                {
                                    "type": "move",
                                    "args": ["numerals.*.numeral", "numerals.*.number"]
                                },
                                {
                                    "type": "move",
                                    "args": ["numerals.*.english", "numerals.*.name"]
                                },
                                {
                                    "type": "del",
                                    "args": ["numerals.*.spanish"]
                                },
                                {
                                    "type": "move",
                                    "args": ["numerals", "numbers"]
                                }
                            ]
                        }
                    }
                }
            ]
        }

    ]
}

5) Martian: Static request & response manipulation

Inject static content in the request & responses of a backend  through a simple DSL definition in config file

5) Martian: Static request & response manipulation

We can inject data in requests and responses using the Martian component as long as it’s static data, hardcoded in the configuration. It does not allow us to place {variables} inside the modifiers

When to use Martian?

Use Martian whenever we need to alter the request or response based on criteria with static values.

  • Set a new cookie during gateway processing
  • Flag requests with query strings or headers when specific criteria is met
  • Add, remove, or change specific headers
  • Do basic authentication between KrakenD and the backend
  • Add query strings before making the backend request (e.g., set an API key)

5) Martian: Static request & response manipulation

Change the state of a request or a response. For instance, you want to add a custom header or a query string in the request before sending it to a backend

Modifiers

Check for specific conditions and execute one modifier or another

Filters

Execute more than one modifier in single request or response

Groups

5) Martian: Static request & response manipulation

{
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "version": 3,
    "echo_endpoint": true,
    "debug_endpoint": true,
    "@comment": "All examples use as backend KrakenD itself",
    "host": [
        "http://localhost:8080"
    ],
    "endpoints": [
        {
            "endpoint": "/header.Modifier",
            "output_encoding": "no-op",
            "backend": [
              {
                "host": ["http://localhost:8080"],
                "url_pattern": "/__echo/header.Modifier",
                "extra_config": {
                  "modifier/martian": {
                    "header.Modifier": {
                      "scope": ["request","response"],
                      "name": "User-Agent",
                      "value": "Late-Night-Commander v2.3"
                    }
                  }
                }
              }
            ]
          },
          {
            "endpoint": "/body.Modifier",
            "backend": [
              {
                "url_pattern": "/__debug/body.Modifier",
                "extra_config": {
                  "modifier/martian": {
                    "body.Modifier": {
                      "scope": [
                        "request",
                        "response"
                      ],
                      "@comment": "Send a {\"msg\":\"you rock!\"}",
                      "body": "eyJtc2ciOiJ5b3Ugcm9jayEifQ=="
                    }
                  }
                }
              }
            ]
          },
          {
            "endpoint": "/header.RegexFilter",
            "input_headers": [
              "X-App-Version"
            ],
            "backend": [
              {
                "url_pattern": "/__echo/header.RegexFilter",
                "allow": ["req_uri"],
                "extra_config": {
                  "modifier/martian": {
                    "header.RegexFilter": {
                      "scope": [
                        "request"
                      ],
                      "header": "X-App-Version",
                      "regex": ".*-(alpha|beta|preview)$",
                      "modifier": {
                        "querystring.Modifier": {
                          "scope": [
                            "request"
                          ],
                          "name": "testing",
                          "value": "1"
                        }
                      }
                    }
                  }
                }
              }
            ]
          },
          {
            "endpoint": "/fifo.Group",
            "output_encoding": "no-op",
            "backend": [
              {
                "url_pattern": "/__echo/fifo.Group",
                "extra_config": {
                  "modifier/martian": {
                    "fifo.Group": {
                      "scope": [
                        "request",
                        "response"
                      ],
                      "aggregateErrors": true,
                      "modifiers": [
                        {
                          "body.Modifier": {
                            "scope": [
                              "request"
                            ],
                            "body": "eyJtc2ciOiJ5b3Ugcm9jayEifQ=="
                          }
                        },
                        {
                          "header.Modifier": {
                            "scope": [
                              "request",
                              "response"
                            ],
                            "name": "X-Additional-Header",
                            "value": "true"
                          }
                        }
                      ]
                    }
                  }
                }
              }
            ]
          }
    ]
}

6) Request Validation: JSON Schema

JSON Schema is a declarative language that allows us to validation JSON documents

6) Request Validation: JSON Schema

6) Request Validation: JSON Schema

{
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "version": 3,
    "port": 8080,
    "host": ["http://127.0.0.1:8080"],
    "echo_endpoint": true,
    "extra_config": {
      "router": {
        "return_error_msg": true
      }
    },
    "endpoints": [
      {
        "endpoint": "/user",
        "method": "POST",
        "extra_config": {
          "validation/json-schema": {
            "$schema": "http://json-schema.org/draft-07/schema",
            "required": ["name", "age"],
            "properties": {
                "age": {
                    "type": "number"
                },
                "name": {
                    "type": "string"
                },
                "pronoun": {
                    "type": "string",
                    "enum": [
                        "she/her/hers",
                          "he/him/his",
                          "they/them/theirs",
                          "ze/hir/hirs",
                          "no pronoun",
                          "no preference",
                          "other"
                    ]
                }
            }
        }
        },
        "backend": [
          {
            "url_pattern": "/__echo/",
            "allow": ["req_body"]
          }
        ]
      }
    ]
  }

5) Security

JWT: Token Validation

Json Web Token

An industry standard to represent claims securely between two parties

JWT: Token Validation

JWT: Token Validation

JWT: Token Validation

JWT: Token Validation

6) Traffic Management

1) Caching

Caching allows us to store backend responses in memory to reduce the number of calls a user sends to the origin, reducing the network traffic and alleviating the pressure on your services.

1) Caching

{
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "version": 3,
    "host": [ "http://localhost:8080" ],
    "debug_endpoint": true,
    "cache_ttl": "1h",
    "endpoints": [
        {
            "endpoint": "/one",
            "cache_ttl": "1s",
            "backend": [
                {
                    "url_pattern": "/__debug/one"
                }
            ]
        },
        {
            "endpoint": "/two",
            "backend": [
                {
                    "host": ["https://api.github.com"],
                    "url_pattern": "/orgs/krakendio",
                    "extra_config": {
                        "qos/http-cache": {
                            "shared": true
                        },
                        "modifier/martian": {
                            "header.Modifier": {
                                "scope": ["response"],
                                "name": "Cache-Control",
                                "value": "max-age=3600, public",
                                "@comment": "Change the max-age policy before KrakenD checks it for caching."
                              }
                        }
                    }
                }
            ]

        }
    ]
}

2) Timeout

Controlling waiting times is crucial because KrakenD connect with many services

2) Timeout

The user will receive an HTTP status code 500 Internal Server Error when there is no content to return

What happens when the timeout is reached?

2) Timeout

{
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "version": 3,
    "port": 8080,
    "timeout": "1s",
    "host": [
        "http://107.155.65.148:5003"
    ],
    "endpoints": [
        {
            "endpoint": "/fast",
            "backend": [
                {
                    "url_pattern": "/speedy/"
                }
            ]
        },
        {
            "endpoint": "/semi-slow",
            "timeout": "100ms",
            "backend": [
                {
                    "@comment": "This one takes over 100ms",
                    "url_pattern": "/delayed/"
                },
                {
                    "url_pattern": "/speedy/"
                }
            ]
        },
        {
            "endpoint": "/slow",
            "timeout": "100ms",
            "backend": [
                {
                    "@comment": "This one takes over 100ms",
                    "url_pattern": "/delayed/"
                }
            ]
        }

    ]
}

3) Concurrent Calls

While we increase the load the backends, we will improve our client's response time and error rate

3) Concurrent Calls

3) Concurrent Calls

{
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "version": 3,
    "port": 8080,
    "timeout": "1s",
    "host": [
        "http://107.155.65.148:5004"
    ],
    "endpoints": [
        {
            "endpoint": "/error-10",
            "concurrent_calls": 3,
            "backend": [
                {
                    "url_pattern": "/error-10"
                }
            ]
        },
        {
            "endpoint": "/error-50",
            "concurrent_calls": 3,
            "backend": [
                {
                    "url_pattern": "/error-50"
                }
            ]
        },
        {
            "endpoint": "/flaky",
            "concurrent_calls": 3,
            "backend": [
                {
                    "url_pattern": "/flaky"
                }
            ]
        }
    ]
}

4) Rate Limiting

Protect infrastructure from abusive actions, mitigate cascade failures, and control the overall API usage 

4) Rate Limiting

Rate limiting is also compliment other traffic management technique such as Circuit-breaker, bot detector and service discovery

Purpose

4) Rate Limiting

  1. Service Rate Limiting**: set the maximum throughput users can do to KrakenD without distinguishing what endpoint the user is requesting
  2. Endpoint Rate Limiting: Set the maximum throughout users can do to specific endpoint.
  3. Proxy Rate Limiting: Set the maximum throughput between KrakenD and its backend

4) Rate Limiting

max_rate: Applies simultaneously to all clients using the endpoint, sharing a unique counter

client_max_rate: Sets a counter to each individual user

 

Both types can coexist and they complement each other, and store the counters in-memory

4) Rate Limiting

Bucket Token Algorithm

When do bucket is empty, they have to wait until the bucket is refilled again with the tokens.

The max_rate and client_max_rate define how many tokens it will refill every second

4) Rate Limiting

{
    "version": 3,
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "host": ["http://localhost:8080"],
    "debug_endpoint": true,
    "endpoints": [
        {
            "endpoint": "/r1",
            "extra_config": {
                "qos/ratelimit/router": {
                    "client_max_rate": 1,
                    "client_capacity": 5,
                    "strategy": "header",
                    "key": "Authorization",
                    "max_rate": 100
                }
            },
            "backend": [
                {
                    "url_pattern": "/__debug/ep",
                    "extra_config": {
                        "qos/ratelimit/proxy": {
                            "capacity": 3,
                            "max_rate": 3
                        }
                    }
                }
            ]
        }
    ]
}

5) Circuit Breaker

Error must be consecutive or the counter will be reset

Text

The Circuit Breaker is a straightforward state machine in the middle of the request and response that monitors all your backend failures. When they reach a configured threshold, the circuit breaker will prevent sending more traffic to a failing backend alleviating its pressure under challenging conditions

5) Circuit Breaker

Text

The backend wont be contacted again until it passes the timeout that we set

When the timeout window passes, it will allow one single request to pass to test it

5) Circuit Breaker

Text

If it fails, it will retry again after another timeout window

If the request passes, all traffic resumes normally

5) Circuit Breaker

Text

{
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "version": 3,
    "host": [ "http://107.155.65.148:5005" ],
    "endpoints": [
        {
            "endpoint": "/cb",
            "backend": [
                {
                    "url_pattern": "/fail-fifty-percent",
                    "extra_config": {
                        "qos/circuit-breaker": {
                            "log_status_change": true,
                            "name": "the fifty percent trouble maker",
                            "interval": 60,
                            "max_errors": 2,
                            "timeout": 5
                        }
                    }
                }
            ]
        }
    ]
}
  • interval: Time window where the errors count, in seconds
  • max_errors: The CONSECUTIVE (not total) number of errors within the interval window to consider the backend unhealthy
  • timeout: For how many seconds the circuit breaker will wait before testing again if the backend is healthy

5) Circuit Breaker

Text

7) Monitoring & Analytics

1) Health Endpoint

Is KrakenD up?

http://localhost:8080/__health

1) Health Endpoint

{
    "version": 3,
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "extra_config": {
        "router": {
            "logger_skip_paths": ["/health"],
            "health_path": "/health",
            "disable_health": false
        }
    }
}

2) Logging

{
    "version": 3,
    "$schema": "https://www.krakend.io/schema/krakend.json",
    "extra_config": {
        "telemetry/logging": {
            "level": "DEBUG",
            "stdout": true,
            "syslog": false,
            "prefix": "[KRAKEND]"
        },
        "router": {
            "disable_access_log": false,
            "logger_skip_paths":["/__health"]
         }
    }
}

3) Monitoring

Logging

Metrics

Tracing

Push log to log monitoring tools

Monitor request and response time, memory consumption and other vital information to understand the performance of the gateway

Gain insight into the flow of requests as they propagate through different services in a distributed system, from the user request to the gateway response

3) Monitoring

Logging

Metrics

Tracing

3) Monitoring

Logging

Metrics

Tracing

3) Monitoring

Logging

Metrics

Tracing

3) Monitoring

  grafana:
    image: grafana/grafana:9.1.2
    ports:
      - "4000:3000"
    volumes:
      - "./config/grafana/datasources/all.yml:/etc/grafana/provisioning/datasources/all.yml"
      - "./config/grafana/dashboards/all.yml:/etc/grafana/provisioning/dashboards/all.yml"
      - "./config/grafana/krakend:/var/lib/grafana/dashboards/krakend"
  influxdb:
    image: influxdb:1.8.10
    environment:
      - "INFLUXDB_DB=krakend"
      - "INFLUXDB_USER=krakend-dev"
      - "INFLUXDB_USER_PASSWORD=pas5w0rd"
      - "INFLUXDB_ADMIN_USER=admin"
      - "INFLUXDB_ADMIN_PASSWORD=supersecretpassword"
    ports:
      - "8086:8086"
  jaeger:
    image: jaegertracing/all-in-one:1
    ports:
      - "16686:16686"
      - "14268:14268"
  elasticsearch:
    image: elasticsearch:8.4.1
    environment:
      - "discovery.type=single-node"
      - "xpack.security.enabled=false"
      - "xpack.security.transport.ssl.enabled=false"
      - "xpack.security.http.ssl.enabled=false"
      - "ES_JAVA_OPTS=-Xms1024m -Xmx1024m"
    ports:
      - "19200:9200"
      - "9300:9300"
  kibana:
    image: kibana:8.4.1
    ports:
      - "5601:5601"
  logstash:
    image: logstash:8.4.1
    ports:
      - "12201:12201/udp"
      - "5044:5044"
    environment:
      - "xpack.monitoring.elasticsearch.url=http://elasticsearch:9200"
    volumes:
      - ./config/logstash/logstash.conf:/usr/share/logstash/pipeline/logstash.conf
    command: ["-f", "/usr/share/logstash/pipeline/logstash.conf"]

docker-compose.yaml

3) Monitoring

  "sequential_start": true,
  "extra_config": {
    "telemetry/metrics": {
      "collection_time": "30s",
      "listen_address": ":8090"
    },
    "telemetry/influx": {
      "address": "http://influxdb:8086",
      "ttl": "25s",
      "buffer_size": 100,
      "username": "krakend-dev",
      "password": "pas5w0rd"
    },
    "telemetry/logging": {
      "level": "DEBUG",
      "prefix": "[KRAKEND]",
      "syslog": false,
      "stdout": true
    },
    "telemetry/gelf": {
      "address": "logstash:12201",
      "enable_tcp": false
    },
    "telemetry/opencensus": {
      "sample_rate": 100,
      "reporting_period": 1,
      "enabled_layers": {
        "backend": true,
        "router": true
      },
      "exporters": {
        "jaeger": {
          "endpoint": "http://jaeger:14268/api/traces",
          "service_name": "krakend"
        }
      }
    },
    "security/cors": {
      "allow_origins": ["*"],
      "allow_methods": ["POST", "GET"],
      "allow_headers": ["Origin", "Authorization", "Content-Type"],
      "expose_headers": ["Content-Length"],
      "max_age": "12h"
    }
  }

krakend.json

3) Monitoring

Your feedback matters

There are no secrets to success. It is the result of preparation, hard work, and learning from failure. - Colin Powell

THANK YOU