Software-Entwicklung im Team ist ohne Einsatz eines Versionsverwaltungssystems (VCS: Version Control System), wie Subversion oder Git, kaum denkbar. Das gemeinsam genutzte Quelltext-Repository erleichtert die Koordinierung zwischen den Entwicklern und es lassen sich problemlos ältere Versionsstände wiederherstellen.
Anhand der Revisionen der Versionsverwaltung wissen wir genau, welcher Programmcode auf unseren unterschiedlichen Stage-Umgebungen (z.B. Entwicklung, Test, Schulung, Produktion) gerade installiert ist. Leider ist es bei vielen Projekten allerdings nicht möglich, die Datenbank auf „Knopfdruck“ zu jeder Revision zu erstellen, weil die Skripte für die Erzeugung der Datenbank nicht gleichwertig revisioniert werden.
Moderne Web-Frameworks beinhalten bereits Werkzeuge für Datenbank-Migrationen, wie beispielsweise Active Record Migrations (Ruby On Rails) oder Evolutions (Play-Framework). Die bekanntesten unabhängigen Vertreter Liquidbase und Flyway werden in dem Heise-Artikel Kontinuierliche Datenbankmigration mit Liquibase und Flyway vorgestellt. In meinem aktuellen Projekt mit einer PostgreSQL-Datenbank habe ich mich für Flyway entschieden, das tatsächlich eine solide Hilfe beim Umgang mit der Datenbank ist.
Flyway auf dem Entwicklungssystem ausführen
Flyway lässt sich auf verschiedene Arten (Command-line, Java-API, Maven, Gradle, Ant, SBT) nutzen. Ich habe zunächst einmal die ‚First Steps‘ der Gradle-Variante durchgeführt, die eine H2-Datenbank verwendet.
Unsere SQL-Skripte werden wegen des Einsatzes von Gradle in dem Verzeichnis src/main/resources/db/migration/
abgelegt. Flyway bietet uns folgende Befehle an, mit der Datenbank zu interagieren:
gradle flywayMigrate
Migriert Schema zur letzten Version und erstellt ggf Metadaten-Tabelle schema_versiongradle flywayClean
Löscht alle Objekte der Datenbankgradle flywayInfo
Status-Informationen zu den Migrationen ausgebengradle flywayValidate
Validiert die durchgeführten Migrationengradle flywayBaseline
Anfang der Migration bei nicht leerer Datenbank mit Baseline markierengradle flywayRepair
Repariert die Metadaten-Tabelle schema_version
Anpassung für eine neu angelegte PosgreSQL-Datenbank
Nachdem das H2-Beispiel problemlos funktioniert hat, sollen die Migrationskripte jetzt gegen eine PostgreSQL-Datenbank ausgeführt werden. Das Gradle-Skript habe ich folgendermaßen angepasst:
buildscript { repositories { mavenCentral() } dependencies { classpath 'org.postgresql:postgresql:9.4-1200-jdbc41' classpath 'org.flywaydb:flyway-gradle-plugin:3.2.1' } } apply plugin: 'org.flywaydb.flyway' apply plugin: 'java' flyway { url = 'jdbc:postgresql://localhost/myDatabase' user = 'myUser' password = 'myPassword' }
Wenn wir eine frische leere Datenbank haben, dann sollte die Migration mit gradle flywayMigrate
funktionieren und folgende Ausgabe in Verbindung mit gradle flywayInfo
machen:
bruno@dev:~/$ gradle flywayInfo :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :compileTestJava UP-TO-DATE :processTestResources UP-TO-DATE :testClasses UP-TO-DATE :flywayInfo +---------+---------------------+---------------------+---------+ | Version | Description | Installed on | State | +---------+---------------------+---------------------+---------+ | 1 | Create person table | | Pending | | 2 | Add people | | Pending | +---------+---------------------+---------------------+---------+ BUILD SUCCESSFUL Total time: 2.312 secs bruno@dev:~/$ gradle flywayMigrate :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :compileTestJava UP-TO-DATE :processTestResources UP-TO-DATE :testClasses UP-TO-DATE :flywayMigrate BUILD SUCCESSFUL Total time: 2.392 secs bruno@dev:~/$ gradle flywayInfo :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :compileTestJava UP-TO-DATE :processTestResources UP-TO-DATE :testClasses UP-TO-DATE :flywayInfo +---------+---------------------+---------------------+---------+ | Version | Description | Installed on | State | +---------+---------------------+---------------------+---------+ | 1 | Create person table | 2016-02-27 09:04:55 | Success | | 2 | Add people | 2016-02-27 09:04:55 | Success | +---------+---------------------+---------------------+---------+ BUILD SUCCESSFUL Total time: 2.374 secs
Anpassung für eine bestehende PosgreSQL-Datenbank
Falls wir Flyway für eine bestehende Datenbank einsetzen wollen, dann muss zunächst eine Baseline mit gradle flywayBaseline
gesetzt werden. Das kann auch nötig sein, wenn eine neu erstellte PostgreSQL-Datenbank bereits eine Extension beinhaltet (wie PostGIS) und damit nicht wirklich leer ist (siehe auch Stackoverflow). Eine Flyway-Migration antwortet bei fehlender Baseline mit:
ERROR: Found non-empty schema "public" without metadata table! Use baseline() or set baselineOnMigrate to true to initialize the metadata table.
Alternativ zu dem Befehl gradle flywayBaseline
können wir in unserer Flyway-Konfiguration auch den Parameter flyway.baselineOnMigrate=true
setzen. Bei Einsatz einer Baseline müssen wir darauf achten, dass damit die Version 1 der SQL-Skripte vergeben ist und wir bei einer folgenden Version, wie 1.1 oder 2, starten müssen:
bruno@dev:~/$ ls -Al src/main/resources/db/migration/ total 16 -rw-r--r-- 1 bruno staff 77 27 Feb 01:10 V1.1__Create_person_table.sql -rw-r--r-- 1 bruno staff 156 27 Feb 01:11 V1.2__Add_people.sql bruno@dev:~/$ gradle flywayBaseline :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :compileTestJava UP-TO-DATE :processTestResources UP-TO-DATE :testClasses UP-TO-DATE :flywayBaseline BUILD SUCCESSFUL Total time: 2.42 secs bruno@dev:~/$ gradle flywayMigrate :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :compileTestJava UP-TO-DATE :processTestResources UP-TO-DATE :testClasses UP-TO-DATE :flywayMigrate BUILD SUCCESSFUL Total time: 2.438 secs bruno@dev:~/$ gradle flywayInfo :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :compileTestJava UP-TO-DATE :processTestResources UP-TO-DATE :testClasses UP-TO-DATE :flywayInfo +---------+-----------------------+---------------------+---------+ | Version | Description | Installed on | State | +---------+-----------------------+---------------------+---------+ | 1 | << Flyway Baseline >> | 2016-02-27 10:36:42 | Baselin | | 1.1 | Create person table | 2016-02-27 10:36:59 | Success | | 1.2 | Add people | 2016-02-27 10:36:59 | Success | +---------+-----------------------+---------------------+---------+ BUILD SUCCESSFUL Total time: 2.387 secs
Flyway auf Server ausführen
Auf den Servern wird nicht die gesamte Gradle-Installation benötigt, sondern lediglich Flyway als Command-line Tool. Nach den folgenden Schritten ist Flyway auch schon auf der Kommandozeile einsatzbereit:
- Herunterladen von Flyway (ohne JRE 12 MB, mit JRE 154 MB)
- Entpacken der Flyway-Datei
- Datenbank-Parameter in Konfiguration eintragen:
conf/flyway.conf
- flyway.url=jdbc:postgresql://localhost/myDatabase
- flyway.user=myUser
- flyway.password=myPassword
- flyway.baselineOnMigrate=true
- SQL-Skripte kopieren in das Verzeichnis:
sql/
- Migration durchführen:
./flyway migrate
wget https://bintray.com/artifact/download/business/maven/flyway-commandline-3.2.1-linux-x64.tar.gz tar -zxvf flyway-commandline-3.2.1-linux-x64.tar.gz cd flyway-3.2.1/ bruno@xyz:~/flyway-3.2.1$ ls -Al total 148 drwxrwxr-x 2 bruno bruno 4096 Feb 22 20:33 conf drwxrwxr-x 2 bruno bruno 4096 Feb 22 20:33 drivers -rwxr--r-- 1 bruno bruno 1441 Mar 20 2015 flyway -rw-r--r-- 1 bruno bruno 1132 Mar 20 2015 flyway.cmd drwxrwxr-x 2 bruno bruno 4096 Feb 22 20:33 jars drwxr-xr-x 4 bruno bruno 4096 Mar 20 2015 jre drwxrwxr-x 2 bruno bruno 4096 Feb 22 20:33 lib -rw-r--r-- 1 bruno bruno 107852 Mar 20 2015 LICENSES-THIRD-PARTY.txt -rw-r--r-- 1 bruno bruno 562 Mar 20 2015 LICENSE.txt -rw-r--r-- 1 bruno bruno 950 Mar 20 2015 README.txt drwxrwxr-x 2 bruno bruno 4096 Feb 22 20:33 sql nano conf/flyway.conf
Die eigentliche Migration führe ich dann mit SCP und SSH durch, indem zuerst die SQL-Dateien auf den Server kopiert werden und anschließend auf dem Server Flyway ausgeführt wird.
scp src/main/resources/db/migration/* bruno@123.134.156.167:flyway-3.2.1/sql/ ssh bruno@123.134.156.167 "cd flyway-3.2.1; ./flyway migrate"
Gerade bei Einsatz von Continuous Integration und Continuous Delivery sind SQL-Skripte für Datenbank-Migration unerlässlich. Ein professionelles Werkzeug, wie Flyway, ist glücklicherweise auch leicht in ein bestehendes Projekt zu integrieren.