Een plek voor VMs binnen je Cloud Native landschap? Het kan met Packer!

Artikel uit SDN magazine 145

Inleiding

In de huidige wereld van cloud native, PaaS en SaaS zou je haast vergeten dat nog heel veel workloads draaien op virtuele machines (VMs). Voor software developers vaak een blok aan het been, en vaak ook niet binnen het blikveld van een team. Hoe kun je nu in je volledig geautomatiseerde CICD omgeving deze machines toch een plek geven?

 

In dit artikel laat ik zien hoe je HashiCorp Packer kan gebruiken om je VMs kan opnemen in je automated deploy en dit herhaalbaar en volledig geautomatiseerd kan neerzetten.

Probleemstelling

Stel je bent met je team een complete applicatie aan het bouwen in de Azure cloud. Een front end in Static Web Apps, een CosmosDB voor je opslag en een ServiceBus voor communicatie. Echter is er één COTS (Commercial-off-the-shelf) applicatie die je moet gebruiken voor wat logica. In dit voorbeeld hebben we niet de ruimte of tijd om deze WindowsService opnieuw te schrijven, en het hosten van een dergelijke service is (buiten VMs) heel erg lastig. Uiteraard neem je in je roadmap op dat je dit component graag wil vervangen, maar dat is wegens licentie redenen niet haalbaar binnen een jaar.

 

Nu kun je er voor kiezen om de service te laten hosten door een managed omgeving. Dit uit te besteden aan je partner of een andere partij. Echter is dat complex. Denk aan bijvoorbeeld connecties die de service moet maken naar je landschap. Je zit met peering, networking, etc. Idealiter wil je dit dus gewoon opnemen binnen je team, en zelf je broek ophouden als het gaat om de hosting, updates en security van het geheel.

De oplossing

VMs uitrollen, compleet met software is relatief eenvoudig, als de image reeds bestaat. Denk aan een DevTest lab in Azure, of een marketplace image in Azure. Dat zijn voorbeelden van images compleet met voorgeïnstalleerde software. Deze rol je uit en je kan ze gebruiken.

 

In de wereld van containers lijken VMs maar lastig, groot en oud, maar een container baseer je simpelweg ook op een image. En ja, de image van een VM is doorgaans groter maar de cloud providers spinnen dat snel op, dus het is een prima oplossing om een image te maken en die te koppelen aan een machine in Azure. In dit hoofdstuk laat ik zien hoe je een image kan opslaan en gebruiken in Azure, en hoe je die maakt met behulp van de HashiCorp tool Packer.

Shared Images

in basis kan een image niet meer zijn dan een .VHD bestand op je harddisk. Maar in Azure kun je een Shared Image Gallery (SIG) maken. Dat is een opslagplek voor images die je daarna kan gebruiken. Dat scheelt lokaal opslag, en het is ook eenvoudig toegankelijk voor je VMs. Voorbeelden van images zijn standaard Windows of Linux omgevingen, tot en met een hardened versie van je omgeving. De basis van je applicatie is dus zo’n image.

 

Omdat wij een COTS applicatie willen neerzetten zonder enige handmatige configuratie, zullen we dus een begin moeten maken met een Shared Image Gallery. Om niet te diep in te gaan op dit concept, slaan we even wat concepten plat door middel van onderstaande afbeelding:

Hier zien we in concept een SIG met daarin een viertal definities. Elke definitie kan ook weer een versie hebben. Denk aan een image voor Windows 10 en een voor Windows 11. Conceptueel niet erg ingewikkeld. Voor dit voorbeeld gaan we uit van één definitie, met één versie. Let wel; dit zijn allemaal nog lege items. De daadwerkelijke image moet gemaakt worden met een tool!

 

Het aanmaken van een SIG kan met behulp van AZ CLI zeer eenvoudig:

# Resource group en image gallery

az group create –name myGalleryRG –location westeurope

az sig create –resource-group myGalleryRG –gallery-name myGallery

 

# Standaard Windows definitie

az sig image-definition create \

–resource-group myGalleryRG \

–gallery-name myGallery \

–gallery-image-definition myImageDefinition \

–publisher myPublisher \

–offer myOffer \

–sku mySKU \

–os-type Windows \

–hyper-v-generation V2 \

–os-state Generalized

 

Deze code maakt de resourcegroup aan, met een shared image gallery en daarin één definitie, voor een Windows image.

 

Let op dat je voor sommige base images een V1 of een V2 hyper-v-generation in moet stellen. By default wordt de V1 genomen. In ons voorbeeld is de basis image waar we de COTS op installeren een 2022-datacenter-azure-edition, en dat is een V2 hyper-v-generation image.

 

Het eindresultaat in de Azure portal ziet eruit als volgt:

Tip: Open een Azure Cloud shell om dit voorbeeld direct na te spelen.

HashiCorp Packer

Waarschijnlijk gebruik je voor het deployen van je infrastructuur en componenten een tool als bicep, Pulumi of Terraform. Die laatste is van HashiCorp, en zij maken ook de tool Packer. Deze tool maakt gebruik van dezelfde taal (HashiCorp Configuration Language) en zorgt voor een zeer eenvoudige en overzichtelijke manier van images maken.

 

In dit artikel gaan we niet in op de installatie van Packer, want die is dermate simpel en te vinden op de site https://packer.io/downloads. Daar zijn ook talloze voorbeelden hoe images te maken voor AWS, Azure, Google en alle andere grote platformen die je kan gebruiken als doelomgeving.

 

In dit voorbeeld gaan we uit van Azure, waar we de SIG hebben gemaakt, met één image definitie met daarin één versie. Hoe maken we nu de Packer code, om deze image te bouwen?

 

Er zijn twee HashiCorp Configuration Language (HCL) bestanden nodig om de meest simpele setup te maken voor een image, en een PowerShell voor alle zaken die in Windows moeten gebeuren. Allereerst de HCL:

build.pkr.hcl

build {

sources = [“source.azure-arm.windows2022”]

 

provisioner “powershell” {

script = “scripts/main.ps1”

}

}

sources.pkr.hcl

source “azure-arm” “windows2022” {

location = “westeurope”

 

tenant_id       = var.tenant_id

subscription_id = var.subscription_id

client_id       = var.client_id

client_secret   = var.client_secret

 

managed_image_resource_group_name = “myGalleryRG”

managed_image_name                = “myImageDefinition-1.0.0”

 

image_offer     = “WindowsServer”

image_publisher = “MicrosoftWindowsServer”

image_sku       = “2022-datacenter-azure-edition”

os_type         = “Windows”

vm_size         = “Standard_D2s_v4”

 

communicator   = “winrm”

winrm_use_ssl  = true

winrm_insecure = true

winrm_timeout  = “10m”

winrm_username = “packer”

 

subscription       = var.subscription_id

resource_group     = “myGalleryRG”

gallery_name       = “myGallery”

image_name         = “myImageDefinition”

image_version      = “1.0.0”

}

 

 

De voornoemde HCL code genereert het image op de Azure plek waar we de shared image gallery hebben aangemaakt. Packer doet dat door ‘onder water’ een VM op te spinnen, uit te rollen en daarna te packagen.

 

Wellicht vraag je je af waarom er een VM size in staat. Dit is dus de VM size waarmee Packer gaat builden. Als de image gereed is kun je gewoon een ander type VM size pakken, naar gelang je nodig hebt. Kies nooit een te kleine VM voor builden, immers is tijd geld in de cloud, en een zwaardere VM zal sneller klaar zijn met alle stappen.

 

De provisioner die in de build file zit, start een PowerShell file. Hieronder de contents van die PowerShell. Mocht je een Linux image opbouwen heb je uiteraard andere opties, bijvoorbeeld een bash script.

scripts/main.ps1

# Start of script

$InstallSourcePath = ‘C:\Install’

New-Item $InstallSourcePath -ItemType “directory” -Force

 

# Start downloading Azure CLI

Invoke-WebRequest -Uri https://aka.ms/installazurecliwindows -OutFile $InstallSourcePath\AzureCLI.msi

 

# Start install Azure CLI

Start-Process -filepath msiexec.exe -Wait -ErrorAction Stop -ArgumentList “/I $InstallSourcePath\AzureCLI.msi /qn”

 

# SysPrep machine

Write-Log “Starting Sysprep”

& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit /mode:vm

while($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select-Object ImageState; if($imageState.ImageState -ne ‘IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE’) { Write-Output $imageState.ImageState; Start-Sleep -s 10  } else { break } }

}

 

# Cleanup downloads

Remove-Item -Path “$InstallSourcePath” -Recurse -Force

 

In bovenstaande code zien we een simpel voorbeeld van het installeren van de Azure CLI op de Windows machine. Daarna wordt er een SysPrep gedaan op de machine, nodig om deze voor te bereiden om als image te kunnen worden uitgerold, en als laatste ruimen we netjes de installatiefolder op.

 

In het real world scenario werd hier de COTS applicatie geïnstalleerd, en daarbij ook firewall poorten opengezet in Windows.

Packer uitvoeren

Als alle code klaar is, kun je Packer uitvoeren om daadwerkelijk de image te maken en te deployen. Omdat er feitelijk een VM wordt gestart, alle software wordt geïnstalleerd en daarna een image wordt getrokken, kan dit proces best wat tijd in beslag nemen. Een half uur is niet uitzonderlijk. Let ook op de VM size in je script, daarmee spelen kan leiden tot betere, snellere builds.

 

Omdat we in dit voorbeeld niet werken met variabelen in bestanden, is een simpel commando in Packer voldoende om de configuratie te valideren:

 

# User logged in using AZ CLI

packer validate .

 

Door het bovenstaande commando valideert Packer alle bestanden in de huidige directory. Net zoals bij Terraform maakt het niet uit of je alles in één bestand plaatst of juist in losse. Voor de overzichtelijkheid is een scheiding wel erg makkelijk. En uiteraard is het PowerShell script wel een apart bestand (in dit voorbeeld in de submap scripts).

 

Nu rest ons slechts nog de start van de build, met behulp van onderstaand commando:

 

# User logged in using AZ CLI

packer build .

 

Ook hier wordt met de punt aangegeven dat de code in de huidige directory aanwezig is.

 

Een voorbeeld van zo’n build in Azure DevOps is hieronder weergegeven:

Hier zie je ook de tijdelijke resourcegroup naam die Packer gebruikt om de machine op te bouwen waarvan een image wordt gemaakt. In dit geval duurt het circa 25 minuten voor de gehele build klaar is, waarbij dus ook de image in de SIG is geplaatst.

Virtual Machine ScaleSet

De image komt natuurlijk pas tot leven als we deze uitrollen. Een van de meest gebruikte oplossingen is een Virtual Machine ScaleSet (VMSS). Het grote voordeel van deze setup is dat we eenvoudig de VMSS kunnen laten wijzen naar een SIG. Daarbij kun je dus automatisch wijzen naar je image, waardoor de VMSS altijd up-to-date is, en de image die je gemaakt hebt met Packer zal gebruiken als de basis.

 

Met de Azure CLI kunnen we nu heel gemakkelijk een VMSS opspinnen met de volgende commando’s:

 

az group create –name myResourceGroup –location eastus

az vmss create \

–resource-group myResourceGroup \

–name myScaleSet \

–image “/subscriptions/<Subscription ID>/resourceGroups/myGalleryRG/providers/Microsoft.Compute/galleries/myGallery/images/myImageDefinition” \

–generalized

 

Als voorbeeld een screenshot van een soortgelijke opzet:

Hier is de zien dat het Operating system wijst naar een image reference. Bijkomend voordeel; zodra er een nieuwe image beschikbaar is, is het eenvoudig om deze weer uit te rollen in de scale set.

 

Er moet wel een kanttekening geplaatst worden met deze opzet. Als je COTS applicatie naar een database wijst, moet deze wel geschikt zijn om meerdere instanties toe te laten. Het is daarom wel belangrijk om te controleren dat je zonder problemen met meerdere COTS instanties kan runnen.

Vervolg; Automation en CICD

Alle stappen die werden getoond in dit document zijn eenvoudig te automatiseren. Een van de screenshots is zelfs van een Azure Pipeline runner. De tool Packer wordt ook intern bij Microsoft gebruikt voor Azure Image Builder, en alle hosted runners hebben Packer (maar ook Terraform) pre-installed. De stap naar CICD is dus erg klein.

 

Later dit jaar komt een whitepaper uit waarin we (Sogeti en HashiCorp) diep ingaan op dit concept, en zelfs images maken op basis van andere images in de SIG, Inception eat your heart out! Hierbij komt ook een complete GitHub repository online waar alle broncode met werkende voorbeelden te vinden zullen zijn. Ook gaan we daar met Terraform te werk om alle componenten neer te zetten in de Azure Cloud, houdt dus de publicaties vooral in de gaten!

 

Over de auteur

Peter Rombouts is een Cloud Software Architect met bijna 20 jaar ervaring, met een brede achtergrond in infrastructuur, firewalling, networking en software architectuur. Ook schrijft hij regelmatig eBooks en whitepapers voor Microsoft, en is hij Udemy auteur met twee gepubliceerde trainingen omtrent Cognitive Services.

 

Zijn werkgebied bevindt zich momenteel op het snijvlak van cloud, software engineering, low-code en integratie. Hij is uitgesproken fan van alles met een stekker, accu of wielen en specialist op Azure, HashiCorp Terraform, Cloud Native en Integration Services.

 

LinkedIn: https://www.linkedin.com/in/peterrombouts78/

Blog: https://peterrombouts.nl