O Packer é uma ferramenta open-source da HashiCorp que automatiza a criação de imagens de máquina para múltiplas plataformas a partir de um único template. Em vez de configurar um servidor manualmente, tirar um snapshot e torcer para ter documentado cada etapa, o Packer codifica todo o processo. Você escreve um template, executa packer build, e obtém imagens idênticas para AWS, Azure, Docker e VMware — tudo a partir de uma única definição.

Este guia cobre a instalação do Packer, a sintaxe de templates HCL, builders, provisioners, builds multi-plataforma e a integração do Packer em pipelines de CI/CD para entrega automatizada de imagens.

Pré-requisitos

  • Packer instalado (v1.9+)
  • Uma conta em pelo menos uma plataforma alvo (AWS, Azure, Docker ou VirtualBox)
  • AWS CLI configurado (caso esteja construindo AMIs)
  • Conhecimento básico de imagens de máquina e provisionamento de servidores
  • Familiaridade com a sintaxe HCL (similar ao Terraform)

Instalando o Packer

Instale a partir do repositório da HashiCorp para receber atualizações automáticas:

# Ubuntu/Debian
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install packer

# macOS
brew install packer

# Verificar instalação
packer version

Seu Primeiro Template Packer

O Packer usa templates HCL (HashiCorp Configuration Language). Veja um exemplo mínimo que constrói uma imagem Docker:

# docker-nginx.pkr.hcl

packer {
  required_plugins {
    docker = {
      version = ">= 1.0.0"
      source  = "github.com/hashicorp/docker"
    }
  }
}

source "docker" "nginx" {
  image  = "ubuntu:22.04"
  commit = true
}

build {
  name = "custom-nginx"

  sources = ["source.docker.nginx"]

  provisioner "shell" {
    inline = [
      "apt-get update",
      "apt-get install -y nginx curl",
      "rm -rf /var/lib/apt/lists/*"
    ]
  }

  post-processor "docker-tag" {
    repository = "myorg/custom-nginx"
    tags       = ["latest", "1.0"]
  }
}

Execute o build:

# Inicializar plugins
packer init docker-nginx.pkr.hcl

# Validar o template
packer validate docker-nginx.pkr.hcl

# Construir a imagem
packer build docker-nginx.pkr.hcl

Criando AWS AMIs com o Packer

O caso de uso mais comum do Packer é a criação de Amazon Machine Images (AMIs):

# aws-webserver.pkr.hcl

packer {
  required_plugins {
    amazon = {
      version = ">= 1.2.0"
      source  = "github.com/hashicorp/amazon"
    }
  }
}

variable "aws_region" {
  type    = string
  default = "us-east-1"
}

variable "app_version" {
  type    = string
  default = "1.0.0"
}

source "amazon-ebs" "webserver" {
  ami_name      = "webserver-{{timestamp}}"
  instance_type = "t3.micro"
  region        = var.aws_region

  source_ami_filter {
    filters = {
      name                = "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"
      root-device-type    = "ebs"
      virtualization-type = "hvm"
    }
    owners      = ["099720109477"]  # Canonical
    most_recent = true
  }

  ssh_username = "ubuntu"

  tags = {
    Name        = "WebServer"
    Version     = var.app_version
    Builder     = "Packer"
    Environment = "production"
  }
}

build {
  sources = ["source.amazon-ebs.webserver"]

  provisioner "shell" {
    inline = [
      "sudo apt-get update",
      "sudo apt-get install -y nginx nodejs npm certbot",
      "sudo systemctl enable nginx"
    ]
  }

  provisioner "file" {
    source      = "config/nginx.conf"
    destination = "/tmp/nginx.conf"
  }

  provisioner "shell" {
    inline = [
      "sudo cp /tmp/nginx.conf /etc/nginx/nginx.conf",
      "sudo nginx -t",
      "sudo systemctl restart nginx"
    ]
  }
}
# Build com sobrescrita de variáveis
packer build -var "aws_region=eu-west-1" -var "app_version=2.1.0" aws-webserver.pkr.hcl

Tipos de Provisioners

O Packer suporta múltiplos provisioners que executam dentro da instância de build temporária:

Provisioner Shell

provisioner "shell" {
  scripts = [
    "scripts/base-setup.sh",
    "scripts/install-app.sh",
    "scripts/harden.sh"
  ]
  environment_vars = [
    "APP_ENV=production",
    "DEBIAN_FRONTEND=noninteractive"
  ]
}

Provisioner Ansible

provisioner "ansible" {
  playbook_file = "ansible/playbook.yml"
  extra_arguments = [
    "--extra-vars", "app_version=${var.app_version}",
    "--tags", "setup,deploy"
  ]
  ansible_env_vars = [
    "ANSIBLE_HOST_KEY_CHECKING=False"
  ]
}

Provisioner de Arquivo

provisioner "file" {
  source      = "app/dist/"
  destination = "/opt/app/"
}

Cenário real: Sua empresa opera 50 instâncias EC2 atrás de um load balancer. A cada deploy, você constrói uma nova AMI com o Packer contendo o código mais recente da aplicação, a configuração do Nginx e os patches de segurança. O Terraform então realiza uma atualização gradual — lançando novas instâncias com a AMI nova e drenando as antigas. Se a nova imagem apresentar problemas, você reverte para a AMI anterior em minutos. Sem SSH em servidores, sem configuration drift, sem servidores snowflake.

Builds Multi-Plataforma

Crie imagens para múltiplas plataformas a partir de um único template:

source "amazon-ebs" "aws" {
  ami_name      = "myapp-aws-{{timestamp}}"
  instance_type = "t3.micro"
  region        = "us-east-1"
  source_ami_filter {
    filters = { name = "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*" }
    owners  = ["099720109477"]
    most_recent = true
  }
  ssh_username = "ubuntu"
}

source "docker" "container" {
  image  = "ubuntu:22.04"
  commit = true
}

source "virtualbox-iso" "local" {
  iso_url      = "https://releases.ubuntu.com/22.04/ubuntu-22.04.3-live-server-amd64.iso"
  iso_checksum = "sha256:abcdef..."
  ssh_username = "vagrant"
  ssh_password = "vagrant"
  # ... configuração adicional do VirtualBox
}

build {
  sources = [
    "source.amazon-ebs.aws",
    "source.docker.container",
  ]

  provisioner "shell" {
    inline = [
      "sudo apt-get update",
      "sudo apt-get install -y nginx",
    ]
  }
}

Execute packer build . e o Packer constrói ambas as imagens em paralelo.

Comparação: Packer vs Alternativas

FuncionalidadePackerDockerfileAWS Image BuilderAnsible (imagens)
Multi-plataformaSim (qualquer nuvem + local)Apenas DockerApenas AWSSim (com plugins)
Linguagem de configHCLSintaxe DockerfileJSON/YAMLYAML
Cache de camadasNão (rebuild completo)Sim (camadas)ParcialNão
Velocidade de buildMinutosSegundos a minutosMinutosMinutos
Suporte a provisionersShell, Ansible, Chef, PuppetComandos RUNSSM/AWSTOENativo
Integração CI/CDExcelenteExcelenteApenas AWSBoa
Saída imutávelSim (snapshot)Sim (imagem)Sim (AMI)Depende
Curva de aprendizadoMédiaBaixaMédiaMédia

Quando escolher o Packer: Ambientes multi-cloud, deployments baseados em VMs, pipelines de golden image, ou quando você precisa da mesma imagem no AWS e no VMware simultaneamente.

Armadilhas e Casos Especiais

  • Filtros de AMI desatualizados: Se o seu source_ami_filter corresponder a múltiplas AMIs, most_recent = true garante que você obtenha a mais recente. Sem isso, o Packer escolhe arbitrariamente e seus builds se tornam não determinísticos.

  • Timeouts de build: O Packer aguarda a conectividade SSH após lançar a instância de build. Regiões de nuvem lentas ou security groups restritivos causam timeouts. Aumente o ssh_timeout em relação ao padrão de 5 minutos se necessário.

  • A ordem dos provisioners importa: Os provisioners executam na ordem em que aparecem no template. Um provisioner file que copia para /opt/app/ vai falhar se o diretório ainda não existir — adicione um provisioner shell antes para criá-lo.

  • Permissões IAM para builds na AWS: O Packer precisa de ec2:RunInstances, ec2:CreateImage, ec2:DescribeImages e várias outras. Use a política IAM mínima para o Packer em vez de conceder acesso de administrador completo.

  • Desregistrando AMIs antigas: O Packer cria novas AMIs, mas nunca exclui as antigas. Sem uma política de limpeza, você acumula centenas de AMIs não utilizadas. Use uma lifecycle policy ou um script agendado para desregistrar imagens com mais de N dias.

  • Espaço em disco durante builds: As instâncias de build temporárias usam o tamanho padrão do volume raiz. Se o provisionamento instalar muitos pacotes, o disco pode encher. Adicione launch_block_device_mappings para aumentar o volume raiz.

Solução de Problemas

ProblemaCausaSolução
”Timeout waiting for SSH”Security group bloqueando SSH ou instância lenta para iniciarVerifique se o SG permite a porta 22 do IP do builder; aumente o ssh_timeout
”AccessDenied” durante criação de AMIPolítica IAM sem ec2:CreateImageAdicione as permissões EC2 e EBS necessárias à role IAM
Scripts do provisioner falhamScript executa antes da atualização de pacotesAdicione apt-get update como primeiro passo; use DEBIAN_FRONTEND=noninteractive
Build bem-sucedido, mas AMI não apareceRegião ou conta erradaVerifique region no bloco source; use aws ec2 describe-images para confirmar
Build do Packer lentoSem builds paralelos configuradosUse -parallel-builds=N ou blocos source separados para paralelismo

Resumo

  • O Packer automatiza a criação de imagens de máquina a partir de código, eliminando a configuração manual de servidores e garantindo builds reproduzíveis
  • Templates HCL definem sources (onde construir), provisioners (o que instalar) e post-processors (o que fazer com o resultado)
  • Builds multi-plataforma produzem imagens idênticas para AWS, Docker e VMware a partir de um único template
  • O provisioner Ansible integra o gerenciamento de configuração existente aos builds do Packer para setups complexos
  • A integração com CI/CD permite builds automáticos de imagens a cada mudança de infraestrutura, criando um pipeline de deploy imutável
  • Use em conjunto com o Terraform para um fluxo completo — o Packer constrói a imagem, o Terraform a implanta

Artigos Relacionados