Ansible sem servidor de CI: automação para equipe pequena
Você não precisa de pipeline, agente instalado nem plataforma para automatizar configuração de servidor. Um arquivo YAML e SSH resolvem — e é assim que a maioria deveria começar.
Equipe EasyOps Cloud · · 7 min de leitura
Automatizar configuração de servidor costuma ser adiado por uma suposição errada: que é preciso montar pipeline, instalar agente nas máquinas e manter uma plataforma para isso.
O Ansible não exige nada disso. Ele conecta por SSH, executa e sai — sem agente, sem servidor central, sem serviço rodando. Se você consegue entrar nas máquinas por SSH, já tem o pré-requisito completo.
Para equipe pequena, isso muda a conta: o custo de adoção é aprender o formato, e mais nada.
O que ele resolve que o script não resolve
Um script de shell também configura servidor. A diferença está em três propriedades.
Idempotência. Rodar o mesmo playbook cinco vezes produz o mesmo resultado. Instalar um pacote já instalado não faz nada; adicionar uma linha já presente não duplica. Script de shell precisa de verificação manual para cada passo, e é justamente o que ninguém escreve.
Descrição em vez de sequência. Você declara o estado desejado — "este pacote
presente, este serviço rodando, esta linha no arquivo" — em vez de listar
comandos. Ler um playbook de seis meses atrás e entender o que ele garante é bem
mais fácil que interpretar cinquenta linhas de bash.
Execução em várias máquinas com o mesmo comando, e um relatório do que mudou em cada uma.
O mínimo para começar
sudo apt install ansible# inventario.ini
[web]
web-01 ansible_host=10.0.0.11
web-02 ansible_host=10.0.0.12
[banco]
db-01 ansible_host=10.0.0.20
[todos:children]
web
banco
[todos:vars]
ansible_user=deploy
ansible_python_interpreter=/usr/bin/python3Confirme que o acesso funciona antes de qualquer coisa:
ansible -i inventario.ini todos -m pingSe isso responde, o resto é escrever o que você quer. Se não responde, o problema é SSH — e o artigo sobre hardening de SSH cobre a autenticação por chave, que é o pré-requisito.
Um playbook de verdade
# base.yml
- name: Configuração base dos servidores
hosts: todos
become: true
tasks:
- name: Pacotes essenciais presentes
ansible.builtin.apt:
name: [ufw, fail2ban, chrony, unattended-upgrades, vnstat]
state: present
update_cache: true
cache_valid_time: 3600
- name: Fuso horário definido
community.general.timezone:
name: America/Sao_Paulo
- name: SSH sem autenticação por senha
ansible.builtin.copy:
dest: /etc/ssh/sshd_config.d/99-hardening.conf
content: |
PasswordAuthentication no
PermitRootLogin no
MaxAuthTries 4
owner: root
mode: "0644"
validate: /usr/sbin/sshd -t -f %s
notify: recarregar ssh
- name: Firewall com política padrão
community.general.ufw:
state: enabled
policy: deny
direction: incoming
- name: SSH liberado no firewall
community.general.ufw:
rule: allow
name: OpenSSH
handlers:
- name: recarregar ssh
ansible.builtin.service:
name: ssh
state: reloadedDois detalhes valem destaque.
O validate testa a sintaxe antes de gravar o arquivo. Se a configuração estiver
errada, nada é escrito — o que evita o cenário em que uma automação derruba o
acesso SSH de todas as máquinas de uma vez.
O notify com handlers faz o serviço recarregar apenas quando o arquivo mudou de
fato. Rodar o playbook dez vezes não reinicia o SSH dez vezes.
Executar com rede de segurança
# O que mudaria, sem mudar nada
ansible-playbook -i inventario.ini base.yml --check --diff
# Aplicar
ansible-playbook -i inventario.ini base.yml
# Só em uma máquina, para testar
ansible-playbook -i inventario.ini base.yml --limit web-01O --check --diff é o equivalente ao plan das ferramentas declarativas: mostra
cada alteração pretendida, inclusive as linhas que mudariam nos arquivos.
O --limit é o hábito que separa automação segura de incidente. Aplique em uma
máquina, confira que ela continua funcionando, e só então estenda ao grupo.
Para mudança arriscada em várias máquinas, vale ir aos poucos:
ansible-playbook -i inventario.ini base.yml --serial 1Assim uma máquina é configurada por vez, e uma falha interrompe antes de atingir todas.
O erro conceitual que mais aparece
Usar o módulo shell ou command para tudo. Funciona, e joga fora a idempotência —
que é o principal motivo de usar a ferramenta.
# Ruim: roda sempre, não sabe se precisava
- name: Adicionar linha
ansible.builtin.shell: echo "vm.swappiness=10" >> /etc/sysctl.conf
# Bom: garante o estado, sem duplicar
- name: Swappiness ajustado
ansible.posix.sysctl:
name: vm.swappiness
value: "10"
sysctl_file: /etc/sysctl.d/99-tuning.conf
state: present
reload: trueQuando não houver módulo para o que você precisa, ao menos declare a condição de parada:
- name: Comando que não tem módulo
ansible.builtin.command: /usr/local/bin/configurar.sh
args:
creates: /var/lib/meuapp/.configuradoO creates faz o comando ser pulado se o arquivo já existir — idempotência
manual, mas idempotência.
Segredos, que não vão em texto claro
ansible-vault create grupo_vars/todos/vault.yml
ansible-vault edit grupo_vars/todos/vault.yml# dentro do vault
vault_db_senha: "a-senha-de-verdade"# variáveis normais, referenciando o vault
db_senha: "{{ vault_db_senha }}"ansible-playbook -i inventario.ini deploy.yml --ask-vault-passO arquivo cifrado pode ser versionado com segurança. A senha do vault fica fora
do repositório — em gerenciador de senhas, ou num arquivo local referenciado por
--vault-password-file. É a mesma lógica do artigo sobre segredos no servidor.
Onde ele encaixa com o resto
A divisão que funciona bem em operação pequena:
- Ferramenta declarativa cria a máquina — servidor, disco, IP, firewall de perímetro. Assunto do artigo sobre infraestrutura como código.
- Ansible configura o que está dentro — pacote, arquivo, serviço, usuário.
- Deploy da aplicação pode ser um playbook, ou continuar sendo o script de
rsynccom troca de link simbólico.
Não é necessário adotar os três de uma vez. Começar pelo Ansible costuma ser o que dá retorno mais rápido, porque configuração de servidor é o que mais diverge entre máquinas com o tempo.
Por onde começar sem virar projeto
A adoção que funciona é incremental:
- Escreva o playbook base com o que toda máquina precisa ter. Duas dúzias de tarefas cobrem bastante.
- Rode com `--check` contra as máquinas existentes. A saída mostra o quanto elas divergiram entre si — e essa lista costuma ser reveladora.
- Aplique em uma máquina só, confirme, estenda.
- Toda configuração nova entra pelo playbook, não pelo SSH manual.
- Rode periodicamente, mesmo sem mudanças. Isso corrige divergência que apareceu por alteração manual e mantém as máquinas iguais.
Esse último ponto é o que entrega o valor de longo prazo. Servidor configurado à mão diverge — sempre. Um playbook executado uma vez por semana transforma "as máquinas deveriam estar iguais" em "as máquinas estão iguais, e eu sei quando deixaram de estar".
Organizar quando crescer
Um playbook único funciona bem até certo ponto. Quando ele passar de duas ou três centenas de linhas, vale separar em papéis:
inventario.ini
site.yml
grupo_vars/
todos.yml
web.yml
roles/
base/tasks/main.yml
web/tasks/main.yml
web/templates/nginx.conf.j2
banco/tasks/main.yml# site.yml
- hosts: todos
become: true
roles: [base]
- hosts: web
become: true
roles: [web]
- hosts: banco
become: true
roles: [banco]A separação por papel torna reutilizável o que é comum e explícito o que é
específico. E os modelos com templates permitem gerar configuração a partir de
variáveis — uma configuração de nginx por ambiente, sem duplicar o arquivo.
Vale resistir a abstrair cedo demais. Papel criado antes de existir o segundo caso de uso costuma sair errado, e reorganizar depois é barato — o playbook é texto.
O ganho que aparece na terceira máquina
O retorno real do Ansible não aparece na primeira máquina, onde escrever o playbook custa mais que configurar à mão. Ele aparece na terceira.
A partir dali, subir um servidor idêntico deixa de ser um exercício de memória e
vira um comando. Ambiente de homologação parecido com produção deixa de ser
aspiração. E a pergunta "essa máquina está configurada como as outras?" ganha
uma resposta objetiva, verificável com --check a qualquer momento.
Para quem opera cinco servidores, essa previsibilidade costuma valer mais que o tempo economizado — porque o que dói em operação pequena raramente é o volume de trabalho, é a incerteza sobre o que está onde.
E vale registrar o que dispensa: nenhum servidor central, nenhum agente instalado nas máquinas, nenhuma porta adicional aberta, nenhum serviço em execução consumindo recurso. O Ansible roda do seu notebook, ou de qualquer máquina com SSH e o inventário — o que significa que a automação não vira mais um componente de infraestrutura para manter, monitorar e atualizar.
Para equipe pequena, essa ausência é justamente a característica mais valiosa.
Um detalhe sobre desempenho
Em máquinas com latência maior, o Ansible pode parecer lento — ele abre conexão para cada tarefa. Duas configurações resolvem a maior parte disso:
# ansible.cfg
[defaults]
forks = 10
[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60sO pipelining reduz drasticamente o número de operações por tarefa, e o ControlPersist
reaproveita a conexão SSH entre elas em vez de reabrir toda vez. Em um playbook
de cinquenta tarefas contra cinco máquinas, a diferença costuma ser de minutos.
O forks define quantas máquinas são configuradas em paralelo. Aumentar ajuda em
inventário grande e atrapalha quando as tarefas disputam o mesmo recurso remoto
— um repositório de pacotes, por exemplo.