Post

Infraestrutura KVM Automatizada com Terraform, Ansible e Docker

Infraestrutura KVM Automatizada com Terraform, Ansible e Docker

Este tutorial detalha a construção de uma infraestrutura automatizada utilizando KVM/Libvirt para virtualização, Terraform para provisionamento de máquinas virtuais e Ansible para configuração pós-provisionamento. O objetivo é implantar uma instância do Dokuwiki em contêineres Docker, gerenciada via Docker Compose. Este guia é destinado a desenvolvedores, administradores de sistemas e entusiastas de DevOps que buscam automatizar a criação e gestão de ambientes virtuais.

Requisitos

Para seguir este tutorial, você precisará ter os seguintes componentes instalados e configurados em seu ambiente:

  • KVM/LIBVIRT: Um ambiente KVM/Libvirt funcional é essencial para a criação das máquinas virtuais. Certifique-se de que o serviço libvirtd esteja em execução e que você tenha as permissões necessárias para gerenciar VMs. Para detalhes sobre a configuração, consulte Configurando o KVM no Ubuntu.
  • Terraform: O Terraform será utilizado para orquestrar o provisionamento da infraestrutura. Verifique se o Terraform CLI está instalado e acessível no seu PATH. Para instruções de instalação, consulte Instalando e Configurando o Terraform.

Nota: O sistema operacional utilizado para o desenvolvimento e teste deste ambiente foi o Ubuntu 24.04 versão desktop.

Estrutura do Projeto

[OPORTUNIDADE DE MELHORIA: Inserir aqui um diagrama de arquitetura que ilustre a relação entre KVM, as VMs, Terraform, Ansible, Docker e Dokuwiki.]

graph LR
    A[Host Físico<br>Ubuntu 24.04] --> B[KVM/Libvirt]
    B --> C[VM Debian]
    D[Terraform] --> B
    E[Ansible] --> C
    C --> F[Docker]
    F --> G[Dokuwiki]
    
    subgraph Automação
        D -->|Provisiona| B
        E -->|Configura| C
    end
    
    H[Usuário] -->|Acessa| G

Abaixo está a estrutura de diretórios e arquivos do projeto. Cada componente desempenha um papel específico na automação da infraestrutura:

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
tree ~/Workspace/terraform/providers/libvirt/docker
/home/gean/Workspace/terraform/providers/libvirt/docker
├── .gitignore            # Regras para ignorar arquivos sensíveis e temporários
├── Ansible               # Projeto Ansible: Contém os playbooks e configurações para automação pós-provisionamento.
│   └── docker
│       ├── ansible
│       ├── ansible.cfg
│       ├── ansible.pub
│       ├── docker-compose.yml
│       ├── group_vars
│       │   └── docker.yml
│       ├── hosts
│       ├── playbook-compose.yml
│       └── playbook.yml
├── main.tf               # Configuração principal do Terraform: Ponto de entrada principal do Terraform, orquestra a criação da infraestrutura.
├── modules               # Módulos Terraform
│   └── vm                # Módulo para criação de VMs: Módulo reutilizável para provisionar máquinas virtuais.
│       ├── cloud-init    # Templates cloud-init: Contém os templates para a configuração inicial das VMs.
│       │   ├── network_config.yml.tpl
│       │   └── user_data.yml.tpl
│       ├── main.tf       # Recursos do módulo VM
│       ├── outputs.tf    # Outputs do módulo VM
│       ├── variables.tf  # Variáveis do módulo VM
│       └── versions.tf   # Versões do módulo VM
├── network.tf            # Definição de rede Libvirt: Define a rede virtual para as VMs.
├── provider.tf           # Configuração do provider Libvirt: Configura o provedor Libvirt para o Terraform.
├── terraform.tfvars      # Variáveis de configuração (não versionado): Contém valores sensíveis ou específicos do ambiente.
├── terraform.tfvars.example # Exemplo de variáveis: Modelo para o arquivo terraform.tfvars.
└── variables.tf          # Variáveis globais: Define as variáveis globais do projeto Terraform.

1. Configuração dos Arquivos Terraform

1.1. main.tf

O arquivo main.tf é o ponto de entrada principal do Terraform, onde a infraestrutura é orquestrada. Ele faz a chamada para o módulo vm_cluster, que é responsável por criar as máquinas virtuais com base nas configurações definidas.

1
2
3
4
5
6
7
module "vm_cluster" {
  source = "./modules/vm"

  vm_configs     = var.vms
  network_id     = libvirt_network.tf_network.id
  ssh_public_key = var.ssh_public_key
}

1.2. network.tf

Este arquivo define a rede virtual tf_network no Libvirt, que será utilizada pelas máquinas virtuais provisionadas. A rede é configurada com um nome, modo de operação (NAT, por exemplo) e um bloco de endereços IP.

1
2
3
4
5
resource "libvirt_network" "tf_network" {
  name      = var.network_name
  mode      = var.network_mode
  addresses = [var.network_addresses]
}

1.3. provider.tf

O arquivo provider.tf configura o provedor libvirt para o Terraform, especificando a fonte e a versão do provedor, além do URI de conexão com o Libvirt. É crucial manter a versão do provedor consistente para evitar problemas de compatibilidade.

1
2
3
4
5
6
7
8
9
10
11
12
terraform {
  required_providers {
    libvirt = {
      source  = "dmacvicar/libvirt"
      version = "0.8.3"
    }
  }
}

provider "libvirt" {
  uri = var.libvirt_uri
}

1.4. variables.tf

Este arquivo contém as definições das variáveis globais utilizadas no projeto Terraform. Elas permitem parametrizar a infraestrutura, tornando-a mais flexível e reutilizável. As validações garantem que os valores fornecidos para as VMs atendam aos requisitos mínimos e de formato.

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
66
67
68
69
70
71
72
73
74
75
76
77
78
variable "libvirt_uri" {
  type    = string
  default = "qemu:///system"
}

variable "network_name" {
  type    = string
  default = "tfnet"
}

variable "network_mode" {
  type    = string
  default = "nat"
}

variable "network_addresses" {
  type    = string
  default = "10.64.0.0/24"
}

variable "ssh_public_key" {
  description = "Conteúdo da chave SSH pública para acesso às VMs"
  type        = string
  sensitive   = true
}

variable "vms" {
  type = map(object({
    username         = string
    gecos            = string
    groups           = list(string)
    network_ip       = string
    nameserver_ip    = string
    route_ip         = string
    memory           = number
    vcpu             = number
    os_image_url     = string
    docker_disk_size = number
    stack_disk_size  = number
  }))
  sensitive = false

  validation {
    # Valida que todas as chaves do mapa são hostnames únicos
    condition     = length(var.vms) == length(distinct(keys(var.vms)))
    error_message = "Todos os hostnames devem ser únicos."
  }

  validation {
    # Valida o formato do hostname
    condition     = alltrue([for k, v in var.vms : can(regex("^[a-z0-9-]{1,63}$", k))])
    error_message = "Hostname deve conter apenas letras minúsculas, números e hifens, com até 63 caracteres."
  }

  validation {
    condition = alltrue([
      for k, v in var.vms :
      can(regex("^\\d+\\.\\d+\\.\\d+\\.\\d+$", v.network_ip))
    ])
    error_message = "Endereços IP devem estar no formato IPv4 válido."
  }

  validation {
    condition = alltrue([
      for k, v in var.vms :
      v.memory >= 512 && v.memory <= 65536
    ])
    error_message = "Memória deve estar entre 512MB e 64GB."
  }

  validation {
    condition = alltrue([
      for k, v in var.vms :
      v.docker_disk_size >= 10737418240 # Mínimo 10GB
    ])
    error_message = "Tamanho do disco Docker deve ser >= 10GB."
  }
}

1.5. modules/vm/main.tf

Este arquivo define os recursos do Terraform para a criação de máquinas virtuais dentro do módulo vm. Ele orquestra a criação dos discos cloud-init, dos volumes para o sistema operacional, Docker e Stack, e finalmente, o domínio da máquina virtual no Libvirt.

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
resource "libvirt_cloudinit_disk" "cloudinit" {
  for_each = var.vm_configs

  name = "cloudinit-${each.key}.iso"
  pool = "default"

  user_data = templatefile("${path.module}/cloud-init/user_data.yml.tpl", {
    hostname  = each.key
    user_name = each.value.username
    gecos     = each.value.gecos
    groups    = each.value.groups
    ssh_key   = var.ssh_public_key
  })

  network_config = templatefile("${path.module}/cloud-init/network_config.yml.tpl", {
    network_ip    = each.value.network_ip
    nameserver_ip = each.value.nameserver_ip
    route_ip      = each.value.route_ip
  })
}

# Disco para o SO
resource "libvirt_volume" "os_image" {
  for_each = var.vm_configs

  name   = "base-${each.key}.qcow2"
  pool   = "default"
  source = each.value.os_image_url
  format = "qcow2"
}

# Disco para /var/lib/docker
resource "libvirt_volume" "docker_disk" {
  for_each = var.vm_configs
  name     = "docker-${each.key}.qcow2"
  pool     = "default"
  size     = each.value.docker_disk_size
  format   = "qcow2"
}

# Disco para /opt/stack
resource "libvirt_volume" "stack_disk" {
  for_each = var.vm_configs
  name     = "stack-${each.key}.qcow2"
  pool     = "default"
  size     = each.value.stack_disk_size
  format   = "qcow2"
}

resource "libvirt_domain" "domain" {
  for_each = var.vm_configs

  name   = each.key
  memory = each.value.memory
  vcpu   = each.value.vcpu

  cpu {
    mode = "host-passthrough"
  }

  cloudinit = libvirt_cloudinit_disk.cloudinit[each.key].id

  network_interface {
    network_id = var.network_id
  }

  # Disco para o SO
  disk {
    volume_id = libvirt_volume.os_image[each.key].id
  }
  
  # Disco para /var/lib/docker
  disk {
    volume_id = libvirt_volume.docker_disk[each.key].id
  }

  # Disco para /opt/stack
  disk {
    volume_id = libvirt_volume.stack_disk[each.key].id
  }

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

1.6. modules/vm/outputs.tf

Este arquivo está atualmente vazio, pois não há saídas (outputs) explícitas definidas para este módulo no escopo deste tutorial. Em projetos mais complexos, este arquivo conteria informações úteis, como IPs das VMs ou nomes de domínio, que poderiam ser referenciadas por outros módulos ou para uso externo.

1
[...]

1.7. modules/vm/variables.tf

Este arquivo define as variáveis específicas para o módulo vm, que são utilizadas para configurar as máquinas virtuais individualmente. Inclui detalhes como nome de usuário, configurações de rede, memória, vCPU e URLs das imagens do sistema operacional, além dos tamanhos dos discos para Docker e Stack.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
variable "vm_configs" {
  type = map(object({
    username      = string
    gecos         = string
    groups        = list(string)
    network_ip    = string
    nameserver_ip = string
    route_ip      = string
    memory        = number
    vcpu          = number
    os_image_url  = string
    docker_disk_size = number   
    stack_disk_size  = number   
  }))
}

variable "network_id" {
  type = string
}

variable "ssh_public_key" {
  type      = string
  sensitive = true
}

1.8. modules/vm/versions.tf

Este arquivo especifica as versões dos provedores Terraform exigidas pelo módulo vm. É fundamental que a versão do provedor libvirt aqui seja a mesma utilizada na configuração raiz do Terraform (provider.tf) para garantir a compatibilidade e evitar erros durante o provisionamento.

1
2
3
4
5
6
7
8
terraform {
  required_providers {
    libvirt = {
      source  = "dmacvicar/libvirt"
      version = "0.8.3" # Use a mesma versão da raiz
    }
  }
}

2. Configuração do Cloud-Init

O Cloud-Init é uma ferramenta padrão da indústria para a inicialização de instâncias de máquinas virtuais, permitindo a automação da configuração inicial, como criação de usuários, configuração de rede e execução de scripts. Neste tutorial, ele é usado para preparar as VMs para a instalação do Docker e do Dokuwiki.

2.1 Configuração do Cloud-Init (modules/vm/cloud-init/user_data.yml.tpl)

Este template define as configurações de usuário e os comandos a serem executados na primeira inicialização da VM. Inclui a criação de um usuário com privilégios sudo, a configuração de chaves SSH para acesso seguro e a formatação/montagem dos discos adicionais para o Docker e o Stack.

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
#cloud-config

users:
  - name: ${user_name}
    gecos: ${gecos} 
    sudo: ALL=(ALL) NOPASSWD:ALL  # ATENÇÃO: Em ambientes de produção, considere limitar os comandos que podem ser executados sem senha ou exigir senha para comandos sensíveis por questões de segurança.
    groups: ${jsonencode(groups)}
    shell: /bin/bash
    lock_passwd: true
    ssh_authorized_keys:
      - "${ssh_key}"

disable_root: true
ssh_pwauth: false

runcmd:
  - hostnamectl set-hostname ${hostname}  
  # Configurar disco para Docker
  - mkfs.ext4 -F /dev/vdb
  - mkdir -p /var/lib/docker
  - mount /dev/vdb /var/lib/docker
  - echo "/dev/vdb /var/lib/docker ext4 defaults 0 0" >> /etc/fstab
  - chown -R ${user_name}:${user_name} /var/lib/docker # Ajusta a propriedade para o usuário criado
  - chmod -R 775 /var/lib/docker # Ajusta as permissões para o diretório do Docker
  # Configurar disco para Stack
  - mkfs.ext4 -F /dev/vdc
  - mkdir -p /opt/stack
  - mount /dev/vdc /opt/stack
  - echo "/dev/vdc /opt/stack ext4 defaults 0 0" >> /etc/fstab
  - chown -R ${user_name}:${user_name} /opt/stack # Ajusta a propriedade para o usuário criado
  - chmod -R 775 /opt/stack # Ajusta as permissões para o diretório do Stack

2.2 Configuração do Cloud-Init (modules/vm/cloud-init/network_config.yml.tpl)

Este template configura as interfaces de rede das VMs, definindo o endereço IP, os servidores DNS (nameservers) e a rota padrão. Isso garante que as VMs possam se comunicar com a rede externa e entre si.

1
2
3
4
5
6
7
8
9
10
11
12
13
#cloud-config
network:
  version: 2
  ethernets:
    ens3:
      addresses:
        - ${network_ip}/24
      nameservers:
        addresses: 
          - ${nameserver_ip}
      routes:
        - to: 0.0.0.0/0
          via: ${route_ip}

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

O arquivo terraform.tfvars é utilizado para definir os valores das variáveis que serão utilizadas pelo Terraform. É crucial que este arquivo não seja versionado em sistemas de controle de versão (como Git) se contiver informações sensíveis, como chaves SSH. Para chaves SSH em ambientes de produção, considere alternativas mais seguras, como variáveis de ambiente (TF_VAR_ssh_public_key) ou gerenciadores de segredos (e.g., HashiCorp Vault).

3.1. Crie terraform.tfvars a partir do Exemplo

Crie o arquivo terraform.tfvars na raiz do seu projeto Terraform e preencha-o com suas configurações. O exemplo abaixo mostra a estrutura e os valores que você pode usar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ssh_public_key = "ssh-ed25519 AAAAC... user@host"

vms = {
  "docker-vm" = {
    username         = "suporte"
    gecos            = "Suporte User"
    groups           = ["users", "sudo"]
    network_ip       = "10.64.0.10"
    nameserver_ip    = "10.64.0.1"
    route_ip         = "10.64.0.1"
    memory           = 4096
    vcpu             = 4
    os_image_url     = "/home/gean/kvm/templates/debian-12-amd64.qcow2"
    docker_disk_size = 10737418240 # 10GB - 10 * 1024 * 1024 * 1024
    stack_disk_size  = 5368709120  # 5GB - 5 * 1024 * 1024 * 1024
  }
}

# Configurações opcionais
libvirt_uri       = "qemu:///system"
network_name      = "tfnet"
network_mode      = "nat"
network_addresses = "10.64.0.0/24"

4. Implantação da VM

Após configurar todos os arquivos Terraform, você pode implantar a máquina virtual executando os seguintes comandos na raiz do seu projeto Terraform:

1
terraform fmt
  • terraform fmt: Este comando formata os arquivos Terraform para um estilo canônico e legível, garantindo consistência no código.
1
terraform init
  • terraform init: Inicializa um diretório de trabalho do Terraform, baixando os provedores necessários (neste caso, o provedor libvirt).
1
terraform plan
  • terraform plan: Gera um plano de execução, mostrando quais ações o Terraform realizará para atingir o estado desejado da infraestrutura. É uma boa prática revisar este plano antes de aplicar as mudanças.
1
terraform apply
  • terraform apply: Executa as ações propostas no plano, provisionando a infraestrutura definida no seu código Terraform. Você será solicitado a confirmar a aplicação.

[OPORTUNIDADE DE MELHORIA: Inserir aqui capturas de tela da saída dos comandos terraform plan e terraform apply.]

5. Configuração do Ambiente Ansible

O Ansible é uma ferramenta de automação de TI que será utilizada para configurar o ambiente dentro das máquinas virtuais provisionadas pelo Terraform. Ele permite a instalação de software, configuração de serviços e gerenciamento de usuários de forma idempotente e eficiente.

Instalação do Ansible

1
2
3
4
sudo apt update && sudo apt upgrade -y
sudo apt install software-properties-common -y
sudo add-apt-repository --yes --update ppa:ansible/ansible
sudo apt install ansible sshpass -y
  • sudo apt update && sudo apt upgrade -y: Atualiza a lista de pacotes e faz o upgrade dos pacotes existentes no sistema.
  • sudo apt install software-properties-common -y: Instala um pacote que facilita a adição de repositórios PPA.
  • sudo add-apt-repository --yes --update ppa:ansible/ansible: Adiciona o PPA oficial do Ansible, garantindo que você obtenha a versão mais recente.
  • sudo apt install ansible sshpass -y: Instala o Ansible e o sshpass, que é útil para automação de SSH em cenários específicos (embora o uso de chaves SSH seja preferível).

Diretório para configuração do Ansible

1
~/Workspace/terraform/providers/libvirt/docker/Ansible/docker/

Chave SSH

Para que o Ansible possa se conectar às máquinas virtuais sem a necessidade de senha, é fundamental configurar o acesso via chave SSH. Os comandos abaixo geram um par de chaves SSH e copiam a chave pública para a VM de destino.

1
ssh-keygen -t ed25519 -f ansible
  • ssh-keygen -t ed25519 -f ansible: Gera um novo par de chaves SSH (pública e privada) usando o algoritmo ed25519 e salva os arquivos com o nome ansible no diretório atual.
1
ssh-copy-id -i ansible.pub suporte@10.64.0.10
  • ssh-copy-id -i ansible.pub suporte@10.64.0.10: Copia a chave pública (ansible.pub) para o usuário suporte na VM com o IP 10.64.0.10. Isso permite que o Ansible se autentique sem senha.

Arquivo hosts

O arquivo hosts define o inventário do Ansible, listando os hosts (máquinas virtuais) que serão gerenciados. Neste caso, definimos um grupo [docker] contendo a VM 10.64.0.10 e especificamos o usuário ansible_user para conexão.

1
2
3
vim hosts
[docker]
10.64.0.10 ansible_user=suporte

Arquivo de configuração

O arquivo ansible.cfg configura o comportamento do Ansible. Aqui, definimos o inventário a ser usado (hosts) e a chave privada para autenticação. A opção host_key_checking = False é utilizada para simplificar o tutorial, mas não é recomendada para ambientes de produção devido a riscos de segurança (ataques man-in-the-middle). Em produção, considere pré-popular o arquivo known_hosts ou usar ssh-keyscan para gerenciar as chaves de forma segura.

1
2
3
4
5
vim ansible.cfg
[defaults]
inventory = hosts
host_key_checking = False
private_key_file = ansible

Diretório group_vars

O diretório group_vars é onde você pode definir variáveis específicas para grupos de hosts no seu inventário Ansible. Isso permite uma configuração mais organizada e reutilizável. Neste caso, criamos um arquivo docker.yml para o grupo docker.

1
mkdir group_vars

Definição do grupo

1
2
3
vim group_vars/docker.yml
docker_users:
  - gean

Playbook de instalação do Docker

O playbook.yml é responsável por instalar o Docker e suas dependências na máquina virtual. Cada task (tarefa) dentro do playbook executa uma ação específica, garantindo que o ambiente Docker seja configurado corretamente.

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
vim playbook.yml
---
- hosts: docker
  become: true  # Garante que as tarefas sejam executadas com privilégios de superusuário
  tasks:

    - name: Atualizar o cache de pacotes APT
      apt:
        update_cache: yes
        cache_valid_time: 3600  # Cache válido por 1 hora

    - name: Instalar dependências do Docker
      apt:
        name: 
          - apt-transport-https
          - ca-certificates
          - curl
          - gnupg
          - lsb-release
        state: present

    - name: Baixar e adicionar chave GPG oficial do Docker
      ansible.builtin.apt_key:
        url: https://download.docker.com/linux/debian/gpg
        state: present
        keyring: /usr/share/keyrings/docker-archive-keyring.gpg

    - name: Adicionar repositório Docker
      ansible.builtin.apt_repository:
        repo: "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian  stable"
        state: present

    - name: Atualizar o cache de pacotes após adicionar o repositório Docker
      apt:
        update_cache: yes

    - name: Instalar Docker e Docker Compose
      apt:
        name:
          - docker-ce
          - docker-ce-cli
          - containerd.io
        state: present

    - name: Adicionar usuários ao grupo Docker
      user:
        name: ""
        groups: docker
        append: yes
      loop: ""

    - name: Iniciar e habilitar o serviço Docker
      systemd:
        name: docker
        enabled: yes
        state: started
  • name: Atualizar o cache de pacotes APT: Garante que a lista de pacotes do sistema esteja atualizada.
  • name: Instalar dependências do Docker: Instala os pacotes necessários para que o Docker funcione corretamente.
  • name: Baixar e adicionar chave GPG oficial do Docker: Adiciona a chave GPG do Docker para verificar a autenticidade dos pacotes.
  • name: Adicionar repositório Docker: Adiciona o repositório oficial do Docker ao sistema.
  • name: Atualizar o cache de pacotes após adicionar o repositório Docker: Atualiza o cache novamente após adicionar o novo repositório.
  • name: Instalar Docker e Docker Compose: Instala os pacotes principais do Docker e do containerd.io.
  • name: Adicionar usuários ao grupo Docker: Adiciona os usuários definidos na variável docker_users ao grupo docker, permitindo que eles executem comandos Docker sem sudo.
  • name: Iniciar e habilitar o serviço Docker: Inicia o serviço Docker e o configura para iniciar automaticamente na inicialização do sistema.

Execução da playbook

Para executar o playbook do Docker e aplicar as configurações na VM, utilize o seguinte comando:

1
ansible-playbook playbook.yml

[OPORTUNIDADE DE MELHORIA: Inserir aqui capturas de tela da saída do comando ansible-playbook playbook.yml.]

Após a execução, você pode verificar a versão do Docker instalada na VM para confirmar o sucesso da implantação:

1
ansible docker -m shell -a "docker --version"

O output esperado será similar a este, confirmando a versão do Docker:

1
2
3
4
[WARNING]: Platform linux on host 10.64.0.10 is using the discovered Python interpreter at /usr/bin/python3.11, but future installation of another Python interpreter could
change the meaning of that path. See https://docs.ansible.com/ansible-core/2.18/reference_appendices/interpreter_discovery.html for more information.
10.64.0.10 | CHANGED | rc=0 >>
Docker version 28.2.2, build e6534b4

Implantação do Dokuwiki

O playbook-compose.yml é responsável por implantar o Dokuwiki utilizando Docker Compose. Ele cria os diretórios necessários, copia o arquivo docker-compose.yml para a VM e inicia os contêineres do Dokuwiki.

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
vim playbook-compose.yml
---
- hosts: docker
  become: true
  tasks:

    - name: Criar diretório para o Docker Compose
      file:
        path: /opt/stack/dokuwiki
        state: directory
        mode: '0755'

    - name: Copiar o arquivo docker-compose.yml para o servidor
      copy:
        src: ./docker-compose.yml  # Caminho do arquivo local no controlador
        dest: /opt/stack/dokuwiki/docker-compose.yml  # Caminho de destino no servidor
        mode: '0644'

    - name: Criar diretório de volumes para o DokuWiki
      file:
        path: /opt/stack/dokuwiki/html
        state: directory
        mode: '0755'

    - name: Ajustar permissões no diretório html
      file:
        path: /opt/stack/dokuwiki/html
        owner: "1000"
        group: "1000"
        recurse: yes
        mode: '0755'

    - name: Garantir que o Docker Compose esteja instalado
      apt:
        name: docker-compose-plugin
        state: present

    - name: Executar docker-compose up
      shell: docker compose -f /opt/stack/dokuwiki/docker-compose.yml up -d
      args:
        chdir: /opt/stack/dokuwiki
  • name: Criar diretório para o Docker Compose: Cria o diretório /opt/stack/dokuwiki na VM para armazenar os arquivos do Docker Compose.
  • name: Copiar o arquivo docker-compose.yml para o servidor: Transfere o arquivo docker-compose.yml do controlador Ansible para a VM de destino.
  • name: Criar diretório de volumes para o DokuWiki: Cria o diretório /opt/stack/dokuwiki/html que será usado como volume persistente para os dados do Dokuwiki.
  • name: Ajustar permissões no diretório html: Define as permissões e o proprietário do diretório de volume do Dokuwiki, garantindo que o contêiner tenha acesso adequado.
  • name: Garantir que o Docker Compose esteja instalado: Assegura que o plugin docker-compose esteja presente na VM.
  • name: Executar docker-compose up: Inicia os serviços definidos no docker-compose.yml em modo detached (em segundo plano).

Arquivo docker-compose.yml

Este arquivo define os serviços Docker para o Dokuwiki. Ele especifica a imagem a ser usada, o mapeamento de portas, as variáveis de ambiente e os volumes persistentes para os dados da wiki.

1
vim docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
services:
  dokuwiki:
    image: dokuwiki/dokuwiki:stable
    user: "1000:1000"
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      PHP_TIMEZONE: America/Sao_Paulo
    volumes:
      - ./html:/storage
  • image: dokuwiki/dokuwiki:stable: Define a imagem Docker a ser utilizada para o serviço Dokuwiki.
  • user: "1000:1000": Especifica o UID/GID sob o qual o contêiner será executado, importante para permissões de arquivo.
  • restart: unless-stopped: Configura o contêiner para reiniciar automaticamente, a menos que seja explicitamente parado.
  • ports: - "8080:8080": Mapeia a porta 8080 do host para a porta 8080 do contêiner, permitindo acesso externo ao Dokuwiki.
  • environment: PHP_TIMEZONE: America/Sao_Paulo: Define a variável de ambiente para o fuso horário dentro do contêiner.
  • volumes: - ./html:/storage: Monta o diretório local ./html (na VM) como /storage dentro do contêiner, garantindo a persistência dos dados do Dokuwiki.

Execução da playbook

Para implantar o Dokuwiki, execute o playbook playbook-compose.yml:

1
ansible-playbook playbook-compose.yml

Após a execução, você pode verificar se o contêiner do Dokuwiki está em execução:

1
ansible docker -m shell -a "sudo docker ps"

O output esperado será similar a este, mostrando o contêiner do Dokuwiki ativo:

1
2
3
4
5
[WARNING]: Platform linux on host 10.64.0.10 is using the discovered Python interpreter at /usr/bin/python3.11, but future installation of another Python interpreter could
change the meaning of that path. See https://docs.ansible.com/ansible-core/2.18/reference_appendices/interpreter_discovery.html for more information.
10.64.0.10 | CHANGED | rc=0 >>
CONTAINER ID   IMAGE                      COMMAND                  CREATED          STATUS                    PORTS                                                 NAMES
c62fda885ad2   dokuwiki/dokuwiki:stable   "/dokuwiki-entrypoin…"   38 seconds ago   Up 38 seconds (healthy)   80/tcp, 0.0.0.0:8081->8080/tcp, [::]:8081->8080/tcp   dokuwiki

Você também pode verificar os arquivos criados no diretório /opt/stack/dokuwiki:

1
ansible docker -m shell -a "ls /opt/stack/dokuwiki"

Output esperado:

1
2
3
4
5
[WARNING]: Platform linux on host 10.64.0.10 is using the discovered Python interpreter at /usr/bin/python3.11, but future installation of another Python interpreter could
change the meaning of that path. See https://docs.ansible.com/ansible-core/2.18/reference_appendices/interpreter_discovery.html for more information.
10.64.0.10 | CHANGED | rc=0 >>
docker-compose.yml
html

Nota: Para seguir com a instalação do Dokuwiki via interface web, acesse: http://10.64.0.10:8081/install.php

6. Solução de Problemas (Troubleshooting)

Esta seção aborda alguns problemas comuns que podem surgir durante a implantação e como resolvê-los.

6.1. Problemas de Conectividade SSH

  • Verifique a chave SSH: Certifique-se de que a chave pública foi copiada corretamente para a VM e que a chave privada está sendo usada pelo Ansible.
  • Firewall: Verifique se o firewall na VM ou no host KVM está bloqueando a porta 22 (SSH).
  • Serviço SSH: Confirme se o serviço SSH está em execução na VM (sudo systemctl status ssh).

6.2. Erros de Terraform

  • Sintaxe: Erros de sintaxe no código Terraform podem ser identificados com terraform validate.
  • Provedor: Verifique se o provedor libvirt foi baixado corretamente (terraform init) e se a versão está consistente.
  • Recursos: Certifique-se de que há recursos suficientes (CPU, memória, disco) no seu host KVM para provisionar as VMs.

6.3. Problemas de Cloud-Init

  • Logs da VM: Verifique os logs do Cloud-Init dentro da VM para identificar erros (sudo cat /var/log/cloud-init-output.log).
  • Configuração de Rede: Se a VM não obtiver um IP, revise o network_config.yml.tpl e as configurações de rede do Libvirt.
  • Permissões de Disco: Se os discos para Docker ou Stack não forem montados, verifique as permissões e os comandos mkfs.ext4, mkdir e mount no user_data.yml.tpl.

6.4. Problemas com Docker ou Dokuwiki

  • Status do Contêiner: Verifique o status dos contêineres Docker (sudo docker ps -a).
  • Logs do Contêiner: Inspecione os logs do contêiner Dokuwiki para mensagens de erro (sudo docker logs <container_id>).
  • Porta Ocupada: Se a porta 8080 já estiver em uso no host KVM, o contêiner Dokuwiki não conseguirá iniciar. Altere o mapeamento de portas no docker-compose.yml.

7. Próximos Passos e Expansão

Este tutorial fornece uma base sólida para a automação de infraestruturas com KVM, Terraform e Ansible. Considere os seguintes próximos passos para expandir e aprimorar seu ambiente:

  • Acesso ao Dokuwiki: Conclua a instalação do Dokuwiki acessando a URL fornecida (http://10.64.0.10:8081/install.php) e siga as instruções na interface web.
  • Destruição da Infraestrutura: Para remover toda a infraestrutura provisionada pelo Terraform, execute:
    1
    
    terraform destroy
    

    ATENÇÃO: Este comando removerá todas as VMs e recursos associados. Use com cautela.

  • Adicionar Mais VMs: Modifique o arquivo terraform.tfvars para incluir mais configurações de VMs e execute terraform apply novamente para provisionar máquinas adicionais.
  • Balanceador de Carga: Explore a possibilidade de adicionar um balanceador de carga (e.g., HAProxy, Nginx) para distribuir o tráfego entre múltiplas instâncias do Dokuwiki.
  • Integração CI/CD: Integre este fluxo de trabalho em um pipeline de CI/CD para automatizar ainda mais o provisionamento e a implantação.
  • Banco de Dados Externo: Para ambientes de produção, considere configurar o Dokuwiki para usar um banco de dados externo (e.g., MySQL, PostgreSQL) em vez de armazenar os dados no volume local do contêiner.
  • Monitoramento: Implemente ferramentas de monitoramento (e.g., Prometheus, Grafana) para acompanhar a saúde e o desempenho das suas VMs e serviços.

Conclusão

Com este tutorial aprimorado, você tem um guia completo para automatizar a criação de infraestruturas KVM com Terraform e Ansible, culminando na implantação de um Dokuwiki funcional. A automação não só economiza tempo, mas também garante a consistência e a reprodutibilidade do seu ambiente. Esperamos que este guia seja um recurso valioso em seus projetos de infraestrutura como código.

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