Microsoft SQL Server mit Docker in Linux-Containern installieren
https://oer-informatik.de/docker-install-mssql
tl/dr; (ca. 20 min Lesezeit): Datenbankserver lassen sich auf so ziemlich jedem Betriebssystem aufsetzen. Es ist jedoch häufig wichtig, unterschiedliche Server, definierte Datenbestände oder reproduzierbare Systemumgebungen auf Knopfdruck zu erstellen: für diese Anwendungsfälle spielen Container eine herausragende Rolle. Im folgenden Infotext wird kurz beschrieben, wie ein Container mit einem MS-SQL-Server aufgesetzt und benutzt werden kann. Es werden Voraussetzungen für den Container zusammengetragen, die nötigen Dateien erstellt und Befehle genannt. 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.
Zu Beginn: ein Einzeiler - und fertig.
Das schöne an SOcker ist ja, dass alles so einfach ist. Einen Container mit einer aktuellen lauffähigen MS-SQL-Installation erzeuge und starte ich mit folgendem Einzeiler:
Das sind viele Argumente, die alle wichtig sind. Werfen wir einen genaueren Blick darauf:
Option | Bedeutung |
---|---|
--name XYZ --hostname=XYZ |
setzt den Containernamen auf XYZ (individualisierbar); sinnvoll ist Zahl am Ende des Containernamens, um mehrere Container des gleichen Images unterschieden zu können. Hostname setzt den internen Namen des Containers und sollte gleichlautend sein. |
-d |
detach: wird im Hintergrund weiter ausgeführt |
-p 3309:1433 |
Der Port 1433 des Containers wird auf den Port 3309 des hosts gemappt.1 |
-e ACCEPT_EULA=Y |
Die Lizenzabfrage wird automatisch akzeptiert |
-e MSSQL_SA_PASSWORD=_hier%1GutesPWnu+2en |
setzt eine (leicht auslesbare) Umgebungsvariable für das SQL-Server-Passwort (muss den MS-Regeln entsprechen). (Achtung: alte Bezeichnung war SA_PASSWORD ). Sicherheitsrisiko: kann ausgelesen werden und sollte geändert werden! |
mcr.microsoft.com/mssql/server:2022-latest |
nutzt das neuste Image des SQL-Servers; es können auch konkrete Versionen per tag übergeben werden, z.B. statt 2022-latest auch latest , dann werden ggf. auch neue Versionen geladen (die ggf. inkompatibel sind), Übersicht der Versionen im Docker Hub zu MS SQL-Server |
Es wird eine Containerinstanz erzeugt, die im Hintergrund läuft. Über die Option -d wird der Container als detached ausgeführt.
Der Rückgabewert dieses Befehls (in meinem Fall 631509...
) ist ein Hashwert, über den der Container identifizierbar ist. Im UML-Sequenzdiagramm sieht der Ablauf etwa wie folgt aus:

Die Ausgabe des obigen docker run
-Befehls sieht bei erstmaliger Ausführung etwa so aus:
Unable to find image 'mcr.microsoft.com/mssql/server:2022-latest' locally
2022-latest: Pulling from mssql/server
210a236fbb96: Pull complete
4d3b5ee6a318: Pull complete
b97468a53f24: Pull complete
Digest: sha256:f57d743a99a4003a085d0fd67dbb5ecf98812c08a616697a065082cad68d77ce
Status: Downloaded newer image for mcr.microsoft.com/mssql/server:2022-latest
631509e7b033...
Zunächst startet der Container und in diesem der SQL Server. In der folgenden Übersicht sollte unter Status
“Up” erscheinen.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
631509e7b033 mcr.microsoft.com/mssql/server:latest "/opt/mssql/bin/perm…" About an hour ago Up About an hour 0.0.0.0:3309->1433/tcp mssql22-1
Container und SQL-Server konfigurieren
Der Container mit MS-SQL-Server läuft nun im Hintergrund. Zunächst sollte das Passwort geändert werden, da es sowohl in der Prozesshistorie des Host-Systems als auch in den Umgebungsvariablen des Containers gespeichert wurde. Mit einem einfachen Befehl kann man es dem Container entlocken:
Für unsere ersten Gehversuche ist das natürlich ganz praktisch, dass wir das Passwort in einer Umgebungsvariablen haben. Für Produktivsysteme ist das aber ein No-Go. Wir müssen also einen SQL-Befehl absetzen, um das Passwort zu ändern. Praktisch, SQL-Befehle wollten wir ja ohnehin nutzen. Da wir die Eingabe der Konsole nutzen wollen, kommt es drauf an, aus welchem System heraus wir arbeiten. Es wird interaktiv zunächst nach dem alten, dann nach einem neuen Passwort gefragt.
In der Powershell/Windows:
In der Bash/Linux,OSX:
sh:/$ docker exec -it mssql22-1 /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "$(read -sp "Enter current SA password: "; echo "${REPLY}")" -Q "ALTER LOGIN SA WITH PASSWORD=\"$(read -sp "Enter new SA password: "; echo "${REPLY}")\""
Wenn man diesen Befehl analysiert, lernt man eine Menge über Wege, den SQL-Server-Container zu administrieren.
Eine Linux-Kommandozeile, die ein Programm (oben: sqlcmd) ausführt erhält man mit:
-it
sorgt dafür, dass die Konsole interaktiv und im Vordergrund bleibt (auf Eingaben wartet)mssql22-1
ist der Containername und muss ggf. angepasst werdenstatt dem SQL-Kommandozeilentool
/opt/mssql-tools/bin/sqlcmd
kann jeglicher im Container vorhandene Befehl genutzt werden (z.B. auchbash
)
alle weitern Argumente legen die Optionen für sqlcmd
fest:
-S localhost
: das Tool soll den lokalen SQL-Server nutzen-U SA
: der Username lautetSA
(Standard bei MS-SQL)-P
: das Anmeldepasswort, hier etwas tricky: die folgenden Befehle fragen es von der Konsole ab und setzen es hier ein.in der Shell/*nix:
"$(read -sp "Enter current SA password: "; echo "${REPLY}")"
in der Powershell/Windows:
"$(read-host "Enter current SA password")"
-Q
: diese Query (SQL-Befehl) soll ausgeführt werden. Es wird doch nicht einfach die Query mit Passwort übergeben, etwa"ALTER LOGIN SA WITH PASSWORD='text'
, sondern wieder (wie oben) das Passwort über die jeweilige Konsole abgefragt.
SQL Befehlszeile im Container öffnen
Der Befehl zum Passwortändern zeigt auch unmittelbar einen Weg, um die SQL-Commandozeile zu erhalten. In der Container-Bash:
CT:/$ /opt/mssql-tools/bin/sqlcmd -S localhost -U SA
Nach Eingabe des Passworts kann man zeilenweise SQL-Befehle eingeben und mit go
abschließen:
1> SELECT Name from sys.Databases
2> go
Name
--------------------------------------------------------------------------------------------------------------------------------
master
tempdb
model
msdb
testDB
(5 rows affected)
En dieser Stelle könnten jetzt die Befehle folgen, die eigene Datenbanken erzeugen (CREATE DATABASE...
), mit Tabellen befüllen (CREATE TABLE...
), diese mit Datensätzen versehen (INSERT INTO ...
) und schließlich auswerten (SELECT
). Wenn alle Befehle mit go
abgeschlossen werden hat man bald eine befüllte nutzbare Datenbank. Aber in der Konsole ist das aber alles etwas mühsam. Wir brauchen ein Frontend.
Verbindung mit einem DB-Frontend aufbauen
Der so eingerichtete und gestartete Container kann direkt über ein Frontend (wie HeidiSQL, DBeaver o.ä.) genutzt werden. Die Einstellungen sind eigentlich in allen Frontends identisch:
Hostname: 127.0.0.1 (die lokale Maschine)
Port: 3309 (wie oben angegeben - ggf. anpassen!)
Benutzername: sa (SQL-Server-Standard)
Passwort: (wie oben eingegeben)

Schick. Mit dem SQL-Frontend können wir jetzt schon ganz gut arbeiten. Insbesondere, wenn Daten nur lesend verwendet werden oder immer gleiche Testdaten genutzt werden sollen, wäre es sehr praktisch, wenn wir direkt eine gefüllte Datenbank erhalten könnten.
Das ganze geht natürlich auch mit dem MS-SQL-Managementstudio. Hier muss aus unerfindlichen Gründen der Port mit Komma abgetrennt werden:

Einen Docker-Container erzeugen, der bereits Daten enthält
Mit dem obigen Beispiel wurde ein Container mit einer leeren Datenbank erzeugt. Es wäre jedoch schön, wenn wir eine Möglichkeit hätten, direkt einen befüllten Container zu erzeugen. Dazu müssen wir ein eigenes Docker-Image erstellen. Hierzu wird eine Art Anleitung erstellt, was alles in das Image aufgenommen werden soll - diese Anleitung nennt sich Dockerfile
. Angelehnt an das Beispiel hier benötigen wir in einem Verzeichnis insgesamt drei Dateien:
Dockerfile
: Diese Datei beschreibt, welche Komponenten in dem Image enthalten sein sollenentrypoint.sh
: Legt fest, was beim Containerstart passieren soll. (MS-SQL starten undconfigure.sh
aufrufen). Diese Datei wird automatisch beim Erstellen des Containers gestartetconfigure-db-sh
: Prüft, ob der MS-SQL-Server gestartet ist und startet dann den Import vonsetup.sql
setup.sql
: Enthält die SQL-Befehle, die zu Beginn ausgeführt werden sollen (also den DB-Dump)
Der Bauplan des Images: Dockerfile
In der Datei namens Dockerfile
wird festgelegt auf welcher Basis mit welchen Anpassungen das Image erstellt wird. Die Kommentare erklären hoffentlich das wesentliche. Das File kann auch hier direkt heruntergeladen werden.
Inhalt der Datei Dockerfile
:
# Angepasst auf Basis von https://github.com/microsoft/mssql-docker/tree/master/linux/preview/examples/mssql-customize
# Basis ist das aktuelle MS-SQL Image
FROM mcr.microsoft.com/mssql/server:2022-latest
# Für config-Zwecke kann ein Editor nicht schaden, dazu braucht's root
USER root
RUN apt update && apt install -y vim nano
# Alle Dateien, die im Verzeichnis des Dockerfiles liegen werden in
# den Container kopiert (also setup.sql, entrypoint.sh und configure-db.sh)
RUN mkdir -p /usr/config
WORKDIR /usr/config
COPY . /usr/config
# Damit die Dateien ausführbar sind benötigen sie noch Rechte
RUN chmod +x /usr/config/entrypoint.sh
RUN chmod +x /usr/config/configure-db.sh
# Bei Container-Erstellung soll dieses Script ausgeführt werden:
ENTRYPOINT ["./entrypoint.sh"]
# Damit unter "docker ps" ausgegeben wird, ob die DBs online sind ("healthy")
# Prüft dieses Script die Datenbank
HEALTHCHECK --interval=20s --timeout=5s --start-period=20s --retries=7 CMD /opt/mssql-tools/bin/sqlcmd -h -1 -t 1 -U sa -P $MSSQL_SA_PASSWORD -Q "SET NOCOUNT ON; Select SUM(state) from sys.databases" || exit 1
Die Importroutine configure-db.sh
Die configure-db.sh
ist auf den aktuellen MS-SQL-Server zugeschnitten. Sie prüft nach 20s alle 10s, ob der SQL-Server schon online ist und importiert dann den Inhalt aller *.sql
-Dateien im aktuellen Ordner.
Das File kann auch hier direkt heruntergeladen werden.
Inhalt der Datei configure-db.sh
:
#!/bin/bash
# customized from https://github.com/microsoft/mssql-docker/blob/master/linux/preview/examples/mssql-customize/
# added ability to import multiple sql-files
DBSTATUS=1 # 0 means all DBs are online
ERRCODE=1 # 0 means no errors
LOOPCOUNT=0 # how many 10s sequences are needet to boot DBMS?
LOOPCONDITION=1 # becomes 0 if DBMS is reachable
echo ""
echo ""
echo "[entrypoint SQL] Importscript for SQL-Files"
echo "#=========================================="
echo ""
echo "Waiting 20s for DB to start..."
sleep 20
echo "[entrypoint SQL] Trying to connect to DB prior to start imports"
while [ ${LOOPCONDITION} -eq 1 ]; do
if [ ${LOOPCOUNT} -lt 20 ]; then # trying max 20 times (200s)
LOOPCOUNT=$((LOOPCOUNT+1)) # incrementing no of loops
# This query checks, whether all db are online, see
# https://docs.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-databases-transact-sql?view=sql-server-2017
DBSTATUS=$(/opt/mssql-tools/bin/sqlcmd -h -1 -t 1 -U sa -P "$MSSQL_SA_PASSWORD" -Q "SET NOCOUNT ON; Select SUM(state) from sys.databases")
ERRCODE=$? # in case of errors
echo "[entrypoint SQL] Checked DBMS: Loop ${loopcount} Waiting for DBs: ${DBSTATUS} / Error-Code ${ERRCODE} (0 means ok)"
else
echo "[entrypoint SQL] Tried 20 times (200 seconds) - Imports aborted"
exit 1
fi
if [ ${DBSTATUS} -eq 0 ] && [ ${ERRCODE} -eq 0 ]; then
LOOPCONDITION=0
echo "Everthing seems fine - starting Import"
else
echo "[entrypoint SQL] Wait another 10s"
sleep 10
fi
done
echo ""
echo "#==================================================="
echo "Trying to gather all SQL-files in working-directory."
echo ""
for FILENAME in ./*.sql; do #Regex Filterung nach Dateiendung und Sortierung
echo ""
echo "[entrypoint SQL] File ${FILENAME}"
echo "----------------------------------------"
echo "Found file ${FILENAME}, sanitizing name (whitespaces)"
FILENAME=$(echo "${FILENAME}" | sed 's/ /\\ /g')
echo "Importing file as ${FILENAME}"
/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "$MSSQL_SA_PASSWORD" -d master -i "${FILENAME}"
echo "Finished import of ${FILENAME}"
done
echo ""
echo ""
echo "[entrypoint SQL] All imports finished - container status should change to healthy now"
echo "#==================================================================="
Das Startscript entrypoint.sh
Gegenüber den recht langen Dateien Dockerfile
und configure-db.sh
ist die entrypoint.sh
unspektakulär: sie startet nur die Importdatei und den SQL-Server. Auch dieses File kann auch hier direkt heruntergeladen werden.
Inhalt der Datei entrypoint.sh
:
Die Importdatei setup.sql
Schließlich wird noch eine SQL-Datei benötigt, die alle Befehle für Datenstrukturen (DDL) und Dateninhalte (DML) enthält. Sie muss die Dateiendung *.sql
haben und im aktuellen Ordner liegen. Wenn mehrere Dateien exitieren, werden sie in alphabetischer Reihenfolge importiert. Ein einfaches Beispiel ist hier abgedruckt, es kann aber natürlich jeder MS-SQL-Datenbankdump verwendet werden. Auch dieses File kann hier direkt heruntergeladen werden.
Inhalt der Datei setup.sql
:
CREATE DATABASE importtest;
go
CREATE TABLE importtest.dbo.planeten(id int IDENTITY(1,1) PRIMARY KEY, name CHAR(255), bahnradius real, mondzahl INT, masse real, durchmesser real );
go
INSERT INTO importtest.dbo.planeten (name, bahnradius, mondzahl, masse, durchmesser) VALUES('Merkur', 57909175, 0, 3.3e23, 2439), ('Venus', 108208930, 0, 4.86e24, 6051), ('Erde', 149597890, 1, 5.97e24, 6378), ('Mars', 227936640, 2, 0.641e24, 3397), ('Jupiter', 778412020, 79, 1898e24, 71492), ('Saturn', 1426725400, 82, 568e24, 60267), ('Uranus', 2870972200, 27, 86e24, 25559 ), ('Neptun', 4498252900, 14, 102e24, 24764);
go
Bauen des ersten eigenen Docker-Images:
Wenn sich alle vier Dateien (und die eigene Konsole) in einem Verzeichnis befinden kann das neue Image (hier mit dem von mir vergebenen Namen mssql-custom
) über den folgenden Befehl erstellt werden (der Punkt am Ende ist wichtig!):
Wie so häufig verrät uns die Ausgabe eine ganze Menge über die Hintergründe von Docker:
[+] Building 4.1s (12/12) FINISHED
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 32B 0.0s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for mcr.microsoft.com/mssql/server:2022-latest 0.0s
=> [1/7] FROM mcr.microsoft.com/mssql/server:2022-latest 0.0s
=> [internal] load build context 0.1s
=> => transferring context: 781B 0.0s
=> CACHED [2/7] RUN apt update && apt install -y vim nano 0.0s
=> CACHED [3/7] RUN mkdir -p /usr/config 0.0s
=> CACHED [4/7] WORKDIR /usr/config 0.0s
=> [5/7] COPY . /usr/config 0.2s
=> [6/7] RUN chmod +x /usr/config/entrypoint.sh 1.8s
=> [7/7] RUN chmod +x /usr/config/configure-db.sh 1.0s
=> exporting to image 0.5s
=> => exporting layers 0.3s
=> => writing image sha256:2726f94e6126e3f6c109b8ff9ac72f60c1dbcd780582978c9076e5c2911bddca 0.0s
=> => naming to docker.io/library/mssql-custom 0.0s
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
Wir erkennen sieben Zeilen aus dem Dockerfile
, die jeweils durchnummeriert (bis [7/7]
) dargestellt werden. Jede dieser Zeilen stellt eine Schicht, einen Layer dar, der für andere Images wiederverwendet werden könnte, deren Dockerfile
sich bis dahin gleicht. Das ist einer der Gründe, warum Docker so sparsam mit Ressourcen umgehen kann. Alle Layer werden am Ende zu dem neuen Image (wie gewünscht benannt: mssql-custom
)zusammengefügt.
Das Image wurde erstellt und sollte nun per docker image ls
angezeigt werden.
REPOSITORY TAG IMAGE ID CREATED SIZE
mssql-custom latest 4e06b70b0d05 22 seconds ago 1.47GB
Ab hier wie immer: Einen Container erstellen und starten
Wir haben ein Image (mssql-custom
), wir benötigen einen laufenden Container. Das kennen wir ja schon. Wir müssen nur darauf achten, dass der Name (ich wähle mssql22c1
) und der externe Port (ich wähle 3314
) ein anderer ist wie bei unseren anderen Containern.
Relativ zügig wird auch der Hashwert zurückgegeben, der Container scheint also gebaut und zu starten.
f9452a082fa65645d02897527bfeef2a0cb35da0303bc9244f27acc1b262479d
Dem Container beim Starten zuschauen
Im Inneren des Containers startet jetzt MS-SQL - und wenn das online ist, wird der Import gestartet. Dass der Container startet erkennen wir an docker ps
(oder mit der Langform des Befehls: docker container ls
):
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f9452a082fa6 mssql-custom "./entrypoint.sh" 6 seconds ago Up 4 seconds (health: starting) 0.0.0.0:3314->1433/tcp mssql22c1
0f838125b001 mcr.microsoft.com/mssql/server:2022-latest "/opt/mssql/bin/perm…" 5 hours ago Up 5 hours 0.0.0.0:3309->1433/tcp mssql22-1
In der Spalte STATUS
wird auch ausgegeben, dass der von uns eingebaute HEALTHY
-Test im Dockerfile wohl noch nicht erfüllt wurde. Das System scheint noch zu starten. Wenn wir es etwas genauer wissen wollen, dann schauen wir mal in die Log-Dateien des Containers:
Importiere SQL-Kommandos
Starte SQL-Server
[entrypoint SQL] Importscript for SQL-Files
#==========================================
Am Anfang erkennen wir nur die Meldungen aus entrypoint.sh
und configure-db.sh
. Nach einiger Zeit (bei mit ca. 32s) versucht er das erste Mal, die SQL-Dateien zu importieren, aber MS-SQL ist noch nicht gestartet:
[entrypoint SQL] Trying to connect to DB prior to start imports
[entrypoint SQL] Checked DBMS: Loop Waiting for DBs: 0 / Error-Code 0 (0 means ok)
Everthing seems fine - starting Import
Dann folgen nach ca. 40s zahllosen MS-SQL-Meldungen, an deren Ende nach ca. 1min die Erfolgsmeldung des Imports steht:
[entrypoint SQL] File ./setup_importtest_mssql.sql
----------------------------------------
Found file ./setup_importtest_mssql.sql, sanitizing name (whitespaces)
Importing file as ./setup_importtest_mssql.sql
...
(8 rows affected)
Finished import of ./setup_importtest_mssql.sql
[entrypoint SQL] All imports finished - container status should change to healthy now
#===================================================================
Acht Datenzeilen wurden importiert (8 rows affected)
.
Der Container soll jetzt healthy
sein, schauen wir doch mal nach:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f9452a082fa6 mssql-custom "./entrypoint.sh" 3 minutes ago Up 3 minutes (healthy) 0.0.0.0:3314->1433/tcp mssql22c1
Mit angepasster Port-Bezeichnung klappt die Verbindung mit dem Frontend in einem Rutsch.
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 mssql22c1
, ginge auch per Hash/ID:
Einen gestoppten Container wieder aktivieren, um ihn am nächsten Tag weiter zu nutzen (hier: Name mssql22c1
, ginge auch per Hash/ID):
Einen Container, den man nicht mehr benötigt, löschen (hier: Name mssql22c1
, ginge auch per Hash/ID)
Ein Image, das man nicht mehr benötigt, löschen (hier: Name server:2022-latest
)
Fazit
So langsam fängt Docker an Spaß zu machen: wir können einen Container mit einem DBMS aus vorhandenen Images erstellen, per Console darauf zugreifen, ein DB-Frontend damit verknüpfen. Das ist alles ganz nett.
Wirklich mächtig wird es aber erst dadurch, dass wir per Knopfdruck ein befülltes Datenbanksystem erhalten können. Wenn wir den Code dafür noch versionieren, haben wir alle Versprechen umgesetzt, die sich hinter dem Buzzword “Infrastructure as Code (IaS)” verstecken.
Links und weitere Informationen
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).
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 folgen. Jeder andere freie Port ist ebenso möglich, muss nur bei der DB-Verbindung anpasst werden.↩