# 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' ```