Docker-Windows-Container mit Ansible managen (1/2)

Seite 2: Vagrant, Marsch!

Inhaltsverzeichnis

Der letzte Schritt in der Packer-Konfigurationsdatei definiert sogenannte Post-Processors und dient dem eigentlichen Ziel: Er weist Packer an, eine Vagrant-Box zu erstellen. Dafür wird ein Vagrantfile-Template benötigt. Im konkreten Beispiel ist das die Datei vagrantfile-windows_2016.template, die wie folgt aufgebaut ist:

Vagrant.configure("2") do |config|
config.vm.box = "windows_2016_docker"
config.vm.guest = :windows

config.windows.halt_timeout = 15

# Configure Vagrant to use WinRM instead of SSH
config.vm.communicator = "winrm"

# Configure WinRM Connectivity
config.winrm.username = "vagrant"
config.winrm.password = "vagrant"

config.vm.provider "virtualbox" do |vb|
vb.name = "WindowsServer2016Docker"
# Display the VirtualBox GUI when booting the machine
vb.gui = true
# More Power for the Windows Box with Docker
vb.memory = 8192
vb.cpus = 4
end
end

Da innerhalb der Vagrant-Box das Virtualisierungsprogramm VirtualBox zum Einsatz kommt, ist eine solche virtuelle Maschine entsprechend konfiguriert. Zur Kommunikation mit ihr sind beispielsweise das bekannte WinRM sowie der Nutzer vagrant zu verwenden. Im Abschnitt config.vm.provider "virtualbox" werden dann der Name, die Arbeitsspeichergröße und die Anzahl der Prozessorkerne konfiguriert.

Nachdem der Packer-Build erfolgreich abgeschlossen wurde, ist die neue Vagrant-Box der lokalen Installation nur noch mit dem folgenden Befehl bekannt zu machen:

vagrant init windows_2016_docker_virtualbox.box

Nun lässt sich die Vagrant-Box mit Windows Server 2016 mit dem einfachen Vagrant-Befehl vagrant up hochfahren (siehe Abb. 2).

Fertig ist die Vagrant-Box mit Windows Server 2016 (Abb. 2).


Ob alles funktioniert hat, lässt sich mit einem einfachen Ansible-Beispielprojekt überprüfen, dass ebenfalls im GitHub-Projektverzeichnis liegt. Dazu wechselt man in das Unterverzeichnis ansible-windows-simple und führt den Testbefehl ansible windows-dev -i hostsfile -m win_ping aus. Liefert er SUCCESS zurück, kann einen Schritt weiter gegangen werden. Der folgende Befehl installiert Firefox in der Windows-Vagrant-Box:

ansible-playbook -i hostsfile windows-playbook.yml --extra-vars ↵ 
"host=windows-dev"

Dazu ist nicht viel Code notwendig (siehe windows-playbook.yml):

- hosts: "{{host}}"

tasks:
- name: Install firefox
win_chocolatey:
name: firefox

Mithilfe der sogenannten Playbooks lässt sich das Verhalten von Ansible konfigurieren. Das YAML-Dateiformat führt zu einer einfach lesbaren Syntax – einer der vielen Vorteile von Ansible. Als eines der jüngsten Provisionierungswerkzeuge nutzen es besonders gern im DevOps-Umfeld angesiedelte Entwickler. Ein weiterer Vorteil gegenüber konkurrierenden Werkzeugen wie Puppet oder Chef besteht darin, dass Ansible keinerlei Agenten auf dem Zielsystem voraussetzt. Dadurch entfallen die in der Praxis oft als anfängliche Hürde wahrgenommene Installation sowie die weitere Wartung und Pflege des Agenten.

Die Installation von Docker ist – zumindest zum Zeitpunkt der Veröffentlichung noch – ungleich komplexer als die von Firefox. Allerdings ist sie hervorragend geeignet, um sich intensiver mit Ansible auseinanderzusetzen. Dazu empfiehlt sich wieder das Öffnen der Kommandozeile. Um die Docker-Installation in der Windows-Vagrant-Box auszuführen, ist zunächst in das Verzeichnis step1-prepare-docker-windows zu wechseln. Damit alles funktioniert, ist eine möglichst aktuelle Ansible-Installation nötig:

ansible-playbook -i hostsfile prepare-docker-windows.yml --extra-vars ↵ 
"host=ansible-windows-docker-springboot-dev"

Das zentrale Playbook prepare-docker-windows.yml vermittelt dabei direkt einen Eindruck einer wichtigen Ansible-Philisophie, die besagt, möglichst von den Details der Provisionierung zu abstrahieren und den Nutzer davon ins Bild zu setzen, was auf dem Zielsystem passiert. Das Stichwort include bindet jeweils weitere Playbooks ein:

tasks:
- name: Check the minimum Windows build number
include: check-build-number.yml

- name: Install Windows Containers and Hyper-V Windows Features (if not ↵
already present)
include: install-windows-container-features.yml

- name: Install Docker on Windows (always the newest version) and pull needed ↵
base images
include: install-docker.yml

- name: Run a Microsoft Docker Windows Testcontainer
include: run-test-container.yml

- name: Build the springboot-oraclejre-nanoserver Docker image
include: build-springboot-oraclejre-nanoserver-image.yml
vars:
image_name: springboot-oraclejre-nanoserver
java8_update_version: 121
java_build_version: b13
server_jre_name: server-jre-8u{{java8_update_version}}-windows-x64.tar.gz

Warum es wichtig ist, mit der korrekten Build-Nummer von Windows zu starten, unterstreicht der erste Task. Er prüft, ob mindestens Version 10.0.14393.206 vorliegt. Wenn nicht, bricht er die Skriptausführung direkt ab, da kein erfolgreiches Betreiben von Windows-Docker-Containern möglich wäre. Im zweiten Task erfolgt die Installation der zwei für die Docker-Ausführung notwendigen Windows-Features Container und Hyper-V. Wie in "Windows- und Linux-basierte Docker-Container auf Windows nutzen (Teil 1 von 2)" beschrieben, kann danach die Installation von Docker beginnen.

Ob alles erfolgreich installiert ist, prüft der folgende Task. Wurde der Testcontainer erfolgreich ausgeführt, lässt sich von einer lauffähigen Docker-Installation ausgehen. Im letzten Schritt wird ein eigenes Docker-Image erzeugt, dass für das Ausführen der Java- beziehungsweise Spring-Boot-Anwendungen benötigt wird.

Microsoft bietet zwei Installationsanleitungen für Windows Server 2016 und Windows 10 an. Allerdings haben beide mit Einschränkungen zu kämpfen und sind jeweils inkompatibel zueinander. Aktuell wird beispielsweise das InstallDocker.msi nicht auf Windows Server 2016 unterstützt. Auf der anderen Seite nutzt der Installationsprozess Befehle, die unter Windows 10 schlicht nicht zur Verfügung stehen. Obwohl Microsoft mit dem neuen Paketmanager OneGet und dem entsprechenden Modul DockerMsftProvider für die Docker-Installation aus der PowerShell Gallery einen begrüßenswerten Schritt in die richtige Richtung geht, ist das Modul leider derzeit nicht zu Windows 10 kompatibel.

Der Schlüssel zur Kompatibilität liegt in den PowerShell Commandlets rund um WindowsOptionalFeature, mit denen beide Windows-Varianten arbeiten können. Die konkrete Umsetzung zeigt das Playbook install-windows-container-features.yml:

- name: Check if Containers are already installed as Windows Feature
win_shell: Get-WindowsOptionalFeature -Online -FeatureName Containers |↵
Where State -CContains "Enabled"
register: check_feature_container_installed

- name: Install Containers natively as Windows Feature (only, if not already ↵
installed)
win_shell: Enable-WindowsOptionalFeature -Online -FeatureName containers -All↵
-NoRestart
when: check_feature_container_installed.stdout == ''
ignore_errors: yes
register: feature_container_installation

- name: Check if Hyper-V is already installed as Windows Feature
win_shell: Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V ↵
| Where State -CContains "Enabled"
register: check_feature_hyperv_installed

- name: Install Hyper-V as Windows Feature (only, if not already installed)
win_shell: Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-↵
Hyper-V -All -NoRestart
when: check_feature_hyperv_installed.stdout == ''
ignore_errors: yes
register: feature_hyperv_installation

- name: When Containers and/or Hyper-V had to be installed, we have to reboot ↵
the Machine to have them take effect
win_reboot:
reboot_timeout_sec: 60
shutdown_timeout_sec: 60
when: feature_container_installation.changed or ↵
feature_hyperv_installation.changed
ignore_errors: yes

Für alle Aufgaben kommen sogenannte Ansible-Module zum Einsatz. In der Ansible-Dokumentation findet sich eine entsprechende Liste, die aktuell mit jedem Versionssprung von Ansible deutlich an Umfang zunimmt.

Im konkreten Playbook führt zuerst das win_shell-Modul einen PowerShell-Befehl aus und das Get-WindowsOptionalFeature-Commandlet prüft, ob das Containers-Windows-Feature installiert ist. Ist im Feld State kein Enabled gesetzt, ist das Feature noch nicht installiert, was beim ersten Ausführen des Playbook häufig der Fall ist. Das lässt sich im nächsten Schritt mit dem Enable-WindowsOptionalFeature-Commandlet und dem Befehl Enable-WindowsOptionalFeature -Online -FeatureName containers -All -NoRestart auf Windows Server 2016 und Windows 10 nachholen. Beide Schritte werden für Hyper-V ebenfalls durchlaufen.

Der letzte Schritt im vorliegenden Playbook veranlasst die Windows-Vagrant-Box via win_reboot-Modul zum Neustart. Allerdings findet der Reboot nur statt, wenn die Prüfung durch das Schlüsselwort when über per register definierte Variablen ergibt, dass eines der beiden Features zu installieren ist. Andernfalls lohnt ein erneutes Hochfahren nicht.