Domine Comandos Ansible Ad-Hoc: Guia Completo com Exemplos!

Foto do autor

By luis

No universo do Ansible, os comandos ad-hoc surgem como ferramentas práticas para a execução imediata de tarefas, dispensando a necessidade de arquivos de configuração persistentes. Este artigo tem como objetivo explorar o funcionamento dos comandos ad-hoc no Ansible.

Existem inúmeras situações no Ansible onde a criação de um playbook específico se mostra desnecessária. Nesses casos, a solução ideal reside na utilização de um comando ad-hoc, que consiste em uma linha de código capaz de realizar uma ação única em um ou mais hosts de destino. Esses comandos, acessíveis em /usr/bin/ansible, agilizam a execução de tarefas pontuais.

A versatilidade dos comandos ad-hoc se manifesta em diversas operações, como testar a conectividade dos hosts através do ping, transferir arquivos, reiniciar servidores ou instalar pacotes. Abaixo, apresentamos uma seleção dos comandos ad-hoc mais essenciais para o seu dia a dia com Ansible.

Comandos Essenciais

O comando ad-hoc a seguir emprega o módulo “ping” para verificar a disponibilidade de todos os hosts listados no arquivo de inventário. A opção -m é utilizada para especificar o módulo a ser executado.

    [email protected]:/home/etechpt.com# ansible all -m ping
    node1 | SUCCESS => {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/bin/python"
        },
        "changed": false,
        "ping": "pong"
    }
  

O comando abaixo demonstra a utilização do módulo “setup” em um grupo específico de hosts, denominado “Cliente”, presente no arquivo de inventário /etc/ansible/hosts. O filtro aplicado retorna informações sobre a distribuição do sistema operacional.

    [email protected]:/home/etechpt.com# ansible Client -m setup -a "filter=ansible_distribution*"
    node1 | SUCCESS => {
        "ansible_facts": {
            "ansible_distribution": "Ubuntu",
            "ansible_distribution_file_parsed": true,
            "ansible_distribution_file_path": "/etc/os-release",
            "ansible_distribution_file_variety": "Debian",
            "ansible_distribution_major_version": "18",
            "ansible_distribution_release": "cosmic",
            "ansible_distribution_version": "18.10",
            "discovered_interpreter_python": "/usr/bin/python"
        },
        "changed": false
    }
  

Para solicitar a autenticação via senha SSH durante a execução do comando, basta adicionar a opção --ask-pass ao final. Após a execução, o sistema solicitará a senha SSH.

    [email protected]:/home/etechpt.com# ansible Client -m ping --ask-pass
    SSH password:
    node1 | SUCCESS => {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/bin/python"
        },
        "changed": false,
        "ping": "pong"
    }
  

O comando seguinte exemplifica como executar comandos ad-hoc como um usuário não root com privilégios administrativos. A opção --become concede os privilégios de root, enquanto -K solicita a senha necessária.

    [email protected]:/home/etechpt.com# ansible Client -m shell -a 'fdisk -l' -u etechpt.com --become -K
    BECOME password:
    node1 | CHANGED | rc=0 >>
    Disk /dev/loop0: 14.5 MiB, 15208448 bytes, 29704 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes


    Disk /dev/loop2: 42.1 MiB, 44183552 bytes, 86296 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes


    Disk /dev/loop3: 149.9 MiB, 157184000 bytes, 307000 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes


    Disk /dev/loop5: 140.7 MiB, 147501056 bytes, 288088 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes


    Disk /dev/loop6: 151.2 MiB, 158584832 bytes, 309736 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes


    Disk /dev/loop7: 14.8 MiB, 15458304 bytes, 30192 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes


    Disk /dev/sda: 500 GiB, 536870912000 bytes, 1048576000 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: dos
    Disk identifier: 0xcef957f5

    Device     Boot     Start        End   Sectors   Size Id Type
    /dev/sda1            2048  462639103 462637056 220.6G 83 Linux
    /dev/sda2  *    462639104  464592895   1953792   954M 83 Linux
    /dev/sda3       464592896  482168831  17575936   8.4G 82 Linux swap / Solaris
    /dev/sda4       482168832 1048573951 566405120 270.1G 83 Linux


    Disk /dev/loop8: 4 MiB, 4218880 bytes, 8240 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
  

O comando ad-hoc a seguir demonstra o reinício do sistema, com a opção -f que define o número de processos paralelos.

    [email protected]:/home/etechpt.com# ansible Client -a "/sbin/reboot" -f 1
  

Transferência de Arquivos

Este comando ad-hoc exemplifica como copiar um arquivo de origem para um destino, em um grupo de hosts (Cliente) especificado no arquivo de inventário. Após a inserção da senha, o parâmetro change exibirá o valor “true”, confirmando a transferência bem-sucedida do arquivo para o destino.

    [email protected]:/home/etechpt.com# ansible Client -m copy -a 'src=/home/etechpt.com/nginx.yml dest=/home/etechpt.com/Desktop/ owner=root mode=0644' -u root --become -K
    BECOME password:
    node1 | CHANGED => {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/bin/python"
        },
        "changed": true,
        "checksum": "5631822866afd5f19b928edb3ba018385df22dd3",
        "dest": "/home/etechpt.com/Desktop/nginx.yml",
        "gid": 0,
        "group": "root",
        "md5sum": "0d6ffe1069fc25ad4f8ad700277c4634",
        "mode": "0644",
        "owner": "root",
        "size": 280,
        "src": "/root/.ansible/tmp/ansible-tmp-1562253463.3-214622150088155/source",
        "state": "file",
        "uid": 0
    }
  

O comando seguinte verifica a efetividade da operação de cópia, confirmando a presença do arquivo no destino especificado.

    [email protected]:/home/etechpt.com# ls Desktop/

    nginx.yml
  

O diretório “example” é criado para a execução do módulo “fetch” no próximo comando ad-hoc.

    [email protected]:/home/etechpt.com# mkdir example

    [email protected]:/home/etechpt.com# ls

    Desktop  Documents  example  examples.desktop  nginx_new.yml  nginx.yml
  

O comando ad-hoc abaixo demonstra como baixar um arquivo de um host para o nó de controle Ansible. Neste exemplo, o módulo “fetch” é utilizado para transferir o arquivo /etc/sudoers.d/nginx.yml do servidor “node1” para um diretório local.

    [email protected]:/home/etechpt.com# ansible node1 -m fetch -a 'src=/etc/sudoers.d/nginx.yml dest=/home/etechpt.com/example/ flat=yes'

    node1 | SUCCESS => {

    "changed": false,

    "checksum": "5631822866afd5f19b928edb3ba018385df22dd3",

    "dest": "/home/etechpt.com/example/nginx.yml",

    "file": "/etc/sudoers.d/nginx.yml",

    "md5sum": "0d6ffe1069fc25ad4f8ad700277c4634"

    }
  

Confirmação da transferência do arquivo para o destino indicado.

    [email protected]:/home/etechpt.com# ls example

    nginx.yml
  

Gerenciamento de Pacotes

O comando abaixo demonstra a instalação do pacote “nginx” em um grupo de hosts (Cliente) usando o módulo “apt”.

    [email protected]:/home/etechpt.com# ansible Client -m apt -a 'name=nginx state=latest' --become

    node1 | SUCCESS => {

    "ansible_facts": {

    "discovered_interpreter_python": "/usr/bin/python"

    },

    "cache_update_time": 1562411227,

    "cache_updated": false,

    "changed": false

    }
  

O comando a seguir ilustra a remoção do pacote “nginx” em um grupo de hosts (Cliente), através do módulo “apt”, incluindo a eliminação de todas as configurações relacionadas.

    [email protected]:/home/etechpt.com# ansible Client -m apt -a 'name=nginx state=absent purge=yes' --become

    node1 | CHANGED => {

    "ansible_facts": {

    "discovered_interpreter_python": "/usr/bin/python"

    },

    "changed": true,

    "stderr": "",

    "stderr_lines": [],

    "stdout": "Reading package lists...nBuilding dependency tree...nReading state information...nThe following packages were automatically installed and are no longer required:n  libnginx-mod-http-geoip libnginx-mod-http-image-filtern  libnginx-mod-http-xslt-filter libnginx-mod-mail libnginx-mod-streamn  nginx-common nginx-corenUse 'sudo apt autoremove' to remove them.nThe following packages will be REMOVED:n  nginx*n0 upgraded, 0 newly installed, 1 to remove and 241 not upgraded.nAfter this operation, 44.0 kB disk space will be freed.n(Reading database ... r(Reading database ... 5%r(Reading database ... 10%r(Reading database ... 15%r(Reading database ... 20%r(Reading database ... 25%r(Reading database ... 30%r(Reading database ... 35%r(Reading database ... 40%r(Reading database ... 45%r(Reading database ... 50%r(Reading database ... 55%r(Reading database ... 60%r(Reading database ... 65%r(Reading database ... 70%r(Reading database ... 75%r(Reading database ... 80%r(Reading database ... 85%r(Reading database ... 90%r(Reading database ... 95%r(Reading database ... 100%r(Reading database ... 180191 files and directories currently installed.)rnRemoving nginx (1.15.5-0ubuntu2.1) ...rn",

    "stdout_lines": [

    "Reading package lists...",

    "Building dependency tree...",

    "Reading state information...",

    "The following packages were automatically installed and are no longer required:",

    "  libnginx-mod-http-geoip libnginx-mod-http-image-filter",

    "  libnginx-mod-http-xslt-filter libnginx-mod-mail libnginx-mod-stream",

    "  nginx-common nginx-core",

    "Use 'sudo apt autoremove' to remove them.",

    "The following packages will be REMOVED:",

    "  nginx*",

    "0 upgraded, 0 newly installed, 1 to remove and 241 not upgraded.",

    "After this operation, 44.0 kB disk space will be freed.",

    "(Reading database ... ",

    "(Reading database ... 5%",

    "(Reading database ... 10%",

    "(Reading database ... 15%",

    "(Reading database ... 20%",

    "(Reading database ... 25%",

    "(Reading database ... 30%",

    "(Reading database ... 35%",

    "(Reading database ... 40%",

    "(Reading database ... 45%",

    "(Reading database ... 50%",

    "(Reading database ... 55%",

    "(Reading database ... 60%",

    "(Reading database ... 65%",

    "(Reading database ... 70%",

    "(Reading database ... 75%",

    "(Reading database ... 80%",

    "(Reading database ... 85%",

    "(Reading database ... 90%",

    "(Reading database ... 95%",

    "(Reading database ... 100%",

    "(Reading database ... 180191 files and directories currently installed.)",

    "Removing nginx (1.15.5-0ubuntu2.1) ..."

    ]

    }
  

Gerenciamento de Serviços

Este comando ad-hoc utiliza o módulo “service” para iniciar o serviço “nginx” no host. O valor do parâmetro “state” deve ser definido como “started”.

    [email protected]:/home/etechpt.com# ansible Client -m service -a 'name=nginx state=started enabled=yes' --become

    node1 | SUCCESS => {

    "ansible_facts": {

    "discovered_interpreter_python": "/usr/bin/python"

    },

    "changed": false,

    "enabled": true,

    "name": "nginx",

    "state": "started",

    "status": {

    "ActiveEnterTimestamp": "Sat 2019-07-06 08:28:02 EDT",

    "ActiveEnterTimestampMonotonic": "31411371",

    "ActiveExitTimestampMonotonic": "0",

    "ActiveState": "active",

    "After": "sysinit.target system.slice systemd-journald.socket basic.target network.target",

    "AllowIsolate": "no",

    "AmbientCapabilities": "",

    "AssertResult": "yes",

    "AssertTimestamp": "Sat 2019-07-06 08:27:59 EDT",

    "AssertTimestampMonotonic": "27694868",

    "Before": "multi-user.target shutdown.target",

    "BlockIOAccounting": "no",

    "BlockIOWeight": "[not set]",

    "CapabilityBoundingSet": "cap_chown cap_dac_override cap_dac_read_search cap_fowner cap_fsetid cap_kill cap_setgid cap_setuid cap_setpcap cap_linux_immutable cap_net_bind_service cap_net_broadcast cap_net_admin cap_net_raw cap_ipc_lock cap_ipc_owner cap_sys_module cap_sys_rawio cap_sys_chroot cap_sys_ptrace cap_sys_pacct cap_sys_admin cap_sys_boot cap_sys_nice cap_sys_resource cap_sys_time cap_sys_tty_config cap_mknod cap_lease cap_audit_write cap_audit_control cap_setfcap cap_mac_override cap_mac_admin cap_syslog cap_wake_alarm cap_block_suspend",

    "CollectMode": "inactive",

    "ConditionResult": "yes",

    "ConditionTimestamp": "Sat 2019-07-06 08:27:59 EDT",

    "ConditionTimestampMonotonic": "27694867",

    "ConfigurationDirectoryMode": "0755",

    "Conflicts": "shutdown.target",

    "ControlGroup": "/system.slice/nginx.service",

    "ControlPID": "0",

    "ExecMainStartTimestamp": "Sat 2019-07-06 08:28:02 EDT",

    "ExecMainStartTimestampMonotonic": "31411353",

    "ExecMainStatus": "0",

    "ExecReload": "{ path=/usr/sbin/nginx ; argv[]=/usr/sbin/nginx -g daemon on; master_process on; -s reload ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }",

    "ExecStart": "{ path=/usr/sbin/nginx ; argv[]=/usr/sbin/nginx -g daemon on; master_process on; ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }",

    "ExecStartPre": "{ path=/usr/sbin/nginx ; argv[]=/usr/sbin/nginx -t -q -g daemon on; master_process on; ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }",

    "ExecStop": "{ path=/sbin/start-stop-daemon ; argv[]=/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid ; ignore_errors=yes ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }",

    "FailureAction": "none",

    "FileDescriptorStoreMax": "0",

    "FragmentPath": "/lib/systemd/system/nginx.service",

    "GID": "[not set]",

    "GuessMainPID": "yes",

    "IOAccounting": "no",

    "IOSchedulingClass": "0",

    "IOSchedulingPriority": "0",

    "IOWeight": "[not set]",

    }

    }
  

O comando a seguir executa o módulo “service” para interromper o serviço “nginx” no host. Nesse caso, o valor do parâmetro “state” é “stopped”.

    [email protected]:/home/etechpt.com# ansible Client -m service -a 'name=nginx state=stopped' --become

    node1 | CHANGED => {

    "ansible_facts": {

    "discovered_interpreter_python": "/usr/bin/python"

    },

    "changed": true,

    "name": "nginx",

    "state": "stopped",

    "status": {

    "ActiveEnterTimestamp": "Sat 2019-07-06 08:28:02 EDT",

    "ActiveEnterTimestampMonotonic": "31411371",

    "ActiveExitTimestampMonotonic": "0",

    "ActiveState": "active",

    "After": "sysinit.target system.slice systemd-journald.socket basic.target network.target",

    "AllowIsolate": "no",

    "AmbientCapabilities": "",

    "AssertResult": "yes",

    "AssertTimestamp": "Sat 2019-07-06 08:27:59 EDT",

    "AssertTimestampMonotonic": "27694868",

    "Before": "multi-user.target shutdown.target",

    "BlockIOAccounting": "no",

    "BlockIOWeight": "[not set]",

    "CPUAccounting": "no",

    "CPUQuotaPerSecUSec": "infinity",

    "CanReload": "yes",

    "CanStart": "yes",

    "CanStop": "yes",

    "CapabilityBoundingSet": "cap_chown cap_dac_override cap_dac_read_search cap_fowner cap_fsetid cap_kill cap_setgid cap_setuid cap_setpcap cap_linux_immutable cap_net_bind_service cap_net_broadcast cap_net_admin cap_net_raw cap_ipc_lock cap_ipc_owner cap_sys_module cap_sys_rawio cap_sys_chroot cap_sys_ptrace cap_sys_pacct cap_sys_admin cap_sys_boot cap_sys_nice cap_sys_resource cap_sys_time cap_sys_tty_config cap_mknod cap_lease cap_audit_write cap_audit_control cap_setfcap cap_mac_override cap_mac_admin cap_syslog cap_wake_alarm cap_block_suspend",

    "CollectMode": "inactive",

    "ConditionResult": "yes",

    "ConditionTimestamp": "Sat 2019-07-06 08:27:59 EDT",

    "ConditionTimestampMonotonic": "27694867",

    "ConfigurationDirectoryMode": "0755",

    "Conflicts": "shutdown.target",

    "ControlGroup": "/system.slice/nginx.service",

    "ControlPID": "0",

    "DefaultDependencies": "yes",

    "Delegate": "no",

    "Description": "A high performance web server and a reverse proxy server",

    "DevicePolicy": "auto",

    "Documentation": "man:nginx(8)",

    "DynamicUser": "no",

    }

    }
  

Verificação do Sistema

O comando ad-hoc a seguir emprega o módulo “shell” para verificar o espaço disponível na partição raiz do disco.

    [email protected]:/home/etechpt.com# ansible Client -m shell -a 'df -h /dev/sda2' --become

    node1 | CHANGED | rc=0 >>

    Filesystem Size Used Avail Use% Mounted on

    /dev/sda2 923M 113M 748M 14% /boot
  

Este comando, também utilizando o módulo “shell”, verifica a memória RAM livre no host.

    [email protected]:/home/etechpt.com# ansible Client -m shell -a 'free -m' --become

    node1 | CHANGED | rc=0 >>

    total used free shared buff/cache available

    Mem: 5101 854 2760 27 1487 3947

    Swap: 8581 0 8581
  

Comando para verificar o tempo de atividade de cada servidor em execução.

    [email protected]:/home/etechpt.com# ansible Client -a "uptime"

    node1 | CHANGED | rc=0 >>

    11:31:17 up 1 day,  2:40,  2 users,  load average: 0.23, 0.05, 0.02
  

Coleta de Informações (Facts)

O comando ad-hoc seguinte, com o módulo “setup”, obtém informações detalhadas do sistema, incluindo todas as variáveis disponíveis.

    [email protected]:/home/etechpt.com# ansible all -m setup

    node1 | SUCCESS => {

    "ansible_facts": {

    "ansible_all_ipv4_addresses": [

    "172.17.0.1",

    "10.0.2.15"

    ],

    "ansible_all_ipv6_addresses": [

    "fe80::763e:c0b4:14df:b273"

    ],

    "ansible_apparmor": {

    "status": "enabled"

    },

    "ansible_architecture": "x86_64",

    "ansible_bios_date": "12/01/2006",

    "ansible_bios_version": "VirtualBox",

    "ansible_cmdline": {

    "BOOT_IMAGE": "/vmlinuz-4.18.0-25-generic",

    "quiet": true,

    "ro": true,

    "root": "UUID=5f85d8b7-0ab2-48c9-9e6e-4ecfbcbdaa83",

    "splash": true

    },

    "ansible_date_time": {

    "date": "2019-07-07",

    "day": "07",

    "epoch": "1562525628",

    "hour": "14",

    "iso8601": "2019-07-07T18:53:48Z",

    "iso8601_basic": "20190707T145348850596",

    "iso8601_basic_short": "20190707T145348",

    "iso8601_micro": "2019-07-07T18:53:48.850697Z",

    "minute": "53",

    "month": "07",

    "second": "48",

    "time": "14:53:48",

    "tz": "EDT",

    "tz_offset": "-0400",

    "weekday": "Sunday",

    "weekday_number": "0",

    "weeknumber": "26",

    "year": "2019"

    },

    "ansible_default_ipv4": {

    "address": "10.0.2.15",

    "alias": "enp0s3",

    "broadcast": "10.0.2.255",

    "gateway": "10.0.2.2",

    "interface": "enp0s3",

    "macaddress": "08:00:27:68:64:9a",

    "mtu": 1500,

    "netmask": "255.255.255.0",

    "network": "10.0.2.0",

    "type": "ether"

    },

    "ansible_default_ipv6": {},

    "ansible_device_links": {

    "ids": {

    "sda": [

    "ata-VBOX_HARDDISK_VB3a0a2351-0b6c0ed5"

    ],

    "sda1": [

    "ata-VBOX_HARDDISK_VB3a0a2351-0b6c0ed5-part1"

    ],

    "sda2": [

    "ata-VBOX_HARDDISK_VB3a0a2351-0b6c0ed5-part2"

    ],

    "sda3": [

    "ata-VBOX_HARDDISK_VB3a0a2351-0b6c0ed5-part3"

    ],

    "sda4": [

    "ata-VBOX_HARDDISK_VB3a0a2351-0b6c0ed5-part4"

    ],

    "sr0": [

    "ata-VBOX_CD-ROM_VB2-01700376"

    ]

    },

    "labels": {

    "sr0": [

    "VBox_GAs_6.0.2"

    ]

    },

    "masters": {},

    "uuids": {

    "sda1": [

    "5f85d8b7-0ab2-48c9-9e6e-4ecfbcbdaa83"

    ],

    "sda2": [

    "b8b7f87b-c3bf-48ed-a44c-f9b3ce0afbe5"

    ],

    "sda3": [

    "a6c77fa6-e292-4a0d-b21f-8804f1949bbd"

    ],

    "sda4": [

    "8207f970-4d9a-47db-a5d5-f620e5b17b7b"

    ],

    "sr0": [

    "2019-01-14-14-57-19-65"

    ]

    }

    },

    "ansible_devices": {

    "loop0": {

    "holders": [],

    "host": "",

    "links": {

    "ids": [],

    "labels": [],

    "masters": [],

    "uuids": []

    },

    "model": null,

    "partitions": {},

    "removable": "0",

    "rotational": "1",

    "sas_address": null,

    "sas_device_handle": null,

    "scheduler_mode": "none",

    "sectors": "29704",

    "sectorsize": "512",

    "size": "14.50 MB",

    "support_discard": "4096",

    "vendor": null,

    "virtual": 1

    },

    "loop1": {

    "holders": [],

    "host": "",

    "links": {

    "ids": [],

    "labels": [],

    "masters": [],

    "uuids": []

    },

    "model": null,

    "partitions": {},

    "removable": "0",

    "rotational": "1",

    "sas_address": null,

    "sas_device_handle": null,

    "scheduler_mode": "none",

    "sectors": "0",

    "sectorsize": "512",

    "size": "0.00 Bytes",

    "support_discard": "4096",

    "vendor": null,

    "virtual": 1

    },

    }
  

Este guia abordou os principais aspectos dos comandos ad-hoc no