Classic Kubernetes: Optional embedded Database

For many applications, a database of some sort is required. A big question when packaging an on-prem app is whether to embed this database alongside the rest of your application, or to require an end customer to provide the connection details for a DB instance that they manage.

Tradeoffs

The key tradeoff is ease-of-setup for the customer vs. ease-of-maintainability for the application vendor, and often the answer is to allow both configurations. You can include an embedded database for trials and proof-of-concept installations, and then when your customer wants to set up a hardened production installation, they can supply connection details.

Kubernetes Postgres Example

Configuration

First, we need some configuration items to allow the end customer to configure which mode they want to run in

config:
- name: db_settings_type
  title: Database Type
  description: Would you like to run an internal database or provide your own?
  items:
  - name: db_type
    type: select_one
    default: embedded
    items:
    - name: embedded
      title: Embedded
    - name: external
      title: External
- name: external_db_config
  title: External DB Settings
  description: DB connection settings
  when: db_type=external
  items:
  - name: database_connection_string
    type: text
    default: postgres://user:password@postgres.mycompany.com/some-database

Kubernetes

On the kubernetes side, we need to reference those config options to control what runs in the cluster. We’ll toggle the number of postgres replicas that run, as well as the connection details for our example API server.

---
# kind: scheduler-kubernetes
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  selector:
    matchLabels:
      app: postgres # has to match .spec.template.metadata.labels
  serviceName: "postgres"
  replicas: {{repl if ConfigOptionEquals "db_type" "embedded"}}1{{repl else}}0{{repl end}}
  template:
    metadata:
      labels:
        app: postgres # has to match .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: postgres
        image: postgres:10
        ports:
        - containerPort: 5432
          name: postgres
  volumeClaimTemplates:
  - metadata:
      name: postgres
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "my-storage-class"
      resources:
        requests:
          storage: 1Gi
---
# kind: scheduler-kubernetes
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  template:
    spec:
      containers:
        - name: api
          image: ....
          env:
            - name: POSTGRES_CONNSTRING
              value: {{repl if ConfigOptionEquals "db_type" "external"}}{{repl ConfigOption "database_connection_string"}}{{repl else}}postgres://fake:fake@postgres/my-app{{repl end}}
           

Note that this example omits (for brevity) how to securely create a persistent password/username for your postgres instance. A good guide for this can be found in the Populating Config Values with Commands