PhotonOS OVA deployen mit cloud-init auf vCenter (Entwurf)

Wie kann ich ein PhotonOS deployen, welches beim Start auf die cloud-init Config zugreift, ohne das ein CD-Rom gemounted werden muss?

Vor längerem habe ich mal die Aufgabe gehabt, ein Template herzustellen, dass beim Start automatisch den Hostname setzt, eine IP-Konfiguration erstellt und ein paar variable Commands durchführen kann.

Der erste Versuch klappte eigentlich nicht ganz so schlecht. Zuerst habe ich PhotonOS deployed, dann die OVF Optionen gesetzt und den Transportweg entsprechend in den vApp Options gesetzt. Auf der VM habe ich mir 2 Python Scripts gebaut, die als Service registriert waren. Eines davon hat direkt während dem booten den Hostname ersetzt und das andere wartete, bis Docker gestartet war und hat dann seine Befehle entsprechend abgesetzt.

Danach exportieren, wandeln von OVF in OVA und gut war. Das erstellen des Templates war, wenn man geübt war, in 10-15 Minuten erledigt. Das Problem am ganzen, trotz guter Vorbereitung können während den ganzen Schritten fehler passieren und es ist doch recht viel manuelle Arbeit dabei.

Via cloud-init etwas zu machen, war damals schon die Idee, aber habs auf keinen Fuss zum Laufen bekommen, da ich schlicht nicht verstanden habe, wie ich denn dieses File in die VM bekommen soll. Ein Kollege hat sich dann mit Terraform beschäftigt und hat mir dann mal gezeigt, wie er das ganze auf Terraform macht.

Während dem arbeiten konnte ich dann beobachten, wie Terraform das handhabt und was Terraform kann, kann Powershell eigentlich auch.

Also kam ich weg von den ganzen Python Scripts und musste mal eine user-data.yml Datei erstellen. Diese wird Base64 kodiert und via OVF Environment der VM mitgegeben.

#cloud-config
hostname: %HOSTNAME%
fqdn: %HOSTNAME%.%DOMAIN%
runcmd:
- iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
- iptables -A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT 
- systemctl start docker
- docker rm $(docker ps -a -q)
- docker run --name nginx -d -p 81:80 nginx
- docker run --name grafana -d -p 3000:3000 grafana/grafana
- docker run --name=influxdb -d -p 8086:8086 influxdb

Was macht die Beispiel user-data.yml Datei? Sie soll einen Hostname setzen, welchen ich später im Script von %HOSTNAME% in eine GUID ersetze, den FQDN wobei %DOMAIN% ebenfalls ersetzt wird.

Dann ein paar Firewallcommands damit die VM pingbar wird.

Als letztes wird der Dockerservice gestartet, vorhandene Docker gelöscht und 3 Dockercontainer geladen und gestartet.

  • nginx auf Port 81
  • grafana auf Port 3000
  • influxdb auf Port 8086

Nun das untere Script ist noch ziemlich in der Entwurfsphase. Aber für einen Betatest reichts soweit aus.

  • Definition der Variablen
  • Herunterladen und speichern vom PhotonOS
  • Einlesen der user-data.yml
  • Ersetzen der Platzhalter
  • Kodieren der Daten in Base64
  • Schreiben der Datei user-data.yml
  • Einlesen der user-data.yml
  • OVA auf vSphere importieren
  • Setzen der OVF Variable user-init und abfüllen vom Wert mit den Base64 Daten
  • Starten der VM
#Variables
$webPath        =   'https://packages.vmware.com/photon/3.0/Rev2/ova/Update1/'
$destPath       =   $env:USERPROFILE+'\Desktop\'
$vCenterServer  =   'VCENTER'
$fileName       =   'photon-hw11-3.0-0a85037c.ova'
$deployto       =   'ESXIHOST'
$datastore      =   'STORAGE'
$connectto      =   'NETWORK'

$hostname       =   New-GUID
$domain         =   'DOMAIN'

Invoke-WebRequest -Uri ($webPath+$fileName) -OutFile ($destPath+$fileName)
Connect-VIServer -Server $vCenterServer

if(Test-Path .\user-data.yml) {
    $userData = Get-Content '.\user-data.yml' -Raw
    $userData = $userData.Replace('%HOSTNAME%', $hostname)
    $userData = $userData.Replace('%DOMAIN%', $domain)
    $userData = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($userData))
    $userData | Out-File '.\user-data.base64' -Force
    Remove-Item '.\user-data.yml' -Force
}

if(Test-Path .\user-data.base64) {
    $userData = Get-Content '.\user-data.base64' -Raw  
}

$OvfConfiguration = Get-OvfConfiguration ($destPath+$fileName)
$OvfConfiguration.NetworkMapping.None.Value = $connectto


$templateVM = Get-VMHost $deployto | Import-VApp -Source ($destPath+$fileName) -Datastore (Get-Datastore $Datastore) -OvfConfiguration $OvfConfiguration -Name $newHostname

#Set OvfEnvironmentTransport Options to VMTools
$spec = New-Object VMware.Vim.VirtualMachineConfigSpec
$spec.VAppConfig = New-Object VMware.Vim.VmConfigSpec
$spec.VAppConfig.OvfEnvironmentTransport = @('com.vmware.guestInfo')
 
$spec.VAppConfig.Property = New-Object VMware.Vim.VAppPropertySpec[] (1)
 
#user-data
$spec.VAppConfig.Property[0] = New-Object VMware.Vim.VAppPropertySpec
$spec.VAppConfig.Property[0].Operation = 'add'
$spec.VAppConfig.Property[0].Info = New-Object VMware.Vim.VAppPropertyInfo
$spec.VAppConfig.Property[0].Info.Key = '0'
$spec.VAppConfig.Property[0].Info.Category = "cloud-init"
$spec.VAppConfig.Property[0].Info.Id = 'user-data'
$spec.VAppConfig.Property[0].Info.Label = 'user-data'
$spec.VAppConfig.Property[0].Info.Value = $userData
$spec.VAppConfig.Property[0].Info.Description = 'user-data'

$templateVM.ExtensionData.ReconfigVM_Task($spec)

Start-VM $templateVM

Bei jedem Ausführen würde nun immer das PhotonOS heruntergeladen, was man nicht will, das kommentiere ich nach dem ersten mal aus. Gleiches gilt für die vCenterverbindung.

Danach kann man das Script immer und immer wieder starten, es wird eine VM generiert, mit GUID als Namen und bei jedem Start werden die 3 Dockercontainer angezogen und gestartet.

Das Grundprinzip funktioniert soweit, nun geht es an Detailsachen, wie man die Daten am besten fixiert, z.B. auf ein NFS Share legen und möglichst den Container stateless zu belassen.

Leave a Reply

Your email address will not be published. Required fields are marked *