Nutzen Unternehmen die Cloud, wollen sie trotzdem möglichst unabhängig vom Anbieter bleiben. Die Managementtools von AWS, GCP und Co. funktionieren aber nur mit der zugehörigen Cloud. Mit Ansible lässt sich das Rollout von virtuellen Maschinen und Anwendungen in der Cloud weitgehend unabhängig vom Provider automatisieren. Wir zeigen in diesem Workshop, wie die dazu nötigen Playbooks und Inventory-Files aussehen müssen.
Viele Cloudanbieter buhlen um die Gunst der Anwender. Neben den Platzhirschen Amazon Web Services (AWS), Azure und Google Cloud Platform (GCP) offerieren zunehmend kleinere, regionale oder spezialisierte Anbieter Ressourcen zum Cloud Computing. Das ist gut für den Nutzer, denn Konkurrenz belebt bekanntlich das Geschäft und sorgt für sinkende Preise. Gerade weil Unternehmen bei Clouddiensten die Wahl haben, sollten sie sich nicht auf einen einzelnen Anbieter fixieren.
Nur wenige professionelle Cloudanwender klicken sich jedoch durch die hübschen Web-GUIs der Anbieter, um dynamische Ressourcen auszurollen. Das Ganze muss schnell und automatisch funktionieren. Alle Cloudprovider offerieren daher leistungsstarke Werkzeuge für die Kommandozeile, über die sich auch Skripte erzeugen lassen, die den Rollout automatisieren.
Aber genau das könnte die schöne Unabhängigkeit vom Anbieter wieder zu Fall bringen. Denn wer viel Zeit in schicke Skripte für AWS investiert hat, kann nicht so ohne weiteres auf GCP oder Azure umsteigen, ohne vorher seine Automatisierung auf ein anderes Toolset umzustellen. Abhilfe schaffen hier zwei Dinge: Ansible als unabhängiges Automatisierungswerkzeug und ein modulares, abstrahierendes Konzept.
Viele Cloudanbieter buhlen um die Gunst der Anwender. Neben den Platzhirschen Amazon Web Services (AWS), Azure und Google Cloud Platform (GCP) offerieren zunehmend kleinere, regionale oder spezialisierte Anbieter Ressourcen zum Cloud Computing. Das ist gut für den Nutzer, denn Konkurrenz belebt bekanntlich das Geschäft und sorgt für sinkende Preise. Gerade weil Unternehmen bei Clouddiensten die Wahl haben, sollten sie sich nicht auf einen einzelnen Anbieter fixieren.
Nur wenige professionelle Cloudanwender klicken sich jedoch durch die hübschen Web-GUIs der Anbieter, um dynamische Ressourcen auszurollen. Das Ganze muss schnell und automatisch funktionieren. Alle Cloudprovider offerieren daher leistungsstarke Werkzeuge für die Kommandozeile, über die sich auch Skripte erzeugen lassen, die den Rollout automatisieren.
Aber genau das könnte die schöne Unabhängigkeit vom Anbieter wieder zu Fall bringen. Denn wer viel Zeit in schicke Skripte für AWS investiert hat, kann nicht so ohne weiteres auf GCP oder Azure umsteigen, ohne vorher seine Automatisierung auf ein anderes Toolset umzustellen. Abhilfe schaffen hier zwei Dinge: Ansible als unabhängiges Automatisierungswerkzeug und ein modulares, abstrahierendes Konzept.
Unabhängig durch modulares Konzept
Niemand rollt leere VMs um ihrer selbst willen auf Plattformen wie AWS oder GCP aus. Es geht immer um die darauf laufende Anwendung. Die Anbieter offerieren hierfür immer mehr komfortable und vorkonfigurierte Dienste. MariaDB gefällig? Kein Problem: Hier ist ein vorgefertigtes Image zum direkten Ausrollen auf AWS. Der Nutzer spart sich die separate OS- und DB-Installation. Das klingt verlockend – vor allem aber für den Cloudanbieter, denn es bindet den Nutzer fix an die Plattform und an genau dieses eine Template, das für eine andere Plattform in dieser Form nicht zur Verfügung steht.
Um hier unabhängig zu bleiben, sollten Administratoren den Teil des VM-Rollouts vom Verteilen der Anwendungen trennen. Dabei helfen "nackte" Minimal-OS-Templates, die auf allen Cloudplattformen mehr oder weniger identisch ausfallen. Alternativ verwenden Sie ein Tool wie den "lorax-composer" [1], um eigene OS-Templates zu bauen, die sich dann in die jeweilige Cloudumgebung hochladen lassen.
Mit einem derart modularen Rollout schreiben Admins ihre Installations- und Konfigurations-Playbooks unabhängig von der verwendeten Cloud. Der Ausrollvorgang muss der Applikationsautomatisierung lediglich eine Reihe an Parametern in einem anbieterunabhängigen Format übermitteln. Dazu gehören auf jeden Fall:
- die externe IP-Adresse, über die die VM für Ansible und später die Applikation erreichbar ist.
- die interne IP-Adresse, die die Applikation zur Kommunikation mit anderen VMs im selben Rollout nutzt.
- der Zweck oder Typ der Maschine.
Damit Ansible die Zielsysteme für den Rollout der Anwendungen ansprechen kann, braucht es ein herstellerunabhängiges Inventory. Die Ansible-Module für GCP, AWS oder Azure liefern selbstverständlich Skripte für dynamische Inventories mit, aber natürlich mit einem Haken: Wer beispielsweise das Inventar eines GCP-Projects dynamisch abfragt und daraus die interne IP-Adresse einer VM erfahren möchte, muss die Variable "gce_private_ip" nutzen. Bei einem AWS Inventory heißt diese dann aber "ec2_private_ip".
Auch unter Ansible stammen die Cloudmodule vom jeweiligen Anbieter und die Benennung von "Facts" und Variablen lehnt sich durch die Bank an die Anbieterstandards an. Also muss der Administrator selbst für ein neutrales Inventory sorgen.
Das dynamische statische Inventory
Ein simpler Trick, um bei der Cloudnutzung so neutral wie möglich zu bleiben, funktioniert so: Das anbieterspezifische Playbook, das die benötigten VMs auf der Zielplattform ausrollt, erzeugt zugleich ein statisches Inventory File und trägt dort die vom Application Rollout benötigten Variablen in einer unabhängigen Form ein. Das Application-Rollout-Playbook wiederum bezieht alle nötigen Variablen aus der Inventory-Datei und kann dann mit jeder Zielplattform arbeiten, egal ob AWS, GCP oder auch lokale Umgebungen wie vSphere.
Dieser Workshop zeigt Ihnen einen anbieterneutralen Cloud-Rollout anhand von zwei Playbooks: Eins für AWS und das andere für GCP. Am Ende erhalten Sie eine Reihe von VMs und ein dazu passendes neutrales Inventory. Die hier gezeigten Beispiele setzen voraus, dass die genutzten Clouds entsprechend vorbereitet wurden: Sie haben die benötigten User und SSH-Keys sowie die passenden Subnetze, Sicherheitsgruppen und Firewallregeln angelegt.
Übrigens: Wer mit variablen Daten in einem Ansible-Playbook arbeitet, sollte diese stets getrennt von der Playbook-Logik aufbewahren, sie also niemals direkt in das Playbook hart codieren. Zudem sollten Sie sich immer zuerst Gedanken darüber machen, welche Daten Sie eigentlich erfassen und welche Sie registrieren müssen, bevor es an die Logik geht. Daher fangen wir mit der Variablendeklaration an.
Basisdaten am Beispiel AWS mitgeben
Um mit einer Cloud zu sprechen, müssen wir uns authentisieren und angeben, in welcher Region oder welchem Projekt wir tätig werden. Unsere Variablendeklaration "ec2_vars.yml" für AWS beginnt daher so:
---
ec2_access_key: (Schlüssel)
ec2_secret_key: (Secret)
ec2_region: us-east-1
ec2_key_name: (Keyname)
ec2_security_group_id: sg-<ID>
ec2_vpc_subnet_id: subnet-<ID>
ec2_hostfile: ec2_hosts
Die Parameter sind weitgehend selbsterklärend, mit Ausnahme von "ec2_host-file", das auf die (noch zu erstellende) Inventory-Datei verweist.
VM-Spezifikationen setzen
Das EC2-Modul für Ansible kann mit einem einzigen Befehl mehrere VMs auf einmal ausrollen. Diese sind dann aber alle gleich groß und basieren auch auf dem gleichen Template. Wir wollen jedoch flexibel bleiben und den VMs individuelle Größen und Templates zuordnen. Zudem wollen wir sie mit weiteren Informationen für den folgenden Applikations-Rollout versehen. Im Beispiel geben wir den Maschinen einen "purpose" wie "Datenbank" oder "Webserver". In der Fortsetzung der "ec2_vars.yml" sieht das dann so wie im Listing 1 aus.
Die hierarchische Variablenstruktur von "vms" bezeichnet Ansible als "Dictionary", über das sich später sehr einfach eine Loop bauen lässt. Das Beispiel erstellt zwei Fedora Minimal VMs mit verschiedenen Disk- und VM-Größen. Das Dictionary "vms" kann natürlich viel mehr Maschinen auflisten und Sie können weitere Variablen einfügen, die später im Inventory landen. In einer Loop über "vms" landen die Elemente in den folgenden Ansible-Variablen, auf die Tasks innerhalb des Loop-Playbooks zugreifen können:
Die Logik des Playbooks "rollout_ec2. yml" erstellt zunächst die Maschinen und fügt dem statischen Inventory wie in Listing 2 ersichtlich alle nötigen Informationen hinzu. Das Playbook läuft auf dem lokalen Host und erstellt zuerst das statische Inventory-File mit seinem Header. Dann "looped" es pro anzulegender VM über das Dictionary und ruft dazu jeweils das "ec2_loop.yml"-Playbook auf. Dessen Inhalt entnehmen Sie Listing 3.
Der erste Task nutzt Amazons EC2-Modul und kreiert die VM. Da wir das Modul einmal pro Loop starten, setzen wir die VM-Zahl "count" fest auf "1". Der Schalter "wait: true" ist leider nötig. Denn ab und zu läuft die Automatisierung schneller als AWS selbst und dann schließt der Task ohne "wait" bereits ab, bevor Amazon der Maschine überhaupt eine öffentliche IP-Adresse zugewiesen hat. Der Automatisierung fehlt dann aber genau diese IP-Adresse. Am Ende sichert "register" die Rückgabewerte des EC2-Moduls in der Variablen "ec2_return":
- name: Debug ec2_return
debug:
msg: "{{ ec2_return }}"
verbosity: 2
Bild 2: Google sortiert VMs nicht nur nach Region, sondern verwaltet Maschinen in Projekten. Der Admin gibt jeder Maschine bei Erstellung einen Namen, dafür registriert GCP aber nicht automatisch eine DNS-Auflösung.
Bei der Variablen "ec2_return" handelt es sich um ein JSON-Konstrukt. Den Debug-Befehl gibt es komplett auf der CLI aus. So können Ansible-Entwickler bei einem ersten Probelauf die benötigten Informationen im JSON ermitteln, um dann die richtige Variablenstruktur für den weiteren Verlauf nutzen zu können. Den Task können Sie im fertigen Playbook natürlich entfernen. Hier bleibt er einfach mit dem Flag "verbosity: 2" enthalten. In diesem Fall kommt dieser Schritt nur dann zur Ausführung, wenn Sie das Playbook mit "-vv", also Debug-Level 2, starten. Vervollständigen Sie aus diesem Grund das Playbook "ec2_loop.yml" (Listing 3) um folgende Zeilen:
Durch den Debug des Return-Arrays (eine Variable, die mehrere Werte indiziert aufnimmt) wissen wir jetzt, wo sich Informationen wie öffentliche und private IP-Adresse im Array befinden, sodass Ansible sie via "lineinfile" an das statische Inventory anhängt. Die Variable "ec2_return_instance[]" ist ein Array, weil das Modul "ec2" mit einem Lauf mehrere VMs anlegen kann. Nach einem erfolgreichen Rollout der zwei Test-VMs auf AWS steht im so erstellten Inventory dann beispielsweise
Was am Ende alles im Inventory landet, ist dem Nutzer überlassen. Sie können natürlich auch weitere Informationen aus dem Loop oder der Variablendeklaration zum Inventory hinzufügen, die der spätere Applikations-Rollout nutzen kann. Im Fall von AWS ergibt es beispielsweise Sinn, die ID der Maschine aus dem Array "ec2_return_instance-" mit in das Inventory zu schreiben. Diesen Parameter braucht ein Rollout-Playbook zwar nicht – aber ein Playbook, das nach Gebrauch des Dienstes die VMs in der AWS-Cloud löscht, muss diese IDs kennen.
...und das Ganze nochmal mit Google
Die Google Cloud Platform organisiert Ressourcen anders als AWS. Sie sortiert die VMs in Projekte ein und regelt Netzwerke und Firewalls etwas unterschiedlich. Die zugehörigen Ansible-Module erstellen die VMs in mehreren Schritten. Am Ende entsteht jedoch ein identisches Inventory wie nach dem AWS-Rollout. Die Variablendeklaration "gcp_vars.yml" zeigt Listing 4.
Parameter wie Netzwerke und Firewallregeln sind optional und GCP setzt die Default-Werte des Projekts ein. Das Dictionary "vms" sieht nahezu identisch zur AWS-Deklaration aus. Lediglich Namen und Pfade für "Image-Type" und "Template-Source" nutzen andere Werte. Das in Listing 5 gezeigte GCP-Rollout-Skript "roll-out_gcp.yml" enthält prinzipiell die gleichen Anweisungen wie beim AWS-Rollout. Erst das Loop-Playbook für GCP unterscheidet sich, wie Listing 6 zeigt, deutlich vom AWS-Rollout.
Wollen Sie die Festplattengröße der VM selbst bestimmen, müssen Sie in einem separaten Task "gcp_compute_disk" erst einmal eine Disk aus dem Template erzeugen und anpassen. Ohne diesen separaten Task würde das folgende Modul die Platte in der Größe der OS-Vorlage erstellen. Im zweiten Task baut GCP dann die eigentliche VM mit der zuvor angelegten Platte. Bei GCP kann der Administrator den Namen der Maschine angeben. Das spielt auch hier keine Rolle für den anstehenden Applikations-Rollout, jedoch identifiziert der VM-Name in einem späteren Playbook die zu löschenden VMs:
- name: debug output
debug:
msg: "{{ gcp_return }}"
verbosity: 2
Wie zuvor schon bei AWS müssen Sie in einem ersten Testlauf die JSON-Struktur des Return-Codes analysieren, um im nächsten Schritt die korrekten Paramter zu extrahieren und dem Playbook (Listing 6) hinzuzufügen:
Auch die GCP-Return-Variable kommt mit Arrays zurück. In GCP lassen sich via Automatisierung VMs mit mehreren Netzwerkadaptern bauen. Auch bei GCP steht am Ende die neutrale Inventory-Datei "gcp_hosts" bereit:
Vor dem eigentlichen Applikations-Roll-out auf den neu erstellten Maschinen geht es ans "Housekeeping": Neben den üblichen Schritten wie Repositories konfigurieren, Runtimes und Dependencies installieren sowie Benutzer, Gruppen und Rechte anlegen sollten Sie die Systeme an einem vom Cloudanbieter unabhängigen DDNS-Server anmelden. So erreichen Sie die Applikation jederzeit über ihren eigenen DNS-Namen. Ein passendes Playbook, das auf zuvor deklarierte Rollen für den Applikations-Rollout verweist, kann so wie in Listing 7 aussehen.
Listing 7: Playbook zum Applikations-Rollout
---
- hosts: all
remote_user: ssh-user
become: yes
tasks:
- name: DDNS
include_role: name: common
- name: Common Config
include_role: name: common
- name: Config Database
include_role: name: database
when: purpose=="database"
- name: Config Webserver
include_role: name: nginx
when: purpose=="webserver"
Der "ssh-user" passt dabei zum Schlüsselpaar der Zielserver. Gibt es in Ihrem Unternehmen verschiedene Nutzer für GCP und AWS, lässt sich "ssh-user" auch als Variable im Inventory deklarieren. Die Tasks "DDNS" und "Common Config" laufen auf allen ausgerollten Maschinen. Über den Filter "when" weisen Sie dann die Rollen jeweils nur den Maschinen mit dem passenden "Purpose" zu. Falls Sie noch keine eigenen Rollen für Applikations-Rollouts in Ansible geschrieben haben, liefert das Community-Repository Galaxy [2] viele fertige Rollen für die üblich verdächtigen Applikationen wie PostgreSQL, MariaDB, Apache oder nginx.
Fazit
Mit Ansible als Automatisierungswerkzeug und einem Abstraktions-Layer – in diesem Fall ein neutrales Inventory-File – können Administratoren ihre Cloud-Rollouts unabhängig vom Anbieter aufsetzen. Nur der Teil der Automatisierung bleibt herstellerabhängig, der direkt mit den VMs auf der jeweiligen Cloud arbeitet. Die Verwaltung der auf den Cloudressourcen laufenden Applikationen hat aber nichts mit der Virtualisierungsschicht darunter zu tun. Das an dieser Stelle vorgestellte Konzept lässt sich daher auch um lokale Virtualisierungen etwa mit vSphere oder RHV erweitern.