Wie man mehrere Webseiten mit Nginx und HAProxy mit LXD hosten kann

Wie man mehrere Webseiten mit Nginx und HAProxy mit LXD hosten kann

LXD und Linuxcontainer sind eine meiner Lieblingstechnologien in Ubuntu. In wenigen Sekunden kann man einen neue virtuelle Maschine in einem Container starten. Wir machen davon in unserer Schule stark Gebrauch. Fast alle Webanwendungen laufen in einem solchen Container. So haben wir die einzelnen Anwendungen besser getrennt. Weiterhin kann man sehr schnell einen Snapshot eines Containers machen und vieles mehr. Wenn man nun diese verschiedenen Webseiten öffentlich zugänglich machen möchte, gibt es ein Problem, denn i.d.R. hat man nur eine öffentliche IP zur Verfügung (fest bzw. dynamisch). Eine Lösung wäre, dass die Webanwendungen auf verschiedenen Ports laufen, aber das ist nicht unbedingt benutzerfreundlich. In diesem Beitrag möchte ich zeigen, wie man mehrere Webseiten mit Nginx und HAProxy mit LXD hosten kann.

Voraussetzungen

Wir brauchen:

  • einen Server (ab Ubuntu 16.04) mit öffentlicher IP
  • Zugang mit Rootrechten (root / sudo)
  • eine Domain und Subdomain, die mit je einem DNS A Eintrag auf die öffentliche IP des Servers zeigen

Schritt 1 – Benutzer der lxd – Gruppe hinzufügen

Damit man mit einem Nicht-Root-Benutzer lxd verwalten kann, müssen wir zuerst den Benutzer der Gruppe hinzufügen:

Damit die Änderungen wirksam werden, müssen wir uns einmal aus- und wieder einloggen!

Schritt 2 – lxd konfigurieren

lxd ist in Ubuntu 16.04 vorinstalliert. Falls nicht, können wir es mit

nachholen. In LXD gibt es verschiedene Möglichkeiten, wie und wo die Container ihren Speicherplatz haben. Man kann verschiedene Dateisysteme auswählen und entscheiden, ob die Container in einer Datei oder extra Partition bzw. Festplatte gespeichert werden. In diesem Tutorial verwenden wir ZFS für Linux mit einen sogenannten „loop device“. Das installieren wir mit

Nun können wir mit der Einrichtung von lxd beginnen:

Es werden verschiedene Fragen gestellt, die man wie folgt beantworten kann (Achtung: Die Netzwerkbrücke nur für IPv4 aktivieren! Das Subnet kann frei gewählt werden)

Die Netzwerkbrücke ist notwendig, damit jeder Container seine eigene IP bekommt und Zugang zum Internet hat.

lxd init

So, jetzt ist alles eingerichtet und wir können unsere ersten Container erstellen!

Schritt 3 – Container erstellen

Um unsere Container zu verwalten, brauchen wir den lxc Befehl. Diese feine Programm ist sehr einfach zu verwenden und dabei sehr mächtig. Zuerst wollen wir uns mal alle Container anzeigen lassen. Das geht mit lxc list:

Es werden keine Container angezeigt, weil wir ja noch keine erstellt haben.

Für unser Beispiel brauchen wir drei Container: einen für HAProxy und 2 Webserver mit Nginx (oder Apache, wenn das jemand bevorzugt).

Wir werden den Befehl lxc launch verwenden, um einen Ubuntu 16.04 (ubuntu:x) Container namens web1 zu erstellen und zu starten. Das x in ubuntu:x ist eine Abkürzung für den ersten Buchstaben von Xenial, der Codename von Ubuntu 16.04. ubuntu: ist der Bezeichner für das vorkonfigurierte Repository von LXD-Images.

Unsere drei Container können wir also mit folgenden Befehlen erstellen:

Wenn man den ersten Container erstellt, wird zuerst das Image heruntergeladen, dass etwas dauern kann. Die anderen beiden Container werden dann sehr schnell erstellt.

Nun können wir mit lxc list schauen, ob unsere Container auch alle da sind:

In dieser Liste sehen wir zuerste den Namen des Containers und seinen Status (RUNNING, STOPPED). Dann die IP Adresse(n) (die hier exemplarisch ist) und der Typ. Zum Schlusso noch die Anzahl der Snapshots / Sicherungspunkte. Die IP-Adressen werden wir gleich brauchen (am besten notieren).

Schritt 4 – Nginx-Container einrichten

Um eine Verbindung zum Container herzustellen, verwenden wir den Befehl lxc exec, der den Namen des Containers und die auszuführenden Befehle enthält.

Die Zeichenkette -- gibt an, dass die Befehlsparameter für lxc dort zu Ende sind, und der Rest der Zeile wird als der auszuführende Befehl innerhalb des Containers übergeben. Der Befehl ist sudo --login --user ubuntu, der eine Login-Shell für das vorkonfigurierte Konto ubuntu im Container bereitstellt.

Hinweis: Wenn man eine Verbindung zu den Containern als root herstellen müssen, kann man stattdessen den Befehl lxc exec web1 -- /bin/bash verwenden

Nun sind wir in der Konsole des Containers (ubuntu@web1) und können nginx installieren:

Damit wir später besser erkennen auf welchem Webserver wir uns gerade befinden, ändern wir die Standard-Seite des Webservers:

Für die Änderung bieten sich der title-Tag und die erste Überschrift (h1) an:

Nachdem wir die Datei an den beiden Stellen geändert haben, können wir mit Strg+X und Y die Datei speichern. Mit Strg+D oder logout verlassen wir wieder den Container.

Zurück auf dem Server testen wir, ob der Webserver auch ordnungsgemäß funktioniert. Mit dem folgenden Befehl sollten wir die Willkommenseite sehen, die wir gerade eben angepasst haben:

Wenn alles geklappt hat, kann der zweite Container nach dem gleichen Muster eingerichtet werden.

Schritt 5 – HAProxy konfigurieren

Jetzt, da alle beiden Webserver-Container eingerichtet sind, fehlt nur noch der Reverse Proxy. Er ist dafür verantwortlich, dass die Anfragen von außen vom richtigen Container verarbeitet werden. HAProxy ist ein sehr leistungsstarker Proxy, der auch von Gihub oder Twitter verwendet wird. Die Möglichkeiten sind auch hier wieder sehr vielfältig. Wir werden deshalb nur ein sehr einfaches Setup machen.

Zuerst loggen wir uns wieder in den Container ein und installieren HAProxy:

Nun konfigurieren wir HAProxy. Dazu öffnen wir die Datei /etc/haproxy/haproxy.conf mit einem Texteditor:

Im Abschnitt defaults fügen wir die Optionen forwardfor und http-server-close hinzu.

Frontends

Als nächstes konfigurieren wir das Frontend so, dass es auf unsere beiden Backend-Container zeigt. Wir fügen einen neuen Frontend-Abschnitt mit dem Namen wwww_frontend hinzu, der so aussieht:

Die acl-Befehle stimmen mit den Hostnamen der Webserver überein und leiten die Anfragen an den entsprechenden Backend-Abschnitt weiter.

Backends

Dann definieren wir zwei neue Backend-Abschnitte, eine für jeden Webserver, und nennen sie web1_cluster bzw. web2_cluster. Wir fügen den folgenden Code zur Datei hinzu, um die Backends zu definieren:

Die balance-Option bezeichnet die Load-Balancing-Strategie. In diesem Fall entscheiden wir uns für die geringste Anzahl von Verbindungen. Die Option http-request setzt einen HTTP-Header mit der realen Web-Client-IP. Wenn wir diesen Header nicht setzen würden, würde der Webserver die HAProxy-IP-Adresse als Quell-IP für alle Verbindungen aufzeichnen, was die Analyse der Herkunft des Datenverkehrs erschwert. Die Server-Option spezifiziert einen beliebigen Namen für den Server (web1), gefolgt von Hostname und Port des Servers.

LXD stellt einen DNS-Server für die Container zur Verfügung, so dass web1.lxd sich auf die mit dem web1-Container verknüpfte IP auflöst. Die anderen Container haben eigene Hostnamen, wie z.B. web2.lxd und haproxy.lxd.

Der check-Parameter weist HAPRoxy an, auf dem Webserver Überprüfungen durchzuführen, um sicherzustellen, dass er verfügbar ist.

Um zu testen, ob die Konfiguration gültig ist, führen Sie den folgenden Befehl aus:

Nun müssen wir noch den HAProxy neu laden:

Damit sind wir im HAProxy-Container fertig und können uns wieder mit Strg+D bzw. logout abmelden.

Wir haben HAProxy so konfiguriert, dass es als Reverse-Proxy fungiert, der alle Verbindungen, die es auf Port 80 empfängt, an den entsprechenden Webserver in den beiden anderen Containern weiterleitet. Testen wir, ob es Haproxy tatsächlich gelingt, die Anfragen an den richtigen Web-Container weiterzuleiten. Dazu führen wir den folgenden Befehl aus:

Dies stellt eine Anfrage an HAProxy und setzt einen HTTP-Host-Header, den HAProxy verwenden soll, um die Verbindung zum entsprechenden Webserver umzuleiten.

Als Ausgabe sollten wir nun die Standarseite des Nginx-Servers vom web2-Container erhalten!

HAProxy hat die Anfrage richtig verstanden und an den web2-Container weitergeleitet. Dort zeigt der Webserver der Standard-Indexseite an, die wir zuvor bearbeitet haben, und zeigt den Text auf dem LXD-Container web2 an.

Schritt 6 – Eingehende Verbindungen an den HAProxy weiterleiten

Zum Schluss müssen wir noch dafür sorgen, dass alle externen Anfragen auf Port 80 an HAProxy weitergeleitet werden, damit die Welt auf unsere Websites zugreifen kann.

HAProxy haben wir in einem Container installiert und ist standardmäßig aus dem Internet nicht erreichbar. Um dies zu lösen, erstellen wir eine iptables-Regel, um Verbindungen weiterzuleiten.

Der Befehl iptables benötigt zwei IP-Adressen: die öffentliche IP-Adresse des Servers (server_ip) und die private IP-Adresse des Haproxy-Containers (haproxy_ip), die man mit dem Befehl lxc list erhält.

Führen Sie diesen Befehl aus, um die Regel zu erstellen:

Um diesen iptables-Befehl zu speichern, damit er nach einem Neustart erneut angewendet wird, installieren wir das Paket iptables-persistent:

Bei der Installation des Pakets werden wir aufgefordert, die aktuellen iptables-Regeln zu speichern. Akzeptieren und speichern wir alle aktuellen iptables-Regeln.

Schritt 7 – Zugriff von außen testen

Wenn wir die beiden Domains (DNS A Eintrag) eingerichtet haben, sollten wir in der Lage sein, sich mit Ihrem Webbrowser mit jeder Website zu verbinden. Alternativ und nur zum lokalem Test können wir auch einen Eintrag in der /etc/hosts Datei machen:

Um zu testen, ob die beiden Webserver tatsächlich über das Internet erreichbar sind, greifen wir von einem lokalen Computer aus mit einem Browser auf unsere beiden Domains zu (example.com bzw. web2.example.com). In beiden Fällen sollten wir nun die entsprechende Standardseite des Webservers sehen!

Fazit

Wir haben zwei Websites eingerichtet, jede in einem eigenen Container, wobei HAProxy den Datenverkehr steuert. Diesen Prozess können wir wiederholen, um viele weitere Websites zu konfigurieren, die jeweils auf einen eigenen Container beschränkt sind.

So könnten wir z.B. auch MySQL in einem neuen Container hinzufügen und dann ein CMS wie WordPress installieren, um eine Website zu starten. Weiterhin könnten wir  diese Vorgehensweise auch verwenden, um ältere Softwareversionen zu unterstützen. Zum Beispiel, wenn eine Installation eines CMS eine ältere Version von Software wie PHP5 erfordert, dann können wir Ubuntu 14.04 in den Container installieren (lxc launch ubuntu:t), anstatt zu versuchen, die Paketmanager-Versionen, die auf Ubuntu 16.04 verfügbar sind, herabzusetzen.

Schließlich bietet LXD die Möglichkeit, Snapshots des vollen Zustands von Containern zu machen, was die Erstellung von Backups und das Zurückrollen von Containern zu einem späteren Zeitpunkt vereinfacht. Darüber hinaus, wenn wir LXD auf zwei verschiedenen Servern installieren, dann ist es möglich, diese miteinander zu verbinden und Container zwischen Servern über das Internet zu migrieren. Doch darüber berichte ich vielleicht ein andern mal…

Update: Als nächsten Schritt sollte man das Setup noch mit SSL sicherer machen.

Setzt du LXD ein und welche Erfahrungen hast du bisher gemacht?

4 Gedanken zu „Wie man mehrere Webseiten mit Nginx und HAProxy mit LXD hosten kann

  1. Hallo zefanja,
    vielen Dank für den interessanten und informativen Artikel. Mir sind beim Lesen ein paar Fragen gekommen. Ich freue mich, wenn du diese beantworten kannst.

    1. Wie groß sind die Container-Images, die heruntergeladen werden?
    2. Entsprechen diese in der Größe in etwa der Ubuntu-Standard-Installation?

    LG
    Tronde

  2. Hallo Tronde,

    die LXD-Images sind nicht sehr groß, ich glaube so 100-150MB. Sie werden natürlich größer, wenn man in diesem Container Software installiert. Die Images sind deshalb so klein, da nur die wesentliche Software enthalten ist, alles ist sehr minimal. Das ist sehr angenehm, da man so kaum unnötige Pakete im Container hat.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.