Post

Provisionamento de uma Máquina Virtual (VM) no KVM utilizando Terraform

Provisionamento de uma Máquina Virtual (VM) no KVM utilizando Terraform

Introdução: Automatizando a Infraestrutura com KVM e Terraform

No cenário tecnológico atual, a automação de infraestrutura tornou-se um pilar fundamental para a eficiência, escalabilidade e reprodutibilidade. Ferramentas como o Terraform, da HashiCorp, revolucionaram a forma como gerenciamos recursos de infraestrutura, permitindo a definição, provisionamento e gerenciamento de infraestrutura como código (IaC). Quando combinado com tecnologias de virtualização robustas como o KVM (Kernel-based Virtual Machine), o Terraform oferece uma solução poderosa e flexível para automatizar a criação e configuração de máquinas virtuais em ambientes Linux.

A combinação KVM e Terraform destaca-se pela leveza do KVM em comparação com outras soluções de virtualização, o que resulta em melhor desempenho e menor sobrecarga de recursos. Além disso, a flexibilidade do Terraform permite gerenciar não apenas o KVM, mas também diversos outros provedores de infraestrutura, tornando-o uma ferramenta versátil para qualquer ambiente. Este tutorial detalhado tem como objetivo guiar você através do processo de provisionamento de uma Máquina Virtual (VM) no ambiente KVM utilizando o Terraform. Abordaremos desde a configuração dos pré-requisitos até a implantação e acesso à VM, fornecendo um guia prático e abrangente para quem busca otimizar seus fluxos de trabalho de virtualização. Ao final deste guia, você terá uma compreensão clara de como alavancar o poder do Terraform para gerenciar suas VMs KVM de forma eficiente e repetível, transformando o gerenciamento de infraestrutura em um processo automatizado e rastreável.

Pré-requisitos

Para seguir este tutorial e provisionar suas VMs com sucesso, certifique-se de que os seguintes pré-requisitos estejam instalados e configurados em seu ambiente:

  • KVM (Kernel-based Virtual Machine): O KVM é a tecnologia de virtualização de código aberto que utilizaremos. Para garantir que seu ambiente KVM esteja pronto e funcionando corretamente, consulte nosso guia detalhado sobre Configurando o KVM no Ubuntu para obter instruções passo a passo.
  • Terraform: O Terraform será a ferramenta principal para a automação da sua infraestrutura. Se você ainda não o tem instalado, siga as instruções em Instalando e Configurando o Terraform para configurar o ambiente necessário.

Estrutura do Projeto

Para manter seu projeto organizado e facilitar a gestão dos recursos de infraestrutura como código, recomendamos a seguinte estrutura de diretórios e arquivos. Esta organização padronizada melhora a legibilidade, a manutenção e a colaboração em projetos Terraform:

1
2
3
4
5
6
7
8
9
10
11
12
13
.
├── .gitignore                  # Regras para ignorar arquivos sensíveis e temporários, garantindo que credenciais e estados do Terraform não sejam versionados.
├── cloudinit.tf                # Configuração do Cloud-Init para personalização da VM, incluindo a criação de usuários, instalação de pacotes e execução de scripts de inicialização na primeira inicialização da máquina.
├── domain.tf                   # Definição principal da máquina virtual KVM, onde são especificados recursos como CPU, memória, rede e o disco de boot, detalhando as características da VM.
├── output.tf                   # Define quais informações importantes (como o endereço IP da VM) serão exibidas após a aplicação do Terraform, facilitando o acesso e a integração com outros sistemas ou scripts.
├── provider.tf                 # Configuração do provedor Libvirt para Terraform, estabelecendo a conexão com o hypervisor KVM e permitindo que o Terraform interaja com a API do Libvirt.
├── README.md                   # Descrição do projeto para versionamento, contendo informações essenciais sobre o propósito do projeto, como configurá-lo e utilizá-lo.
├── terraform.tfvars            # Arquivo real de variáveis sensíveis (NÃO versionado), contendo valores específicos para as variáveis definidas em `variables.tf` que não devem ser expostos publicamente.
├── terraform.tfvars.example    # Modelo de variáveis sensíveis (exemplo), fornecendo um template para o `terraform.tfvars` sem expor dados reais.
├── user_data.yml               # Template do Cloud-Init para configuração inicial da VM, escrito em sintaxe YAML e contendo as instruções para personalização da VM.
├── variables.tf                # Definição das variáveis de entrada do projeto, permitindo a reutilização do código Terraform com diferentes valores para cada VM ou ambiente.
├── versions.tf                 # Define as versões usadas no projeto, garantindo a compatibilidade entre o Terraform Core e os provedores utilizados, o que é crucial para a reprodutibilidade.
└── volumes.tf                  # Definição dos volumes de disco da VM, incluindo a imagem base do sistema operacional que será utilizada para criar o disco da nova VM.

1. Configuração dos Arquivos Terraform

Nesta seção, detalharemos a configuração de cada arquivo Terraform, explicando seu propósito e a lógica por trás de cada bloco de código. Compreender a função de cada um é fundamental para gerenciar sua infraestrutura de forma eficaz.

1.1. cloudinit.tf

O Cloud-Init é uma ferramenta padrão da indústria para personalização de VMs na primeira inicialização, permitindo a injeção de configurações como usuários, chaves SSH e scripts. Este recurso Terraform cria um disco de Cloud-Init que será anexado à sua VM, fornecendo as instruções de configuração inicial.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
resource "libvirt_cloudinit_disk" "cloudinit" {

  name = "cloudinit-${var.vm_name}.iso"
  pool = "default"

  user_data = templatefile(var.user_data, {
    hostname  = var.vm_name
    user_name = var.user_name
    gecos     = var.gecos
    groups    = var.groups
    ssh_key   = var.ssh_public_key
  })

  lifecycle {
    precondition {
      condition     = fileexists(var.user_data)
      error_message = "Arquivo user_data \'${var.user_data}\' não encontrado"
    }
  }
}

1.2. domain.tf

Este arquivo define a máquina virtual KVM em si, especificando seus recursos e como ela se integra com outros componentes. O bloco cpu { mode = "host-passthrough" } é importante para o desempenho e compatibilidade, pois permite que a VM utilize as mesmas características de CPU do host físico, otimizando a performance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
resource "libvirt_domain" "domain" {
  name   = var.vm_name
  memory = var.memory_mb
  vcpu   = var.vcpu_count

  cpu {
    mode = "host-passthrough"
  }

  cloudinit = libvirt_cloudinit_disk.cloudinit.id

  network_interface {
    network_name   = var.network_name
    wait_for_lease = true
  }

  disk {
    volume_id = libvirt_volume.os_image.id
  }

  console {
    type        = "pty"
    target_port = "0"
    target_type = "virtio"
  }
}

1.3. output.tf

O arquivo output.tf é utilizado para expor informações importantes sobre a infraestrutura provisionada. O uso da função try neste contexto é particularmente útil para lidar com casos onde o IP da VM ainda não foi alocado, tornando o output mais robusto e evitando erros.

1
2
3
4
5
6
output "vm_ip" {
  value = try(
    libvirt_domain.domain.network_interface[0].addresses[0],
    "Nenhum IP alocado"
  )
}

1.4. provider.tf

O provedor Libvirt atua como uma interface entre o Terraform e o KVM, permitindo que o Terraform interaja com a API do Libvirt para gerenciar os recursos de virtualização. Este arquivo configura a conexão com o hypervisor KVM.

1
2
3
provider "libvirt" {
  uri = var.libvirt_uri
}

1.5. variables.tf

Este arquivo define as variáveis de entrada do projeto, permitindo a reutilização do código Terraform com diferentes valores para cada VM ou ambiente. Isso torna o código mais flexível e parametrizável.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
variable "libvirt_uri" {
  description = "URI de conexão libvirt (ex: qemu:///system, qemu+ssh://user@host/system)"
  type        = string
  default     = "qemu:///system"
}

variable "vm_name" {
  description = "Nome da máquina virtual. Exemplo: 'vm-producao-01'"
  type        = string
  default     = "vm-tf-01"
}

variable "user_name" {
  description = "Nome do usuário a ser criado na VM. Este usuário será usado para acesso SSH."
  type        = string
  default     = "suporte"
}

variable "gecos" {
  description = "Nome Completo do usuário (campo GECOS). Exemplo: 'Usuário de Suporte'"
  type        = string
  default     = "Suporte User"
}

variable "groups" {
  description = "Nome do grupo ao qual o usuário fará parte (Debian: sudo; RHEL: wheel). Exemplo: ['users', 'sudo']"
  type        = list(string)
  default     = ["users", "sudo"]
}

variable "ssh_public_key" {
  description = "Conteúdo da chave SSH pública a ser injetada na VM para acesso seguro. Exemplo: 'ssh-rsa AAAAB3Nz...'"
  type        = string
  sensitive   = true
}

variable "user_data" {
  description = "Caminho para o template do cloud-init (user_data.yml)."
  type        = string
  default     = "user_data.yml"
}

variable "base_image_path" {
  description = "Caminho da imagem base (.qcow2) do sistema operacional a ser utilizada para a VM. Exemplo: '~/kvm/templates/debian-12-amd64.qcow2'"
  type        = string
  default     = "~/kvm/templates/debian-12-amd64.qcow2"
}

variable "memory_mb" {
  description = "Memória em MB a ser alocada para a VM. Exemplo: 4096 (para 4GB)"
  type        = number
  default     = 2048
}

variable "vcpu_count" {
  description = "Número de vCPUs a serem alocadas para a VM. Exemplo: 4"
  type        = number
  default     = 2
}

variable "network_name" {
  description = "Nome da rede Libvirt à qual a VM será conectada. Exemplo: 'default' ou 'minha_rede_isolada'"
  type        = string
  default     = "default"
}

1.6. versions.tf

Este arquivo define as versões mínimas necessárias do Terraform Core e dos provedores utilizados no projeto. Fixar as versões é uma boa prática para garantir a reprodutibilidade do ambiente, evitando problemas de compatibilidade que podem surgir com atualizações futuras dos provedores ou do Terraform.

1
2
3
4
5
6
7
8
9
10
terraform {
  required_version = ">= 1.11.0"

  required_providers {
    libvirt = {
      source  = "dmacvicar/libvirt"
      version = "0.8.3"
    }
  }
}

1.7. volumes.tf

Este recurso Terraform é responsável por criar o disco virtual para a VM. O atributo source aponta para a imagem base do sistema operacional que será clonada ou utilizada para criar o disco da nova VM, garantindo que a VM inicie com um sistema operacional pré-configurado.

1
2
3
4
5
6
resource "libvirt_volume" "os_image" {
  name   = "base-${var.vm_name}.qcow2"
  pool   = "default"
  source = var.base_image_path
  format = "qcow2"
}

2. Configuração do Cloud-Init

O Cloud-Init é uma ferramenta de inicialização de máquinas virtuais que permite a personalização automática de VMs na primeira inicialização. Ele lê configurações fornecidas em formatos como YAML (no caso do user_data.yml) e executa ações como criação de usuários, instalação de pacotes, configuração de rede e execução de scripts. Isso é fundamental para a automação, pois garante que cada VM provisionada já venha com as configurações básicas necessárias.

2.1 Configuração do Cloud-Init (user_data.yml)

O arquivo user_data.yml contém as instruções de configuração que o Cloud-Init aplicará à sua VM. A sintaxe YAML é utilizada para definir essas configurações de forma estruturada. É importante notar que disable_root: true e ssh_pwauth: false são configurações de segurança recomendadas, desabilitando o login direto como root e a autenticação por senha via SSH, respectivamente, forçando o uso de chaves SSH para acesso seguro.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#cloud-config

users:
  - name: ${user_name}
    gecos: ${gecos} 
    sudo: ALL=(ALL) NOPASSWD:ALL
    groups: ${jsonencode(groups)}
    shell: /bin/bash
    lock_passwd: true
    ssh_authorized_keys:
      - "${ssh_key}"

disable_root: true 
ssh_pwauth: false 

# Hostname da VM 
runcmd:
  - hostnamectl set-hostname ${hostname}

Para garantir que seu arquivo user_data.yml esteja sintaticamente correto e siga o schema do Cloud-Init, é altamente recomendável validá-lo. Isso evita erros de configuração na VM durante a inicialização. Você pode fazer isso com o seguinte comando:

1
cloud-init schema --config-file user_data.yml

3. Configurações Sensíveis (terraform.tfvars)

O arquivo terraform.tfvars é onde você define os valores reais para as variáveis do seu projeto Terraform. É crucial entender que este arquivo NUNCA deve ser versionado em sistemas de controle de versão como o Git. Isso ocorre porque ele geralmente contém informações sensíveis, como chaves SSH, senhas ou outros dados confidenciais que não devem ser expostos publicamente. O terraform.tfvars.example serve como um modelo seguro para que outros colaboradores saibam quais variáveis precisam ser definidas, sem expor os valores reais.

3.1. Crie terraform.tfvars a partir do Exemplo

1
2
3
4
5
6
7
8
9
10
vm_name         = "vm-tf-01"
user_name       = "suporte"
gecos           = "Suporte User"
groups          = ["users", "sudo"]
ssh_public_key  = "ssh-ed25519 AAAAC... user@host"
user_data       = "user_data.yml"
memory_mb       = 4096
vcpu_count      = 4
network_name    = "default"
base_image_path = "/home/gean/kvm/templates/debian-12-amd64.qcow2"

4. Versionamento com .gitignore

No contexto de Infraestrutura como Código (IaC), o versionamento é fundamental para rastrear mudanças, colaborar em projetos e garantir a reprodutibilidade. O arquivo .gitignore desempenha um papel crucial nesse processo, pois ele especifica quais arquivos e diretórios o Git deve ignorar, evitando que dados sensíveis, arquivos temporários ou gerados automaticamente sejam acidentalmente commitados ao repositório. Isso é vital para a segurança e para manter o histórico do Git limpo e relevante.

# Terraform
.terraform/
*.tfstate
*.tfstate.*
*.tfplan
*.tfout

# Arquivos sensíveis
terraform.tfvars
*.tfvars.json

# Arquivos temporários
*~
.swp
.swo
.swn

# Logs e saídas
logs/
output/

Passos para Aplicar o .gitignore:

  1. Crie o arquivo: Se ainda não existir, crie o arquivo .gitignore na raiz do seu projeto:
    1
    
    touch .gitignore
    
  2. Cole as regras: Adicione o conteúdo acima ao arquivo .gitignore.
  3. Adicione ao repositório: Adicione e commite o .gitignore ao seu repositório Git:
    1
    2
    
    git add .gitignore
    git commit -m "Adiciona .gitignore para ignorar arquivos sensíveis e temporários"
    
  4. Remova arquivos sensíveis já versionados (se necessário): Se você acidentalmente já versionou arquivos que deveriam ser ignorados (como terraform.tfvars ou .tfstate), você precisará removê-los do histórico do Git. Cuidado ao executar estes comandos, pois eles reescrevem o histórico e podem causar perda de dados se não forem usados corretamente.
    1
    2
    3
    
    git rm -r --cached .terraform
    git rm -r --cached terraform.tfstate
    git commit -m "Remove arquivos sensíveis do histórico do Git"
    

Recomendações Extras para Segurança e Versionamento:

  • Não ignore o .terraform.lock.hcl: Este arquivo deve ser commitado para garantir consistência nas versões dos provedores entre diferentes ambientes e colaboradores. Ele garante que todos os membros da equipe usem as mesmas versões exatas dos provedores Terraform.
  • Proteja segredos: Para ambientes de produção, considere usar ferramentas mais robustas para gerenciamento de segredos, como HashiCorp Vault ou variáveis de ambiente seguras em pipelines de CI/CD, em vez de terraform.tfvars. Essas soluções oferecem maior segurança e controle de acesso.
  • Documentação: Mantenha um README.md atualizado explicando a estrutura do projeto, como usá-lo e quaisquer considerações de segurança. Uma boa documentação é essencial para a colaboração e a manutenção a longo prazo.

5. Implantação da VM

Após configurar todos os arquivos Terraform e o Cloud-Init, o próximo passo é implantar a máquina virtual. Este processo envolve três comandos principais do Terraform, cada um com uma função específica no ciclo de vida da infraestrutura como código.

5.1. Inicialize o Terraform

O comando terraform init inicializa o diretório de trabalho do Terraform. Ele baixa os provedores necessários (neste caso, o provedor libvirt) e configura o backend, preparando o ambiente para que o Terraform possa interagir com sua infraestrutura.

1
terraform init

5.2. Planeje a Infraestrutura

O comando terraform plan gera um plano de execução que mostra as ações que o Terraform realizará para atingir o estado desejado da infraestrutura. Ele compara o estado atual da sua infraestrutura com a configuração definida nos seus arquivos .tf e exibe um resumo das mudanças (criações, modificações, exclusões) que serão aplicadas, sem realmente executar essas mudanças. Isso é crucial para revisar e validar as operações antes de aplicá-las.

1
terraform plan

5.3. Aplique as Mudanças

O comando terraform apply aplica as mudanças definidas no plano de execução, provisionando ou modificando a infraestrutura real de acordo com a sua configuração. Após a execução, o Terraform exibirá um resumo das operações realizadas e, se tudo ocorrer bem, sua VM será provisionada no KVM.

1
terraform apply

6. Acessando a VM

Após o provisionamento bem-sucedido da sua Máquina Virtual, o próximo passo é acessá-la. A forma mais comum e segura de acesso a VMs Linux é via SSH (Secure Shell).

6.1. Acesso via SSH

Para acessar sua VM via SSH, você precisará do nome de usuário configurado no Cloud-Init e do endereço IP da VM, que pode ser obtido através do terraform output vm_ip. O comando ssh é utilizado da seguinte forma:

1
2
3
ssh -i ~/.ssh/tfvms <user_name>@<vm_ip>
# Exemplo:
ssh -i ~/.ssh/tfvms suporte@10.64.0.10

O parâmetro -i é utilizado para especificar o caminho para o arquivo da sua chave SSH privada. Certifique-se de que a chave privada correspondente à chave pública que você injetou na VM via Cloud-Init esteja localizada no caminho especificado e que suas permissões estejam corretamente configuradas (geralmente chmod 400 ~/.ssh/tfvms).

7. Testando Variáveis com terraform console

O terraform console é uma ferramenta interativa poderosa que permite testar expressões e variáveis do Terraform em tempo real. Isso é extremamente útil para depurar configurações, entender o comportamento de funções e validar valores de variáveis antes de aplicá-los à sua infraestrutura. Além disso, o terraform console é uma excelente ferramenta para depurar módulos complexos ou validar a lógica condicional em seus arquivos Terraform.

Para iniciar o console, navegue até o diretório raiz do seu projeto Terraform e execute:

1
terraform console

Uma vez no console, você pode testar suas variáveis. Por exemplo, para verificar o valor da variável groups que definimos como ["users", "sudo"]:

1
2
> var.groups
[ "users", "sudo" ]

Você também pode testar expressões mais complexas ou funções do Terraform. Por exemplo, para verificar se um determinado grupo está presente na lista:

1
2
3
4
> contains(var.groups, "sudo")
true
> contains(var.groups, "admin")
false

Para sair do console, digite exit ou pressione Ctrl+D.

Informações Adicionais: Boas Práticas e Considerações Avançadas

Para otimizar ainda mais o uso do Terraform com KVM e garantir um ambiente robusto, seguro e escalável, considere as seguintes boas práticas e informações adicionais. A adoção dessas práticas eleva a qualidade da sua infraestrutura como código e melhora a eficiência operacional.

Gerenciamento de Imagens Base

É crucial ter um processo bem definido para o gerenciamento de suas imagens base (.qcow2). Mantenha suas imagens atualizadas com as últimas correções de segurança e pacotes. Você pode automatizar a criação dessas imagens usando ferramentas como o Packer, que permite criar imagens customizadas e pré-configuradas para diferentes sistemas operacionais. Os benefícios do Packer incluem a automatização do processo de criação de imagens, a garantia de consistência entre as imagens e a redução de erros manuais. Isso garante que todas as VMs provisionadas a partir dessas imagens já venham com as configurações básicas e dependências necessárias, reduzindo o tempo de provisionamento e a chance de erros.

Redes e Segurança

Ao configurar redes para suas VMs KVM, planeje cuidadosamente a topologia de rede. O Libvirt oferece diversas opções de rede, como redes em bridge (para comunicação direta com a rede física), redes NAT (para VMs com acesso à internet, mas isoladas da rede física) e redes isoladas (para comunicação apenas entre VMs dentro da mesma rede virtual). Utilize redes isoladas para diferentes ambientes (desenvolvimento, teste, produção) e implemente regras de firewall robustas para controlar o tráfego de entrada e saída. Considere também a implementação de grupos de segurança ou listas de controle de acesso (ACLs) para granularidade no controle de acesso entre VMs, aumentando a segurança da sua infraestrutura.

Monitoramento e Logging

Após o provisionamento das VMs, é fundamental implementar soluções de monitoramento e logging. Ferramentas como Prometheus e Grafana podem ser utilizadas para coletar métricas de desempenho das VMs e do host KVM (CPU, memória, disco, rede), enquanto soluções de logging centralizado como ELK Stack (Elasticsearch, Logstash, Kibana) ou Loki podem agregar logs de todas as suas VMs (logs de sistema, logs de aplicação). Isso permite identificar proativamente problemas de desempenho, segurança e disponibilidade, garantindo a estabilidade da sua infraestrutura e facilitando a depuração.

CI/CD para Infraestrutura

Integrar seu código Terraform em um pipeline de CI/CD (Integração Contínua/Entrega Contínua) é uma prática recomendada para garantir a consistência e a automação de suas implantações. Ferramentas como GitLab CI/CD, Jenkins ou GitHub Actions podem ser configuradas para executar terraform plan em cada pull request e terraform apply após a aprovação e merge do código. Isso não só automatiza o processo de implantação, mas também garante que todas as mudanças na infraestrutura passem por um processo de revisão e validação, minimizando riscos e garantindo a imutabilidade da infraestrutura e a rastreabilidade das mudanças.

Manutenção e Atualizações

Periodicamente, revise e atualize suas configurações Terraform e as versões dos provedores. O ecossistema Terraform está em constante evolução, e novas versões trazem melhorias de desempenho, segurança e novas funcionalidades. Mantenha-se atualizado com as últimas versões do Terraform e do provedor libvirt para aproveitar ao máximo os recursos disponíveis e garantir a compatibilidade com as versões mais recentes do KVM. É altamente recomendável testar todas as atualizações em ambientes de não-produção antes de aplicá-las em produção para evitar interrupções inesperadas.

Conclusão

Este tutorial demonstrou como o Terraform pode ser uma ferramenta incrivelmente poderosa para automatizar o provisionamento e gerenciamento de máquinas virtuais no KVM. Ao adotar a abordagem de Infraestrutura como Código (IaC), você ganha não apenas em velocidade e eficiência, mas também em consistência, reprodutibilidade e rastreabilidade de suas infraestruturas. A capacidade de definir sua infraestrutura em arquivos de código permite que você versionize, revise e colabore em suas configurações, transformando o gerenciamento de VMs em um processo mais robusto e menos propenso a erros manuais.

Ao seguir as etapas e considerar as boas práticas e informações adicionais apresentadas, você estará bem equipado para construir e manter ambientes virtuais complexos com facilidade. A combinação de KVM e Terraform abre um leque de possibilidades para desenvolvedores, engenheiros de DevOps e administradores de sistemas que buscam otimizar suas operações e focar em inovação, em vez de tarefas repetitivas de configuração manual. A capacidade de recriar ambientes rapidamente para testes, recuperação de desastres ou escalabilidade é um valor de longo prazo inestimável que a IaC proporciona. Continue explorando as capacidades dessas ferramentas e adapte-as às suas necessidades específicas para maximizar o potencial da sua infraestrutura automatizada.

This post is licensed under CC BY 4.0 by the author.