Die Datenbank Postgres bzw. PostgreSQL erfreut sich steigender Beliebtheit. Eben so verhält es sich mit Docker: Schließlich kann man eine neue Datenbank damit sehr einfach installieren und testen. In diesem Artikel wird Schritt für Schritt gezeigt, wie man Postgres mithilfe von Docker zum Laufen bekommt.
Warum Postgres mit Docker installieren?
PostgreSQL, auch als Postgres bezeichnet, ist ein objektrelationales Open-Source-Datenbankmanagementsystem. Entwickler entscheiden sich häufig für diese relationale Datenbank, da sie frei, stabil und flexibel ist. Tatsächlich sind PostgreSQL und MySQL die beliebtesten relationalen Datenbankverwaltungssysteme.
Heute ist Postgres eines der am weitesten verbreiteten “Docker-Images“, die in Containern laufen. Die Beliebtheit von containerisierten Datenbanken wird der Einfachheit zugeschrieben, mit der sie eingesetzt werden können. Anstatt eine zentrale Datenbank für viele Anwendungen zu haben, können Entwickler auch einen PostgreSQL-Container für jede Anwendung verwenden.
Container können je nach Bedarf flexibel erzeugt und auch wieder gelöscht werden. Dabei findet kein tiefer Eingriff in das Betriebssystem statt. Durch deren Isolation kommt es selbst bei der parallelen Verwendung mehrere Versionen nicht zu Konflikten. Daher sind Docker-Container ideal, um Erfahrung mit einer neuen Datenbank zu sammeln.
Voraussetzungen
Ihr braucht ein Linux-System, auf dem Docker in einer möglichst aktuellen Version installiert wurde. Docker-Compose ist ebenso zu empfehlen, aber nicht zwingend notwendig.
Variante 1: Postgres mit Plain Docker installieren/starten
Am einfachsten und schnellsten kann ein Container über die Kommandozeile gestartet werden. Dies ist für die ersten Schritte sinnvoll. Spätestens beim Arbeiten mit mehreren Containern bzw. komplexeren Konfigurationen wird das Arbeiten mit langen Kommandozeilenbefehlen unübersichtlich und fehleranfällig. Hier ist die Verwendung von Docker-Compose zu empfehlen (siehe Variante 2).
In beiden Fällen verwenden wir das offizielle Postgres-Image vom Docker-Hub. Die erzeugten Container sind somit identisch. Nur die Art des Startens variiert.
docker run --name my-postgres -e POSTGRES_PASSWORD=1234 -d postgres
Dieser Befehl startet einen neuen Container namens my-postgres. Würden wir dies nicht angeben, erzeugt Docker einen zufällig generierten Namen. Funktionell ist er irrelevant, aber wichtig, um den Container später wieder zu finden.
Mit dem Schalter -e übergeben wir eine Umgebungsvariable. Die Doku im Docker-Hub zeigt, welche für das jeweilige Image zur Verfügung stehen. Hier legen wir das Passwort für den Postgres-Benutzer auf 1234 fest.
Der Schalter -d steht für daemonize. Er startet den Container im Hintergrund. So kann unser Konsolenfenster weiter verwendet werden. Der Prozess läuft im Hintergrund weiter und ist nicht an die Konsolensitzung gebunden.
Schlussendlich benötigt Docker noch das Abbild. Da wir keinen Tag angegeben haben, wird latest (also die aktuellste Version) verwendet. Ein Blick in den Docker-Hub zeigt, welche Tags zur Verfügung stehen. Für die weitere Verwendung ist grundsätzlich zu empfehlen, zumindest einen Tag für die Hauptversion (z.B. postgres:12) zu setzen. Sonst werden ggf. unkontrolliert Updates eingespielt, falls das Image gelöscht wird.
Auf die Datenbank zugreifen
Mit docker ps prüfen wir zunächst, ob der Container erfolgreich gestartet wurde. Um ihn wieder zu finden, kommt der zuvor vergebene Name als Filter zum Einsatz:
$ docker ps | grep my-postgres
88c9e89f6b80 postgres "docker-entrypoint.s…" About a minute ago Up About a minute 5432/tcp my-postgres
Nun betreten wir den Container, um auf das Kommandozeilenwerkzeug von Postgres zuzugreifen. Dies geschieht durch docker exec. Auch an dieser Stelle wird der Containername verwendet:
$ docker exec -it --user postgres my-postgres bash
Bei Postgres ist die Angabe des Benutzers notwendig, da die Datenbank im Kontext des Linux-Nutzers arbeitet. Nun erhalten wir eine Shell im Container und können den Client mit psql starten:
$ docker exec -it --user postgres my-postgres bash
postgres@88c9e89f6b80:/$ psql
psql (12.3 (Debian 12.3-1.pgdg100+1))
Type "help" for help.
postgres=#
Ab hier können die Standardbefehle von Postgres genutzt werden. Wer diese noch nicht kennt, findet weiter unten einige Beispiele.
Variante 2: Postgres mit Docker-Compose
Docker-Compose bietet den Vorteil, die Konfiguration in eine Yaml-Datei auszulagern. Damit entfallen lange CLI-Aufrufe von docker run. Außerdem können mehrere Container gestartet und in Abhängigkeit zueinander gesetzt werden. Docker-Compose startet dann beispielsweise zuerst die Datenbank, danach einen Anwendungsserver.
Zunächst legen wir einen Ordner an
mkdir postgres
cd postgres
und erstellen darin eine Datei namens docker-compose.yml mit folgendem Inhalt:
version: '2.4'
services:
my-postgres:
image: postgres
environment:
- POSTGRES_PASSWORD=1234
Dies definiert einen Container mit dem Postgres-Image, in dem die Umgebungsvariable für das Passwort gesetzt wird. Die Yaml-Datei entspricht dem obigen docker run Aufruf aus Variante 1. Da sich die gesamte Konfiguration darin befindet, genügt zum Starten folgender kurzer Befehl:
docker-compose up -d
Eine Übersicht aller Container aus der Yaml-Datei mit ihrem Status liefert docker-compose ps. Um einen Befehl im Container auszuführen, existiert docker-compose exec analog zu docker exec. Als Parameter ist der definierte Name (hier ebenfalls my-postgres) anzugeben:
$ docker-compose exec --user postgres my-postgres bash
postgres@878b80da23f7:/$
Durch die Eingabe von psql wird eine Postgres-Shell gestartet. Alternativ können wir statt der Bash aber dies auch direkt als Befehl für docker-compose exec angeben. So entfällt dieser Schritt, da die Postgres-Shell direkt gestartet wird:
$ docker-compose exec --user postgres my-postgres psql
psql (12.3 (Debian 12.3-1.pgdg100+1))
Type "help" for help.
postgres=#
Postgres für ander Anwendungen erreichbar machen (Portfreigabe)
Unabhängig davon, für welche der beiden Varianten man sich entschieden hat: Postgres ist nur innerhalb seines Containers erreichbar. Andere Anwendungen auf dem PC können sie nicht nutzen. Dies ließe sich ändern, in dem man bei Docker-Compose einen weiteren Container hinzufügt. Er kann über den Hostname (hier my-postgres) auf die Datenbank zugreifen.
Gerade beim Testen und Entwickeln möchte man jedoch auch außerhalb des Containers eine Verbindung herstellen. Beispielsweise für einen Client wie DbWeaver. Oder eine lokal laufende Applikation, die noch nicht Containerisiert wurde. In diesem Falle helfen Portfreigaben.
Plain Docker (docker run)
Hier fügen wir mit dem Schalter -p eine Portfreigabe hinzu. Standardmäßig ist dies Port 5432, sodass -p „5432:5432“ an den obigen Befehl angehangen werden muss. Vorher den Container mit docker stop my-postgres beenden.
Docker-Compose
In der docker-compose.yml einfach ans Ende eine Liste ports anfügen. Das gesamte Compose-File sieht danach so aus:
version: '2.4'
services:
my-postgres:
image: postgres
environment:
- POSTGRES_PASSWORD=1234
ports:
- 5432:5432
Um die Änderungen anzuwenden, zunächst die Container mit docker-compose down beenden. Anschließend wie gehabt docker-compose up -d zum Starten ausführen.
Einstieg in Postgres: Erste Gehversuche auf der Kommandozeile
Nun haben wir eine Postgres-Instanz. Doch wie arbeite ich nun damit? Diese Frage dürfen sich vor allem jene stellen, die bisher noch nicht damit gearbeitet haben – unabhängig von Docker. Wer sich mit anderen großen Datenbanken wie MySQL auskennt, kann Teile seines Wissens auch hier nutzen. Doch gerade der Einstieg ist etwas ungewohnt: Bekannte Befehl wie SHOW DATABASES funktionieren hier nicht.
Im Folgenden wird davon ausgegangen, dass ihr eine Postgres-Shell wie oben beschrieben geöffnet habt. Sie ist daran zu erkennen, dass links postgres=# steht.
Aufbau von Postgres-Befehlen
Grundsätzlich beginnen die meisten Befehle mit einem umgekehrten Schrägstrich \ der als Backslash bezeichnet wird. Im Anschluss folgt ein Befehl. Hier steht die Wahl zwischen der Kurz- und Langschreibweise. Ähnliches kennt man bereits von CLI-Parametern, bei denen oft z.B. -u und –user angeboten wird.
Bei Postgres gibt es analog \h oder \help, um die Hilfe aufzurufen. Dadurch erhält man eine Liste aller Befehle. Benötigt man die Doku zu einem konkreten Befehl, kann dieser optional angegeben werden. Durch Eingabe von \h create table bzw. \help create table können wir also nachschlagen, wie eine Tabelle angelegt wird.
\l – Datenbanken auflisten
Eine Datenbank ist eine Sammlung von Tabellen, Informationen über diese Tabellen, Informationen über Benutzer und ihre Berechtigungen und vieles mehr. Einige dieser Datenbanken (und die darin enthaltenen Tabellen) werden von PostgreSQL automatisch aktualisiert, wenn man sie benutzt.
Der Befehl \l liefert uns eine kompakte Übersicht aller Datenbanken. Hängt man ein Plus an \l+ werden weitere Meta-Informationen wie z.B. die Größe dargestellt. Auch in einer frischen Postgres-Instanz existieren bereits einige automatisch erstellte Tabellen:
postgres=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+----------+----------+------------+------------+-----------------------
postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 |
template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres +
| | | | | postgres=CTc/postgres
(3 rows)
Datenbank anlegen und öffnen
In einer neuen Datenbankinstanz wird man zuerst eine Datenbank zur Speicherung der gewünschten Daten anlegen. Dies funktioniert entweder per SQL
postgres=# CREATE DATABASE mydb;
CREATE DATABASE
oder mit dem Kommandozeilenwerkzeug createdb <name>. Das CLI-Tool kann nur außerhalb der Postgres-Shell genutzt werden, weswegen es hier nicht zum Einsatz kommt.
Um sie zu öffnen, wird mittels \c eine Verbindung hergestellt. Dies ist vergleichbar mit USE <dbname> von MySQL/MariaDB:
postgres=# \c mydb
You are now connected to database "mydb" as user "postgres".
Tabelle anlegen
Das Anlegen einer Datenbank dürfte Benutzern mit SQL-Erfahrung leicht fallen. Hier gibt es kaum einen Unterschied zwischen anderen DBMS wie MySQL.
create table if not exists sensors(
id SERIAL PRIMARY KEY,
temp NUMERIC NOT NULL,
dateTime timestamp NOT NULL
);
Wurde die Datenbank erfolgreich angelegt, reagiert die Konsole mit der Zechenfolge CREATE TABLE.
\dt – Tabellen auflisten
Analog zu \l kann \dt (display table) alle Tabellen in einer Datenbank auflisten. Fügt man ein Plus + an den Befehl an, erscheinen zusätzliche Informationen wie Größe oder Beschreibung.
mydb=# \dt
List of relations
Schema | Name | Type | Owner
--------+---------+-------+----------
public | sensors | table | postgres
(1 row)
Datensätze einfügen
mydb=# insert into sensors(temp, datetime) values(25, now());
INSERT 0 1
mydb=# select * from sensors;
id | temp | datetime
----+------+----------------------------
1 | 25 | 2020-07-30 17:11:08.555956
(1 row)
Die SELECT-Anfragen sind wiederum identisch zu MySQL. Ein einfaches Beispiel:
mydb=# select * from sensors;
id | temp | datetime
----+------+----------------------------
1 | 25 | 2020-07-30 17:11:08.555956
(1 row)