Introduction

I was motivated to move away from a databaseless Kong ingress setup to a maintained Postgres database. As the cloud provider is DO, Casandra as a database was not an option unless Casandra is installed on a droplet, which is always a good option but more work when it comes to maintenance. Anyhow, Kong strongly recommends Postgres over Casandra. By moving to a database setup, I could remove all the additional Kubernetes YAML manifests for plugins, consumer keys, consumers, and so on. Furthermore, I can now use Konga to easily update any services, routes, consumers, plugins, etc from a nice GUI. More on Konga later.

Preparation and Installation

I generally write my code for deployment and destruction with Makefiles. However, for simplicity we will stick to command lines. The following commands are used to install Kong on a cluster. Helm 3 is required along with kubectl.

kubectl apply -f PostgresSecret.yaml
helm repo add kong https://charts.konghq.com
helm repo update
helm install ingress-kong kong/kong \
--set ingressController.installCRDs=false,\
postgresql.enabled=false,\
env.database=postgres,\
env.pg_user.valueFrom.secretKeyRef.key=username,\
env.pg_user.valueFrom.secretKeyRef.name=kong-database-secret,\
env.pg_password.valueFrom.secretKeyRef.key=password,\
env.pg_password.valueFrom.secretKeyRef.name=kong-database-secret,\
env.pg_host.valueFrom.secretKeyRef.key=host,\
env.pg_host.valueFrom.secretKeyRef.name=kong-database-secret,\
env.pg_port=25060,\
env.pg_database.valueFrom.secretKeyRef.key=database,\
env.pg_database.valueFrom.secretKeyRef.name=kong-database-secret,\
env.pg_ssl=true

Let’s go over the above commands. First, I wrote a manifest PostgresSecret.yaml that contains a secret:

---
apiVersion: v1
kind: Secret
metadata:
  name: kong-database-secret
  namespace: default
type: Opaque
data:
  username: <bas64 encoded string>
  password: <bas64 encoded string>
  host: <bas64 encoded string>
  database: <bas64 encoded string>

The <bas64 encoded string> can be generated by echo 'some string' | basee64. kubectl apply -f PostgresSecret.yaml simply updates/creates the secret in a cluster. The subsequent commands are related to Helm and the installation of a specific repo and the update of all repos. Finally,

helm install ingress-kong kong/kong \
 --set ingressController.installCRDs=false,\
 postgresql.enabled=false,\
 env.database=postgres,\
 env.pg_user.valueFrom.secretKeyRef.key=username,\
 env.pg_user.valueFrom.secretKeyRef.name=kong-database-secret,\
 env.pg_password.valueFrom.secretKeyRef.key=password,\
 env.pg_password.valueFrom.secretKeyRef.name=kong-database-secret,\
 env.pg_host.valueFrom.secretKeyRef.key=host,\
 env.pg_host.valueFrom.secretKeyRef.name=kong-database-secret,\
 env.pg_port=25060,\
 env.pg_database.valueFrom.secretKeyRef.key=database,\
 env.pg_database.valueFrom.secretKeyRef.name=kong-database-secret,\
 env.pg_ssl=true

is executed. Here, we are simply installing Kong with the release name ingress-kong with several environmental variables set. As we want to dynamically pull information from our secret we must include valueFrom.secretKeyRef.key and valueFrom.secretKeyRef.name. Of course, we do not require a secret, and we could literally hardcode all the database information in the command.

As an interface to Kong, a pretty GUI, I installed Konga via docker-compose locally:

version: '3.8'

services:
  # run once
  konga-prepare:
    image: pantsel/konga:next
    command: "-c prepare -a postgres -u postgresql://user:password@server:25060/konga?sslmode=require&ssl=true"
    restart: on-failure

  konga:
    image: pantsel/konga:next
    restart: unless-stopped
    environment:
      DB_ADAPTER: 'postgres'
      DB_HOST: 'server'
      DB_DATABASE: 'konga'
      DB_USER: 'user'
      DB_PASSWORD: 'password'
      DB_PORT: 25060
      DB_SSL: 1
      NODE_TLS_REJECT_UNAUTHORIZED: 0 # may not be needed
      TOKEN_SECRET: 'token'
      NODE_ENV: 'production'
    ports:
      - "1337:1337"
    depends_on: # comment out after first run
      - konga-prepare

  dockerhost:
    image: qoomon/docker-host
    container_name: dockerhost
    cap_add:
      - NET_ADMIN
      - NET_RAW
    restart: on-failure

docker-compose up will allow you to access Konga from localhost:1337 but only after konga-prepare runs and installs the new database. I use dockerhost for access to my host. This will make sense in a bit. Note, make sure to include the correct database information in the docker-compose.yaml above.

With docker-compose running we need to create a port-forward to our Kong admin interface on port 8444 (ssl). This can easily be achieved with the following (assuming namespace was left as default):

kubectl port-forward $(kubectl get pod -l app.kubernetes.io/instance=ingress-kong -o jsonpath='{.items[0].metadata.name}') 8444:8444

With the port-forward to your cluster made we now can go to localhost:1337 and create a new admin account. Once done, we need to create a new connection to our Kong and this is where dockerhost is applied. As a new connection, we need to use https://dockerhost:8444. This should create a successful connection to your Kong instance. Now create your consumers, add your plugins, create your routes and services, etc. Have fun! You could always deploy Konga, but I avoid this for security reasons.

Final Words

I have quickly detailed how to get Kong with a dedicated database installed and configured via Konga and Docker Compose. There really is not any challenge here. However, if you do have an issue with initializing your database you can always check your pod kubectl logs ingress-kong-kong-init-migrations-<somestring> -c wait-for-postgres.