{"id":174,"date":"2026-09-05T17:28:13","date_gmt":"2026-09-05T20:28:13","guid":{"rendered":"https:\/\/www.linuxpro.com.br\/?p=174"},"modified":"2026-09-23T14:20:08","modified_gmt":"2026-09-23T17:20:08","slug":"automatizando-servidores-linux-com-ansible","status":"publish","type":"post","link":"https:\/\/www.linuxpro.com.br\/es\/2026\/09\/automatizando-servidores-linux-com-ansible\/","title":{"rendered":"Automatizando servidores Linux con Ansible"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"\/wp-content\/uploads\/2026\/09\/ansible-servidores-v4.webp\" alt=\"Mascote do LinuxPro aperta o bot\u00e3o do playbook e aponta para quatro racks atualizados um por vez sob o logo do Ansible, ao lado de um cofre trancado e do cachorro caramelo ciborgue\" width=\"1486\" height=\"856\" \/><\/p>\n<p>Administrar um servidor na m\u00e3o \u00e9 tranquilo. Administrar dez, entrando por SSH em um por um, \u00e9 pedir para errar. O <strong>Ansible<\/strong> resolve isso: voc\u00ea descreve o estado desejado em YAML e ele aplica em todos os servidores, sem agente, s\u00f3 com SSH e Python do outro lado. Este guia \u00e9 o passo seguinte para quem j\u00e1 sabe instalar o Ansible e rodar o primeiro playbook. Se voc\u00ea est\u00e1 come\u00e7ando do zero, leia antes os nossos <a href=\"\/2026\/09\/primeiros-passos-com-ansible\/\">primeiros passos com Ansible<\/a>.<\/p>\n<p>Aqui vamos montar um projeto de verdade: invent\u00e1rio com grupos, firewall, Nginx com template e handler que testa a configura\u00e7\u00e3o antes de recarregar, senha guardada no vault e uma atualiza\u00e7\u00e3o em ondas que para sozinha se um servidor n\u00e3o voltar. Todos os comandos e sa\u00eddas foram testados em tr\u00eas m\u00e1quinas virtuais Ubuntu 24.04 com o <code>ansible-core<\/code> 2.21.<\/p>\n<h2>Estrutura do projeto<\/h2>\n<pre><code class=\"language-bash\">projeto\/\n\u251c\u2500\u2500 ansible.cfg\n\u251c\u2500\u2500 inventario.ini\n\u251c\u2500\u2500 requirements.yml\n\u251c\u2500\u2500 group_vars\/\n\u2502   \u251c\u2500\u2500 all\/main.yml\n\u2502   \u2514\u2500\u2500 web\/\n\u2502       \u251c\u2500\u2500 main.yml\n\u2502       \u2514\u2500\u2500 vault.yml        # criptografado\n\u251c\u2500\u2500 templates\/\n\u2502   \u251c\u2500\u2500 site.conf.j2\n\u2502   \u251c\u2500\u2500 index.html.j2\n\u2502   \u2514\u2500\u2500 app.env.j2\n\u251c\u2500\u2500 site.yml                 # estado desejado de todos os servidores\n\u2514\u2500\u2500 atualiza.yml             # atualiza\u00e7\u00e3o em ondas\n<\/code><\/pre>\n<p>O <code>ansible.cfg<\/code> na raiz evita repetir <code>-i inventario.ini<\/code> em todo comando:<\/p>\n<pre><code class=\"language-ini\">[defaults]\ninventory = inventario.ini\n<\/code><\/pre>\n<p>Cuidado com coment\u00e1rio na mesma linha dentro do <code>ansible.cfg<\/code>: em <code>host_key_checking = True # coment\u00e1rio<\/code> o valor inteiro vira uma string que n\u00e3o \u00e9 booleano v\u00e1lido e o Ansible passa a ler <strong>False<\/strong>. Coloque coment\u00e1rios em linha pr\u00f3pria, come\u00e7ando com <code>#<\/code> ou <code>;<\/code>.<\/p>\n<h2>Invent\u00e1rio com grupos<\/h2>\n<p>Os servidores se dividem em grupos, e um grupo pode conter outros (<code>:children<\/code>). Assim uma vari\u00e1vel definida em <code>producao<\/code> vale para web e banco ao mesmo tempo:<\/p>\n<pre><code class=\"language-ini\">[web]\nweb1 ansible_host=10.0.10.11\nweb2 ansible_host=10.0.10.12\n\n[db]\ndb1 ansible_host=10.0.10.21\n\n[producao:children]\nweb\ndb\n\n[producao:vars]\nansible_user=deploy\nansible_python_interpreter=\/usr\/bin\/python3\n<\/code><\/pre>\n<p>Confira a \u00e1rvore antes de rodar qualquer coisa:<\/p>\n<pre><code class=\"language-bash\">ansible-inventory --graph\n<\/code><\/pre>\n<pre><code class=\"language-bash\">@all:\n  |--@ungrouped:\n  |--@producao:\n  |  |--@web:\n  |  |  |--web1\n  |  |  |--web2\n  |  |--@db:\n  |  |  |--db1\n<\/code><\/pre>\n<p>As vari\u00e1veis de cada grupo ficam em <code>group_vars\/<\/code>, com o nome do grupo:<\/p>\n<pre><code class=\"language-yaml\"># group_vars\/all\/main.yml\n---\npacotes_base:\n  - htop\n  - vim\n  - unattended-upgrades\n<\/code><\/pre>\n<pre><code class=\"language-yaml\"># group_vars\/web\/main.yml\n---\ndominio: exemplo.com.br\napp_env: producao\napp_db_senha: \"{{ vault_app_db_senha }}\"\n<\/code><\/pre>\n<h2>Cole\u00e7\u00f5es: o ufw n\u00e3o vem no ansible-core<\/h2>\n<p>O m\u00f3dulo <code>community.general.ufw<\/code> faz parte da cole\u00e7\u00e3o <code>community.general<\/code>. O pacote completo <code>ansible<\/code> j\u00e1 traz essa cole\u00e7\u00e3o, mas quem instalou s\u00f3 o <code>ansible-core<\/code> precisa instal\u00e1-la. Declare as depend\u00eancias em <code>requirements.yml<\/code>:<\/p>\n<pre><code class=\"language-yaml\">---\ncollections:\n  - name: community.general\n<\/code><\/pre>\n<pre><code class=\"language-bash\">ansible-galaxy collection install -r requirements.yml\nansible-galaxy collection list community.general\n<\/code><\/pre>\n<h2>O playbook completo<\/h2>\n<p>O <code>site.yml<\/code> tem duas plays. A primeira vale para todos os servidores: pacotes b\u00e1sicos e firewall. A segunda s\u00f3 para o grupo <code>web<\/code>: Nginx, site e arquivo de configura\u00e7\u00e3o da aplica\u00e7\u00e3o.<\/p>\n<pre><code class=\"language-yaml\">---\n- name: Base de todos os servidores\n  hosts: producao\n  become: true\n  tags: [base]\n  tasks:\n    - name: Instalar pacotes b\u00e1sicos\n      ansible.builtin.apt:\n        name: \"{{ pacotes_base }}\"\n        state: present\n        update_cache: true\n        cache_valid_time: 3600\n\n    - name: Liberar SSH no ufw antes de ativar o firewall\n      community.general.ufw:\n        rule: allow\n        name: OpenSSH\n\n    - name: Ativar o ufw bloqueando o resto por padr\u00e3o\n      community.general.ufw:\n        state: enabled\n        policy: deny\n        direction: incoming\n\n- name: Servidores web\n  hosts: web\n  become: true\n  tags: [web]\n  tasks:\n    - name: Instalar o Nginx\n      ansible.builtin.apt:\n        name: nginx\n        state: present\n\n    - name: Liberar HTTP e HTTPS no ufw\n      community.general.ufw:\n        rule: allow\n        port: \"{{ item }}\"\n        proto: tcp\n      loop: [\"80\", \"443\"]\n\n    - name: Criar a raiz do site\n      ansible.builtin.file:\n        path: \/var\/www\/{{ dominio }}\n        state: directory\n        mode: \"0755\"\n\n    - name: Publicar a p\u00e1gina inicial\n      ansible.builtin.template:\n        src: templates\/index.html.j2\n        dest: \/var\/www\/{{ dominio }}\/index.html\n        mode: \"0644\"\n\n    - name: Configurar o site\n      ansible.builtin.template:\n        src: templates\/site.conf.j2\n        dest: \/etc\/nginx\/sites-available\/{{ dominio }}.conf\n        mode: \"0644\"\n      notify: Nginx alterado\n\n    - name: Ativar o site\n      ansible.builtin.file:\n        src: \/etc\/nginx\/sites-available\/{{ dominio }}.conf\n        dest: \/etc\/nginx\/sites-enabled\/{{ dominio }}.conf\n        state: link\n      notify: Nginx alterado\n\n    - name: Remover o site padr\u00e3o\n      ansible.builtin.file:\n        path: \/etc\/nginx\/sites-enabled\/default\n        state: absent\n      notify: Nginx alterado\n\n    - name: Garantir o Nginx ativo no boot\n      ansible.builtin.service:\n        name: nginx\n        state: started\n        enabled: true\n\n    - name: Criar o diret\u00f3rio da aplica\u00e7\u00e3o\n      ansible.builtin.file:\n        path: \/etc\/meuapp\n        state: directory\n        mode: \"0750\"\n\n    - name: Gravar as vari\u00e1veis da aplica\u00e7\u00e3o, com a senha vinda do vault\n      ansible.builtin.template:\n        src: templates\/app.env.j2\n        dest: \/etc\/meuapp\/app.env\n        owner: root\n        group: root\n        mode: \"0600\"\n      no_log: true\n\n  handlers:\n    - name: Testar a configura\u00e7\u00e3o do Nginx\n      ansible.builtin.command: nginx -t\n      changed_when: false\n      listen: Nginx alterado\n\n    - name: Recarregar o Nginx\n      ansible.builtin.service:\n        name: nginx\n        state: reloaded\n      listen: Nginx alterado\n<\/code><\/pre>\n<p>Tr\u00eas detalhes merecem aten\u00e7\u00e3o:<\/p>\n<ul>\n<li><strong>A ordem do firewall importa.<\/strong> Primeiro libera o OpenSSH, depois ativa o ufw com pol\u00edtica <code>deny<\/code>. Na ordem inversa, o Ansible derruba a pr\u00f3pria conex\u00e3o e voc\u00ea perde o acesso ao servidor.<\/li>\n<li><strong>Handlers com <code>listen<\/code>.<\/strong> As tr\u00eas tarefas que mexem na configura\u00e7\u00e3o notificam o mesmo evento, <code>Nginx alterado<\/code>. No fim da play, os dois handlers rodam uma \u00fanica vez, na ordem em que foram declarados: primeiro <code>nginx -t<\/code>, depois o reload. Se a configura\u00e7\u00e3o estiver quebrada, o teste falha e o reload n\u00e3o acontece, e o Nginx segue servindo a vers\u00e3o anterior.<\/li>\n<li><strong><code>no_log: true<\/code><\/strong> esconde da sa\u00edda o conte\u00fado da tarefa que grava a senha. Sem ele, um <code>-v<\/code> ou um <code>--diff<\/code> imprime o segredo no terminal e no log do CI.<\/li>\n<\/ul>\n<h2>Templates Jinja2<\/h2>\n<p>Um template \u00e9 um arquivo com vari\u00e1veis. O mesmo <code>site.conf.j2<\/code> serve para qualquer dom\u00ednio:<\/p>\n<pre><code class=\"language-nginx\"># {{ ansible_managed }}\nserver {\n    listen 80;\n    server_name {{ dominio }} www.{{ dominio }};\n    root \/var\/www\/{{ dominio }};\n    index index.html;\n\n    location \/ {\n        try_files $uri $uri\/ =404;\n    }\n}\n<\/code><\/pre>\n<pre><code class=\"language-html\">&lt;!-- templates\/index.html.j2 --&gt;\n&lt;h1&gt;{{ dominio }}&lt;\/h1&gt;\n&lt;p&gt;Servido por {{ inventory_hostname }}&lt;\/p&gt;\n<\/code><\/pre>\n<pre><code class=\"language-ini\"># templates\/app.env.j2\n# {{ ansible_managed }}\nAPP_ENV={{ app_env }}\nDB_PASSWORD={{ app_db_senha }}\n<\/code><\/pre>\n<p>A linha <code>{{ ansible_managed }}<\/code> vira um coment\u00e1rio avisando que o arquivo \u00e9 gerenciado pelo Ansible, o que ajuda quem abrir o arquivo no servidor e pensar em edit\u00e1-lo \u00e0 m\u00e3o. Se voc\u00ea precisa de v\u00e1rias vers\u00f5es de PHP atr\u00e1s desse Nginx, o template \u00e9 o lugar certo para os blocos <code>location<\/code>. Veja como montar isso em <a href=\"\/2026\/09\/nginx-varias-versoes-php-debian-ubuntu\/\">Nginx e v\u00e1rias vers\u00f5es de PHP no Debian e Ubuntu<\/a>.<\/p>\n<h2>Segredos com ansible-vault<\/h2>\n<p>A senha do banco n\u00e3o pode ficar em YAML aberto no Git. Guarde-a em <code>group_vars\/web\/vault.yml<\/code> e criptografe o arquivo:<\/p>\n<pre><code class=\"language-bash\">cat &gt; group_vars\/web\/vault.yml &lt;&lt;'EOF'\n---\nvault_app_db_senha: \"troque-esta-senha\"\nEOF\nansible-vault encrypt group_vars\/web\/vault.yml\nhead -1 group_vars\/web\/vault.yml       # $ANSIBLE_VAULT;1.1;AES256\nansible-vault view group_vars\/web\/vault.yml\nansible-vault edit group_vars\/web\/vault.yml\n<\/code><\/pre>\n<p>A conven\u00e7\u00e3o de prefixar com <code>vault_<\/code> e referenciar a vari\u00e1vel em <code>main.yml<\/code> (<code>app_db_senha: \"{{ vault_app_db_senha }}\"<\/code>) deixa vis\u00edvel, sem abrir o cofre, quais vari\u00e1veis existem e de onde v\u00eam.<\/p>\n<p>Para um valor avulso, sem criar arquivo, use <code>encrypt_string<\/code> e cole o resultado direto no YAML:<\/p>\n<pre><code class=\"language-bash\">ansible-vault encrypt_string 'meu-token' --name api_token\n<\/code><\/pre>\n<p>Uma pegadinha que aparece no primeiro uso: <strong>o Ansible carrega o <code>vault.yml<\/code> para qualquer play que toque o grupo<\/strong>, mesmo que nenhuma tarefa use a senha. Rodar o playbook sem informar a senha falha antes da primeira tarefa:<\/p>\n<pre><code class=\"language-bash\">[ERROR]: Attempting to decrypt but no vault secrets found.\n<\/code><\/pre>\n<p>Informe a senha com <code>--ask-vault-pass<\/code> ou aponte para um arquivo fora do reposit\u00f3rio, com permiss\u00e3o <code>600<\/code>. A vari\u00e1vel de ambiente evita repetir a op\u00e7\u00e3o em todo comando e n\u00e3o quebra o projeto de quem n\u00e3o tem o arquivo:<\/p>\n<pre><code class=\"language-bash\">export ANSIBLE_VAULT_PASSWORD_FILE=~\/.config\/ansible\/vault-pass\n<\/code><\/pre>\n<p>Colocar <code>vault_password_file<\/code> no <code>ansible.cfg<\/code> tamb\u00e9m funciona, mas a\u00ed o playbook falha em qualquer m\u00e1quina onde esse arquivo n\u00e3o exista.<\/p>\n<h2>Primeira execu\u00e7\u00e3o e idempot\u00eancia<\/h2>\n<p>Antes de aplicar, valide a sintaxe e liste o que vai rodar:<\/p>\n<pre><code class=\"language-bash\">ansible-playbook site.yml --syntax-check\nansible-playbook site.yml --list-tasks\nansible-playbook site.yml\n<\/code><\/pre>\n<p>O resumo da primeira execu\u00e7\u00e3o no laborat\u00f3rio:<\/p>\n<pre><code class=\"language-bash\">RUNNING HANDLER [Testar a configura\u00e7\u00e3o do Nginx] ****\nok: [web1]\nok: [web2]\n\nRUNNING HANDLER [Recarregar o Nginx] ****\nchanged: [web1]\nchanged: [web2]\n\nPLAY RECAP ****\ndb1   : ok=4    changed=3    unreachable=0    failed=0\nweb1  : ok=17   changed=13   unreachable=0    failed=0\nweb2  : ok=17   changed=13   unreachable=0    failed=0\n<\/code><\/pre>\n<p>Agora rode de novo. Na segunda vez nada muda, e os handlers nem aparecem, porque nenhuma tarefa notificou:<\/p>\n<pre><code class=\"language-bash\">db1   : ok=4    changed=0    unreachable=0    failed=0\nweb1  : ok=15   changed=0    unreachable=0    failed=0\nweb2  : ok=15   changed=0    unreachable=0    failed=0\n<\/code><\/pre>\n<p>Isso \u00e9 <strong>idempot\u00eancia<\/strong>: o Ansible s\u00f3 age quando o estado real difere do declarado. Por isso d\u00e1 para rodar o mesmo playbook toda noite, e o <code>changed<\/code> diferente de zero passa a ser um alerta de que algu\u00e9m mexeu no servidor fora do Ansible.<\/p>\n<p>No servidor, o resultado \u00e9 o esperado: <code>ufw status verbose<\/code> mostra <code>Default: deny (incoming)<\/code> com 22, 80 e 443 liberados nos servidores web e s\u00f3 o OpenSSH no banco, e o <code>\/etc\/meuapp\/app.env<\/code> est\u00e1 com <code>-rw------- root root<\/code>.<\/p>\n<h2>Simular antes de aplicar: <code>--check --diff<\/code><\/h2>\n<p>Mudou o template? Veja o efeito antes, em um \u00fanico servidor e s\u00f3 na parte web:<\/p>\n<pre><code class=\"language-bash\">ansible-playbook site.yml --tags web --limit web1 --check --diff\n<\/code><\/pre>\n<pre><code class=\"language-diff\">TASK [Configurar o site] ****\n--- before: \/etc\/nginx\/sites-available\/exemplo.com.br.conf\n+++ after: ...\/site.conf.j2\n@@ -3,7 +3,7 @@\n     listen 80;\n     server_name exemplo.com.br www.exemplo.com.br;\n     root \/var\/www\/exemplo.com.br;\n-    index index.html;\n+    index index.html index.htm;\n<\/code><\/pre>\n<p>O modo check n\u00e3o altera nada no servidor. As <code>tags<\/code> que colocamos em cada play (<code>base<\/code> e <code>web<\/code>) permitem rodar s\u00f3 uma parte do projeto, e o <code>--limit<\/code> restringe a um host ou grupo. Juntos, servem para testar uma mudan\u00e7a em uma m\u00e1quina antes das outras.<\/p>\n<h2>Atualiza\u00e7\u00e3o em ondas (rolling update)<\/h2>\n<p>Com servidores atr\u00e1s de um balanceador, ningu\u00e9m quer atualizar e reiniciar todos ao mesmo tempo. O <code>atualiza.yml<\/code> atualiza um servidor por vez, reinicia s\u00f3 se o sistema pedir e confere se o site voltou antes de passar ao pr\u00f3ximo:<\/p>\n<pre><code class=\"language-yaml\">---\n- name: Atualizar a frota web sem derrubar tudo\n  hosts: web\n  become: true\n  serial: 1\n  max_fail_percentage: 0\n  tasks:\n    - name: Atualizar os pacotes\n      ansible.builtin.apt:\n        upgrade: dist\n        update_cache: true\n\n    - name: Verificar se o sistema pede rein\u00edcio\n      ansible.builtin.stat:\n        path: \/var\/run\/reboot-required\n      register: reinicio\n\n    - name: Reiniciar se for preciso\n      ansible.builtin.reboot:\n        reboot_timeout: 600\n      when: reinicio.stat.exists\n\n    - name: Conferir se o site responde antes de seguir para o pr\u00f3ximo\n      ansible.builtin.uri:\n        url: \"http:\/\/{{ ansible_host }}\/\"\n        status_code: 200\n      delegate_to: localhost\n      become: false\n      register: saude\n      retries: 5\n      delay: 3\n      until: saude.status == 200\n<\/code><\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" alt=\"Diagrama do rolling update com Ansible: o n\u00f3 de controle atualiza web1, reinicia se preciso e checa HTTP 200 antes de seguir para web2; se a checagem falha, a onda para e web2 n\u00e3o \u00e9 tocado\" src=\"\/wp-content\/uploads\/2026\/09\/ansible-rolling-update.webp\" width=\"1200\" height=\"640\" \/><\/p>\n<ul>\n<li><code>serial: 1<\/code> processa um host por vez. Aceita tamb\u00e9m porcentagem (<code>serial: \"25%\"<\/code>) ou uma lista que cresce, como <code>[1, 5, \"50%\"]<\/code>.<\/li>\n<li><code>max_fail_percentage: 0<\/code> interrompe tudo na primeira falha.<\/li>\n<li>A checagem roda no n\u00f3 de controle (<code>delegate_to: localhost<\/code>), do mesmo ponto de vista de quem acessa o site, e tenta cinco vezes antes de desistir.<\/li>\n<\/ul>\n<p>Lembre do vault: como <code>atualiza.yml<\/code> tamb\u00e9m toca o grupo <code>web<\/code>, ele precisa da senha mesmo sem usar nenhum segredo.<\/p>\n<p>No laborat\u00f3rio, web1 e web2 foram atualizados e reiniciados um depois do outro. Para ver a prote\u00e7\u00e3o funcionar, paramos o Nginx de web1 e rodamos de novo:<\/p>\n<pre><code class=\"language-bash\">FAILED - RETRYING: [web1 -&gt; localhost]: Conferir se o site responde antes de seguir para o pr\u00f3ximo (4 retries left).\n...\nfatal: [web1 -&gt; localhost]: FAILED! =&gt; {\"attempts\": 5, \"changed\": false, \"msg\": \"Status code was -1 and not [200]: Connection failure: [Errno 104] Connection reset by peer\", \"status\": -1, ...}\n\nNO MORE HOSTS LEFT ****\n<\/code><\/pre>\n<p>O web2 nem foi tocado e seguiu no ar. Uma nova execu\u00e7\u00e3o do <code>site.yml<\/code> religou o Nginx em web1, porque a tarefa <code>state: started<\/code> corrige o desvio.<\/p>\n<h2>Comandos r\u00e1pidos (ad-hoc)<\/h2>\n<p>Nem tudo precisa de playbook. Para uma a\u00e7\u00e3o pontual, use o comando <code>ansible<\/code> com um m\u00f3dulo:<\/p>\n<pre><code class=\"language-bash\"># quanto tempo cada servidor est\u00e1 no ar\nansible producao -a \"uptime -p\"\n\n# reiniciar o Nginx nos servidores web\nansible web -b -m ansible.builtin.systemd_service -a \"name=nginx state=restarted\"\n\n# espa\u00e7o em disco no banco\nansible db -a \"df -h \/\"\n<\/code><\/pre>\n<p>Se a a\u00e7\u00e3o virar rotina, transforme-a em tarefa de playbook: a\u00ed ela ganha hist\u00f3rico no Git e idempot\u00eancia.<\/p>\n<h2>Qualidade: ansible-lint<\/h2>\n<p>O <a href=\"https:\/\/ansible.readthedocs.io\/projects\/lint\/\" rel=\"noopener\" target=\"_blank\">ansible-lint<\/a> aponta m\u00f3dulos sem o nome completo (FQCN), permiss\u00f5es de arquivo impl\u00edcitas, tarefas sem nome e outros problemas que viram bug em produ\u00e7\u00e3o. Os dois playbooks deste post passam no perfil <code>production<\/code>, o mais rigoroso:<\/p>\n<pre><code class=\"language-bash\">pipx install ansible-lint\nansible-lint site.yml atualiza.yml\n<\/code><\/pre>\n<p>Coloque o lint no CI. Assim, o erro aparece no pull request, antes de chegar a um servidor.<\/p>\n<h2>Pr\u00f3ximos passos<\/h2>\n<p>Quando o <code>site.yml<\/code> crescer, quebre-o em <strong>roles<\/strong>: uma para a base, outra para o Nginx, outra para a aplica\u00e7\u00e3o. Um exemplo completo de role, com testes e CI, \u00e9 o <a href=\"https:\/\/github.com\/jniltinho\/ansible-linux-users\" rel=\"noopener\" target=\"_blank\">ansible-linux-users no GitHub<\/a>, explicado passo a passo em <a href=\"\/2026\/09\/usuarios-linux-ansible-ssh-sudo-git\/\">usu\u00e1rios no Linux com Ansible: chaves SSH, sudo e hist\u00f3rico no Git<\/a>. Para ir al\u00e9m dos m\u00f3dulos usados aqui, a refer\u00eancia \u00e9 a <a href=\"https:\/\/docs.ansible.com\/\" rel=\"noopener\" target=\"_blank\">documenta\u00e7\u00e3o oficial do Ansible<\/a>. E se automa\u00e7\u00e3o \u00e9 o seu rumo de carreira, o Ansible \u00e9 pe\u00e7a central da <a href=\"\/2026\/09\/certificacao-lpi-devops\/\">certifica\u00e7\u00e3o LPI DevOps Tools Engineer<\/a>.<\/p>\n<p>Comece automatizando uma tarefa que voc\u00ea repete toda semana. Em um m\u00eas, voc\u00ea n\u00e3o vai lembrar como vivia sem.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Proyecto Ansible completo y probado: inventario con grupos, ufw, Nginx con template y handler que prueba la configuraci\u00f3n, contrase\u00f1a en ansible-vault y rolling update que se detiene solo si un servidor no vuelve.<\/p>","protected":false},"author":0,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[46,2,120],"tags":[121,122,47,72],"class_list":["post-174","post","type-post","status-publish","format-standard","hentry","category-devops","category-linux","category-servidores","tag-ansible","tag-automacao","tag-devops","tag-servidores"],"_links":{"self":[{"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/posts\/174","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/comments?post=174"}],"version-history":[{"count":10,"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/posts\/174\/revisions"}],"predecessor-version":[{"id":1747,"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/posts\/174\/revisions\/1747"}],"wp:attachment":[{"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/media?parent=174"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/categories?post=174"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/tags?post=174"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}