How to Postgres on Kubernetes, deel 1

Door: Nathan Koopmans 21-8-2023

Categorieën
:
BLOG, Cloud, Ontwerp, PostgreSQL, Review,

Inmiddels alweer de nodige jaren geleden schreef collega Craig een driedelig blog over PostgreSQL in combinatie met Docker. De techniek heeft niet stil gestaan en een update is dan ook wel op zijn plaats, vond Nathan Koopmans, cloudplatform-engineer bij OptimaData. Daarom: How to Postgres on Kubernetes.

Verschillen tussen Docker en Kubernetes

In deze blog ga ik er vanuit dat je al een werkende Kubernetes-setup hebt. Of dit nu met MiniKube, K3s, een lokale complete Kubernetes-setup is of bij een cloudprovider als AWS, GCP of Azure, dat maakt niet uit. Zelf gebruik ik onze lokale Kubernetes-omgeving. Kubernetes heeft een paar voordelen ten opzichte van Docker, waaronder het stukje self-healing. Om de verschillen tussen Docker en Kubernetes overzichtelijk te maken, heb ik een mooie afbeelding van internet geleend. 

Verschillen tussen Docker en Kubernetes

De basis

Laten we beginnen bij het begin. Daarom laat ik in deel één een setup zien op basis van een standaard PostgreSQL-image, in blog deel twee (over 2 weken) zal ik een Kubernetes-database-operator (CloudnativePG / EDB Operator) gebruiken. Maar nu eerst: Plain PostgreSQL.

Eerst maak je een YAML file aan om hierin een aantal zaken te definiëren voor de PostgreSQL-pod / -container. Welke zaken zijn nodig en waarvoor dienen ze?

Secret

Een secret is een nette manier om data zoals wachtwoorden op te slaan. In het geval van PostgreSQL geef je een user en password op om te gebruiken voor verbinding met de database. Secrets zijn voor meerdere pods te gebruiken en bevatten slechts een kleine hoeveelheid aan data. Voor alle ins- en outs over secrets kan je terecht op de Kubernetes-pagina over secrets.

apiVersion: v1

kind: Secret

metadata:

  name: postgres-env

type: Opaque

stringData:

  POSTGRES_USER: example

  POSTGRES_PASSWORD: verysecret

---

PersistentVolumeClaim

Een PersistentVolumeClaim is een claim op een stukje ruimte om te gebruiken. Deze wordt gekoppeld aan een pod, zodat hier data opgeslagen kan worden. Dit heeft als voordeel dat bij het crashen van een pod, de data niet verloren gaat, maar meegenomen worden in een nieuwe pod. Je vindt meer info over PVCs en alle opties die er zijn op de Kubernetes-website.

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

  name: postgres-pv-claim

spec:

  accessModes:

  - ReadWriteOnce

  resources:

    requests:

      storage: 1Gi

---

Deployment:

De deployment zorgt voor het aanmaken en beheren van de pods binnen de deployment. Wanneer je de deployment schaalt van een naar twee pods, zorgt de deployment ervoor dat de kube-scheduler de tweede pod aan gaat maken. Pas je iets aan in de deployment, dan zullen de pods een voor een worden vervangen met de gewenste aanpassingen. In de deployment geef je ook aan welke volumes (claims) gebruikt moeten worden. Indien gewenst kan je ook andere resources definieren, zoals bijvoorbeeld CPU en RAM gebruik. Meer info vind je op de Kubernetes-website.

apiVersion: apps/v1

kind: Deployment

metadata:

  labels:

    app: postgres

  name: postgresql

spec:

  replicas: 1

  strategy:

    type: Recreate

  selector:

    matchLabels:

      app: postgres

  template:

    metadata:

      labels:

        app: postgres

    spec:

      containers:

      - envFrom:

        - secretRef:

            name: postgres-env

        image: docker.io/postgres:14

        name: postgresql

        ports:

        - containerPort: 5432

          name: postgresql

        volumeMounts:

        - mountPath: /var/lib/postgresql

          name: postgres-data

      volumes:

      - name: postgres-data

        persistentVolumeClaim:

          claimName: postgres-pv-claim

Bij mij ziet het er dan uiteindelijk zo uit: 

Postgres on Kubernetes deployment example

Postgres on Kubernetes deployment example part 2

Let op de “---" zoals ik deze hierboven in het screenshot heb gebruikt. Vergeet deze niet toe te voegen!

Best practise

Om alles schoon en overzichtelijk te houden, maak je een aparte namespace aan waar je deze container deployt. Omdat ik voor deze blog nog geen aparte namespace heb, maak ik deze nu eerst aan:

kubectl create ns postgresdeploy

Postgres on Kubernetes best practise namespace

De namespace is succesvol aangemaakt. Nu is het tijd om de YAML-file toe te passen zodat de Secret, PersistentVolumeClaim en de Deployment aangemaakt worden:

kubectl apply -f postgresdeploy.yaml -n postgresdeploy

Postgres on Kubernetes deployment example YAML file

Krijg je geen foutmelding? Dan heb je het goed gedaan. Krijg je wel een foutmelding, dan heeft dit meestal met een tikfout of indentatie te maken. YAML is erg gevoelig voor de uitlijning van de verschillende onderdelen in de file.

Controle

Geef het proces een paar minuten om alles aan te maken en te starten. Daarna kan je controleren of alles aangemaakt is en de container ready is en als status Running heeft.

kubectl get all -n postgresdeploy

Postgres on Kubernetes deployment check

Wat opvalt is dat de PVC hier mist. Dat is correct omdat een PVC clusterbreed is. Wil je controleren of de PVC is aangemaakt? Voer dan het volgende commando uit:

kubectl get pvc -n postgresdeploy

Postgres on Kubernetes deployment PVC

Verbinding maken

Nu ga je verbinding maken met de container:

kubectl exec -it postgresql-d684bfd65-5cjzm -n postgresdeploy -- /bin/bash

Je krijgt dan de bashprompt. Voer nu in:

su - postgres


Dit zorgt ervoor dat je de postgres-user wordt die standaard binnen de postgres-image actief is. Tik vervolgens het volgende in: 

psql -U example

En voila, je zit in postgres. Via deze weg hoef je geen wachtwoord in te voeren. Bij extern verbinden wordt wel om het wachtwoord gevraagd. 

Met \l+ controleer ik vervolgens of ik data terug krijg en alles naar behoren reageert.

Postgres on Kubernetes deployment connection

Extern bereikbaar maken

Nu dit werkt kan je zorgen dat PostgreSQL ook benaderbaar is zonder de container te benaderen met een bashprompt. Door een service aan te maken voor PostgreSQL kun je bijvoorbeeld eenvoudig data van de desktop importeren in de database.

Voor de service maak je ook een YAML file aan. Door op deze manier te werken kun je het ook in een andere omgeving eenvoudig toepassen. Daarnaast zorgt het er voor dat je snel omgevingen op kan zetten nadat je bijvoorbeeld je cluster hebt verwijderd.

In de YAML file plaats je het volgende.

apiVersion: v1

kind: Service

metadata:

  name: postgres

  labels:

    app: postgres

spec:

  type: NodePort

  ports:

    - port: 5432

  selector:

    app: postgres

Meer info vind je op de Kubernetes-website.

Bij mij ziet dat er zo uit:

Postgres on Kubernetes deployment extern bereikbaar

Nu kan je deze service toevoegen in de postgresdeploy namespace door middel van het volgende commando: 

kubectl apply -f postgresql-svc.yaml -n postgresdeploy

Controleer vervolgens of de service daadwerkelijk gecreëerd is. Schrijf ook meteen de poorten op voor de volgende stap:

kubectl get svc -n postgresdeploy

Postgres on Kubernetes deployment poorten

Tenslotte kan je kijken of de service goed werkt en of je PostgreSQL kan benaderen op het IP van de node waarop de pod staat. Je moet in dit geval eerst weten op welke node de pod staat en dan het IP van de node weten. Dat kan je op de volgende manier doen:

kubectl get pods -o wide -n postgresdeploy

(waar postgresdeploy de namespace is die ik voor deze how-to heb gebruikt)

kubectl get nodes -o wide

Postgres on Kubernetes deployment node IP check

Bij het opvragen van de pods met de optie -o wide zie je op welke node de pod staat. In dit geval op node nk-k8s- worker-1. Met het opvragen van de nodes doe je hetzelfde en gebruik je weer de optie -o wide. Zo zie je het IP-adres van node nk-k8s-worker-1. Deze gebruik je vervolgens bij het benaderen van PostgreSQL. Als je kijkt naar de service zie je dat deze in dit geval poort 5432 doorzet naar poort 32647. Dit gebruik je dan ook als poort bij de psql commandline client op het werkstation of laptop.

Psql -U example -h 192.168.54.91 -p 32647

Postgres on Kubernetes deployment node check poort

Resultaat

Het resultaat is een container met PostgreSQL, die je ook kan benaderen van bijvoorbeeld een laptop. Op dit moment heb je één replica en dus ook een pod. Dit kan je opschalen. Echter, om echt nut te hebben moet er ook een synchronisatie opgezet worden, zodat data van de een naar de andere PostgreSQL-instantie wordt doorgezet. Om het leven wat makkelijker te maken, gaan we in deel twee gebruik maken van een Kubernetes-operator, die ons veel zaken uit de hand neemt. Uiteraard is het ook mogelijk met Kubernetes een omgeving zonder operator high available te maken met streaming replication. Maar een operator biedt zoveel meerwaarde, dat dit toch vaak de voorkeur heeft. Meer hierover in deel twee.

Meer weten?

Ben je bezig met PostgreSQL en Kubernetes of zou je de stap willen maken? Wij hebben al meerdere PostgreSQL-omgevingen in combinatie met Kubernetes opgezet en in beheer. Neem gerust contact met ons op. Wij staan voor je klaar via 035 369 0307 of informatie@optimadata.nl

Andere interessante blogs:

How to Postgres on Kubernetes, deel 1

How to Postgres on Docker

Native PostgreSQL of Supported PostgreSQL

Hoe waarom en wanneer van databases in containers

Geboren en getogen in de Cloud

Terug naar blogoverzicht