Personalização Avançada de Imagens com Packer
Nota: Este tutorial é o quinto de uma série de 6 tutoriais sobre criação de imagens QEMU/KVM com Packer. Se você ainda não leu os tutoriais anteriores sobre os fundamentos do Packer e a criação de imagens para Oracle Linux, Debian e Ubuntu, recomendamos que o faça antes de prosseguir. Criação de Imagem Ubuntu com Packer e Cloud-init.
Introdução
Nos tutoriais anteriores, exploramos os fundamentos do Packer e QEMU/KVM, e aprendemos a criar imagens automatizadas para Oracle Linux, Debian e Ubuntu usando diferentes métodos de automação (Kickstart, Preseed e Cloud-init). Agora, vamos avançar para técnicas avançadas de personalização de imagens.
Criar uma imagem básica é apenas o primeiro passo. Para ambientes de produção, você geralmente precisa personalizar suas imagens com pacotes específicos, configurações de segurança, otimizações de desempenho e muito mais. Neste tutorial, vamos explorar como transformar uma imagem básica em uma imagem pronta para produção.
Neste tutorial, vamos:
- Explorar diferentes métodos de provisionamento no Packer
- Adicionar pacotes personalizados às imagens
- Configurar serviços específicos
- Implementar hardening de segurança básico
- Otimizar o desempenho das imagens
- Criar imagens multi-propósito com variáveis
Métodos de Provisionamento no Packer
O Packer oferece vários “provisionadores” que permitem personalizar suas imagens de diferentes maneiras. Vamos explorar os mais úteis:
Provisionador shell
O provisionador shell
executa comandos shell diretamente na máquina virtual durante o processo de build. É o método mais simples e direto para personalização.
1
2
3
4
5
6
7
provisioner "shell" {
inline = [
"apt-get update",
"apt-get install -y nginx",
"systemctl enable nginx"
]
}
Você também pode executar scripts externos:
1
2
3
provisioner "shell" {
script = "scripts/setup.sh"
}
Ou vários scripts em sequência:
1
2
3
4
5
6
7
8
provisioner "shell" {
scripts = [
"scripts/update.sh",
"scripts/install_packages.sh",
"scripts/configure_services.sh",
"scripts/cleanup.sh"
]
}
Provisionador file
O provisionador file
copia arquivos da máquina host para a máquina virtual. É útil para transferir arquivos de configuração, scripts ou outros recursos.
1
2
3
4
5
6
7
8
provisioner "file" {
source = "files/nginx.conf"
destination = "/tmp/nginx.conf"
}
provisioner "shell" {
inline = ["sudo mv /tmp/nginx.conf /etc/nginx/nginx.conf"]
}
Você também pode copiar diretórios inteiros:
1
2
3
4
provisioner "file" {
source = "files/app/"
destination = "/tmp/app"
}
Provisionador ansible
O provisionador ansible
executa playbooks Ansible na máquina virtual. É uma opção poderosa para configurações complexas.
1
2
3
provisioner "ansible" {
playbook_file = "ansible/playbook.yml"
}
Provisionador ansible-local
O provisionador ansible-local
instala o Ansible na máquina virtual e executa playbooks localmente. É útil quando você não pode executar o Ansible diretamente na máquina host.
1
2
3
4
5
provisioner "ansible-local" {
playbook_file = "ansible/playbook.yml"
playbook_dir = "ansible"
extra_arguments = ["--extra-vars", "\"variable1=value1 variable2=value2\""]
}
Preparando o Ambiente
Vamos preparar nosso ambiente de trabalho para este tutorial:
1
2
3
4
5
6
# Criar diretório para o quinto tutorial
mkdir -p ~/packer-kvm-tutorial/tutorial5
cd ~/packer-kvm-tutorial/tutorial5
# Criar subdiretórios necessários
mkdir -p http scripts files ansible build/os-base
Adicionando Pacotes Personalizados
Uma das personalizações mais comuns é adicionar pacotes específicos às suas imagens. Vamos criar um script que instala pacotes comuns para um servidor web:
1
nano scripts/install_packages.sh
Adicione o seguinte conteúdo:
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
#!/bin/bash -eux
# Detectar o sistema operacional
if [ -f /etc/debian_version ]; then
# Debian/Ubuntu
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get upgrade -y
apt-get install -y \
nginx \
php-fpm \
php-mysql \
mariadb-client \
fail2ban \
ufw \
htop \
vim \
git \
curl \
wget \
unzip \
net-tools \
python3-pip
elif [ -f /etc/redhat-release ]; then
# RHEL/CentOS/Oracle Linux
dnf update -y
dnf install -y \
nginx \
php-fpm \
php-mysqlnd \
mariadb \
fail2ban \
firewalld \
htop \
vim \
git \
curl \
wget \
unzip \
net-tools \
python3-pip
fi
# Instalar pacotes Python comuns
pip3 install \
requests \
boto3 \
awscli \
ansible
Torne o script executável:
1
chmod +x scripts/install_packages.sh
Configurando Serviços Específicos
Agora, vamos criar um script para configurar o Nginx e o PHP-FPM:
1
nano scripts/configure_services.sh
Adicione o seguinte conteúdo:
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
#!/bin/bash -eux
# Detectar o sistema operacional
if [ -f /etc/debian_version ]; then
# Debian/Ubuntu
NGINX_CONF="/etc/nginx/sites-available/default"
PHP_FPM_CONF="/etc/php/*/fpm/pool.d/www.conf"
SYSTEMD_CMD="systemctl"
elif [ -f /etc/redhat-release ]; then
# RHEL/CentOS/Oracle Linux
NGINX_CONF="/etc/nginx/conf.d/default.conf"
PHP_FPM_CONF="/etc/php-fpm.d/www.conf"
SYSTEMD_CMD="systemctl"
fi
# Configurar Nginx
cat > $NGINX_CONF << 'EOF'
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.php index.html index.htm;
server_name _;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}
EOF
# Configurar PHP-FPM
sed -i 's/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/' /etc/php/*/fpm/php.ini 2>/dev/null || true
sed -i 's/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/' /etc/php.ini 2>/dev/null || true
# Criar página de teste
mkdir -p /var/www/html
cat > /var/www/html/index.php << 'EOF'
<?php
phpinfo();
EOF
# Ajustar permissões
chown -R www-data:www-data /var/www/html 2>/dev/null || true
chown -R nginx:nginx /var/www/html 2>/dev/null || true
# Habilitar e iniciar serviços
$SYSTEMD_CMD enable nginx
$SYSTEMD_CMD enable php-fpm
Torne o script executável:
1
chmod +x scripts/configure_services.sh
Implementando Hardening de Segurança
A segurança é crucial para imagens de produção. Vamos criar um script de hardening básico:
1
nano scripts/security_hardening.sh
Adicione o seguinte conteúdo:
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#!/bin/bash -eux
# Detectar o sistema operacional
if [ -f /etc/debian_version ]; then
# Debian/Ubuntu
FIREWALL_CMD="ufw"
SSH_CONF="/etc/ssh/sshd_config"
SYSTEMD_CMD="systemctl"
elif [ -f /etc/redhat-release ]; then
# RHEL/CentOS/Oracle Linux
FIREWALL_CMD="firewall-cmd"
SSH_CONF="/etc/ssh/sshd_config"
SYSTEMD_CMD="systemctl"
fi
# Configurar SSH
sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' $SSH_CONF
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' $SSH_CONF
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' $SSH_CONF
sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' $SSH_CONF
sed -i 's/X11Forwarding yes/X11Forwarding no/' $SSH_CONF
sed -i 's/#AllowAgentForwarding yes/AllowAgentForwarding no/' $SSH_CONF
sed -i 's/#AllowTcpForwarding yes/AllowTcpForwarding no/' $SSH_CONF
echo "Protocol 2" >> $SSH_CONF
echo "ClientAliveInterval 300" >> $SSH_CONF
echo "ClientAliveCountMax 2" >> $SSH_CONF
# Configurar limites de recursos
cat > /etc/security/limits.conf << 'EOF'
* soft nofile 65536
* hard nofile 65536
* soft nproc 4096
* hard nproc 4096
EOF
# Configurar sysctl para segurança
cat > /etc/sysctl.d/99-security.conf << 'EOF'
# IP Spoofing protection
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Ignore ICMP broadcast requests
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Disable source packet routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0
# Ignore send redirects
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# Block SYN attacks
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 5
# Log Martians
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
# Disable IPv6 if not needed
net.ipv6.conf.all.disable_ipv6 = 0
net.ipv6.conf.default.disable_ipv6 = 0
EOF
# Aplicar configurações sysctl
sysctl -p /etc/sysctl.d/99-security.conf
# Configurar firewall
if [ "$FIREWALL_CMD" = "ufw" ]; then
# UFW (Ubuntu/Debian)
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow http
ufw allow https
echo "y" | ufw enable
elif [ "$FIREWALL_CMD" = "firewall-cmd" ]; then
# firewalld (RHEL/CentOS/Oracle)
$SYSTEMD_CMD enable firewalld
$SYSTEMD_CMD start firewalld
firewall-cmd --permanent --add-service=ssh
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
fi
# Configurar fail2ban
if command -v fail2ban-client &> /dev/null; then
$SYSTEMD_CMD enable fail2ban
$SYSTEMD_CMD start fail2ban
# Configuração básica do fail2ban
cat > /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
[sshd]
enabled = true
EOF
fi
Torne o script executável:
1
chmod +x scripts/security_hardening.sh
Otimizando o Desempenho
Vamos criar um script para otimizar o desempenho do sistema:
1
nano scripts/performance_tuning.sh
Adicione o seguinte conteúdo:
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
#!/bin/bash -eux
# Detectar o sistema operacional
if [ -f /etc/debian_version ]; then
# Debian/Ubuntu
SYSTEMD_CMD="systemctl"
elif [ -f /etc/redhat-release ]; then
# RHEL/CentOS/Oracle Linux
SYSTEMD_CMD="systemctl"
fi
# Configurar sysctl para desempenho
cat > /etc/sysctl.d/99-performance.conf << 'EOF'
# Aumentar o tamanho do buffer de rede
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 262144
net.core.wmem_default = 262144
net.core.optmem_max = 65536
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# Aumentar o número máximo de conexões
net.core.somaxconn = 65536
net.ipv4.tcp_max_syn_backlog = 65536
net.ipv4.tcp_max_tw_buckets = 65536
# Reutilização de sockets em TIME_WAIT
net.ipv4.tcp_tw_reuse = 1
# Otimizações de TCP
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_mtu_probing = 1
net.ipv4.tcp_fastopen = 3
EOF
# Aplicar configurações sysctl
sysctl -p /etc/sysctl.d/99-performance.conf
# Configurar limites para o Nginx
if command -v nginx &> /dev/null; then
mkdir -p /etc/systemd/system/nginx.service.d/
cat > /etc/systemd/system/nginx.service.d/limits.conf << 'EOF'
[Service]
LimitNOFILE=65536
EOF
$SYSTEMD_CMD daemon-reload
fi
# Otimizar configuração do Nginx
if [ -f /etc/nginx/nginx.conf ]; then
sed -i 's/worker_processes.*/worker_processes auto;/' /etc/nginx/nginx.conf
sed -i 's/# multi_accept.*/multi_accept on;/' /etc/nginx/nginx.conf
sed -i 's/# worker_connections.*/worker_connections 4096;/' /etc/nginx/nginx.conf
# Adicionar configurações de cache e gzip se não existirem
if ! grep -q "open_file_cache" /etc/nginx/nginx.conf; then
sed -i '/http {/a \ open_file_cache max=1000 inactive=20s;\n open_file_cache_valid 30s;\n open_file_cache_min_uses 2;\n open_file_cache_errors on;' /etc/nginx/nginx.conf
fi
if ! grep -q "gzip" /etc/nginx/nginx.conf; then
sed -i '/http {/a \ gzip on;\n gzip_vary on;\n gzip_proxied any;\n gzip_comp_level 6;\n gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;' /etc/nginx/nginx.conf
fi
fi
# Otimizar configuração do PHP-FPM
if [ -f /etc/php/*/fpm/pool.d/www.conf ]; then
# Debian/Ubuntu
PHP_FPM_CONF=$(find /etc/php -name www.conf)
sed -i 's/pm = dynamic/pm = ondemand/' $PHP_FPM_CONF
sed -i 's/pm.max_children = 5/pm.max_children = 50/' $PHP_FPM_CONF
sed -i 's/pm.start_servers = 2/pm.start_servers = 10/' $PHP_FPM_CONF
sed -i 's/pm.min_spare_servers = 1/pm.min_spare_servers = 5/' $PHP_FPM_CONF
sed -i 's/pm.max_spare_servers = 3/pm.max_spare_servers = 20/' $PHP_FPM_CONF
sed -i 's/;pm.max_requests = 500/pm.max_requests = 500/' $PHP_FPM_CONF
elif [ -f /etc/php-fpm.d/www.conf ]; then
# RHEL/CentOS/Oracle Linux
sed -i 's/pm = dynamic/pm = ondemand/' /etc/php-fpm.d/www.conf
sed -i 's/pm.max_children = 5/pm.max_children = 50/' /etc/php-fpm.d/www.conf
sed -i 's/pm.start_servers = 2/pm.start_servers = 10/' /etc/php-fpm.d/www.conf
sed -i 's/pm.min_spare_servers = 1/pm.min_spare_servers = 5/' /etc/php-fpm.d/www.conf
sed -i 's/pm.max_spare_servers = 3/pm.max_spare_servers = 20/' /etc/php-fpm.d/www.conf
sed -i 's/;pm.max_requests = 500/pm.max_requests = 500/' /etc/php-fpm.d/www.conf
fi
Torne o script executável:
1
chmod +x scripts/performance_tuning.sh
Criando um Script de Limpeza Final
Finalmente, vamos criar um script de limpeza para otimizar a imagem final:
1
nano scripts/cleanup.sh
Adicione o seguinte conteúdo:
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
#!/bin/bash -eux
# Detectar o sistema operacional
if [ -f /etc/debian_version ]; then
# Debian/Ubuntu
export DEBIAN_FRONTEND=noninteractive
apt-get autoremove -y
apt-get clean
rm -rf /var/lib/apt/lists/*
elif [ -f /etc/redhat-release ]; then
# RHEL/CentOS/Oracle Linux
dnf clean all
rm -rf /var/cache/dnf/*
fi
# Limpar logs e arquivos temporários
find /var/log -type f -exec truncate --size=0 {} \;
rm -rf /tmp/*
rm -rf /var/tmp/*
# Remover informações específicas da máquina
truncate -s 0 /etc/machine-id
rm -f /etc/ssh/*key*
rm -f ~/.bash_history
# Configurar cloud-init para NoCloud
if command -v cloud-init &> /dev/null; then
cat > /etc/cloud/cloud.cfg.d/99-nocloud.cfg << 'EOF'
datasource_list: [ NoCloud, None ]
EOF
fi
# Zerar espaço livre
dd if=/dev/zero of=/EMPTY bs=1M || true
rm -f /EMPTY
# Sincronizar sistema de arquivos
sync
Torne o script executável:
1
chmod +x scripts/cleanup.sh
Criando o Arquivo Packer com Variáveis
Agora, vamos criar um arquivo Packer que utiliza variáveis para criar imagens multi-propósito:
1
nano advanced-template.pkr.hcl
Adicione o seguinte conteúdo:
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
packer {
required_version = ">= 1.10.0"
required_plugins {
qemu = {
version = "= 1.1.0"
source = "github.com/hashicorp/qemu"
}
}
}
variable "vm_name" {
type = string
default = "advanced-template.raw"
}
variable "disk_size" {
type = string
default = "20480"
}
variable "memory" {
type = string
default = "2048"
}
variable "cpus" {
type = string
default = "2"
}
variable "iso_url" {
type = string
default = "https://releases.ubuntu.com/24.04/ubuntu-24.04-live-server-amd64.iso"
}
variable "iso_checksum" {
type = string
default = "sha256:c5ea60d25d64b3e3a47a6171c1dc78c94e29c5e6e8284b0c44b8a1feddc83e75"
}
variable "os_type" {
type = string
default = "ubuntu"
validation {
condition = contains(["ubuntu", "debian", "oracle"], var.os_type)
error_message = "The os_type must be one of: ubuntu, debian, oracle."
}
}
variable "install_web_server" {
type = bool
default = true
}
variable "enable_security_hardening" {
type = bool
default = true
}
variable "enable_performance_tuning" {
type = bool
default = true
}
locals {
boot_command = {
ubuntu = [
"c<wait>",
"linux /casper/vmlinuz --- autoinstall ds=nocloud-net\\;s=http://{{.HTTPIP}}:{{.HTTPPort}}/ ",
"console=tty1 console=ttyS0<enter><wait>",
"initrd /casper/initrd<enter><wait>",
"boot<enter>"
],
debian = [
"<esc><wait>",
"install <wait>",
"preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg <wait>",
"debian-installer=pt_BR.UTF-8 <wait>",
"auto <wait>",
"locale=pt_BR.UTF-8 <wait>",
"kbd-chooser/method=br <wait>",
"keyboard-configuration/xkb-keymap=br <wait>",
"netcfg/get_hostname=packer <wait>",
"netcfg/get_domain=local <wait>",
"fb=false <wait>",
"debconf/frontend=noninteractive <wait>",
"console-setup/ask_detect=false <wait>",
"console-keymaps-at/keymap=br <wait>",
"<enter><wait>"
],
oracle = [
"<up>",
"<tab><wait>",
" inst.ks=http://{{.HTTPIP}}:{{.HTTPPort}}/ks.cfg",
"<enter>"
]
}
http_directory = {
ubuntu = "http/ubuntu",
debian = "http/debian",
oracle = "http/oracle"
}
}
source "qemu" "template" {
vm_name = var.vm_name
iso_url = var.iso_url
iso_checksum = var.iso_checksum
disk_size = var.disk_size
memory = var.memory
cpus = var.cpus
disk_image = false
output_directory = "build/os-base"
accelerator = "kvm"
disk_interface = "virtio"
format = "raw"
net_device = "virtio-net"
boot_wait = "5s"
boot_command = local.boot_command[var.os_type]
http_directory = local.http_directory[var.os_type]
cpu_model = "host"
shutdown_command = "echo 'packer' | sudo -S shutdown -P now"
ssh_username = "packer"
ssh_password = "packer"
ssh_timeout = "60m"
}
build {
sources = ["source.qemu.template"]
# Instalar pacotes personalizados (condicional)
provisioner "shell" {
script = "scripts/install_packages.sh"
only = [var.install_web_server ? "qemu.template" : ""]
}
# Configurar serviços (condicional)
provisioner "shell" {
script = "scripts/configure_services.sh"
only = [var.install_web_server ? "qemu.template" : ""]
}
# Aplicar hardening de segurança (condicional)
provisioner "shell" {
script = "scripts/security_hardening.sh"
only = [var.enable_security_hardening ? "qemu.template" : ""]
}
# Aplicar otimizações de desempenho (condicional)
provisioner "shell" {
script = "scripts/performance_tuning.sh"
only = [var.enable_performance_tuning ? "qemu.template" : ""]
}
# Executar limpeza final
provisioner "shell" {
script = "scripts/cleanup.sh"
}
}
Este arquivo Packer usa variáveis e condicionais para criar imagens personalizadas com base em diferentes parâmetros:
os_type
: Tipo de sistema operacional (ubuntu, debian, oracle)install_web_server
: Se deve instalar e configurar um servidor webenable_security_hardening
: Se deve aplicar configurações de segurançaenable_performance_tuning
: Se deve aplicar otimizações de desempenho
Criando Arquivos de Automação para Diferentes Sistemas
Para que o template funcione com diferentes sistemas operacionais, precisamos criar os diretórios e arquivos de automação:
1
mkdir -p http/ubuntu http/debian http/oracle
Para Ubuntu (Cloud-init)
1
nano http/ubuntu/user-data
Adicione o conteúdo do arquivo Cloud-init que criamos no tutorial anterior.
1
touch http/ubuntu/meta-data
Para Debian (Preseed)
1
nano http/debian/preseed.cfg
Adicione o conteúdo do arquivo Preseed que criamos no tutorial anterior.
Para Oracle Linux (Kickstart)
1
nano http/oracle/ks.cfg
Adicione o conteúdo do arquivo Kickstart que criamos no tutorial anterior.
Usando o Template Avançado
Agora podemos usar nosso template avançado para criar diferentes tipos de imagens:
Imagem Ubuntu Básica
1
2
3
4
5
6
packer build -var "os_type=ubuntu" \
-var "vm_name=ubuntu-basic.raw" \
-var "install_web_server=false" \
-var "enable_security_hardening=false" \
-var "enable_performance_tuning=false" \
advanced-template.pkr.hcl
Imagem Ubuntu com Servidor Web
1
2
3
4
5
6
packer build -var "os_type=ubuntu" \
-var "vm_name=ubuntu-web.raw" \
-var "install_web_server=true" \
-var "enable_security_hardening=true" \
-var "enable_performance_tuning=true" \
advanced-template.pkr.hcl
Imagem Debian com Servidor Web
1
2
3
4
5
6
7
8
packer build -var "os_type=debian" \
-var "vm_name=debian-web.raw" \
-var "iso_url=https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.5.0-amd64-netinst.iso" \
-var "iso_checksum=sha256:013f5b44670d81280b5b1bc02455842b247951bf8ba45f6.." \
-var "install_web_server=true" \
-var "enable_security_hardening=true" \
-var "enable_performance_tuning=true" \
advanced-template.pkr.hcl
Imagem Oracle Linux com Servidor Web
1
2
3
4
5
6
7
8
packer build -var "os_type=oracle" \
-var "vm_name=oracle-web.raw" \
-var "iso_url=https://yum.oracle.com/ISOS/OracleLinux/OL9/u4/x86_64/OracleLinux-R9-U4-x86_64-boot.iso" \
-var "iso_checksum=sha256:975de11be8761efa4aa2c87d7d3bedcb62c9dc956909b68f62c99062d11599e9" \
-var "install_web_server=true" \
-var "enable_security_hardening=true" \
-var "enable_performance_tuning=true" \
advanced-template.pkr.hcl
Usando Arquivos de Variáveis
Para simplificar ainda mais, podemos criar arquivos de variáveis para diferentes cenários:
1
nano ubuntu-web.pkrvars.hcl
Adicione o seguinte conteúdo:
1
2
3
4
5
6
7
os_type = "ubuntu"
vm_name = "ubuntu-web.raw"
iso_url = "https://releases.ubuntu.com/24.04/ubuntu-24.04-live-server-amd64.iso"
iso_checksum = "sha256:c5ea60d25d64b3e3a47a6171c1dc78c94e29c5e6e8284b0c44b8a1feddc83e75"
install_web_server = true
enable_security_hardening = true
enable_performance_tuning = true
Agora podemos usar este arquivo de variáveis:
1
packer build -var-file=ubuntu-web.pkrvars.hcl advanced-template.pkr.hcl
Técnicas Avançadas de Personalização
Usando Ansible para Configuração Avançada
Para configurações mais complexas, o Ansible é uma excelente opção. Vamos criar um playbook básico:
1
nano ansible/playbook.yml
Adicione o seguinte conteúdo:
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
---
- name: Configure Web Server
hosts: all
become: yes
tasks:
- name: Update apt cache
apt:
update_cache: yes
when: ansible_os_family == "Debian"
- name: Install Nginx
package:
name: nginx
state: present
- name: Install PHP-FPM
package:
name:
- php-fpm
- php-mysql
state: present
- name: Configure Nginx
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/default
when: ansible_os_family == "Debian"
- name: Configure Nginx (RHEL)
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/conf.d/default.conf
when: ansible_os_family == "RedHat"
- name: Enable and start Nginx
service:
name: nginx
state: started
enabled: yes
- name: Enable and start PHP-FPM
service:
name: "{{ 'php-fpm' if ansible_os_family == 'RedHat' else 'php7.4-fpm' }}"
state: started
enabled: yes
Crie o diretório de templates e o arquivo de configuração do Nginx:
1
2
mkdir -p ansible/templates
nano ansible/templates/nginx.conf.j2
Adicione o seguinte conteúdo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.php index.html index.htm;
server_name _;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}
Para usar o Ansible com o Packer, adicione o seguinte provisionador ao arquivo advanced-template.pkr.hcl
:
1
2
3
4
5
provisioner "ansible-local" {
playbook_file = "ansible/playbook.yml"
playbook_dir = "ansible"
only = [var.install_web_server ? "qemu.template" : ""]
}
Usando Post-Processors
Os post-processors do Packer permitem realizar ações após a criação da imagem. Por exemplo, podemos converter automaticamente a imagem para o formato QCOW2:
1
2
3
4
5
6
post-processor "shell-local" {
inline = [
"qemu-img convert -O qcow2 -c build/os-base/${var.vm_name} build/os-base/${var.vm_name}.qcow2",
"rm build/os-base/${var.vm_name}"
]
}
Exercícios Práticos
Para fixar o conhecimento adquirido neste tutorial, tente os seguintes exercícios:
- Crie um template personalizado para um servidor de banco de dados (MySQL/MariaDB)
- Adicione suporte para mais uma distribuição Linux (como Fedora ou CentOS)
- Implemente um provisionador Ansible para configurar um ambiente LAMP completo
- Crie um arquivo de variáveis para uma imagem otimizada para execução de containers Docker
Solução de Problemas Comuns
Erros nos Scripts de Provisionamento
Se os scripts de provisionamento falharem:
- Verifique se os scripts têm permissão de execução
- Verifique se os comandos são compatíveis com a distribuição Linux escolhida
- Tente executar os comandos manualmente para identificar o problema
Problemas com Variáveis
Se houver problemas com as variáveis:
- Verifique a sintaxe e os tipos de dados
- Certifique-se de que os valores estão corretos
- Use o comando
packer validate
para verificar a configuração
Problemas com Ansible
Se o provisionador Ansible falhar:
- Verifique se o Ansible está instalado na máquina virtual
- Verifique a sintaxe do playbook
- Verifique se os caminhos dos arquivos estão corretos
Conclusão
Neste tutorial, exploramos técnicas avançadas de personalização de imagens com Packer. Aprendemos a:
- Usar diferentes métodos de provisionamento
- Adicionar pacotes personalizados às imagens
- Configurar serviços específicos
- Implementar hardening de segurança básico
- Otimizar o desempenho das imagens
- Criar imagens multi-propósito com variáveis
Estas técnicas permitem criar imagens altamente personalizadas e otimizadas para diferentes casos de uso, desde servidores web até ambientes de banco de dados ou containers.
No próximo e último tutorial da série, vamos explorar a integração do Packer com pipelines CI/CD e técnicas avançadas de solução de problemas.