Saltar al contenido

SpainClouds |

Cómo desplegar Terraform de Azure Container Instances (ACI)

Vamos a explorar cómo desplegar Azure Container Instances (ACI) utilizando Terraform. Como ya hemos comentado en varias ocasiones en este blog, ACI es un servicio fundamental de Azure para ejecutar contenedores Docker sin necesidad de gestionar máquinas virtuales subyacentes. Ofrece una solución rápida, sencilla y escalable para aplicaciones basadas en contenedores.

En esta guía, detallaré los pasos necesarios para configurar y desplegar ACI de manera eficiente y eficaz con Terraform, maximizando los beneficios de la automatización y la reproducibilidad que ofrece la infraestructura como código.

Esta guía cubre todos los aspectos básicos y esenciales, desde la creación de un grupo de recursos y una red virtual, hasta el despliegue de las instancias de contenedores. En una futura publicación, profundizaremos en la incorporación de funcionalidades adicionales y configuraciones más avanzadas. No obstante, para un despliegue sencillo y funcional, esta guía ofrece todo lo necesario.

Requisitos Previos

  • Necesitas tener instalado Terraform CLI en tu máquina local. Si es la primera vez que utilizas Terraform para desplegar recursos en Microsoft Azure, te recomiendo consultar este enlace.

  • Un editor de texto o un IDE de tu preferencia (recomiendo Visual Studio Code con la extensión de Terraform).

Declarar el Proveedor de Azure en Terraform

El archivo provider.tf en Terraform se utiliza para especificar y configurar los proveedores utilizados en tu configuración. Un proveedor es un servicio o plataforma donde se gestionarán los recursos. Esto puede ser un proveedor en la nube como Microsoft Azure, AWS, Google Cloud, etc.

Este archivo es importante porque indica a Terraform qué API de proveedor utilizar para crear, actualizar y eliminar recursos. Sin él, Terraform no sabría dónde gestionar tus recursos.

provider "azurerm" {
  features {}
  subscription_id = var.subscription_id
}

Importante: Terraform ahora requiere un ID de suscripción explícito en la configuración del proveedor para una gestión de infraestructura más clara, predecible y segura en entornos de nube complejos.

Despliegue de recursos de Azure usando Terraform

En el caso del despliegue de Azure Container Instances (ACI), el archivo main.tf contiene los siguientes componentes clave:

  • azurerm_resource_group: Este bloque crea el grupo de recursos de Azure donde se desplegarán el resto de los recursos.

  • azurerm_virtual_network: Define la red virtual (VNet) de Azure, que proporciona un entorno de red aislado para tus recursos.

  • azurerm_subnet: Define las subredes dentro de la red virtual, permitiendo segmentar la red. Utiliza local.flattened_subnets para iterar sobre la configuración de las subredes.

  • azurerm_container_group: Configura las instancias de contenedor de Azure, especificando parámetros como la imagen, CPU, memoria y configuración de red. Soporta múltiples contenedores dentro de un mismo grupo mediante for_each basado en var.aci_instances.

  • image_registry_credential: Es necesario cuando se extraen imágenes de contenedor desde el repositorio de Docker Hub. Ayuda a evitar los límites de extracción de imágenes públicas, ya que la autenticación incrementa esos límites

resource "azurerm_resource_group" "rg" {
  name     = var.resource_group_name
  location = var.resource_group_location
  tags     = var.tags
}

resource "azurerm_virtual_network" "vnet" {
  for_each            = var.networks
  name                = each.key
  address_space       = [each.value.address_space]
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  tags                = var.tags
}

locals {
  flattened_subnets = flatten([
    for vnet_key, vnet_value in var.networks : [
      for subnet in vnet_value.subnets : {
        vnet_name      = vnet_key
        subnet_name    = subnet.name
        address_prefix = subnet.address_prefix
      }
    ]
  ])
}

resource "azurerm_subnet" "subnet" {
  for_each = { for subnet in local.flattened_subnets : "${subnet.vnet_name}-${subnet.subnet_name}" => subnet }

  name                 = each.value.subnet_name
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = each.value.vnet_name
  address_prefixes     = [each.value.address_prefix]

  delegation {
    name = "container-instance-delegation"
    service_delegation {
      name    = "Microsoft.ContainerInstance/containerGroups"
      actions = ["Microsoft.Network/virtualNetworks/subnets/action"]
    }
  }
  depends_on = [azurerm_virtual_network.vnet]
}

resource "azurerm_container_group" "aci" {
  for_each            = { for idx, aci in var.aci_instances : idx => aci }
  name                = each.value.name
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  os_type             = "Linux"
  restart_policy      = each.value.restart_policy

  ip_address_type = each.value.is_public ? "Public" : "Private"
  subnet_ids      = each.value.is_public ? null : [azurerm_subnet.subnet["${each.value.vnet_name}-${each.value.subnet_name}"].id]
  dns_name_label  = each.value.is_public ? each.value.dns_label : null

  dynamic "container" {
    for_each = each.value.containers
    content {
      name   = container.value.name
      image  = container.value.image
      cpu    = container.value.cpu
      memory = container.value.memory

      dynamic "ports" {
        for_each = container.value.ports
        content {
          port     = ports.value.port
          protocol = ports.value.protocol
        }
      }
    }
  }

  image_registry_credential {
    server   = "index.docker.io"
    username = var.docker_username
    password = var.docker_password
  }

  tags       = var.tags
  depends_on = [azurerm_subnet.subnet]
}

Declaración de variables de entrada

El archivo variables.tf define las variables usadas en main.tf. Estas variables permiten una mayor flexibilidad y reutilización del código.

En este ejemplo, las variables definidas incluyen:

  • subscription_id: Utilizado para pasar dinámicamente el ID de suscripción de Azure a Terraform, mejorando la flexibilidad y seguridad.

  • resource_group_name: Variable tipo cadena para especificar el nombre del grupo de recursos de Azure donde se desplegarán todos los recursos.

  • resource_group_location: Especifica la ubicación o región en la que se creará el grupo de recursos.

  • tags: Mapa de cadenas clave-valor para asignar etiquetas a los recursos creados en Azure. Por ejemplo, se puede usar Terraform = true para indicar que el recurso se desplegó con Terraform.

  • networks: Es un mapa de objetos, cada uno representando la configuración de una red virtual, incluyendo su espacio de direcciones y una lista de subredes.

  • aci_instances: Mapa de objetos, cada uno representando la configuración de una instancia ACI. Incluye propiedades básicas como nombre, imagen, CPU, memoria, y otras más avanzadas como puertos, protocolo, accesibilidad pública, política de reinicio y etiqueta DNS. Soporta múltiples contenedores por instancia.

  • Credenciales de Docker Hub: Estas variables son opcionales. Aquí se pueden definir las credenciales de acceso a Docker Hub para evitar errores al acceder a imágenes públicas debido a los límites de extracción.

variable "subscription_id" {
  type        = string
  description = "Azure Subscription ID"
}

variable "resource_group_name" {
  description = "The name of the resource group"
  type        = string
}

variable "resource_group_location" {
  description = "The location of the resource group"
  type        = string
}

variable "networks" {
  description = "Map of virtual networks and their subnets"
  type = map(object({
    address_space = string
    subnets = list(object({
      name           = string
      address_prefix = string
    }))
  }))
}

variable "aci_instances" {
  type = map(object({
    name           = string
    vnet_name      = string
    subnet_name    = string
    containers     = list(object({
      name    = string
      image   = string
      cpu     = number
      memory  = number
      ports   = list(object({
        port     = number
        protocol = string
      }))
    }))
    is_public      = bool
    restart_policy = string
    dns_label      = string
  }))

  validation {
    condition     = alltrue([for aci in var.aci_instances : alltrue([for container in aci.containers : alltrue([for port in container.ports : port.protocol == "TCP" || port.protocol == "UDP"])])])
    error_message = "Protocol must be either 'TCP' or 'UDP'."
  }

  validation {
    condition     = alltrue([for aci in var.aci_instances : aci.restart_policy == "Always" || aci.restart_policy == "OnFailure" || aci.restart_policy == "Never"])
    error_message = "Restart policy must be either 'Always', 'OnFailure', or 'Never'."
  }
}

variable "docker_username" {
  description = "Docker Hub username (optional)"
  type        = string
  default     = null
}

variable "docker_password" {
  description = "Docker Hub password (optional)"
  type        = string
  default     = null
  sensitive   = true
}

variable "tags" {
  description = "Common tags for all resources"
  type        = map(string)
  default = {
    Environment = "www.jorgebernhardt.com"
    Terraform   = "true"
  }
}

Declaración de valores de salida

El archivo output.tf en Terraform extrae y muestra información sobre los recursos creados o gestionados por tu configuración. Estos outputs se definen con la palabra clave output y pueden ser útiles para el usuario, para otras configuraciones de Terraform o para su uso programático en scripts u otras herramientas.

En este ejemplo, el archivo output.tf devuelve información sobre el grupo de recursos, redes virtuales, subredes e instancias de Azure Container Instances (ACI) creadas.

Una vez que Terraform haya aplicado tu configuración, mostrará los valores de salida definidos.

output "resource_group_name" {
  description = "The name of the resource group"
  value       = azurerm_resource_group.rg.name
}

output "virtual_networks" {
  description = "List of virtual networks with their name and address space"
  value = [
    for vnet in azurerm_virtual_network.vnet : {
      name          = vnet.name
      address_space = vnet.address_space
    }
  ]
}

output "subnets" {
  description = "List of subnets with their name, address prefix, and associated virtual network name"
  value = [
    for subnet in azurerm_subnet.subnet : {
      name           = subnet.name
      address_prefix = subnet.address_prefixes
      vnet_name      = subnet.virtual_network_name
    }
  ]
}

output "aci_instances" {
  description = "Details of the deployed ACI instances"
  value = {
    for aci in azurerm_container_group.aci : aci.name => {
      name           = aci.name
      location       = aci.location
      restart_policy = aci.restart_policy
      resource_id    = aci.id
      ip_address     = aci.ip_address
      fqdn           = aci.fqdn
      containers = [
        for container in aci.container : {
          name   = container.name
          image  = container.image
          cpu    = container.cpu
          memory = container.memory
          ports  = [
            for port in container.ports : {
              port     = port.port
              protocol = port.protocol
            }
          ]
        }
      ]
    }
  }
}

Ejecución del despliegue con Terraform

Ahora que has declarado correctamente los recursos, es momento de realizar los siguientes pasos para desplegarlos en tu entorno de Azure:

  • Inicialización: Ejecuta el comando terraform init. Esto inicializa el directorio de trabajo que contiene los archivos .tf, descarga el proveedor especificado en provider.tf y configura el backend de Terraform. Si quieres saber más sobre este paso, consulta [este enlace].

  • Planificación: A continuación, ejecuta terraform plan. Este comando genera un plan de ejecución y muestra las acciones que realizará Terraform para alcanzar el estado deseado. Te permite revisar los cambios antes de aplicarlos.

  • Aplicación: Cuando estés satisfecho con el plan, ejecuta terraform apply. Este comando implementará las modificaciones necesarias para alcanzar el estado deseado de la infraestructura. Antes de aplicar cualquier cambio, se te pedirá confirmación.

  • Inspección: Tras aplicar los cambios, puedes usar terraform show para ver el estado actual de tu infraestructura.

  • Destrucción (opcional): Cuando ya no se necesita un proyecto o los recursos han quedado obsoletos, puedes usar el comando terraform destroy. Esto eliminará todos los recursos creados por Terraform.

Recibe nuestra Newsletter de Cloud semanal