En este artículo quiero compartir una plantilla ARM para crear múltiples máquinas virtuales Azure en un conjunto de disponibilidad (availability set). Sí, así como lees, múltiples máquinas, y lo mejor, sin escribir mucho código. Para ello, utilizaremos loops (bucles) en Bicep. Los bucles en Bicep son útiles para definir varias copias de un recurso y evitar repetir la sintaxis en la plantilla. Con esta plantilla, solo debes indicar en un parámetro el número de máquinas que deseas desplegar, y dinámicamente se creará la cantidad de máquinas indicadas, distribuidas en dos subredes diferentes. También, puedes elegir el sistema operativo de forma dinámica, Ubuntu o Windows.
En la siguiente ilustración, puedes ver la topología resultante de la implementación de 4 máquinas virtuales. Usando la plantilla, esto lo tendrás en cuestión de segundos.
Antes de pasar a ver el código, es importante conocer que es availability set o conjunto de disponibilidad.
¿Qué es un conjunto de disponibilidad en Azure?
Al momento de diseñar una arquitectura en la nube, tenemos que pensar en alta disponibilidad, la infraestructura tiene que ser resistente. Un Availability set o conjunto de disponibilidad es una agrupación de máquinas virtuales para proporcionar redundancia a la aplicación. Azure recomienda agregar dos o más máquinas a un availability set. Esto garantiza que, durante un evento de mantenimiento planeado o no planeado, tendremos como mínimo una máquina virtual disponible y en funcionamiento. De esta forma garantizamos la alta disponibilidad, además, de cumplir con el SLA de Azure. El conjunto de disponibilidad indica a Azure Fabric Controller (componente que gestiona los recursos hardware) que las máquinas virtuales que contiene deben distribuirse en diferentes hosts y hardware, evitando así, tener punto de falla único que pueda hacer caer todos los servidores.
Automatizar el aprovisionamiento de máquinas virtuales en Azure con plantillas ARM
Lo primero, crear el archivo con la extensión .bicep. Para este ejemplo, lo he nombrado availavility-set.bicep
Definición de parámetros
En este primer bloque he definido los parámetros que considero necesarios. Puedes ver en la descripción una breve explicación. En el parámetro numberVM se define la cantidad de máquinas a desplegar. Puedes modificar el valor o eliminarlo y hacer que lo solicite al momento de ejecutar la implementación desde la CLI. Con el parámetro sistemaOperativo se le indica que sistema operativo utilizar, en este caso he dejado Ubuntu de forma predeterminada, pero la plantilla está preparada para usar Windows Server 2019 o Ubuntu 21.04 como podrás comprobar en el apartado de las variables.
@description('Nombre de la aplicación o proyecto - Prefijo para el nombre de los recursos')
param resourceName string = 'crashell'
@description('Región para crear los recursos')
param location string = resourceGroup().location
@description('Cantidad de máquinas virtuales a crear')
param numberVM int = 4
@description('Sistema Operativo (Windows o Ubuntu)')
param sistemaOperativo string = 'Ubuntu'
@description('Nombre de usuario para las máquinas virtuales')
param adminUsername string
@description('Contraseña para las máquinas virtuales')
param adminPassword string
Definición de variables
A continuación, la definición de las propiedades de la imagen de referencia para la máquina, tenemos opción de usar Ubuntu 21.04 y Windows Server 2016. Si requieres otro sistema operativo o una imagen personalizada, puedes agregar o modificar los valores correspondientes. También, he definido variables para los nombres de los recursos y demás propiedades que serán reutilizadas dentro de la plantilla.
var imageReference = {
Ubuntu: {
publisher: 'Canonical'
offer: '0001-com-ubuntu-server-hirsute'
sku: '21_04-gen2'
version: 'latest'
}
Windows: {
publisher: 'MicrosoftWindowsServer'
offer: 'WindowsServer'
sku: '2019-Datacenter'
version: 'latest'
}
}
var avSetName = '${resourceName}-avset'
var vnetName = '${resourceName}-vnet'
var vnetAddress = '10.0.0.0/16'
var subnet1Name = 'subnet1'
var subnet2Name = 'subnet2'
var subnet1Address = '10.0.1.0/24'
var subnet2Adress = '10.0.2.0/24'
var subnet1Ref = resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, subnet1Name)
var subnet2Ref = resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, subnet2Name)
var vmName = '${resourceName}-vm'
var vmSize = 'Standard_B1s'
var nicName = '${resourceName}-nic'
var envTag = 'dev'
Cómo crear un conjunto de disponibilidad en Azure con Bicep
El siguiente bloque crea un conjunto de disponibilidad (availability set) con 2 dominios de error y 5 dominios de actualización. Dado que las máquinas virtuales de este grupo tendrán discos administrados, el valor de es sku es Aligned.
resource availavilitySet 'Microsoft.Compute/availabilitySets@2021-07-01' = {
name: avSetName
location: location
properties: {
platformFaultDomainCount: 2
platformUpdateDomainCount: 5
}
sku: {
name: 'Aligned'
}
tags: {
Name: resourceName
env: envTag
}
}
Crear red virtual en Azure con Bicep
El siguiente bloque crea una red virtual y dos subredes. Todas las propiedades se han definido previamente en variables.
resource vnet 'Microsoft.Network/virtualNetworks@2021-03-01' = {
name: vnetName
location: location
properties: {
addressSpace: {
addressPrefixes: [
vnetAddress
]
}
subnets: [
{
name: subnet1Name
properties: {
addressPrefix: subnet1Address
}
}
{
name: subnet2Name
properties: {
addressPrefix: subnet2Adress
}
}
]
}
tags: {
Name: resourceName
env: envTag
}
}
Crear interfaces de red en Azure con Bicep
Al inicio mencioné que reutilizaríamos código para crear recursos. Ha llegado el momento de usar los bucles. Como son varias máquinas virtuales (dependiendo del valor en el parámetro numberVM) se necesita una tarjeta de red para cada servidor. En este bloque se crean las interfaces de red virtuales de forma dinámica, para ello, un típico ciclo for se encarga de iterar desde 0 hasta el número de máquinas. Finalmente, con una condición determina a que subred asociarla, si el número es par se agrega a la subred1, si es impar, se asocia a la subred2.
resource nic 'Microsoft.Network/networkInterfaces@2021-03-01' = [for i in range(0, numberVM): {
name: '${nicName}-${i}'
location: location
properties: {
ipConfigurations: [
{
name: 'ipconfig1'
properties: {
privateIPAllocationMethod: 'Dynamic'
subnet: {
id: (((i % 2) == 0) ? subnet1Ref : subnet2Ref)
}
}
}
]
}
dependsOn: [
vnet
]
tags: {
Name: resourceName
env: envTag
}
}]
Crear máquinas virtuales en Azure con Bicep
En este último bloque se crean todas las máquinas virtuales de forma dinámica, también usando un bucle con la misma funcionalidad que el anterior. Las máquinas se agregan al conjunto de disponibilidad creado previamente. Y lo demás, las propiedades de la máquina, el tamaño, las credenciales de acceso, la imagen de máquina o sistema operativo y la interfaz de red, que también es seleccionada de forma dinámica.
resource vm 'Microsoft.Compute/virtualMachines@2021-07-01' = [for i in range(0, numberVM): {
name: '${vmName}-${i}'
location: location
properties: {
availabilitySet: {
id: availavilitySet.id
}
hardwareProfile: {
vmSize: vmSize
}
osProfile: {
computerName: 'VM-${i}'
adminUsername: adminUsername
adminPassword: adminPassword
}
storageProfile: {
imageReference: imageReference[sistemaOperativo]
osDisk: {
createOption: 'FromImage'
}
}
networkProfile: {
networkInterfaces: [
{
id: resourceId('Microsoft.Network/networkInterfaces', '${nicName}-${i}')
}
]
}
}
dependsOn: [
availavilitySet
nic
]
tags: {
Name: resourceName
env: envTag
}
}]
Plantilla completa
A continuación, la plantilla ARM completa para crear máquinas virtuales en un conjunto de disponibilidad de Azure.
@description('Nombre de la aplicación o proyecto - Prefijo para el nombre de los recursos')
param resourceName string = 'crashell'
@description('Región para crear los recursos')
param location string = resourceGroup().location
@description('Cantidad de máquinas virtuales a crear')
param numberVM int = 4
@description('Sistema Operativo (Windows o Ubuntu)')
param sistemaOperativo string = 'Ubuntu'
@description('Nombre de usuario para las máquinas virtuales')
param adminUsername string
@description('Contraseña para las máquinas virtuales')
param adminPassword string
var imageReference = {
Ubuntu: {
publisher: 'Canonical'
offer: '0001-com-ubuntu-server-hirsute'
sku: '21_04-gen2'
version: 'latest'
}
Windows: {
publisher: 'MicrosoftWindowsServer'
offer: 'WindowsServer'
sku: '2019-Datacenter'
version: 'latest'
}
}
var avSetName = '${resourceName}-avset'
var vnetName = '${resourceName}-vnet'
var vnetAddress = '10.0.0.0/16'
var subnet1Name = 'subnet1'
var subnet2Name = 'subnet2'
var subnet1Address = '10.0.1.0/24'
var subnet2Adress = '10.0.2.0/24'
var subnet1Ref = resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, subnet1Name)
var subnet2Ref = resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, subnet2Name)
var vmName = '${resourceName}-vm'
var vmSize = 'Standard_B1s'
var nicName = '${resourceName}-nic'
var envTag = 'dev'
resource availavilitySet 'Microsoft.Compute/availabilitySets@2021-07-01' = {
name: avSetName
location: location
properties: {
platformFaultDomainCount: 2
platformUpdateDomainCount: 5
}
sku: {
name: 'Aligned'
}
tags: {
Name: resourceName
env: envTag
}
}
resource vnet 'Microsoft.Network/virtualNetworks@2021-03-01' = {
name: vnetName
location: location
properties: {
addressSpace: {
addressPrefixes: [
vnetAddress
]
}
subnets: [
{
name: subnet1Name
properties: {
addressPrefix: subnet1Address
}
}
{
name: subnet2Name
properties: {
addressPrefix: subnet2Adress
}
}
]
}
tags: {
Name: resourceName
env: envTag
}
}
resource nic 'Microsoft.Network/networkInterfaces@2021-03-01' = [for i in range(0, numberVM): {
name: '${nicName}-${i}'
location: location
properties: {
ipConfigurations: [
{
name: 'ipconfig1'
properties: {
privateIPAllocationMethod: 'Dynamic'
subnet: {
id: (((i % 2) == 0) ? subnet1Ref : subnet2Ref)
}
}
}
]
}
dependsOn: [
vnet
]
tags: {
Name: resourceName
env: envTag
}
}]
resource vm 'Microsoft.Compute/virtualMachines@2021-07-01' = [for i in range(0, numberVM): {
name: '${vmName}-${i}'
location: location
properties: {
availabilitySet: {
id: availavilitySet.id
}
hardwareProfile: {
vmSize: vmSize
}
osProfile: {
computerName: 'VM-${i}'
adminUsername: adminUsername
adminPassword: adminPassword
}
storageProfile: {
imageReference: imageReference[sistemaOperativo]
osDisk: {
createOption: 'FromImage'
}
}
networkProfile: {
networkInterfaces: [
{
id: resourceId('Microsoft.Network/networkInterfaces', '${nicName}-${i}')
}
]
}
}
dependsOn: [
availavilitySet
nic
]
tags: {
Name: resourceName
env: envTag
}
}]
Implementar plantilla ARM desde la CLI
Dinámicamente Es ideal comprobar para encontrar errores y obtener una vista previa de los cambios antes de hacer la implementación. Para ello, ejecuta:
az deployment group create --resource-group CrashellRG --template-file availavility-set.bicep -c
El comando anterior hace la comprobación y pregunta si quieres hacer el deployment. Para hacer la implementación sin hacer una comprobación, ejecuta la siguiente instrucción:
az deployment group create --resource-group CrashellRG --template-file availavility-set.bicep
Cambia CrashellRG por tu grupo de recursos.
Para eliminar todos los recursos creados, la forma más rápida es eliminar el grupo de recursos.
Puedes visitar el repositorio Infraestructura como código en Azure con Bicep para encontrar para encontrar esta plantilla completa y otras más.
With supporting text below as a natural lead-in to additional content.
Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled.