Praxis Dokumentation: Docker bei Hetzner aufsetzen

Einführung

Ich wurde von einem bekannten gefragt, wie ich die Konfiguration bei Hetzner mit Docker genau durchgeführt. Aus dieser Diskussion ist dieser Blog Artikel entstanden, in dem ich detailliert beschreibe, wie ich diese Lösung aufgebaut habe.

Einen groben High-Überblick hatte ich ja bereits unter den folgenden Artikel zu meinen Überlegungen wurde im letzten Monat bereits unter gegeben: http://www.vr-worlds.de/vr-worlds-de-jetzt-live-auf-shipyard-docker-und-hetzner-infrastruktur/

Auswahl eines Anbieters

Ich wollte weg von dem aktuellen Amazon Konfiguration und hin zu einer selbstgehosteten Lösung. Nach einigen prüfen verschiedener Angebote habe ich mich entschlossen es mit Hetzner(https://www.hetzner.de/) als Anbieter zu probieren, da dieser in Deutschland basiert ist und einen guten Eindruck gemacht hatte. Eine ansprechende Alternative mit ähnlichen Angeboten gab es aber natürlich auch bei 1&1 (https://www.1und1.de/).
Ausschlaggebend war, das man relativ neue aber gebrauchte Server bei Hetzner mieten kann:
https://serverboerse.de/index.php?country=DE

Hier fand ich ein günstiges Angebot, dass für meine Ansprüche ausreichend war.
Aus meiner bisherigen Erfahrung kann ich sagen, dass ich von Hetzner bisher nicht enttäuscht wurde.

Allerdings möchte ich nicht behaupten eine komplette Marktanalyse gemacht zu haben und kann mir durchaus vorstellen, dass es andere Anbieter mit besseren Angeboten gibt. Der Markt auch wenn man sich nur auf deutsche Anbieter beschränkt ist unüberschaubar groß.

Architektur Überlegungen Basis Betriebsystem

Als nächstes stand für mich an, welches Betriebssystem. Windows fiel dabei sehr schnell raus, da dies hohe Lizenzkosten im Monat verursacht hätte, zudem bin ich seit 1992 mit Unix (AIX) und Linux Systemen vertraut und hatte daher keine Berührungsängste.

Am Ende habe ich mich für CentOS 7.3 entschieden, da es in folgenden Punkten mich überzeugte: (https://www.centos.org/)

  • Von Hetzner unterstützt
  • Freie Verfügbarkeit
  • Hohe Stabilität
  • Aktive Community
  • Guten Enterprise Support (durch die nähe zu Red Hat Linux)

Die einzige ernsthafte Alternative wäre für mich noch „Debian“ gewesen.

Überlegung Basisaufbau von Docker

Für meine Webseite habe ich folgende Basisfunktionen mir vorgestellt.

Als Dienste soll folgendes zur Verfügung stehen:

  • Ghost als Blog-Service
  • ownCloud als Dateispeicher, vor allem um mit Partnern und Kunden Dateien auszutauschen
  • GITLab um zusammen mit Partnern an Software gemeinsam zu Entwickeln. Zudem hat es minimale Wiki und TaskPlanung, um die Projektentwicklung zu koordinieren
  • Shipyard damit auch Remote eine Verwaltung der Docker Container über eine Oberfläche möglich ist.

Damit diese Dienste über einen Port (80 für HTTP bzw. 443 für HTTPS) ansprechbar sein sollen, wird NGINX als Proxy verwendet.

Als Datenbank für Ghost und ownCloud soll MariaDb verwendet werden.
Im Schaubild stellt sich dies dann folgendermaßen dar:

Damit das alles korrekt zusammen spielt, müssen die Komponenten in folgender Reihenfolge aufgesetzt werden:

  • Basiskonfiguration von CentOS und Installation von Docker
  • Installation von MariaDb
  • Installation von Ghost als Blog Dienst und anbinden an MariaDb
  • Installation der OwnCloud und anbinden an MariaDb
  • Installation GitLab
  • Installation der NGINX und einrichten des Proxy Routings
  • Shipyard und Einbindung in NGINX
  • Zu guter letzt natürlich noch die Firewall einschränken, damit möglichst wenig Ports offen sind.

Damit man mit der Konfiguration beginnen kann, benötigt man die folgenden beiden Tools, sofern man auf Windows arbeitet. (Wird Linux als Client verwendet müssen keine zusätzlichen Tools installiert werden, da ssh und scp bereits vorhanden sind)

Basiskonfiguration von CentOS und Installation von Docker

Nach der Installation von CentOS sollte zunächst ein Update durchgeführt werden:

yum -y update  

Anschließend kommt die Installation von Docker

yum -y install docker docker-registry  

Damit Docker auch automatisch beim neustart läuft, muss der Docker Service noch eingerichtet werden:

systemctl enable docker.service  
systemctl start docker.service  

Anschließend kann man prüfen, ob alles ordentlich konfiugriert ist in dem man den Status abfragt:

root@CentOS-73-64-minimal ~]# systemctl status docker.service  
docker.service - Docker Application Container Engine  
   Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
   Active: active (running) since Fri 2017-03-31 22:14:56 CEST; 1 months 0 days ago
     Docs: http://docs.docker.com
 Main PID: 13307 (dockerd-current)
   Memory: 107.5M
   CGroup: /system.slice/docker.service

Konfiguration der MariaDB und Einführung in Grundlegende Docker Befehle

Damit Ghost und die ownCloud auf eine zentrale Datenbank zugreifen können, muss diese als erster Container installiert werden.

Auch wenn Docker viel Arbeit abnimmt sind ein paar Vorüberlegungen zu treffen: (Es ist hilfreich sich auch die Dokumentation vom Docker Hub hierzu anzusehen https://hub.docker.com/_/mariadb/)

  • Welchen Namen soll die MariaDb verwenden? Ich habe „nicht perfekt leider ghost-mysql verwendet“.
  • Über welchen Port möchte man gehen? Ich habe mich entschlossen, den Standard Port 3306 zu verwenden.
  • An welcher Stelle soll die Daten abgelegt werden?
  • Wie lautet das Root Passwort?

Die Daten werden im Home abgelegt. Ein entsprechendes Data Verzeichnis wurde erzeugt:

mkdir /home/data  
mkdir /home/data/MariaDb  

Auf Basis dieser Überlegungen habe ich folgende Konfiguration gewählt:

  • Name: ghost-mysql
  • Datenverzeichnis nach: /home/data/MariaDb/ mappen
  • SQL Password: XXXXXXX
  • Docker Image das zu verwenden ist: mariadb
docker run --name ghost-mysql -v /home/data/MariaDb/:/var/lib/mysql:z -e MYSQL_ROOT_PASSWORD=XXXXXXX -d -p 3306:3306 mariadb  

Mit „docker ps“ kann man sich nun ansehen, ob die Datenbank korrekt läuft:

[root@CentOS-73-64-minimal ~]# docker ps
CONTAINER ID        IMAGE                          COMMAND                  CREATED             STATUS                 PORTS                                                                                       NAMES  
d952d2b55a4e        mariadb                        "docker-entrypoint.sh"   3 weeks ago         Up 3 weeks             0.0.0.0:3306->3306/tcp                                                                      ghost-mysql  

Das Verzeichnis hat auch die entsprechenden Daten der MariaDB:

Möchten Sie einen laufende Container löschen, dann können Sie das mit „docker rm“ durchführen. Sollte der Container noch laufen, kann man mit „-f“ das löschen erzwingen:

Mit den Kommando „docker start“ kann man einen stehenden Container wieder neu starten. Mit „restart“ beenden und neu starten und mit „docker stop“ natürlich den Container beenden:

Sollte das Docker-Image nicht starten, kann man ggf. ohne die Detach Option „-d“ den Fehler erkennen:

In diesem Fall „chown“ error, hat die Option „:z“ gefehlt, damit das Verzeichnis auch korrekt gemounted wird, so wäre es korrekt:

Die installierten Images (also die Basisplattform für jeden Container) kann man mit „docker images“ angezeigt bekommen:

Mit dieser Konfiguration ist es anschließend möglich auch mit bspw. HeidiSQL (https://www.heidisql.com/download.php) auf die Datenbank zugreifen zu können.

Allerdings muss man in diesen Fall (ich würde es immer nur temporär machen) die Firewall des Servers um eine Regeln bei Hetzner erweitern und die IP auf die eigene Einschränken:

Anschließend sollte es möglich sein, sich mit der mySQL/MariaDb Datenbank zu verbinden:

Mit dem Kommando „docker logs ghost-mysql“ ist es möglich das Log des Container anzusehen:

Konfiguration von Ghost als Blog

Als nächstes ist Ghost auf der Maschine zu installieren.

Hier sind auch ein paar Grundüberlegungen durchzuführen:

  • Welcher Port ist zu verwenden? Ich habe hier auch den Standard port 2368 verwendet, der vom Internet nicht erreichbar ist (Zum testen kann man diesen öffnen, wie bei der MariaDb)
  • Der Name soll einfach „blog“ sein.
  • Und die Ablage soll natürlich auch in einem Verzeichnis für die Daten liegen. In diesen Fall haben ich „/home/data/Ghost“ gewählt.
  • Die mySQL Datenbank wird zu ghost-mysql gemapped.

Der Start von Docker beläuft sich hier dann auf:

mkdir /home/data/Ghost  
docker run --name blog -p 2368:2368 -v /home/data/Ghost/:/var/lib/ghost:z -d --link ghost-mysql:mysql ghost  

Auf die gesamte Konfiguration werde ich in diesem Artikel nicht eingehen, da dies ein eigener Artikel wert wäre. Ich beschränke mich hier nur auf das wesentliche, nämlich das Ghost intern auf die SQL Datenbank umgestellt werden muss, statt die interne zu verwenden:

Hier muss die Datei „config.js“ angepasst werden. Am einfachsten geht dies über WinSCP. Man kann natürlich auch „vi“ verwenden auf den Server, wenn man sich damit sicher genug fühlt:(https://scotch.io/tutorials/getting-started-with-vim-an-interactive-guide)

Hier ist die Datenbank konfiguration entsprechend einzutragen. Als Hostnamen verwendet man „mysql“ da dies auch so im Docker definiert wurde. Als User habe ich „blog_user“ verwendet und ein password vergeben: (ACHTUNG: Die Konfiguration muss in "development" gemacht werden)

Jetzt muss noch in der MariaDb die Datenbank angelegt und der User eingerichtet werden.

Zunächst wird die Datenbank erstellt über HeidiSQL:

Und in diesem Dialog folgendes eingeben:

Anschließend ist der User noch zu berechtigen auf der Datenbank:

Und für diesen das Passwort vergeben und alle notwendigen Rechte:

Anschließend muss der Blog neu gestartet werden, damit die neuen Konfigurationen aus der config.js gezogen werden:

Hat alles geklappt, dann ist der Blog wieder über „http“ verfügbar und in der MariaDb sind jetzt zahlreiche Tabellen verfügbar: (Neu laden mit F5, damit dies auch in der HeidiSQL sichtbar wird!)

ownCloud und GitLab einrichten

Prinzipiell ist für diese beiden Dienste ähnlich vorzugehen, wie bereits für Ghost beschrieben.

Für die ownCloud ist auch ein User einzurichten in MariaDb, während sich das bei GitLab einfacher gestaltet. Ich fasse daher nur die wichtigsten Punkte zusammen.

Eine entsprechende Datenbank in der MariaDB anlegen:

Für die ownCloud habe ich folgendes Kommando verwendet:

docker run --name owncloud -v /home/data/owncloud:/var/www/html:z -p 8082:80 --link ghost-mysql:mysql -d owncloud:8.1  

Die entsprechenden Konfigurationen für die ownCloud MariaDB auch einstellen:

Anschließend sind in der MariaDB auch die entsprechenden Tabellen:

Für Gitlab habe ich folgendes Kommando verwendet:

docker run --detach --name gitlab --hostname git.vr-worlds.de --sysctl net.core.somaxconn=1024 --ulimit sigpending=62793 --ulimit nproc=131072 --ulimit nofile=60000 --ulimit core=0 --publish 8443:443 --publish 8083:80 --publish 8022:22 --publish 8060:8060 --restart always --volume /home/data/gitlab/config:/etc/gitlab:z --volume /home/data/gitlab/logs:/var/log/gitlab:z --volume /home/data/gitlab/data:/var/opt/gitlab:z --volume /etc/localtime:/etc/localtime gitlab/gitlab-ce  

NGINX Einrichten

Nun kommt der spannende Teil des Artikels. Die NGINX soll so konfiguriert werden, dass Anfragen an den Port 80 an die entsprechenden Docker-Container durchgereicht werden.
Erfolgen soll dies aufgrund der angegebenen Sub-Domain:

  • www.vr-worlds.de -> Weiterleitung zum Blog
  • cloud.vr-worlds.de -> Weiterleitung an die ownCloud
  • git.vr-worlds.de -> Weiterleitung an GitLab

Hierzu müssen zunächst alle diese Sub-Domains zur gleichen IP Adresse des Servers eingerichtet werden: (hier verwende ich leider noch Route53 (https://aws.amazon.com/route53) von AWS Amazon, da GoDaddy leider keine DE Domänen übernehmen kann (https://de.godaddy.com/help/about-de-domains-5825)).

Es kann einige Zeit dauern, bis die neuen Adressen entsprechend über DNS verfügbar sind. Man kann dies vom eigenen PC regelmäßig mit „nslookup“ prüfen:

Für die NGINX benötigen wir folgende Vorüberlegungen:

  • Welche Ports werden nach aussen gegben? Natürlich 80 (HTTP) und 443 (HTTPS) in diesen Fall
  • Wir benötigen Links zu den anderen Docker Containern: blog, gitlab, owncloud
  • Der Name soll „nginxserver“ lauten
  • Datenablage für sites-enabled, certs, logs soll im /home/data/Nginx Verzeichnissen erfolgen

Entsprechend könnte man folgendes Kommando verwenden (das wir aber nicht verwenden werden):

docker run -v /home/data/Nginx/sites-enabled/:/etc/nginx/conf.d/ -v /home/data/Nginx/certs/:/etc/nginx/certs -v /home/data/Nginx/logs/:/var/log/nginx --name nginxserver -p 80:80 -p 443:443 -d --link blog:blog --link gitlab:gitlab --link owncloud:owncloud  

Da man diese Konfiguration öfters ändern muss, bspw. wenn neue Hosts hinzukommen empfiehlt es sich die Konfiguration des Docker-Containers in einer Datei abzulegen und „docker-compose“ zu verwenden. (https://docs.docker.com/compose/install/)

Die Installation erfolgt so direkt über „curl“:

curl -L https://github.com/docker/compose/releases/download/1.12.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose  

Docker Compose hat anschließend noch keine Ausführungsberechtigung, diese müssen mit chmod noch hinzugefügt werden:

Nun können wir testen, ob „docker-compose“ korrekt funktioniert:

Hierzu legen wir ein Verzeichnis im Root Homeverzeichnis an und dort ein „docker-compose.yml“:

Die Datei sollte wie folgt aussehen:

version: '2'  
services:  
  nginxserver:
    container_name: nginxserver
    image: nginx
    network_mode: bridge
    external_links:
       - blog:blog
       - gitlab:gitlab
       - owncloud:owncloud
    ports:
       - "80:80"
       - "443:443"
    volumes:
       - /home/data/Nginx/sites-enabled/:/etc/nginx/conf.d:z
       - /home/data/Nginx/certs/:/etc/nginx/certs:z
       - /home/data/Nginx/logs/:/var/log/nginx:z

Anschließend kann man mit „docker-compose“ die „nginx“ starten:

Nachdem Nginx gestartet ist muss die Konfiguration erstellt werden. Für jede Subdomain ist das durchreichen zum entsprechenden gemappten Docker Container einzutragen:

Nachdem die Konfiguration durchgeführt wurde, kann die „nginx“ neu gestartet werden:

Sollte es nicht funktionieren, kann „docker logs“ hier weiterhelfen:

Konfiguration von Shipyard

Als letztes Element habe ich noch Shipyard hinzugefügt, damit auch remote eine Verwaltung der Docker Container möglich ist.

Die Installation ist sehr einfach und benöigt kaum überlegungen:

  • DNS soll ship.vr-worlds.de werden (Nginx Configuration ist hierfür anzupassen)

Das Deployment erfolgt hier nicht über "docker run" sondern über curl:

curl -sSL https://shipyard-project.com/deploy | bash -s  

Anschließend ist shipyard über Port 8080 verfügbar und es wurde einige Container installiert:

[root@CentOS-73-64-minimal ~]# docker ps
CONTAINER ID        IMAGE                          COMMAND                  CREATED             STATUS                 PORTS                                                                                       NAMES  
48f5cd2ce123        shipyard/shipyard:latest       "/bin/controller --de"   2 weeks ago         Up 2 weeks             0.0.0.0:8080->8080/tcp                                                                      shipyard-controller  
ec4955037d0f        swarm:latest                   "/swarm j --addr 176."   2 weeks ago         Up 2 weeks             2375/tcp                                                                                    shipyard-swarm-agent  
48487fb7223c        swarm:latest                   "/swarm m --replicati"   2 weeks ago         Up 2 weeks             2375/tcp                                                                                    shipyard-swarm-manager  
fee6b7fcc71e        shipyard/docker-proxy:latest   "/usr/local/bin/run"     2 weeks ago         Up 2 weeks             0.0.0.0:2375->2375/tcp                                                                      shipyard-proxy  
2058c074314b        alpine                         "sh"                     2 weeks ago         Up 2 weeks                                                                                                         shipyard-certs  
d710310dae40        microbox/etcd:latest           "/bin/etcd -addr 176."   2 weeks ago         Up 2 weeks             0.0.0.0:4001->4001/tcp, 0.0.0.0:7001->7001/tcp                                              shipyard-discovery  
0fe8eb95b8fb        rethinkdb                      "rethinkdb --bind all"   2 weeks ago         Up 2 weeks             8080/tcp, 28015/tcp, 29015/tcp                                                              shipyard-rethinkdb  

Nun muss “Shipyard” nur noch in die nginx eingebunden werden und konfiguiert.
In die nginx Konfiguration ist folgendes zu ergänzen: (/home/data/Nginx/sites_enabled)

Jetzt zahlt es sich erstmals aus über „docker-compose“ die Konfiguration für Nginx gemacht zu haben, dann diese ist nun einfach zu erweitern:

Anschließend kann der Container neu gebaut werden mittels „docker-compose up -d“:

Konfiguration über Docker-Compose zentral steuern

Damit die Automatisierung von Docker wirklich zum tragen kommt, werde ich nun die komplette Konfiguration in einem neuen Docker-Compose File zusammen führen:

Die Datei hat folgendes aussehen:

version: '2.1'  
services:  
  ghost-mysql:
    container_name: ghost-mysql
    image: mariadb
    network_mode: bridge
    ports:
       - "3306:3306"
    volumes:
       - /home/data/MariaDb/:/var/lib/mysql:z 
    environment:
       - MYSQL_ROOT_PASSWORD=rosi2511
  blog:
    container_name: blog
    image: ghost
    network_mode: bridge
    links:
       - ghost-mysql:mysql
    ports:
       - "2368:2368"
    volumes:
       - /home/data/Ghost/:/var/lib/ghost:z
  owncloud:
    container_name: owncloud
    image: owncloud:8.1
    network_mode: bridge
    links:
       - ghost-mysql:mysql
    ports:
       - "8082:80"
    volumes:
       - /home/data/owncloud:/var/www/html:z 
  gitlab:
    container_name: gitlab
    image: gitlab/gitlab-ce
    network_mode: bridge
    ports:
       - "8443:443"
       - "8083:80"
       - "8022:22"
       - "8060:8060"
    volumes:
       - /home/data/gitlab/config:/etc/gitlab:z 
       - /home/data/gitlab/logs:/var/log/gitlab:z
       - /home/data/gitlab/data:/var/opt/gitlab:z
       - /etc/localtime:/etc/localtime
    sysctls:
       - net.core.somaxconn=1024
    ulimits:
       sigpending: 62793 
       nproc: 131072
       nofile: 60000
       core: 0
  nginxserver:
    container_name: nginxserver
    image: nginx
    network_mode: bridge
    links:
       - blog:blog
       - gitlab:gitlab
       - owncloud:owncloud
    external_links:
       - shipyard-controller:shipyard-controller
    ports:
       - "80:80"
       - "443:443"
    volumes:
       - /home/data/Nginx/sites-enabled/:/etc/nginx/conf.d:z
       - /home/data/Nginx/certs/:/etc/nginx/certs:z

Nun können wir alle Container wieder löschen und dann mit „docker-compose“ neu anlegen:

Konfiguration der Firewall bei Hetzner

Meine Firewall Konfiguration bei Hetzner, sieht wie folgt aus:

Die Regel #1 ermögicht es, dass ICMP Packete, bspw. Ping aber auch Requests bezüglich ermitteln der Netzwerkbandbreite (MTU-Size etc.) ermittelt werden können. Es empfiehlt sich daher dies auf „accept“ stehen zu lassen.

Die Regel #2 ermöglicht es, sich auf dem Server mit ssh zu verbinden. Man muss diesen Port nicht immer auf lassen und kann diesen, wenn keine Wartung ansteht auf „discard“ stellen. Anschließend ist eine Verbindung mittels „ssh“ nicht mehr möglich und daher können „putty“ und „winscp“ sich dann nicht mehr verbinden:

Die Regel #3 ist dafür zuständige damit mittels „http“ (port 80) und „https“ (Port 443) auf das System zugegriffen werden kann.

Die Regel #4 ist notwendig, damit der Linux Host auch antworten kann.
Selbstverständlich kann man die Regeln noch weiter optimieren.

Zusammenfassung

In diesem Artikel habe ich gezeigt welche Dinge im einzelnen zu tun sind, damit eine Konfiguration mit einem NGINX Proxy und ein paar anderen Container miteinander zusammen spielen. Ich hoffe das dieses recht Praxisnahe Beschreibung hilfreich ist.

Wenn Sie Unterstützung bei einer Docker Umgebung benötigen, dann kommen Sie genre auf mich zu und kontaktieren Sie mich unter christian.mueller@vr-worlds.de