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 provedorlibvirt
).
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 osshpass
, 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 algoritmoed25519
e salva os arquivos com o nomeansible
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áriosuporte
na VM com o IP10.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 docontainerd.io
.name: Adicionar usuários ao grupo Docker
: Adiciona os usuários definidos na variáveldocker_users
ao grupodocker
, permitindo que eles executem comandos Docker semsudo
.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 arquivodocker-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 plugindocker-compose
esteja presente na VM.name: Executar docker-compose up
: Inicia os serviços definidos nodocker-compose.yml
em mododetached
(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
emount
nouser_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 executeterraform 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.