Postgres-Testserver mit Docker als Linux-Container installieren

https://oer-informatik.de/docker-install-postgres

tl/dr; (ca. 20 min Lesezeit): Mit Postgres lassen sich besonders leicht Container aufsetzen und mit Daten befüllen. In diesem Tutorial werden Voraussetzungen für den Container zusammengetragen, die nötigen Befehle zusammengestellt. Am Ende können wir individuelle versionierbare Datenbanken auf Knopfdurck zur Verfügung stellen. Oder, um es zu buzzworden: “Infrastructure as Code”.

Voraussetzungen

Hilfreich (aber nicht zwingend) für dieses Tutorial sind Grundkenntnisse im Umgang mit Docker. Wir benötigen ein Terminal nach Wahl (Powershell, Bash…), eine lauffähige Docker-Umgebung. Ich gebe die Befehle, die auf dem Host-System ausgeführt werden immer in Powershell-Notation an (mit führendem PS>), die Befehle, die im Container abgesetzt werden, mit CT:/$. Ich hoffe, dass erleichtert die Zuordnung auch bei allen, die (sinnvollerweise) in beiden Fällen Linux nutzen. Sofern es Abweichungen hinsichtlich des Hostbetriebssystems gibt sind diese genannt.

Ein aktuelles Docker Image für Postgres vom Docker Hub laden und starten

Das Image wird durch folgenden Befehl geladen und als Dienst im Hintergrund gestartet. Individuell gewählt werden kann der Name (hier: somePostgres1), das Initialpasswort des Root-Datenbanknutzers (hier _hier%1GutesPWnu+2en) sowie die Port-Weiterleitung (hier auf den lokalen Port 3312):

Das Image als Dienst im Hintergrund starten:

$ docker run --name=somePostgres1 -d -p 3312:5432 -e POSTGRES_PASSWORD=_hier%1GutesPWnu+2en postgres:latest

wobei:

Option Bedeutung
--name XYZ setzt den Containernamen auf XYZ (individualisierbar); sinnvoll ist Zahl am Ende des Containernamens, um mehrere Container des gleichen Images unterschieden zu können
-d detach: wird im Hintergrund weiter ausgeführt
-p 3312:5432 Der Port 5432 des Containers wird auf den Port 3312 des hosts gemappt.1
postgres:latest nutzt bzw. läd das neuste Image postgres; nach einem Doppelpunkt folgt der Tag, diesen kann man auch auf eine bestimmte Version festlegen, z.B. postgres:15. Infos zu allen möglichen Tags finden sich hier
-e POSTGRES_PASSWORD=mysecretpassword Setzt das Passwort des Datenbank-Root-Nutzers

Im UML-Sequenzdiagramm sieht der Ablauf etwa wie folgt aus (natürlich weicht der Docker-Befehl in diesem Beispiel ab, da MS SQL-Server im Diagramm geladen wird):

UML-Sequenzdiagramm, dass zeigt, wie das Image geladen und schließlich gestartet wird
UML-Sequenzdiagramm, dass zeigt, wie das Image geladen und schließlich gestartet wird

Anhand der Ausgabe lässt sich schön erkennen, dass unterschiedliche Layer, die aufeinander aufbauen und ggf. wiederverwendet werden können, geladen werden:

Unable to find image 'postgres:latest' locally
latest: Pulling from library/postgres
bd159e379b3b: Pull complete
b955aac8d5e0: Pull complete
922fe4565b9a: Pull complete
c39aa91943e9: Pull complete
59e6d12f4c90: Pull complete
d058e68b8750: Pull complete
03549096a058: Pull complete
c941aeed5670: Pull complete
dccb429e64dd: Pull complete
e257dcd3a491: Pull complete
228f40788ef8: Pull complete
3afb5af902e0: Pull complete
c37cbd0077ed: Pull complete
Digest: sha256:4cb6474...
Status: Downloaded newer image for postgres:latest
0c981b...

Am Ende steht der Hashwert, über den der laufende Container identifizierbar ist (Container ID).

Mit der Postgres-Konsole verbinden

Der Datenbankcontainer läuft im Hintergrund. Um ihm Ausgaben zu entlocken, müssen wir uns damit verbinden.

Die rudimentärste Variante ist die Konsole per psql.

Die Konsole antwortet:

psql (15.0 (Debian 15.0-1.pgdg110+1))
Type "help" for help.

Und in der folgenden Kommandozeile können wir SQL-Befehle absetzen (immer mit ; beenden, sonst werden sie nicht ausgeführt).

postgres=# SELECT VERSION();
                                                           version
-----------------------------------------------------------------------------------------------------------------------------
 PostgreSQL 15.0 (Debian 15.0-1.pgdg110+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit
(1 row)

Mittelfristig wollen wir den Container aber mit einem Frontend bedienen:

Frontend installieren (pgadmin)

Ein stark verbreitetes Frontend für postgres ist pgAdmin. Unter “Create Server” können dann die Zugangsdaten des erstellten postgres-Containers eingegeben werden. Auf dem Reiter “General” zunächst nur ein Name:

pgadmin Server-Verbindung herstellen
pgadmin Server-Verbindung herstellen

… auf dem Reiter “Connection” dann Benutzername und Passwort:

pgadmin Benutzerdaten eingeben
pgadmin Benutzerdaten eingeben

Frontend installieren (HeidiSQL)

Als Frontend/Client kann auch HeidiSQL oder DBeaver verwendet werden: Benutzername, Passwort und Port müssen hier aus der obigen Installation übernommen werden.

Postgres in Docker-Container automatisch mit Daten füllen

Oft ist es hilfreich, wenn man unmittelbar bei Container-Erstellung ein File mit SQL-Befehlen ausführen kann - beispielsweise um direkt eine Testdatenbank verfügbar zu haben. Im einfachsten Fall kann das erreicht werden, in dem man ein Verzeichnis des Hostsystems innerhalb des Containers als “Volume” mountet (es also im Container unter einem definierten Pfad einbindet). Wenn dieses Volume unter /docker-entrypoint-initdb.d eingebunden wird und eine SQL-Datei enthält, so wird diese dann direkt in der DB ausgeführt. Aber Vorsicht: Wenn die Datei Fehler enthält wird der Container gestoppt.

Die Option, um die unser Befehl oben ergänzt werden muss lautet:

-v /lokaler/Pfad/zur/SQLOrdner:/docker-entrypoint-initdb.d

Am einfachsten ist es, sich direkt in dem Verzeichnis zu befinden, dass eingefügt werden soll. Dann kann das aktuelle Workingdirectory direkt mit dem PWD-Befehl (“Print working directory”) eingebunden werden. Die Syntax ist in der Powershell etwas anders als in der Bash:

Linux oder *nix/Bash

-v $(pwd):/docker-entrypoint-initdb.d

Windows/Powershell:

Eine einfache Beispieldatei zum Import findet sich hier.

Wenn also beispielsweise eine Datei setup.sql im aktuellen Verzeichnis liegt, könnte auf einem Windows-Rechner in der Powershell der Container gestartet werden mit:

Wenn die Beispieldatei importiert wurde, kann das mit den folgenden Befehlen überprüft werden:

Per SQL-Konsole verbinden:

postgres=# SELECT * FROM planeten;
 id |               name               |  bahnradius   | mondzahl |   masse   | durchmesser
----+----------------------------------+---------------+----------+-----------+-------------
  1 | Merkur                           | 5.7909176e+07 |        0 |   3.3e+23 |        2439
  2 | Venus                            | 1.0820893e+08 |        0 |  4.86e+24 |        6051
  3 | Erde                             | 1.4959789e+08 |        1 |  5.97e+24 |        6378
  4 | Mars                             | 2.2793664e+08 |        2 |  6.41e+23 |        3397
  5 | Jupiter                          | 7.7841203e+08 |       79 | 1.898e+27 |       71492
  6 | Saturn                           | 1.4267254e+09 |       82 |  5.68e+26 |       60267
  7 | Uranus                           | 2.8709722e+09 |       27 |   8.6e+25 |       25559
  8 | Neptun                           |  4.498253e+09 |       14 |  1.02e+26 |       24764
(8 rows)

Falls irgendetwas nicht geklappt hat geben oft die Logs Auskunft:

Zum Schluss: Container-Admin in 20s: ausschalten, wieder einschalten oder löschen

Irgendwann ist Feierabend und auch der Container soll schlafen. Wie das bei Containern grundsätzlich geht habe ich hier zusammengefasst, im Schnelldurchgang seien hier aber die wichtigsten Befehle nochmals genannt:

Welche Container wurden erzeugt, welche laufen und wie viel Speicher benötigen sie?

Welche Images wurden geladen und wie viel Speicher verwenden sie?

Einen laufenden Container ausschalten, um ihn später wieder zu nutzen (hier: Name somePostgres1, ginge auch per Hash/ID:

Einen gestoppten Container wieder aktivieren, um ihn am nächsten Tag weiter zu nutzen (hier: Name somePostgres1, ginge auch per Hash/ID):

Einen Container, den man nicht mehr benötigt, löschen (hier: Name somePostgres1, ginge auch per Hash/ID)

Ein Image, das man nicht mehr benötigt, löschen (hier: Name postgres:latest)

Fazit

Gerade in OpenSource-Projekten ist Postgres weit verbreitet. Ich in Container lässt es sich offensichtlich leicht einbinden. Die Container sind mit 327 MB relativ schlank. Jetzt fehlt nur noch eine eigene Applikation, die auf den Container zugreift. Dazu später mehr.

Quellen und offene Ressourcen (OER)

Die Ursprungstexte (als Markdown), Grafiken und zugrunde liegende Diagrammquelltexte finden sich (soweit möglich in weiterbearbeitbarer Form) in folgendem git-Repository:

https://gitlab.com/oer-informatik/db-sql/dbms.

Sofern nicht explizit anderweitig angegeben sind sie zur Nutzung als Open Education Resource (OER) unter Namensnennung (H. Stein, oer-informatik.de) freigegeben gemäß der Creative Commons Namensnennung 4.0 International Lizenz (CC BY 4.0).

Creative Commons Lizenzvertrag


  1. In diesen Beispielen mappe ich die Ports unterschiedlicher DBMS auf die lokalen Ports 3307-3312, da diese durch kein lokales DBMS belegt sind, aber direkt hinter dem MySQL-Standardport 3306 folgen. Jeder andere freie Port ist ebenso möglich, muss nur bei der DB-Verbindung anpasst werden.

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: