← Voltar para o Blog

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/python3

Confirme que o acesso funciona antes de qualquer coisa:

ansible -i inventario.ini todos -m ping

Se 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: reloaded

Dois 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-01

O --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 1

Assim 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: true

Quando 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/.configurado

O 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-pass

O 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 rsync com 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=60s

O 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.

Suba um servidor em minutos

Preço em real, suporte em português e dados no Brasil.

Criar conta