# The Kong Microservice API Gateway
###### tags: `kong` `inference-server`
## Install - Kong Gateway Community
Ref: https://konghq.com/get-started/#install
:::spoiler 1. Create a Docker network
You will need to create a custom network to allow the containers to discover and communicate with each other. In this example `kong-net` is the network name, you can use any name.
```bash
$ docker network create kong-net
```
:::
:::spoiler 2. Start your database
If you wish to use a Cassandra container:
```bash
$ docker run -d --name kong-database \
--network=kong-net \
-p 9042:9042 \
cassandra:3
```
If you wish to use a PostgreSQL container:
```bash
$ docker run -d --name kong-database \
--network=kong-net \
-p 5432:5432 \
-e "POSTGRES_USER=kong" \
-e "POSTGRES_DB=kong" \
-e "POSTGRES_PASSWORD=kong" \
postgres:9.6
```
:::
:::spoiler 3. Prepare database
Run the migrations with an ephemeral Kong container:
```bash
$ docker run --rm \
--network=kong-net \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=kong-database" \
-e "KONG_PG_USER=kong" \
-e "KONG_PG_PASSWORD=kong" \
-e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" \
kong:latest kong migrations bootstrap
```
:::
:::spoiler 4. Start Kong
When the migrations have run and your database is ready, start a Kong container that will connect to your database container, just like the ephemeral migrations container:
```bash
$ docker run -d --name kong \
--network=kong-net \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=kong-database" \
-e "KONG_PG_USER=kong" \
-e "KONG_PG_PASSWORD=kong" \
-e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" \
-e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
-e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
-e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" \
-p 8000:8000 \
-p 8443:8443 \
-p 127.0.0.1:8001:8001 \
-p 127.0.0.1:8444:8444 \
kong:latest
```
:::
:::spoiler 5. Check Kong alive
Kong is running:
```bash
$ curl -i http://localhost:8001/
```
:::
## Connect docker network
Make out services and kong service in the same docker network.
```bash
$ docker network connect kong-net ${service-container}
```
```bash
# docker network inspect kong-net
"Containers": {
"0a4604b7fe2f163ce1f091c6ecd54a7e128d0e4893a92cdd8402cc589ffaa746": {
"Name": "kong",
"EndpointID": "fc30722817707555c2ab9c177b7b723036e610c7817986bbae3cce2eba6a72e7",
"MacAddress": "02:42:c0:a8:50:03",
"IPv4Address": "192.168.80.3/20",
"IPv6Address": ""
},
"c42035156d3395f80ee4fe4fcfa3313c7d0bb26a6d222542806532e63a8f043d": {
"Name": "kong-database",
"EndpointID": "6aecdd779de8ab6865488fddcb6e9c0a981b6054e24a76c3fd226d4948a733fb",
"MacAddress": "02:42:c0:a8:50:02",
"IPv4Address": "192.168.80.2/20",
"IPv6Address": ""
},
"eb84399c39f00100eba4740d5e0a3172eb905305868282f6e1ef405aec7401df": {
"Name": "${service-container}",
"EndpointID": "08b96938f7f292d38779ae38367cef510d831222dc134a89852bf4c1afdb6d38",
"MacAddress": "02:42:c0:a8:50:05",
"IPv4Address": "192.168.80.5/20",
"IPv6Address": ""
}
},
```
## Configuring a Service
### 1. Add your Service using the Admin API
apiDoc: https://docs.konghq.com/2.2.x/admin-api/#add-service
```bash
$ curl -i -X POST \
--url http://localhost:8001/services/ \
--data 'name=alex-model' \
--data 'host=192.168.80.5' \
--data 'port=80'
```
You should receive a response similar to:
```json=
HTTP/1.1 201 Created
Content-Type: application/json
Connection: keep-alive
{
"host": "192.168.80.5",
"id": "ada68f4a-def9-437a-92a1-9da4aee7dc7d",
"protocol": "http",
"read_timeout": 60000,
"tls_verify_depth": null,
"port": 80,
"updated_at": 1604994714,
"ca_certificates": null,
"created_at": 1604990320,
"connect_timeout": 60000,
"write_timeout": 60000,
"name": "alex-model",
"retries": 200,
"path": "/",
"tls_verify": null,
"tags": [],
"client_certificate": null,
"extras": {
"createdUser": null,
"updatedUser": null,
"id": 1,
"service_id": "ada68f4a-def9-437a-92a1-9da4aee7dc7d",
"kong_node_id": "4",
"description": null,
"tags": null,
"createdAt": "2020-11-10T06:38:40.000Z",
"updatedAt": "2020-11-10T07:51:54.000Z"
}
}
```
### 2. Add a Route for the Service
apiDoc: https://docs.konghq.com/2.2.x/admin-api/#add-route
```bash
$ curl -i -X POST \
--url http://localhost:8001/services/alex-model/routes \
--data 'name=create' \
--data 'paths[]=/api/create'
```
The answer should be similar to:
```json=
HTTP/1.1 201 Created
Content-Type: application/json
Connection: keep-alive
{
"id": "a1679488-026d-4edb-8f0a-a01fb678f30e",
"tags": null,
"paths": [
"/api/create"
],
"destinations": null,
"headers": null,
"protocols": [
"http",
"https"
],
"strip_path": false,
"created_at": 1604991375,
"request_buffering": true,
"hosts": null,
"name": "create",
"updated_at": 1604995060,
"snis": null,
"preserve_host": false,
"regex_priority": 0,
"methods": null,
"sources": null,
"response_buffering": true,
"https_redirect_status_code": 426,
"path_handling": "v1",
"service": {
"host": "192.168.80.5",
"id": "ada68f4a-def9-437a-92a1-9da4aee7dc7d",
"protocol": "http",
"read_timeout": 60000,
"tls_verify_depth": null,
"port": 80,
"updated_at": 1604994714,
"ca_certificates": null,
"created_at": 1604990320,
"connect_timeout": 60000,
"write_timeout": 60000,
"name": "mlsteam_version",
"retries": 200,
"path": "/",
"tls_verify": null,
"tags": [],
"client_certificate": null,
"extras": {
"createdUser": null,
"updatedUser": null,
"id": 1,
"service_id": "ada68f4a-def9-437a-92a1-9da4aee7dc7d",
"kong_node_id": "4",
"description": null,
"tags": null,
"createdAt": "2020-11-10T06:38:40.000Z",
"updatedAt": "2020-11-10T07:51:54.000Z"
}
}
}
```
Kong is now aware of your Service and ready to proxy requests.
### 3. Forward your requests through Kong
Issue the following cURL request to verify that Kong is properly forwarding requests to your Service. Note that by [default](https://docs.konghq.com/2.2.x/configuration/#nginx-section) Kong handles proxy requests on port :8000:
```bash
$ curl -i -X GET \
--url http://localhost:8000/
```
Or you can your browser and check http://localhost:8000/api/create.
A successful response means Kong is now forwarding requests made to http://localhost:8000 to the url we configured in step #1, and is forwarding the response back to us. Kong knows to do this through the header defined in the above cURL request:
* Host: \<given host\>
## Kong Admin GUI
Ref: https://github.com/pantsel/konga/blob/master/README.md
### 1. Prepare the database
:::info
Note: You can skip this step if using the mongo adapter.
You can prepare the database using an ephemeral container that runs the prepare command.
:::
Args:
| argument 1 | description 2 | default |
| ---------- | ---------------------------------- | ------- |
| -c | command | - |
| -a | adapter (can be postgres or mysql) | - |
| -u | [full database connection url](https://stackoverflow.com/questions/3582552/postgresql-connection-url) | - |
```bash
# $ docker run --rm pantsel/konga:latest -c prepare -a {{adapter}} -u {{connection-uri}}
$ docker run --network=kong-net --rm \
pantsel/konga:latest \
-c prepare \
-a postgres \
-u postgresql://kong@kong-database:5432/konga
```
```bash
$ docker run -p 1337:1337 \
--network kong-net \
-e "TOKEN_SECRET=KONGAKEY" \
-e "DB_ADAPTER=postgres" \
-e "DB_URI=postgresql://kong@kong-database:5432/konga" \
-e "NODE_ENV=production" \
--name konga \
pantsel/konga
```
The GUI will be available at `http://{your server's public ip}:1337`
## Plugins
:::spoiler Plugins Lists
Plugins hub: https://docs.konghq.com/hub/?itm_source=website&itm_medium=nav
* jwt
* acl
* cors
* oauth2
* tcp-log
* udp-log
* file-log
* http-log
* key-auth
* hmac-auth
* basic-auth
* ip-restriction
* request-transformer
* response-transformer
* request-size-limiting
* rate-limiting
* response-ratelimiting
* aws-lambda
* bot-detection
* correlation-id
* datadog
* galileo
* ldap-auth
* loggly
* statsd
* syslo
:::
### 1. Add Plugins on the Service
ApiDoc: https://docs.konghq.com/2.2.x/admin-api/#add-plugin
Add extra functionality by using Kong Plugins. You can also create your own plugins!
Plugins can be configured for various entities, combination of entities, or even globally.
The complete order of precedence when a plugin has been configured multiple times is:
:::spoiler Precedence
1. Plugins configured on a combination of: a Route, a Service, and a Consumer. (Consumer means the request must be authenticated).
2. Plugins configured on a combination of a Route and a Consumer. (Consumer means the request must be authenticated).
3. Plugins configured on a combination of a Service and a Consumer. (Consumer means the request must be authenticated).
4. Plugins configured on a combination of a Route and a Service.
5. Plugins configured on a Consumer. (Consumer means the request must be authenticated).
6. Plugins configured on a Route.
7. Plugins configured on a Service.
8. Plugins configured to run globally.
:::
#### Rate-limiting
https://docs.konghq.com/hub/kong-inc/rate-limiting/
```bash
$ curl -i -X POST \
--url http://localhost:8001/plugins/ \
--data 'name=rate-limiting' \
--data 'config.minute=100'
```
#### Key Auth
https://docs.konghq.com/hub/kong-inc/key-auth/
```bash
$ curl -i -X POST \
--url http://localhost:8001/plugins/ \
--data 'name=key-auth'
```
#### Http log
https://docs.konghq.com/hub/kong-inc/http-log/
```bash
$ curl -i -X POST \
--url http://localhost:8001/plugins/ \
--data 'name=http-log' \
--data 'config.http_endpoint=http://192.168.80.5/api/logs'
```
Response
```json=
{
"latencies": {
"request": 9,
"kong": 1,
"proxy": 8
},
"service": {
"host": "192.168.80.5",
"created_at": 1604994635,
"connect_timeout": 60000,
"id": "8fb7191d-370f-43ad-a217-4819fe15350b",
"protocol": "http",
"name": "mlsteam_health",
"read_timeout": 60000,
"port": 80,
"path": "/",
"updated_at": 1604994709,
"write_timeout": 60000,
"retries": 200,
"tags": {},
"ws_id": "e76b0794-bf11-4fc3-8218-1d2179c898a2"
},
"request": {
"querystring": {},
"size": 677,
"uri": "/api/health",
"url": "http://192.168.0.17:8000/api/health",
"headers": {
"host": "192.168.0.17:18000",
"connection": "keep-alive",
"upgrade-insecure-requests": "1",
"cache-control": "max-age=0",
"cookie": "_xsrf=2|7aa707d1|7a68975e6586278e2fd30dd1c6286dd7|1602483298; io=bgm6zfR3TwwXfJhCAAAQ; session=eyJfcGVybWFuZW50Ijp0cnVlfQ.X6s_Kw.CUmPnYHY8QAF8wTTYE4GfNkdEr8",
"accept-encoding": "gzip, deflate",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36 Edg/86.0.622.63",
"accept-language": "zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7,en-GB;q=0.6",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
},
"method": "GET"
},
"client_ip": "192.168.0.241",
"tries": [
{
"balancer_latency": 0,
"port": 80,
"balancer_start": 1605058631147,
"ip": "192.168.80.5"
}
],
"upstream_uri": "/api/health",
"response": {
"headers": {
"content-type": "application/json",
"x-permitted-cross-domain-policies": "none",
"connection": "close",
"cache-control": "no-store",
"referrer-policy": "no-referrer",
"x-robots-tag": "none",
"x-download-options": "noopen",
"date": "Wed, 11 Nov 2020 01:37:11 GMT",
"x-xss-protection": "1; mode=block",
"via": "kong/2.2.0",
"vary": "Cookie",
"content-security-policy": "script-src "self" "unsafe-inline" "unsafe-eval"; frame-ancestors "self";",
"content-length": "58",
"x-kong-proxy-latency": "1",
"server": "nginx/1.14.0 (Ubuntu)",
"set-cookie": "session=eyJfcGVybWFuZW50Ijp0cnVlfQ.X6tARw.5D_S8vTEb44CzC1fHIC4XX63nlI; Expires=Wed, 11-Nov-2020 06:37:11 GMT; HttpOnly; Path=/",
"x-kong-upstream-latency": "8",
"x-content-type-options": "nosniff",
"access-control-allow-origin": "*"
},
"status": 200,
"size": 786
},
"route": {
"id": "3f54e2ed-1899-4a88-8107-a3d210e747fa",
"paths": [
"/api/health"
],
"protocols": [
"http",
"https"
],
"strip_path": False,
"created_at": 1604994724,
"ws_id": "e76b0794-bf11-4fc3-8218-1d2179c898a2",
"request_buffering": True,
"name": "health",
"updated_at": 1604994928,
"preserve_host": False,
"regex_priority": 0,
"response_buffering": True,
"https_redirect_status_code": 426,
"path_handling": "v1",
"service": {
"id": "8fb7191d-370f-43ad-a217-4819fe15350b"
}
},
"started_at": 1605058631146
}
```
## Api Gateway
Ref: https://docs.konghq.com/hub/kong-inc/key-auth/
{%youtube F-YphLgKuz4 %}
{%youtube hQ-4yGfX5aM %}
Out purpose is different users use his own api-tokens accessing different services.
### Pre-work
We pre-create two different services. Each service has one route.
```json=
# curl -i -X GET localhost:8001/services
{
"next": null,
"data": [
{
"host": "192.168.80.5",
"id": "8fb7191d-370f-43ad-a217-4819fe15350b",
"protocol": "http",
"read_timeout": 60000,
"tls_verify_depth": null,
"port": 80,
"updated_at": 1604994709,
"ca_certificates": null,
"created_at": 1604994635,
"connect_timeout": 60000,
"write_timeout": 60000,
"name": "mlsteam_health",
"retries": 200,
"path": "\/",
"tls_verify": null,
"tags": [],
"client_certificate": null
},
{
"host": "192.168.80.5",
"id": "ada68f4a-def9-437a-92a1-9da4aee7dc7d",
"protocol": "http",
"read_timeout": 60000,
"tls_verify_depth": null,
"port": 80,
"updated_at": 1604994714,
"ca_certificates": null,
"created_at": 1604990320,
"connect_timeout": 60000,
"write_timeout": 60000,
"name": "mlsteam_version",
"retries": 200,
"path": "\/",
"tls_verify": null,
"tags": [],
"client_certificate": null
}
]
}
```
```json=
# curl -i -X GET localhost:8001/routes
{
"next": null,
"data": [
{
"id": "3f54e2ed-1899-4a88-8107-a3d210e747fa",
"tags": null,
"paths": [
"\/api\/health"
],
"destinations": null,
"headers": null,
"protocols": [
"http",
"https"
],
"strip_path": false,
"created_at": 1604994724,
"request_buffering": true,
"hosts": null,
"name": "health",
"updated_at": 1604994928,
"snis": null,
"preserve_host": false,
"regex_priority": 0,
"methods": null,
"sources": null,
"response_buffering": true,
"https_redirect_status_code": 426,
"path_handling": "v1",
"service": {
"id": "8fb7191d-370f-43ad-a217-4819fe15350b"
}
},
{
"id": "a1679488-026d-4edb-8f0a-a01fb678f30e",
"tags": null,
"paths": [
"\/api\/version"
],
"destinations": null,
"headers": null,
"protocols": [
"http",
"https"
],
"strip_path": false,
"created_at": 1604991375,
"request_buffering": true,
"hosts": null,
"name": "version",
"updated_at": 1604995060,
"snis": null,
"preserve_host": false,
"regex_priority": 0,
"methods": null,
"sources": null,
"response_buffering": true,
"https_redirect_status_code": 426,
"path_handling": "v1",
"service": {
"id": "ada68f4a-def9-437a-92a1-9da4aee7dc7d"
}
}
]
}
```
### Add key-auth plugin
Ref: https://docs.konghq.com/hub/kong-inc/key-auth/#enabling-the-plugin-on-a-service
Add key-auth plugin for each services.
```bash
$ curl -i -X POST \
--url http://localhost:8001/services/mlsteam_health/plugins/ \
--data 'name=key-auth'
$ curl -i -X POST \
--url http://localhost:8001/services/mlsteam_version/plugins/ \
--data 'name=key-auth'
```
Reponses
```json
{
"next": null,
"data": [
{
"created_at": 1604995406,
"id": "4cd73a72-ef66-4137-afd3-760beabeeeca",
"tags": null,
"enabled": true,
"protocols": [
"grpc",
"grpcs",
"http",
"https"
],
"name": "key-auth",
"consumer": null,
"service": {
"id": "ada68f4a-def9-437a-92a1-9da4aee7dc7d"
},
"route": null,
"config": {
"key_names": [
"apikey"
],
"run_on_preflight": true,
"anonymous": null,
"hide_credentials": false,
"key_in_body": false
}
}
]
}
```
So now if we make the same request to th port 8000 this time. You'll see that now we immediately get a `401` for request that worked
```json
chine@workst03:~$ curl -i -X GET http://192.168.0.17:18000/api/version
HTTP/1.1 401 Unauthorized
Date: Tue, 10 Nov 2020 08:05:52 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
WWW-Authenticate: Key realm="kong"
Content-Length: 45
X-Kong-Response-Latency: 0
Server: kong/2.2.0
{
"message":"No API key found in request"
}
```
### Create consumers(user)
Ref: https://docs.konghq.com/hub/kong-inc/key-auth/#create-a-consumer
You need to associate a credential to an existing [Consumer](https://docs.konghq.com/2.2.x/admin-api/#consumer-object) object. A Consumer can have many credentials.
To create a Consumer, you can execute the following request:
```bash
curl -i -X POST \
--url http://localhost:8001/consumers/ \
--data 'username=somebody'
```
Response:
```json=
{
"custom_id": null,
"created_at": 1605002460,
"id": "40cae96b-ee1f-4f85-84ec-dce447e57456",
"tags": null,
"username": "somebody"
}
```
### Bind apiKey to consumer
```bash
curl -i -X POST \
--url http://localhost:8001/consumers/somebody/key-auth/ \
--data 'key= 4cd73a72-ef66-4137-afd3-760beabeeeca'
```