Ansible Ad-Hoc


Ansible 命令入门

一、ansible

我们来深入的看下,ansible 命令的使用,语法如下:

ansible <pattern> -m <module_name> -a <arguments>

这里补充一个图示:

图片来源:Devops Junction,一家 IT 解决方案提供商,网站有很多 DevOps 技术栈的相关文章,非常推荐!

1.1 Patterns

首先看第一个参数,在Ansible中,Patterns 主要是用来匹配节点,应用特定配置,常见的使用方式:

1.1.1 通配符匹配主机

此前我们用过 all 关键字,其实还可以用 * 通配符

bash
$ ansible '*' -m ping
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
bj-huawei-hecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

部分通配

bash
# 通配 ecs 节点
$ ansible '*ecs*' -m ping                         
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
bj-huawei-hecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

# 通配 server 主机组
$ ansible '*server*' -m ping
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
bj-huawei-hecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

1.1.2 匹配特定主机组

bash
$ ansible bj_server -m ping 
bj-huawei-hecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

1.1.3 匹配多个特定目标对象

语法:用:分割多个目标对象(组、节点)

匹配多个主机组

bash
$ ansible bj_server:sz_server -m ping
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
bj-huawei-hecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

匹配多个节点

bash
$ ansible sz-aliyun-ecs-1:bj-huawei-hecs-1 -m ping
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
bj-huawei-hecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

匹配多个节点(带通配符)

bash
$ ansible 'bj-huawei-hecs-1:sz*' -m ping
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
bj-huawei-hecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

匹配多个组(带通配符)

bash
$ ansible 'ecs:*_server' -m ping                     
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
bj-huawei-hecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

1.1.4 取反排除特定目标对象

语法:!<target>

匹配 非 sz_server 组 的节点

bash
$ ansible '!sz_server' -m ping 
bj-huawei-hecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

匹配 ecs 组中非 bj_server 组 的节点

bash
$ ansible 'ecs:!bj_server' -m ping
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

1.1.5 取两个组的交集节点

语法:<group>:&<group>

匹配同属于 ecs 云主机组 与 prod 环境组 的节点

bash
$ ansible 'ecs:&prod' -m ping     
bj-huawei-hecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

再看个烧脑的,ecs:web_server:&prod:!maintenance,我们把它拆开来看:

  1. ecs: 匹配 ecs 组的节点:bj-huawei-hecs-1sz-aliyun-ecs-1
  2. web_server:&prod:匹配 同属于 web组 和 正式环境 的节点 bj-huawei-hecs-1
  3. :!maintenance:匹配 非维护状态的节点 bj-huawei-hecs-1
bash
$ ansible 'ecs:web_server:&prod:!maintenance' -m ping
bj-huawei-hecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

1.1.6 匹配分组中特定区间的节点

语法:group[n|n:m]

匹配 ecs 分组中第 1 个节点

bash
ansible 'ecs[0]' -m ping              
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

匹配 ecs 分组中第 0:2 区间内节点

bash
$ ansible 'ecs[0:2]' -m ping
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
bj-huawei-hecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

1.1.7 正则表达式匹配节点

语法:~

匹配 深圳区域阿里云 节点

bash
 ansible '~sz-aliyun.*' -m ping 
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

1.2 Ad-Hoc

什么是 ad-hoc?,一句话就可以讲清楚,“一次性的 Ansible 指令”

Ansible 提供两种方式去完成任务,

  1. ad-hoc:适用于解决一些简单,且临时的任务
  2. playbook:适用于处理较复杂的任务,且需要持久化的任务

ad-hoc 之于 playbook,等同 command 之于 bash scripts

在开始之前,我们先了解两个命令

  • 查看 ansible 支持那些模块

    bash
    $ ansible-doc -l      
    fortios_router_community_list                                 Configure community lists in Fortinet's FortiOS and FortiGate
    azure_rm_devtestlab_info                                      Get Azure DevTest Lab facts
    ecs_taskdefinition                                            register a task definition in ecs
    avi_alertscriptconfig                                         Module for setup of AlertScriptConfig Avi RESTful Object
    tower_receive                                                 Receive assets from Ansible Tower
    netapp_e_iscsi_target                                         NetApp E-Series manage iSCSI target configuration
    azure_rm_acs                                                  Manage an Azure Container Service(ACS) instance
    fortios_log_syslogd2_filter                                   Filters for remote system server in Fortinet's FortiOS and FortiGate             
    junos_rpc                                                     Runs an arbitrary RPC over NetConf on an Juniper JUNOS device                    
    na_elementsw_vlan                                             NetApp Element Software Manage VLAN
    pn_ospf                                                       CLI command to add/remove ospf protocol to a vRouter                             
    pn_snmp_vacm                                                  CLI command to create/modify/delete snmp-vacm
    cp_mgmt_service_sctp                                          Manages service-sctp objects on Check Point over Web Serv
  • 查看 ansible 模块参数列表

    bash
    $ ansible-doc -s shell
    - name: Execute shell commands on targets
      shell:
          chdir:                 # Change into this directory before running the command.
          cmd:                   # The command to run followed by optional arguments.
          creates:               # A filename, when it already exists, this step will *not* be run.
          executable:            # Change the shell used to execute the command. This expects an absolute path to the executable.
          free_form:             # The shell module takes a free form command to run, as a string. There is no actual parameter named 'free form'. See the examples on how to use this module.
          removes:               # A filename, when it does not exist, this step will *not* be run.
          stdin:                 # Set the stdin of the command directly to the specified value.
          stdin_add_newline:     # Whether to append a newline to stdin data.
          warn:                  # Whether to enable task warnings.

1.2.1 命令执行

command 模块

顾名思义,command 模块可以在远程主机执行命令,需要特别说明的,命令不是经由 shell 执行的,所以并不支持诸如:管道、重定向、逻辑操作符等,如果需要这些功能,可以用下面的 shell 模块

例1:执行特定命令

执行命令

bash
$ ansible 'ecs[0]' -m command -a "echo 123"                                                          
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
123
例2:在特定目录执行命令

执行命令

bash
$ ansible 'ecs[0]' -m command -a "chdir=/prodata ls"   
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
docker
gitbook
jupyterlab
leanote
mrdoc
scripts
web
wiz
例3:文件存在,则不执行命令
bash
$ ansible 'ecs[0]' -m command -a "creates=/tmp/testfile ls"                 
sz-aliyun-ecs-1 | SUCCESS | rc=0 >>
skipped, since /tmp/testfile exists
例4:文件不存在,则执行命令

执行命令

bash
$ ansible 'ecs[0]' -m command -a "removes=/tmp/testfile ls"
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
anki
data
install-release.sh
node_exporter-0.16.0.linux-amd64
node_exporter-0.16.0.linux-amd64.tar.gz
prometheus.tar.gz
prometheus-webhook-dingtalk-1.4.0.linux-amd64.tar.gz
sqlite-autoconf-3350500
sqlite-autoconf-3350500.tar.gz
shell 模块

Shell 模块同样是用来远程执行命令,但区别是 命令 会经由 shell 执行,所以最常用的就是它

例1:执行特定命令

执行命令

bash
$ ansible 'ecs[0]' -m shell -a "echo Hello > /tmp/testfile && cat /tmp/testfile"
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
Hello
例2:调用特定 shell 执行命令

执行命令

bash
$ ansible 'ecs[0]' -m shell -a 'executable=/bin/zsh echo zsh > /tmp/testfile && cat /tmp/testfile'
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
zsh
scrips 模块

必学的一个模块,不多说直接开始

创建一个测试脚本

bash
$ cat t1.sh   
#!/bin/bash
echo "This is t1 shell scripts"
例1:执行特定脚本

执行命令

bash
$ ansible 'ecs[0]' -m script -a "chdir=/tmp t1.sh"     
sz-aliyun-ecs-1 | CHANGED => {
    "changed": true, 
    "rc": 0, 
    "stderr": "Shared connection to sz-aliyun-ecs-1 closed.\r\n", 
    "stderr_lines": [
        "Shared connection to sz-aliyun-ecs-1 closed."
    ], 
    "stdout": "This is t1 shell scripts\r\n", 
    "stdout_lines": [
        "This is t1 shell scripts"
    ]
}
例2:文件存在,则不执行脚本

执行命令

bash
$ ansible 'ecs[0]' -m script -a "chdir=/tmp creates=/tmp/testfile t1.sh"
sz-aliyun-ecs-1 | SKIPPED
例3:文件存在,则不执行脚本

执行命令

bash
$ ansible 'ecs[0]' -m script -a "chdir=/tmp removes=/tmp/testfile t1.sh"
sz-aliyun-ecs-1 | CHANGED => {
    "changed": true, 
    "rc": 0, 
    "stderr": "Shared connection to sz-aliyun-ecs-1 closed.\r\n", 
    "stderr_lines": [
        "Shared connection to sz-aliyun-ecs-1 closed."
    ], 
    "stdout": "This is t1 shell scripts\r\n", 
    "stdout_lines": [
        "This is t1 shell scripts"
    ]
}

1.2.1 并行执行

串行执行任务
bash
$ ansible ecs -a 'date "+%H:%M:%S"' -f 1    
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
20:28:12
bj-huawei-hecs-1 | CHANGED | rc=0 >>
20:28:13

PS:当不指定-m 模块时,将使用 ansible 的默认模块 command,它不会通过 shell 进行处理,所以像 $HOME 和像<>|;将不工作

并行执行任务
bash
$ ansible ecs -a 'date "+%H:%M:%S"' -f 2
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
20:28:20
bj-huawei-hecs-1 | CHANGED | rc=0 >>
20:28:20

这里涉及到 2 个新的参数:

  • -a MODULE_ARGS, --args :模块参数 ``,目前个人理解为 所执行的命令及参数
  • -f FORKS, --forks:并行进程数,最多 fork 出几个子进程去执行任务,默认为 5
指明执行用户

有时,我们需要使用特殊的用户去运行程序,例如:安全场景要求下,我们需要用最小化权限的用户启动应用进程,针对这个需求,我们可以通过 -u 参数指明我们远程执行时使用那个用户

bash
$ ansible ecs -a 'id' -u lotus -k 
SSH password: 
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
uid=1001(lotus) gid=1001(lotus) groups=1001(lotus)
bj-huawei-hecs-1 | CHANGED | rc=0 >>
uid=1000(lotus) gid=1000(lotus) groups=1000(lotus)

又是一个新参数:-k

  • -k, --ask-pass:提示用户输入密码进行远程验证

根据官方文档的中文版本,尝试了下 --sudo 参数,目前看来是无法工作的

官方示例

bash
$ ansible atlanta -a "/usr/bin/foo" -u username --sudo [--ask-sudo-pass]

实验结果:

bash
$ ansible ecs -a 'id' -u lotus --sudo
usage: ansible [-h] [--version] [-v] [-b] [--become-method BECOME_METHOD]
               [--become-user BECOME_USER] [-K] [-i INVENTORY] [--list-hosts]
               [-l SUBSET] [-P POLL_INTERVAL] [-B SECONDS] [-o] [-t TREE] [-k]
               [--private-key PRIVATE_KEY_FILE] [-u REMOTE_USER]
               [-c CONNECTION] [-T TIMEOUT]
               [--ssh-common-args SSH_COMMON_ARGS]
               [--sftp-extra-args SFTP_EXTRA_ARGS]
               [--scp-extra-args SCP_EXTRA_ARGS]
               [--ssh-extra-args SSH_EXTRA_ARGS] [-C] [--syntax-check] [-D]
               [-e EXTRA_VARS] [--vault-id VAULT_IDS]
               [--ask-vault-pass | --vault-password-file VAULT_PASSWORD_FILES]
               [-f FORKS] [-M MODULE_PATH] [--playbook-dir BASEDIR]
               [-a MODULE_ARGS] [-m MODULE_NAME]
               pattern
ansible: error: unrecognized arguments: --sudo

1.2.2 文件操作

Ansible 支持以并行的方式发送文件到多台节点上,对应的功能模块为 copyfile

copy 模块发送文件
bash
$ ansible ecs -m copy -a "src=/etc/hosts dest=/tmp/hosts"
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "checksum": "188c9c72076e04a2ef823bf9dad28504469cd08c", 
    "dest": "/tmp/hosts", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "e4bdcda443cdcad751b0253aebf2aed7", 
    "mode": "0644", 
    "owner": "root", 
    "size": 290, 
    "src": "/root/.ansible/tmp/ansible-tmp-1632056206.08-10548-194351796284871/source", 
    "state": "file", 
    "uid": 0
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "checksum": "188c9c72076e04a2ef823bf9dad28504469cd08c", 
    "dest": "/tmp/hosts", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "e4bdcda443cdcad751b0253aebf2aed7", 
    "mode": "0644", 
    "owner": "root", 
    "size": 290, 
    "src": "/root/.ansible/tmp/ansible-tmp-1632056206.58-10550-120318189335979/source", 
    "state": "file", 
    "uid": 0
}

确认文件是否存在

bash
$ ansible ecs -a 'ls -l /tmp/hosts' -f 2
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
-rw-r--r-- 1 root root 290 Sep 19 20:56 /tmp/hosts
bj-huawei-hecs-1 | CHANGED | rc=0 >>
-rw-r--r-- 1 root root 290 Sep 19 20:56 /tmp/hosts
file 模块操作文件
变更元数据

通过 file模块,我们可以变更文件的属主/属组/权限等元数据信息

bash
$ ansible ecs -m file -a "dest=/tmp/hosts mode=600 owner=lotus group=lotus"
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "gid": 1001, 
    "group": "lotus", 
    "mode": "0600", 
    "owner": "lotus", 
    "path": "/tmp/hosts", 
    "size": 290, 
    "state": "file", 
    "uid": 1001
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "gid": 1000, 
    "group": "lotus", 
    "mode": "0600", 
    "owner": "lotus", 
    "path": "/tmp/hosts", 
    "size": 290, 
    "state": "file", 
    "uid": 1000
}

查看文件信息

bash
$ ansible ecs -a 'ls -l /tmp/hosts' -f 2                  
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
-rw------- 1 lotus lotus 290 Sep 19 20:56 /tmp/hosts
bj-huawei-hecs-1 | CHANGED | rc=0 >>
-rw------- 1 lotus lotus 290 Sep 19 20:56 /tmp/hosts
创建目录

通过参数 state=directory 表明我们要删除这个文件(目录)

bash
 ansible ecs -m file -a "dest=/tmp/ansible_temp_folder mode=755 owner=lotus group=lotus state=directory"
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "gid": 1001, 
    "group": "lotus", 
    "mode": "0755", 
    "owner": "lotus", 
    "path": "/tmp/ansible_temp_folder", 
    "size": 4096, 
    "state": "directory", 
    "uid": 1001
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "gid": 1000, 
    "group": "lotus", 
    "mode": "0755", 
    "owner": "lotus", 
    "path": "/tmp/ansible_temp_folder", 
    "size": 4096, 
    "state": "directory", 
    "uid": 1000
}

查看目录信息

bash
$ ansible ecs -a 'ls -la /tmp/ansible_temp_folder' -f 2
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
total 24
drwxr-xr-x   2 lotus lotus  4096 Sep 19 21:08 .
drwxrwxrwt. 30 root  root  20480 Sep 19 21:16 ..
bj-huawei-hecs-1 | CHANGED | rc=0 >>
total 24
drwxr-xr-x   2 lotus lotus  4096 Sep 19 21:08 .
drwxrwxrwt. 11 root  root  20480 Sep 19 21:16 ..
删除目录

通过参数 state=absent 表明我们要删除这个文件(目录)

bash
$ ansible ecs -m file -a "dest=/tmp/ansible_temp_folder state=absent"                                    
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "path": "/tmp/ansible_temp_folder", 
    "state": "absent"
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "path": "/tmp/ansible_temp_folder", 
    "state": "absent"
}
确认目录是否存在
bash
$ ansible ecs -a 'ls -la /tmp/ansible_temp_folder' -f 2              
sz-aliyun-ecs-1 | FAILED | rc=2 >>
ls: cannot access /tmp/ansible_temp_folder: No such file or directorynon-zero return code
bj-huawei-hecs-1 | FAILED | rc=2 >>
ls: cannot access /tmp/ansible_temp_folder: No such file or directorynon-zero return code
blockinfile 模块变更内容

blockinfile 模块可以帮助我们在指定的文件中插入”被标记的一段文本”,加标记是为了后续找到找到这段文本,然后修改或者删除它

相关参数一览:

参数 描述
path 指定要操作的文件
block 此参数用于指定我们想要操作的那”一段文本”,此参数有一个别名叫”content”,使用 content 或 block 的作用是相同的
marker 自定义开始结束标记,默认 ansible 会为文本前后添加开始结束标记 `# [BEGIN
state present(默认值)absent(删除内容)
insertafter 插入哪行的后面,可以用 正则EOF/BOF(End/Begin of File) 关键字 设定插入位置,默认插入文件末尾
insertbefore 插入哪行的前面,可以用 正则EOF/BOF(End/Begin of File) 关键字 设定插入位置
backup 更新时自动备份
create 操作文件并不存在时,是否自动创建

我们将 /etc/rc.d/rc.local 文件复制到 /tmp/ 目录中,以做测试

bash
$ ansible ecs -m shell -a "cp /etc/rc.d/rc.local /tmp/rc.local" 
sz-aliyun-ecs-1 | CHANGED | rc=0 >>

bj-huawei-hecs-1 | CHANGED | rc=0 >>
插入末尾

尝试在 /testdir/rc.local 文件尾部插入如下两行服务启动的命令

bash
$ ansible ecs -m blockinfile -a 'path=/tmp/rc.local block="systemctl start ntpd\nsystemctl start docker" '
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "msg": "Block inserted"
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "msg": "Block inserted"
}

查看文件内容

bash
$ ansible ecs -m shell -a "tail -5 /tmp/rc.local" 
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
touch /var/lock/subsys/local
# BEGIN ANSIBLE MANAGED BLOCK
systemctl start ntpd
systemctl start docker
# END ANSIBLE MANAGED BLOCK
bj-huawei-hecs-1 | CHANGED | rc=0 >>
touch /var/lock/subsys/local
# BEGIN ANSIBLE MANAGED BLOCK
systemctl start ntpd
systemctl start docker
# END ANSIBLE MANAGED BLOCK
定义内容标识

上面示例中,Ansible 会为添加的内容前后添加 “ANSIBLE MANAGED BLOCK” 标识,有时我们需要自己去定义附和需求的标识,如下:service <svcName> to start

bash
$ ansible ecs -m blockinfile -a 'path=/tmp/rc.local block="systemctl start vsftpd" marker="#{mark} service vsftpd to start"' 
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "msg": "Block inserted"
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "msg": "Block inserted"
}

查看文件内容

bash
$ ansible ecs -m shell -a "tail -5 /tmp/rc.local"
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
systemctl start docker
# END ANSIBLE MANAGED BLOCK
#BEGIN service vsftpd to start
systemctl start vsftpd
#END service vsftpd to start
bj-huawei-hecs-1 | CHANGED | rc=0 >>
systemctl start docker
# END ANSIBLE MANAGED BLOCK
#BEGIN service vsftpd to start
systemctl start vsftpd
#END service vsftpd to start
更新指定内容

blockinfile 模块是基于特定标记实现更新的,简单来说,如果某一标记不存在,则插入内容;如果某一标记存在,则更新内容,如下:

bash
$ ansible ecs -m blockinfile -a 'path=/tmp/rc.local block="systemctl start vsftpd-server" marker="#{mark} service vsftpd to start"'
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "msg": "Block inserted"
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "msg": "Block inserted"
}

查看文件内容

bash
$ ansible ecs -m shell -a "tail -5 /tmp/rc.local"     
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
systemctl start docker
# END ANSIBLE MANAGED BLOCK
#BEGIN service vsftpd to start
systemctl start vsftpd-server
#END service vsftpd to start
bj-huawei-hecs-1 | CHANGED | rc=0 >>
systemctl start docker
# END ANSIBLE MANAGED BLOCK
#BEGIN service vsftpd to start
systemctl start vsftpd-server
#END service vsftpd to start

vsftpd 更新为了 vsftpd-server

更新时备份
bash
$ ansible ecs -m blockinfile -a 'path=/tmp/rc.local block="backup..." marker="#{mark} test backup" insertafter=EOF backup=yes'
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "msg": "Block inserted"
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "msg": "Block inserted"
}

查看文件列表

bash
$ ansible ecs -m shell -a "ls -l /tmp/rc.local* | grep rc.local" 
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
-rw-r--r-- 1 root root 663 Sep 24 19:54 /tmp/rc.local
-rw-r--r-- 1 root root 617 Sep 24 19:49 /tmp/rc.local.4872.2021-09-24@19:54:23~
bj-huawei-hecs-1 | CHANGED | rc=0 >>
-rw-r--r-- 1 root root 663 Sep 24 19:54 /tmp/rc.local
-rw-r--r-- 1 root root 617 Sep 24 19:49 /tmp/rc.local.16633.2021-09-24@19:54:18~

查看文件内容

bash
$ ansible ecs -m shell -a "tail -3 /tmp/rc.local"                                                                             
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
#BEGIN test backup
backup...
#END test backup
bj-huawei-hecs-1 | CHANGED | rc=0 >>
#BEGIN test backup
backup...
#END test backup

查看备份文件

bash
$ ansible ecs -m shell -a "tail -3 /tmp/rc.local.*" 
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
# that this script will be executed during boot.

touch /var/lock/subsys/local
bj-huawei-hecs-1 | CHANGED | rc=0 >>
# that this script will be executed during boot.

touch /var/lock/subsys/local
删除指定内容

老套路,还是基于 state=absent 属性

bash
$ ansible ecs -m blockinfile -a 'path=/tmp/rc.local block="systemctl start vsftpd-server" marker="#{mark} service vsftpd to start" state=absent'
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "msg": "Block removed"
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "msg": "Block removed"
}

查看文件内容

bash
$ ansible ecs -m shell -a "tail -5 /tmp/rc.local"
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
touch /var/lock/subsys/local
# BEGIN ANSIBLE MANAGED BLOCK
systemctl start ntpd
systemctl start docker
# END ANSIBLE MANAGED BLOCK
bj-huawei-hecs-1 | CHANGED | rc=0 >>
touch /var/lock/subsys/local
# BEGIN ANSIBLE MANAGED BLOCK
systemctl start ntpd
systemctl start docker
# END ANSIBLE MANAGED BLOCK

OK,vsftpd 服务的内容删除掉了~

插入文首

通过 insertbefore=BOF参数值,可以实现插入指定内容到文件开头

文首 BOF(Begin Of File)、 文末 EOF(End Of File)

bash
$ ansible ecs -m blockinfile -a 'path=/tmp/rc.local block="systemctl start vsftpd-server" insertbefore=BOF marker="#{mark} temp test rc.local" '            
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "msg": "Block inserted"
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "msg": "Block inserted"
}

查看文件内容

bash
$ ansible ecs -m shell -a "head -5 /tmp/rc.local" 
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
#BEGIN temp test rc.local
systemctl start vsftpd-server
#END temp test rc.local
#!/bin/bash
# THIS FILE IS ADDED FOR COMPATIBILITY PURPOSES
bj-huawei-hecs-1 | CHANGED | rc=0 >>
#BEGIN temp test rc.local
systemctl start vsftpd-server
#END temp test rc.local
#!/bin/bash
# THIS FILE IS ADDED FOR COMPATIBILITY PURPOSES
插入正则匹配行后

某些场景下我们需要更精确的插入位置,这时可以使用 insertafter/insertbefore + 正则表达式 实现需求:

bash
$ ansible ecs -m blockinfile -a 'path=/tmp/rc.local block="systemctl start vsftpd-server" marker="#{mark} test regex" insertafter="^#!/bin/bash" '
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "msg": "Block inserted"
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "msg": "Block inserted"
}

查看文件内容

bash
$ ansible ecs -m shell -a "head -10 /tmp/rc.local" 
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
#BEGIN temp test rc.local
systemctl start vsftpd-server
#END temp test rc.local
#!/bin/bash
#BEGIN test regex
systemctl start vsftpd-server
#END test regex
...
bj-huawei-hecs-1 | CHANGED | rc=0 >>
#BEGIN temp test rc.local
systemctl start vsftpd-server
#END temp test rc.local
#!/bin/bash
#BEGIN test regex
systemctl start vsftpd-server
#END test regex
...
自动创建并插入

当插入内容时,如果目标文件不存在,自动执行创建在插入内容

bash
$ ansible ecs -m blockinfile -a 'path=/tmp/testfile7 block="backup..." marker="#{mark} test create"  create=yes'
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "msg": "File created"
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "msg": "File created"
}

查看文件内容

bash
$ ansible ecs -m shell -a "cat /tmp/testfile7"                                     
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
#BEGIN test create
backup...
#END test create
bj-huawei-hecs-1 | CHANGED | rc=0 >>
#BEGIN test create
backup...
#END test create
lineinfile 模块

blockinfile 模块 是基于通常是基于段的粒度来操作文本,lineinfile 则基于行的粒度操作文本,它可以实现 确保某行存在与文本中、删除特定行、以及根据表达式替换某行

相关参数一览:

参数 描述
path 指定要操作的文件
line 此参数用于指定我们想要操作的那 ”一行文本”
regexp 使用正则表达式匹配对应的行,匹配到多行时,替换操作 只影响尾行、删除操作 影响所有匹配行
state present(确保行存在)absent(删除行)
backrefs backrefs 为 yes,开启向引用,当使用正则匹配时,默认情况下不允许引用分组(即使存在)
backrefs 为 yes,取消补插入,默认情况,若正则未匹配任何行,则插入 line 内容到文本末尾
insertafter 插入哪行的后面,可以用 正则EOF/BOF(End/Begin of File) 关键字 设定插入位置行后,默认插入文件末尾
当使用 backrefs 参数时此参数失效
insertbefore 插入哪行的前面,可以用 正则EOF/BOF(End/Begin of File) 关键字 设定插入位置行前
当使用 backrefs 参数时此参数失效
backup 更新时自动备份
create 操作文件并不存在时,是否自动创建
例1:插入确保行存在(没有即插入)

准备一个测试文件,内容如下:

bash
$ cat testfile   
Hello ansible,Hiiii
lineinfile -
Ensure a particular line is in a file,
lineinfile -
or replace an existing line using a back-referenced regular expression.

执行命令

bash
$ ansible 'ecs[0]' -m lineinfile -a 'path=/tmp/testfile line="test line"'
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": true, 
    "msg": "line added"
}

查看效果

bash
$ hostname && cat /tmp/testfile                                          
sz-aliyun-ecs-1
Hello ansible,Hiiii
lineinfile -
Ensure a particular line is in a file,
lineinfile -
or replace an existing line using a back-referenced regular expression.
test line
例2:正则匹配替换或插入

通过上一例子的内容也可以看到,有 2 行 lineinfile -,我们通过正则去尝试替换,测试是否是尾行被替换掉了

执行命令

bash
$ ansible 'ecs[0]' -m lineinfile -a 'path=/tmp/testfile regex="^line" line="test line"'
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": true, 
    "msg": "line replaced"
}

执行效果

bash
$ cat testfile
Hello ansible,Hiiii
lineinfile -
Ensure a particular line is in a file,
test line
or replace an existing line using a back-referenced regular expression.
test line

OK 符合预期!

例3:删除特定行

老套路 absent,删除的时候可不管你匹配几行,只要匹配全都删除!

执行命令

bash
$ ansible 'ecs[0]' -m lineinfile -a 'path=/tmp/testfile line="test line" state=absent'
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": true, 
    "found": 2, 
    "msg": "2 line(s) removed"
}

执行效果

bash
$ cat testfile 
Hello ansible,Hiiii
lineinfile -
Ensure a particular line is in a file,
or replace an existing line using a back-referenced regular expression.

同样正则删除也是都删

执行命令

bash
$ ansible 'ecs[0]' -m lineinfile -a 'path=/tmp/testfile regex='file' line="test line" state=absent' 
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": true, 
    "found": 2, 
    "msg": "2 line(s) removed"
}

执行效果

bash
$ cat testfile 
Hello ansible,Hiiii
or replace an existing line using a back-referenced regular expression.
例4:反向引用替换

通过设置 backrefs 参数为 yes 启用反向引用功能

执行命令

bash
$ ansible 'ecs[0]' -m lineinfile -a 'path=/tmp/testfile regex="(\w+\s\w+),\w+" line="\1" backrefs=yes'
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup": "", 
    "changed": true, 
    "msg": "line replaced"
}

执行效果

bash
$ cat testfile
Hello ansible
or replace an existing line using a back-referenced regular expression.

其他部分都基本与 blockinfile 类似,暂不演示~

find 模块

顾名思义,类似于 find 命令

直接看几个小例子吧~

例1:在指定目录中查找包含特定内容的文件

几个点,指定目录、正则匹配文件内容、递归查找

执行命令

bash
$ ansible 'ecs[0]' -m find -a "paths=/tmp contains='^Hel' recurse=yes" 
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "examined": 24, 
    "files": [
        {
            "atime": 1633103275.1560168, 
            "ctime": 1633103271.1926205, 
            "dev": 64769, 
            "gid": 0, 
            "gr_name": "root", 
            "inode": 1193873, 
            "isblk": false, 
            "ischr": false, 
            "isdir": false, 
            "isfifo": false, 
            "isgid": false, 
            "islnk": false, 
            "isreg": true, 
            "issock": false, 
            "isuid": false, 
            "mode": "0644", 
            "mtime": 1633103271.1926205, 
            "nlink": 1, 
            "path": "/tmp/testfile", 
            "pw_name": "root", 
            "rgrp": true, 
            "roth": true, 
            "rusr": true, 
            "size": 86, 
            "uid": 0, 
            "wgrp": false, 
            "woth": false, 
            "wusr": true, 
            "xgrp": false, 
            "xoth": false, 
            "xusr": false
        }
    ], 
    "matched": 1, 
    "msg": ""
}
例2:查找以 .sh 几位的文件,包括隐藏文件

几个点:等值匹配文件名、包括隐藏文件

执行命令

bash
$ ansible 'ecs[0]' -m find -a "paths=/tmp patterns='*.py' hidden=yes"
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "examined": 25, 
    "files": [
        {
            "atime": 1633103962.7277672, 
            "ctime": 1633103962.7277672, 
            "dev": 64769, 
            "gid": 0, 
            "gr_name": "root", 
            "inode": 1189829, 
            "isblk": false, 
            "ischr": false, 
            "isdir": false, 
            "isfifo": false, 
            "isgid": false, 
            "islnk": false, 
            "isreg": true, 
            "issock": false, 
            "isuid": false, 
            "mode": "0644", 
            "mtime": 1633103962.7277672, 
            "nlink": 1, 
            "path": "/tmp/.app.py", 
            "pw_name": "root", 
            "rgrp": true, 
            "roth": true, 
            "rusr": true, 
            "size": 0, 
            "uid": 0, 
            "wgrp": false, 
            "woth": false, 
            "wusr": true, 
            "xgrp": false, 
            "xoth": false, 
            "xusr": false
        }
    ], 
    "matched": 1, 
    "msg": ""
}

查询所有类型的匹配文件

bash
$ ansible 'ecs[0]' -m find -a "paths=/tmp patterns='*.py' file_type=any hidden=yes"
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "examined": 26, 
    "files": [
        {
            "atime": 1633103962.7277672, 
            "ctime": 1633103962.7277672, 
            "dev": 64769, 
            "gid": 0, 
            "gr_name": "root", 
            "inode": 1189829, 
            "isblk": false, 
            "ischr": false, 
            "isdir": false, 
            "isfifo": false, 
            "isgid": false, 
            "islnk": false, 
            "isreg": true, 
            "issock": false, 
            "isuid": false, 
            "mode": "0644", 
            "mtime": 1633103962.7277672, 
            "nlink": 1, 
            "path": "/tmp/.app.py", 
            "pw_name": "root", 
            "rgrp": true, 
            "roth": true, 
            "rusr": true, 
            "size": 0, 
            "uid": 0, 
            "wgrp": false, 
            "woth": false, 
            "wusr": true, 
            "xgrp": false, 
            "xoth": false, 
            "xusr": false
        }, 
        {
            "atime": 1633104055.3080244, 
            "ctime": 1633104055.3080244, 
            "dev": 64769, 
            "gid": 0, 
            "gr_name": "root", 
            "inode": 1189847, 
            "isblk": false, 
            "ischr": false, 
            "isdir": true, 
            "isfifo": false, 
            "isgid": false, 
            "islnk": false, 
            "isreg": false, 
            "issock": false, 
            "isuid": false, 
            "mode": "0755", 
            "mtime": 1633104055.3080244, 
            "nlink": 2, 
            "path": "/tmp/.django.py", 
            "pw_name": "root", 
            "rgrp": true, 
            "roth": true, 
            "rusr": true, 
            "size": 4096, 
            "uid": 0, 
            "wgrp": false, 
            "woth": false, 
            "wusr": true, 
            "xgrp": true, 
            "xoth": true, 
            "xusr": true
        }
    ], 
    "matched": 2, 
    "msg": ""
}
例3:正则匹配文件名

开启 use_regex=yes 后,patterns 支持正则匹配

执行命令

bash
$ ansible 'ecs[0]' -m find -a "paths=/tmp patterns='.[a-z]+.py' use_regex=yes  file_type=any hidden=yes"
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "examined": 26, 
    "files": [
        {
            "atime": 1633103962.7277672, 
            "ctime": 1633103962.7277672, 
            "dev": 64769, 
            "gid": 0, 
            "gr_name": "root", 
            "inode": 1189829, 
            "isblk": false, 
            "ischr": false, 
            "isdir": false, 
            "isfifo": false, 
            "isgid": false, 
            "islnk": false, 
            "isreg": true, 
            "issock": false, 
            "isuid": false, 
            "mode": "0644", 
            "mtime": 1633103962.7277672, 
            "nlink": 1, 
            "path": "/tmp/.app.py", 
            "pw_name": "root", 
            "rgrp": true, 
            "roth": true, 
            "rusr": true, 
            "size": 0, 
            "uid": 0, 
            "wgrp": false, 
            "woth": false, 
            "wusr": true, 
            "xgrp": false, 
            "xoth": false, 
            "xusr": false
        }, 
        {
            "atime": 1633104055.3080244, 
            "ctime": 1633104055.3080244, 
            "dev": 64769, 
            "gid": 0, 
            "gr_name": "root", 
            "inode": 1189847, 
            "isblk": false, 
            "ischr": false, 
            "isdir": true, 
            "isfifo": false, 
            "isgid": false, 
            "islnk": false, 
            "isreg": false, 
            "issock": false, 
            "isuid": false, 
            "mode": "0755", 
            "mtime": 1633104055.3080244, 
            "nlink": 2, 
            "path": "/tmp/.django.py", 
            "pw_name": "root", 
            "rgrp": true, 
            "roth": true, 
            "rusr": true, 
            "size": 4096, 
            "uid": 0, 
            "wgrp": false, 
            "woth": false, 
            "wusr": true, 
            "xgrp": true, 
            "xoth": true, 
            "xusr": true
        }
    ], 
    "matched": 2, 
    "msg": ""
}
例4:查找指定时间范围内的文件

age=-<num><单位hmswMY>

执行命令

bash
 ansible 'ecs[0]' -m find -a "paths=/tmp age=-1h hidden=yes"
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "examined": 26, 
    "files": [
        {
            "atime": 1633103962.7277672, 
            "ctime": 1633103962.7277672, 
            "dev": 64769, 
            "gid": 0, 
            "gr_name": "root", 
            "inode": 1189829, 
            "isblk": false, 
            "ischr": false, 
            "isdir": false, 
            "isfifo": false, 
            "isgid": false, 
            "islnk": false, 
            "isreg": true, 
            "issock": false, 
            "isuid": false, 
            "mode": "0644", 
            "mtime": 1633103962.7277672, 
            "nlink": 1, 
            "path": "/tmp/.app.py", 
            "pw_name": "root", 
            "rgrp": true, 
            "roth": true, 
            "rusr": true, 
            "size": 0, 
            "uid": 0, 
            "wgrp": false, 
            "woth": false, 
            "wusr": true, 
            "xgrp": false, 
            "xoth": false, 
            "xusr": false
        }, 
        {
            "atime": 1633103275.1560168, 
            "ctime": 1633103271.1926205, 
            "dev": 64769, 
            "gid": 0, 
            "gr_name": "root", 
            "inode": 1193873, 
            "isblk": false, 
            "ischr": false, 
            "isdir": false, 
            "isfifo": false, 
            "isgid": false, 
            "islnk": false, 
            "isreg": true, 
            "issock": false, 
            "isuid": false, 
            "mode": "0644", 
            "mtime": 1633103271.1926205, 
            "nlink": 1, 
            "path": "/tmp/testfile", 
            "pw_name": "root", 
            "rgrp": true, 
            "roth": true, 
            "rusr": true, 
            "size": 86, 
            "uid": 0, 
            "wgrp": false, 
            "woth": false, 
            "wusr": true, 
            "xgrp": false, 
            "xoth": false, 
            "xusr": false
        }
    ], 
    "matched": 2, 
    "msg": ""
}
例5:查找大小超过指定容量的文件

size=<num><单位>

先创建一个 100m 的测试文件

bash
$ dd if=/dev/zero of=/tmp/testfile2 count=100 bs=1M  
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 0.734994 s, 143 MB/s

执行命令

bash
$ ansible 'ecs[0]' -m find -a "paths=/tmp size=100m"         
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "examined": 26, 
    "files": [
        {
            "atime": 1633103762.3407304, 
            "ctime": 1633104519.3404229, 
            "dev": 64769, 
            "gid": 0, 
            "gr_name": "root", 
            "inode": 1192944, 
            "isblk": false, 
            "ischr": false, 
            "isdir": false, 
            "isfifo": false, 
            "isgid": false, 
            "islnk": false, 
            "isreg": true, 
            "issock": false, 
            "isuid": false, 
            "mode": "1204", 
            "mtime": 1633104519.3404229, 
            "nlink": 1, 
            "path": "/tmp/testfile2", 
            "pw_name": "root", 
            "rgrp": false, 
            "roth": true, 
            "rusr": false, 
            "size": 104857600, 
            "uid": 0, 
            "wgrp": false, 
            "woth": false, 
            "wusr": true, 
            "xgrp": false, 
            "xoth": false, 
            "xusr": false
        }
    ], 
    "matched": 1, 
    "msg": ""
}
例6:查找文件并返回 sha1 校验码
bash
$ ansible 'ecs[0]' -m find -a "paths=/tmp patterns='*.py' file_type=file hidden=yes get_checksum=yes"
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "examined": 26, 
    "files": [
        {
            "atime": 1633104731.9406807, 
            "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709", 
            "ctime": 1633103962.7277672, 
            "dev": 64769, 
            "gid": 0, 
            "gr_name": "root", 
            "inode": 1189829, 
            "isblk": false, 
            "ischr": false, 
            "isdir": false, 
            "isfifo": false, 
            "isgid": false, 
            "islnk": false, 
            "isreg": true, 
            "issock": false, 
            "isuid": false, 
            "mode": "0644", 
            "mtime": 1633103962.7277672, 
            "nlink": 1, 
            "path": "/tmp/.app.py", 
            "pw_name": "root", 
            "rgrp": true, 
            "roth": true, 
            "rusr": true, 
            "size": 0, 
            "uid": 0, 
            "wgrp": false, 
            "woth": false, 
            "wusr": true, 
            "xgrp": false, 
            "xoth": false, 
            "xusr": false
        }
    ], 
    "matched": 1, 
    "msg": ""
}

1.2.3 包管理

Ansible 提供了 yumapt 模块,以实现对常见包管理操作的支持,语法规则如下:

ansible ecs -m yum -a "name=<package>" state=<action>

yum 模块
安装软件

安装软件可以使用presentlatestinstalled 3 个动作

  • present installed :仅安装指定的软件包,不对其进行检查更新
  • latest:安装软件包的同时对其进行检查更新,确保使用的是最新版本
bash
$ ansible ecs -m yum -a "name=iotop state=present" -f 2              
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "changes": {
        "installed": [
            "iotop"
        ]
    }, 
    "msg": "", 
    "rc": 0, 
    "results": [
        "...\n\nComplete!\n"
    ]
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "changes": {
        "installed": [
            "iotop"
        ]
    }, 
    "msg": "", 
    "rc": 0, 
    "results": [
        "...\n\nComplete!\n"
    ]
}
卸载软件

卸载软件 可以使用absentremove 2 个动作

bash
 ansible ecs -m yum -a "name=iotop state=removed" -f 2
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "changes": {
        "removed": [
            "iotop"
        ]
    }, 
    "msg": "", 
    "rc": 0, 
    "results": [
        "\n\nComplete!\n"
    ]
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "changes": {
        "removed": [
            "iotop"
        ]
    }, 
    "msg": "", 
    "rc": 0, 
    "results": [
        "\n\nComplete!\n"
    ]
}

目前看来,没发现两个动作在执行效果上有什么区别~

yum_repo 模块

操作管理 yum 仓库文件的模块,直接上例子:

例1:创建 ali-epel 源,并配置开启

执行命令

bash
$ ansible 'ecs[0]' -m yum_repository -a 'name=ali-epel description="Alibaba EPEL" baseurl=https://mirrors.aliyun.com/epel/7/$basearch enabled=yes' 
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "repo": "ali-epel", 
    "state": "present"
}

执行效果

bash
$ ls /etc/yum.repos.d              
ali-epel.repo  CentOS-Base.repo  docker-ce.repo  epel.repo  epel.repo.rpmnew  epel-testing.repo
$ cat /etc/yum.repos.d/ali-epel.repo
[ali-epel]
baseurl = https://mirrors.aliyun.com/epel/7/$basearch
enabled = 1
name = Alibaba EPEL

刷新仓库

bash
$ yum repolist      
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
ali-epel         | 4.7 kB  00:00:00     
(1/3): ali-epel/x86_64/group_gz       |  96 kB  00:00:00     
(2/3): ali-epel/x86_64/updateinfo     | 1.0 MB  00:00:00     
(3/3): ali-epel/x86_64/primary_db     | 7.0 MB  00:00:03     
repo id                              repo name                                      status
ali-epel/x86_64                      Alibaba EPEL                                   13,674
base/7/x86_64                        CentOS-7                                       10,072
docker-ce-stable/7/x86_64            Docker CE Stable - x86_64                      123
epel/x86_64                          Extra Packages for Enterprise Linux 7 - x86_64 13,674
extras/7/x86_64                      CentOS-7                                       500
updates/7/x86_64                     CentOS-7                                       2,751
repolist: 40,794

例2:删除仓库

老套路,state=absent

执行命令

bash
$ ls /etc/yum.repos.d 
CentOS-Base.repo  docker-ce.repo  epel.repo  epel.repo.rpmnew  epel-testing.repo
$ ansible 'ecs[0]' -m yum_repository -a 'name=ali-epel state=absent'
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "repo": "ali-epel", 
    "state": "absent"

刷新仓库

bash
$ yum repolist 
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
repo id                              repo name                                      status
base/7/x86_64                        CentOS-7                                       10,072
docker-ce-stable/7/x86_64            Docker CE Stable - x86_64                      123
epel/x86_64                          Extra Packages for Enterprise Linux 7 - x86_64 13,674
extras/7/x86_64                      CentOS-7                                       500
updates/7/x86_64                     CentOS-7                                       2,751
repolist: 40,794
easy_install 模块

通过 easy_install 模块安装 Pythondjango

bash
$ ansible ecs -s -m easy_install -a "name=django"

1.2.4 管理用户、组

生成密文

使用 debug 模块生成 密文格式的用户密码

bash
$ ansible localhost -m debug -a "msg={{ 'Passw0rd' | password_hash('sha512', 'salt') }}"
localhost | SUCCESS => {
    "msg": "$6$salt$N.PquVfbm9DyVhvrzy9pZ5yAfFM2ihCS9ZKzdllgl2ZPYIqcVYsVendbrH1Pa38eAfTLK0eBdQjjuSaS/dGVC."
}
创建用户

创建用户 foo 填入早先此前生成的密文,注意 $ 符号需要转移处理

bash
$ ansible ecs -m user -a 'name=foo shell=/bin/bash password='\$6\$salt\$N.PquVfbm9DyVhvrzy9pZ5yAfFM2ihCS9ZKzdllgl2ZPYIqcVYsVendbrH1Pa38eAfTLK0eBdQjjuSaS/dGVC.' create_home=yes'
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "comment": "", 
    "create_home": true, 
    "group": 1004, 
    "home": "/home/foo", 
    "name": "foo", 
    "password": "NOT_LOGGING_PASSWORD", 
    "shell": "/bin/bash", 
    "state": "present", 
    "system": false, 
    "uid": 1004
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "comment": "", 
    "create_home": true, 
    "group": 1001, 
    "home": "/home/foo", 
    "name": "foo", 
    "password": "NOT_LOGGING_PASSWORD", 
    "shell": "/bin/bash", 
    "state": "present", 
    "system": false, 
    "uid": 1001
}

确认用户创建情况

bash
$ ansible ecs -a "id" -u foo -k 
SSH password: 
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
uid=1004(foo) gid=1004(foo) groups=1004(foo)
bj-huawei-hecs-1 | CHANGED | rc=0 >>
uid=1001(foo) gid=1001(foo) groups=1001(foo)
删除用户

通过参数 state=absent 删除特定用户

bash
$ ansible ecs -m user -a "name=foo state=absent"
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "force": false, 
    "name": "foo", 
    "remove": false, 
    "state": "absent"
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "force": false, 
    "name": "foo", 
    "remove": false, 
    "state": "absent"
}
创建组

通过模块 group 参数 state=absent 创建组

bash
$ ansible ecs -m group -a "name=develop state=present"
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "gid": 1004, 
    "name": "develop", 
    "state": "present", 
    "system": false
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "gid": 1001, 
    "name": "develop", 
    "state": "present", 
    "system": false
}

查看创建情况

bash
$ ansible ecs -a "getent group develop"               
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
develop:x:1004:
bj-huawei-hecs-1 | CHANGED | rc=0 >>
develop:x:1001:
删除组

老样子,state=absent

bash
$ ansible ecs -m group -a "name=develop state=absent"
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "name": "develop", 
    "state": "absent"
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "name": "develop", 
    "state": "absent"
}

确认删除情况

bash
$ ansible ecs -a "getent group develop"              
sz-aliyun-ecs-1 | FAILED | rc=2 >>
non-zero return code
bj-huawei-hecs-1 | FAILED | rc=2 >>
non-zero return code

1.2.6 Git 操作

代码拉取

拉取代码前,需要提前确保服务器对 Git 仓库有访问权限,如使用 SSH 拉取代码,别忘了将对应节点的 SSH 公钥添加到 Git 平台

bash
$ ansible ecs -m git -a "repo=git@gitee.com:l0tusch1ng/hello.git dest=/tmp/hello version=HEAD accept_hostkey=yes"
sz-aliyun-ecs-1 | CHANGED => {
    "after": "0be478d0f2be0f93823bcfb6af6f0c344ba7800d", 
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "before": null, 
    "changed": true
}
bj-huawei-hecs-1 | CHANGED => {
    "after": "0be478d0f2be0f93823bcfb6af6f0c344ba7800d", 
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "before": null, 
    "changed": true
}

查看拉取下来的 Git 仓库

bash
$ ansible ecs -a "ls /tmp/hello"          
sz-aliyun-ecs-1 | CHANGED | rc=0 >>
LICENSE
README.en.md
README.md
bj-huawei-hecs-1 | CHANGED | rc=0 >>
LICENSE
README.en.md
README.md

1.2.7 服务管理

老套路,通过 service 模块中 state 参数进行服务管理

启动服务
bash
$ ansible ecs -m service -a "name=ntpd state=started"
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "name": "ntpd", 
    "state": "started", 
    "status": {
      # ...
    }
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "name": "ntpd", 
    "state": "started", 
    "status": {
      # ...
    }
}
重启服务
bash
$ ansible ecs -m service -a "name=ntpd state=restarted"
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "name": "ntpd", 
    "state": "started", 
    "status": {
      # ...
    }
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "name": "ntpd", 
    "state": "started", 
    "status": {
      # ...
    }
}
停止服务
bash
ansible ecs -m service -a "name=ntpd state=stopped"  
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "name": "ntpd", 
    "state": "stopped", 
    "status": {
      # ...
    }
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "name": "ntpd", 
    "state": "stopped", 
    "status": {
      # ...
    }
}

1.2.8 计划任务

cron 模块基本实现了 crontab 的功能,常用的使用范例如下

例1:创建计划任务每天8点执行

创建计划任务 test crontab,设置为 每天早上 8 点执行,任务是执行 /tmp/t1.sh

执行命令

bash
$ ansible 'ecs[0]' -m cron -a "name='test crontab' hour=8 minute=0 job='/tmp/t1.sh'"
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "envs": [], 
    "jobs": [
        "test crontab"
    ]
}

执行效果

bash
$ crontab -l
#Ansible: test crontab
0 8 * * * /tmp/t1.sh
例2:创建计划任务每3天一次,每次8:00执行

执行命令

bash
$ ansible 'ecs[0]' -m cron -a "name='test crontab demo2' hour=8 minute=0 day=*/3 job='/tmp/t1.sh'"
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "envs": [], 
    "jobs": [
        "test crontab", 
        "test crontab demo2"
    ]
}

执行效果

bash
$ crontab -l                               
#Ansible: test crontab
0 8 * * * /tmp/t1.sh
#Ansible: test crontab demo2
0 8 */3 * * /tmp/t1.sh
例2:创建计划任务 特殊时间执行

所谓特殊时间包括:rebootyearlymonthlyweeklydailyhourly

执行命令

bash
$ ansible 'ecs[0]' -m cron -a "name='test crontab demo3' special_time=reboot  job='/tmp/t1.sh'"
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "envs": [], 
    "jobs": [
        "test crontab", 
        "test crontab demo2", 
        "test crontab demo3"
    ]
}

执行效果

bash
$ crontab -l
#Ansible: test crontab
0 8 * * * /tmp/t1.sh
#Ansible: test crontab demo2
0 8 */3 * * /tmp/t1.sh
#Ansible: test crontab demo3
@reboot /tmp/t1.sh
例4:更新计划任务,并保存备份

执行命令

bash
$ ansible 'ecs[0]' -m cron -a "name='test crontab' hour=8 minute=8 job='/tmp/t1.sh' backup=yes"
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup_file": "/tmp/crontabU0yxKe", 
    "changed": true, 
    "envs": [], 
    "jobs": [
        "test crontab", 
        "test crontab demo2", 
        "test crontab demo3"
    ]
}

执行效果

bash
$ crontab -l
# 50 17 * * * systemctl stop node_exporter.service
#27 0 * * * /root/.pyenv/versions/3.9.0/bin/python /prodata/scripts/bmcc_user_check_in.py
* * * * * cd /prodata/gitbook/typora && git pull &> /dev/null
#Ansible: test crontab
8 8 * * * /tmp/t1.sh
#Ansible: test crontab demo2
0 8 */3 * * /tmp/t1.sh
#Ansible: test crontab demo3
@reboot /tmp/t1.sh

查看备份

bash
$ cat /tmp/crontabU0yxKe
# 50 17 * * * systemctl stop node_exporter.service
#27 0 * * * /root/.pyenv/versions/3.9.0/bin/python /prodata/scripts/bmcc_user_check_in.py
* * * * * cd /prodata/gitbook/typora && git pull &> /dev/null
#Ansible: test crontab
0 8 * * * /tmp/t1.sh
#Ansible: test crontab demo2
0 8 */3 * * /tmp/t1.sh
#Ansible: test crontab demo3
@reboot /tmp/t1.sh
例5:禁用计划任务,并保存备份

无论是更新、禁用、删除,只要是操作变更计划任务,最好都要开启备份参数 backup=yes

执行命令

bash
$ ansible 'ecs[0]' -m cron -a "name='test crontab' hour=8 minute=8 job='/tmp/t1.sh' disabled=yes backup=yes" 
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup_file": "/tmp/crontab9RjAvH", 
    "changed": true, 
    "envs": [], 
    "jobs": [
        "test crontab", 
        "test crontab demo2", 
        "test crontab demo3"
    ]
}

执行效果

bash
$ crontab -l            
# 50 17 * * * systemctl stop node_exporter.service
#27 0 * * * /root/.pyenv/versions/3.9.0/bin/python /prodata/scripts/bmcc_user_check_in.py
* * * * * cd /prodata/gitbook/typora && git pull &> /dev/null
#Ansible: test crontab
#8 8 * * * /tmp/t1.sh
#Ansible: test crontab demo2
0 8 */3 * * /tmp/t1.sh
#Ansible: test crontab demo3
@reboot /tmp/t1.sh

可以看到,相关任务已被注释

例6:删除计划任务,并保存备份

执行命令

bash
$ ansible 'ecs[0]' -m cron -a "name='test crontab' state=absent backup=yes" 
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "backup_file": "/tmp/crontabKiLobo", 
    "changed": true, 
    "envs": [], 
    "jobs": [
        "test crontab demo2", 
        "test crontab demo3"
    ]
}

执行效果

bash
$ crontab -l
#Ansible: test crontab demo2
0 8 */3 * * /tmp/t1.sh
#Ansible: test crontab demo3
@reboot /tmp/t1.sh

1.2.9 下载文件(todo)

bash
$ ansible testserver -m get_url -a "url=https://nodejs.org/dist/v14.17.4/node-v14.17.4-linux-x64.tar.xz dest=/tmp mode=0755" -i prod-ansible-hosts

1.2.10 系统资源检测(采集)

setup 模块
例1:采集节点资源信息

跳过 setup 模块我们可以获取节点的资源信息,这个过程也叫 Gathering Facts,示例如下:

bash
$ ansible 'ecs[0]' -m setup

内容太多,贴个地址

主要包括硬件、系统、网卡、CPU、磁盘、内存等,很多、很全面。但是,我们或许暂时用不到这么多信息,对此,我们可以通过关键字对信息进行过滤,比如:只获取内存相关的信息

例2:采集指定资源信息
bash
$ ansible 'ecs[0]' -m setup -a 'filter=ansible_memory_mb'
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "ansible_memory_mb": {
            "nocache": {
                "free": 571, 
                "used": 1163
            }, 
            "real": {
                "free": 73, 
                "total": 1734, 
                "used": 1661
            }, 
            "swap": {
                "cached": 0, 
                "free": 0, 
                "total": 0, 
                "used": 0
            }
        }, 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false
}

如果记不住关键字,我们还可以使用通配符,进行相对模糊的过滤,示例如下:

bash
$ ansible 'ecs[0]' -m setup -a 'filter=*memory_mb'
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "ansible_memory_mb": {
            "nocache": {
                "free": 572, 
                "used": 1162
            }, 
            "real": {
                "free": 74, 
                "total": 1734, 
                "used": 1660
            }, 
            "swap": {
                "cached": 0, 
                "free": 0, 
                "total": 0, 
                "used": 0
            }
        }, 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false
}
例3:自定义资源信息

除了节点本身信息外,我们还能够在远程主机中定义一些特殊的信息,用以标识更多的内容,例如地区、可用区、机柜、所属环境等等

ansible 默认会去目标主机的 /etc/ansible/facts.d 目录下查找以 .fact 为后缀的自定义信息,同时文件格式需要是 INIJSON

创建测试文件

INI 风格

ini
[testmsg]
region=aliyun-shenzhen
environment=development

JSON 风格

json
{
    "testmsg": {
        "region": "aliyun-shenzhen",
        "environment": "development"
    }
}

创建 fact 定义

bash
$ mkdir -p /etc/ansible/facts.d 
$ cat > /etc/ansible/facts.d/testmsg.fact << EOF
{
    "testmsg": {
        "region": "aliyun-shenzhen",
        "environment": "development"
    }
}
EOF

执行命令

bash
$ ansible 'ecs[0]' -m setup -a 'filter=ansible_local'
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "ansible_local": {
            "testmsg": {
                "testmsg": {
                    "environment": "development", 
                    "region": "aliyun-shenzhen"
                }
            }
        }, 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false
}

当然,你也可以通过 fact_path=/dir 显示声明 fact 文件所属目录,使 ansible 到指定目录去查找 fact

bash
$ mv /etc/ansible/facts.d/testmsg.fact /tmp 
$ ansible 'ecs[0]' -m setup -a 'fact_path=/tmp filter=ansible_local' 
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "ansible_local": {
            "testmsg": {
                "testmsg": {
                    "environment": "development", 
                    "region": "aliyun-shenzhen"
                }
            }
        }, 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false
}
listen_ports_facts 模块
端口检测
bash
$ ansible testserver -m listen_ports_facts -i prod-ansible-hosts

1.2.11 后台执行(未理解)

后台执行,适用于执行 长时间 的任务,例如:编译安装软件包、初始化系统等

启动任务

  • -B:异步执行任务
  • -P:任务状态信息拉取间隔,默认 15s
bash
$ ansible ecs -B 30 -P 0 -a "vmstat 5" 
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "ansible_job_id": "129378104578.20485", 
    "changed": true, 
    "finished": 0, 
    "results_file": "/root/.ansible_async/129378104578.20485", 
    "started": 1
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "ansible_job_id": "362800085138.19147", 
    "changed": true, 
    "finished": 0, 
    "results_file": "/root/.ansible_async/362800085138.19147", 
    "started": 1
}

查看任务状态

bash
$ ansible sz-aliyun-ecs-1 -m async_status -a "jid=129378104578.20485"
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "ansible_job_id": "129378104578.20485", 
    "changed": false, 
    "finished": 0, 
    "started": 1
}

执行到这的时候,产生了一些疑惑,startedfinished 不论多久,始终值保持不变,这是什么意思?状态信息未变更?

我试着调整了 -P 参数的值,再次执行

bash
$ ansible ecs -B 10 -P 5 -a "vmstat 3"
sz-aliyun-ecs-1 | FAILED | rc=-1 >>
async task did not complete within the requested time - 10s
bj-huawei-hecs-1 | FAILED | rc=-1 >>
async task did not complete within the requested time - 10s

提示,任务未在 10s 内完成,当然这是肯定的,vmstat 是持续输出的,为什么任务突然变成前天执行了?这 -P 参数到底起到了个什么作用呢?

暂时摸不清,先撂这儿吧~


嗯,还在摸,测试个命令

  • vmstat 命令以每秒一次的间隔打印系统状态信息,一共 5 次
  • -B 10:只给任务 10 秒钟的时间运行
  • -P 1:以每秒的间隔,拉取任务的执行状态(包括命令打印到控制台的输出)
bash
$ ansible ecs -B 10 -P 1 -a "vmstat 1 5"
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_job_id": "604443018422.21970", 
    "changed": true, 
    "cmd": [
        "vmstat", 
        "1", 
        "5"
    ], 
    "delta": "0:00:04.034279", 
    "end": "2021-09-19 23:38:02.394496", 
    "finished": 1, 
    "rc": 0, 
    "start": "2021-09-19 23:37:58.360217", 
    "stderr": "", 
    "stderr_lines": [], 
    "stdout": "procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----\n r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st\n 1  0      0  84640  29188 597456    0    0    21     9    1    1  2  0 98  0  0\n 1  0      0  84568  29200 597392    0    0     0   152 2494 3712  8  6 85  0  0\n 0  0      0  78944  29200 597736    0    0     0     4 2525 3513 17  5 78  0  0\n 0  0      0  74148  29212 597920    0    0    68   284 3071 4366 14  8 78  0  0\n 0  0      0  78876  29212 597840    0    0     0   116 2829 4217 12  4 84  0  0", 
    "stdout_lines": [
        "procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----", 
        " r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st", 
        " 1  0      0  84640  29188 597456    0    0    21     9    1    1  2  0 98  0  0", 
        " 1  0      0  84568  29200 597392    0    0     0   152 2494 3712  8  6 85  0  0", 
        " 0  0      0  78944  29200 597736    0    0     0     4 2525 3513 17  5 78  0  0", 
        " 0  0      0  74148  29212 597920    0    0    68   284 3071 4366 14  8 78  0  0", 
        " 0  0      0  78876  29212 597840    0    0     0   116 2829 4217 12  4 84  0  0"
    ]
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_job_id": "58453485557.19502", 
    "changed": true, 
    "cmd": [
        "vmstat", 
        "1", 
        "5"
    ], 
    "delta": "0:00:04.013519", 
    "end": "2021-09-19 23:38:03.612380", 
    "finished": 1, 
    "rc": 0, 
    "start": "2021-09-19 23:37:59.598861", 
    "stderr": "", 
    "stderr_lines": [], 
    "stdout": "procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----\n r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st\n 1  0      0 132920  35908 385196    0    0     3    18   14   14  3  0 96  0  0\n 0  0      0 132664  35908 385224    0    0     0    12  361  607  8  0 92  0  0\n 2  0      0 123088  35920 385412    0    0     0   136  557  846 11  3 86  0  0\n 0  0      0 134284  35920 385352    0    0     0     0  394  631 10  2 88  0  0\n 0  0      0 132436  35936 385336    0    0     0   268  487  741  9  3 87  1  0", 
    "stdout_lines": [
        "procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----", 
        " r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st", 
        " 1  0      0 132920  35908 385196    0    0     3    18   14   14  3  0 96  0  0", 
        " 0  0      0 132664  35908 385224    0    0     0    12  361  607  8  0 92  0  0", 
        " 2  0      0 123088  35920 385412    0    0     0   136  557  846 11  3 86  0  0", 
        " 0  0      0 134284  35920 385352    0    0     0     0  394  631 10  2 88  0  0", 
        " 0  0      0 132436  35936 385336    0    0     0   268  487  741  9  3 87  1  0"
    ]
}

喏,可以看到,当我确保命令能在限期内完成时,前台执行的任务成功打印了返回,其中可以看到stdout_lines 输出了 5 次 vmstat 信息

可我还是很奇怪,为什么 -B 会失效,导致前台 Shell 终端发生阻塞?

有机会再摸索下…


似乎明白些了,示例如下:

  • vmstat 命令以每秒一次的间隔打印系统状态信息,一共 5 次
  • -B 10:只给任务 10 秒钟的时间运行
  • -P 0:不等待任务执行(启动并忽略)
bash
$ ansible ecs -B 20 -P 0 -a "vmstat 1 5" -f 2
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "ansible_job_id": "491593461135.22847", 
    "changed": true, 
    "finished": 0, 
    "results_file": "/root/.ansible_async/491593461135.22847", 
    "started": 1
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "ansible_job_id": "317449108493.19704", 
    "changed": true, 
    "finished": 0, 
    "results_file": "/root/.ansible_async/317449108493.19704", 
    "started": 1
}

稍等几秒钟后…

bash
$ ansible sz-aliyun-ecs-1 -m async_status -a "jid=491593461135.22847"             
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "ansible_job_id": "491593461135.22847", 
    "changed": true, 
    "cmd": [
        "vmstat", 
        "1", 
        "5"
    ], 
    "delta": "0:00:04.034584", 
    "end": "2021-09-19 23:50:27.016328", 
    # 完成状态为 1,说明任务执行完成
    "finished": 1, 
    "rc": 0, 
    "start": "2021-09-19 23:50:22.981744", 
    "stderr": "", 
    "stderr_lines": [], 
    "stdout": "procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----\n r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st\n 1  0      0 123856  29672 597824    0    0    21     9    1    1  2  0 98  0  0\n 0  0      0 111608  29672 597724    0    0     0     0 2143 3455  8  3 89  0  0\n 0  0      0 175956  29672 597152    0    0     0     0 1974 2995  8  2 90  0  0\n 0  0      0 176120  29672 597152    0    0     0     0 1106 1698  2  1 98  0  0\n 0  0      0 176120  29672 597152    0    0     0     0 1027 1660  1  1 98  0  0", 
    "stdout_lines": [
        "procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----", 
        " r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st", 
        " 1  0      0 123856  29672 597824    0    0    21     9    1    1  2  0 98  0  0", 
        " 0  0      0 111608  29672 597724    0    0     0     0 2143 3455  8  3 89  0  0", 
        " 0  0      0 175956  29672 597152    0    0     0     0 1974 2995  8  2 90  0  0", 
        " 0  0      0 176120  29672 597152    0    0     0     0 1106 1698  2  1 98  0  0", 
        " 0  0      0 176120  29672 597152    0    0     0     0 1027 1660  1  1 98  0  0"
    ]
}

通过返回可以看到 "finished": 1 这是不是说明,当任务执行 成功 or 失败 finished都会为 1,而执行时间超过 -B <timeout> 则显示为0?

验证任务执行失败

bash
$ ansible ecs -B 5 -P 0 -a "ls -l /tmp/nofile" -f 2           
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "ansible_job_id": "227924770130.23660", 
    "changed": true, 
    "finished": 0, 
    "results_file": "/root/.ansible_async/227924770130.23660", 
    "started": 1
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "ansible_job_id": "397730636493.19834", 
    "changed": true, 
    "finished": 0, 
    "results_file": "/root/.ansible_async/397730636493.19834", 
    "started": 1
}

查看状态信息

bash
$ ansible sz-aliyun-ecs-1 -m async_status -a "jid=227924770130.23660"
sz-aliyun-ecs-1 | FAILED! => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "ansible_job_id": "227924770130.23660", 
    "changed": true, 
    "cmd": [
        "ls", 
        "-l", 
        "/tmp/nofile"
    ], 
    "delta": "0:00:00.040787", 
    "end": "2021-09-19 23:59:36.361970", 
    "finished": 1, 
    "msg": "non-zero return code", 
    "rc": 2, 
    "start": "2021-09-19 23:59:36.321183", 
    "stderr": "ls: cannot access /tmp/nofile: No such file or directory", 
    "stderr_lines": [
        "ls: cannot access /tmp/nofile: No such file or directory"
    ], 
    "stdout": "", 
    "stdout_lines": []
}

OK,符合预期~

验证任务执行超时

bash
$ ansible ecs -B 5 -P 0 -a "vmstat 1" -f 2                           
sz-aliyun-ecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "ansible_job_id": "90702411198.23358", 
    "changed": true, 
    "finished": 0, 
    "results_file": "/root/.ansible_async/90702411198.23358", 
    "started": 1
}
bj-huawei-hecs-1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "ansible_job_id": "679177421403.19797", 
    "changed": true, 
    "finished": 0, 
    "results_file": "/root/.ansible_async/679177421403.19797", 
    "started": 1
}

稍等片刻后…

bash
$ ansible sz-aliyun-ecs-1 -m async_status -a "jid=90702411198.23358"
sz-aliyun-ecs-1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "ansible_job_id": "90702411198.23358", 
    "changed": false, 
    "finished": 0, 
    "started": 1
}

任务状态始终不变

啊…大致算是明白一点了,当然也仅限于 状态字段,其中执行原理还要在深入的摸索下…

1.3 扩展模块

ansible 为我们提供了各式各样丰富的模块,但是即便如此,有些时候它们不一定能满足我们的需求,为此我们可能需要自己去编写满足需求的模块

基于 ansible 灵活的设计,编写集成模块并不复杂,Shell 或 Python 都可以编写模块,下面我们会分别演示

1.3.1 Shell 编写模块

脚本编写 library/docker_sh

bash
#!/bin/bash
set -e
source $1 $2 $3
IMAGE=$image
NAME=$name
TAG=$tag
if [ ! -z "$IMAGE" ] && [ ! -z "$NAME" ] && [ ! -z "$TAG" ]; then
  container_id=$(/usr/bin/docker run -d --name ${NAME} ${IMAGE}:${TAG})
  if [ -z "$id" ]; then
    CHANGED="True"
    echo {\"containerId\":\"$container_id\"}
    exit 0
  fi
else
  echo {\"msg\": \"run docker container error.\"}
fi

执行命令

bash
$ ansible 'ecs[0]' -M library -m docker_sh -a "name=ansible-nginx-demo image=nginx tag=latest"
ecs-1.aliyun.sz | SUCCESS => {
    "changed": false,
    "containerId": "4c3ad2ea664108e40f680c1d6a23faca7ea3eb7aed8533ec38798875db87231c"
}

执行效果

bash
$ docker ps |grep ansible
4c3ad2ea6641   nginx:latest                "/docker-entrypoint.…"   11 seconds ago   Up 9 seconds   80/tcp                                        ansible-nginx-demo

1.3.3 修改模块路径配置

为了避免重复填写 -M 参数,我们可以修改配置文件中的 library 配置项,声明 ansible 模块查找的路径

bash
$ vim /etc/ansible/ansible.cfg
[defaults]

# some basic default values...

#inventory      = /etc/ansible/hosts
#library        = /usr/share/my_modules/
library        = /usr/share/ansible-library

创建目录,并拷贝文件

bash
$ mkdir /usr/share/ansible-library
$ cp library/docker_sh /usr/share/ansible-library

执行命令

bash
$ ansible 'ecs[0]' -m docker_sh -a "name=ansible-nginx-demo2 image=nginx tag=latest"
ecs-1.aliyun.sz | SUCCESS => {
    "changed": false,
    "containerId": "7f84791d535ea56c1f740c63990af3409e926f7cebed414ea91ae3d644f97374"
}

执行效果

bash
$ docker ps |grep ansible
7f84791d535e   nginx:latest                "/docker-entrypoint.…"   5 seconds ago   Up 5 seconds   80/tcp                                        ansible-nginx-demo2
4c3ad2ea6641   nginx:latest                "/docker-entrypoint.…"   7 minutes ago   Up 7 minutes   80/tcp                                        ansible-nginx-demo

二、ansible-console

ansible-console 命令在实际工作中用的不多,主要用来操作或命令的快速调试,毕竟 ad-hoc 手敲的话重复内容太多,用 playbook 又多少有些大材小用,下面我们看下具体的使用方法

ansible-console 为交互式命令执行方式,示例如下:

bash
$ ansible-console ecs
Welcome to the ansible console.
Type help or ? to list commands.
root@ecs (2)[f:5]$ list
ecs-1.aliyun.sz
huawei
root@ecs (2)[f:5]$ forks 2
root@ecs (2)[f:2]$ ping
ecs-1.aliyun.sz | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
huawei | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}

我们分解下上面 ansible-console 命令交互信息

首先看命令提示符,root@ecs (2)[f:5]:

  • (2):括号内的数字为当前分组内的主机数,当前有两台主机
  • [f:5]:并发线程数,默认为 5,可以通过 forks 命令修改

顺着往下看,list 命令表示列出当前分组内的主机

bash
root@ecs (2)[f:5]$ list
ecs-1.aliyun.sz
huawei

forks 5 设置并发线程数

bash
root@ecs (2)[f:5]$ forks 2

可以看到执行后,命令提示符中原本的 [f:5] 变为 [f:2] ,接着调用 ping 模块

bash
root@ecs (2)[f:2]$ ping
ecs-1.aliyun.sz | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
huawei | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}

调用 debug 模块,并传入 msg 参数及值

bash
root@ecs (2)[f:2]$ debug msg="DaYo"
ecs-1.aliyun.sz | SUCCESS => {
    "msg": "DaYo"
}
huawei | SUCCESS => {
    "msg": "DaYo"
}

如果不知道如何运行某些模块,可以通过 help 变量获取帮助,例如:

bash
root@ecs (2)[f:2]$ help debug
Print statements during execution
Parameters:
  msg The customized message that is printed. If omitted, prints a generic message.
  var A variable name to debug.
  verbosity A number that controls when the debug is run, if you set to 3 it will only run debug when -vvv or above

三、ansible-doc

非常重要的命令,主要用来插件列表以及模块使用方法,示例如下

查看系统可用插件列表

bash
$ ansible-doc -l
fortios_router_community_list                                 Configure community lists in Fortinet's FortiOS and FortiGate
azure_rm_devtestlab_info                                      Get Azure DevTest Lab facts
ecs_taskdefinition                                            register a task definition in ecs
avi_alertscriptconfig                                         Module for setup of AlertScriptConfig Avi RESTful Object
tower_receive                                                 Receive assets from Ansible Tower
netapp_e_iscsi_target                                         NetApp E-Series manage iSCSI target configuration
azure_rm_acs                                                  Manage an Azure Container Service(ACS) instance
fortios_log_syslogd2_filter                                   Filters for remote system server in Fortinet's FortiOS and FortiGate
junos_rpc                                                     Runs an arbitrary RPC over NetConf on an Juniper JUNOS device
na_elementsw_vlan                                             NetApp Element Software Manage VLAN
pn_ospf                                                       CLI command to add/remove ospf protocol to a vRouter
...
# 省略下面内容

查看特定类别插件列表

由于 ansible 内置了非常多的插件,假如我指向看某一类的插件,可以这么做

bash
$ ansible-doc -t become -l
ksu        Kerberos substitute user
pbrun      PowerBroker run
enable     Switch to elevated permissions on a network device
sesu       CA Privileged Access Manager
pmrun      Privilege Manager run
runas      Run As user
sudo       Substitute User DO
su         Substitute User
doas       Do As user
pfexec     profile based execution
machinectl Systemd's machinectl privilege escalation
dzdo       Centrify's Direct Authorize

查看特定插件使用帮助

OK,这下清爽多了,可是如何使用呢?假如我们此时想要用 sudo 插件,这也不难

bash
$ ansible-doc -t become sudo

执行效果

yaml
> SUDO    (/usr/lib/python2.7/site-packages/ansible/plugins/become/sudo.py)

        This become plugins allows your remote/login user to execute commands as another user via the sudo utility.

  * This module is maintained by The Ansible Community
OPTIONS (= is mandatory):

- become_exe
        Sudo executable
        [Default: sudo]
        set_via:
          env:
          - name: ANSIBLE_BECOME_EXE
          - name: ANSIBLE_SUDO_EXE
          ini:
          - key: become_exe
            section: privilege_escalation
          - key: executable
            section: sudo_become_plugin
          vars:
          - name: ansible_become_exe
          - name: ansible_sudo_exe

- become_flags
        Options to pass to sudo
        [Default: -H -S -n]
        set_via:
          env:
          - name: ANSIBLE_BECOME_FLAGS
          - name: ANSIBLE_SUDO_FLAGS
          ini:
          - key: become_flags
            section: privilege_escalation
          - key: flags
            section: sudo_become_plugin
          vars:
          - name: ansible_become_flags
          - name: ansible_sudo_flags

- become_pass
        Password to pass to sudo
        [Default: (null)]
        set_via:
          env:
          - name: ANSIBLE_BECOME_PASS
          - name: ANSIBLE_SUDO_PASS
          ini:
          - key: password
            section: sudo_become_plugin
          vars:
          - name: ansible_become_password
          - name: ansible_become_pass
          - name: ansible_sudo_pass   

- become_user
        User you 'become' to execute the task
        [Default: root]
        set_via:
          env:
          - name: ANSIBLE_BECOME_USER
          - name: ANSIBLE_SUDO_USER
          ini:
          - key: become_user
            section: privilege_escalation
          - key: user
            section: sudo_become_plugin
          vars:
          - name: ansible_become_user
          - name: ansible_sudo_user
        
AUTHOR: ansible (@core)
        METADATA:
          status:
          - preview
          supported_by: community

我们可以参照着上述帮助信息,使用 sudo 插件

四、ansible-galaxy

Ansible 提供了一个名为 Galaxy 的 Role 分享平台,这个平台提供了包括大量官方、第三方组织、个人提交的扩展模块,ansible-galaxy 正是用于从 https://galaxy.ansible.com/ 站点下载第三方扩展的命令,基本类似于 python 的 pip、centos 的 yum 等

根据关键字搜索角色

虽然 search 可以搜,但还是建议网页端操作,毕竟信息更直观也更方便

bash
$ ansible-galaxy search nginx

Found 1604 roles matching your search. Showing first 1000.

 Name                                                    Description
 ----                                                    -----------
 0x0i.prometheus                                         Prometheus - a multi-dimensional time-series data monitoring and alerting toolkit
 0x5a17ed.ansible_role_netbox                            Installs and configures NetBox, a DCIM suite, in a production setting.
 1davidmichael.ansible-role-nginx                        Nginx installation for Linux, FreeBSD and OpenBSD.
 1it.sudo                                                Ansible role for managing sudoers
 1mr.zabbix_host                                         configure host zabbix settings
 1nfinitum.php                                           PHP installation role.
 2goobers.jellyfin                                       Install Jellyfin on Debian.
 2kloc.trellis-monit                                     Install and configure Monit service in Trellis.
...
# 省略后续内容

安装指定角色

todo

创建 role 初始化模版

bash
$ ansible-galaxy init nginx
- Role nginx was created successfully

查看目录结构

bash
$ tree nginx
nginx
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── README.md
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
    └── main.yml
8 directories, 8 files

获取角色详细信息

bash
$ ansible-galaxy info alexdzyoba.redis

Role: alexdzyoba.redis
        description: Provision Redis for Debian as in official Docker image
        active: True
        commit: ab14252cc472fe433477f70099134e2058d8a2a0
        commit_message: Initial
        commit_url: https://github.com/alexdzyoba/ansible-redis/commit/ab14252cc472fe433477f70099134e2058d8a2a0
        company:
        created: 2017-12-02T20:45:35.049885Z
        download_count: 68
        forks_count: 0
        github_branch: master
        github_repo: ansible-redis
        github_user: alexdzyoba
        id: 22537
        imported: 2017-12-02T15:45:39.121966-05:00
        is_valid: True
        issue_tracker_url: https://github.com/alexdzyoba/ansible-redis/issues
        license: MIT
        min_ansible_version: 2.4.0
        modified: 2018-06-30T05:10:25.884196Z
        open_issues_count: 0
        path: (u'/root/.ansible/roles', u'/usr/share/ansible/roles', u'/etc/ansible/roles')
        role_type: ANS
# 省略后续

五、ansible-pull

默认情况下,Ansible 工作方式是 PUSH,在主控端执行命令或剧本时,根据 ansible.cfgforks 参数配置,生成目标主机数量的子进程,并发的向主机连接,执行 Ansible 生成的 Python 脚本,如下所示:

bash
# 以执行剧本为例,剧本内 hosts 包含 3 台目标主机
$ ansible-playbook playbook-filters-demo7.yml

# 观察进程树信息
$ pstree -apsl `ps -ef|grep ansible-playbook|head -n1|awk '{print $2}'`
systemd,1 --switched-root --system --deserialize 22
  └─sshd,1068 -D
      └─sshd,3014
          └─zsh,3017   # 当前 shell 终端
              └─ansible-playboo,9734 /usr/bin/ansible-playbook playbook-filters-demo7.yml  # 执行剧本父进程
                  ├─ansible-playboo,9744 /usr/bin/ansible-playbook playbook-filters-demo7.yml   、
                  │   # 子进程执行剧本,并向目标主机发起远程 SSH 连接,执行 Python 脚本
                  │   └─ssh,9782 -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User="root" -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/1320de7946 -tt sz-aliyun-ecs-1 /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1678103105.08-9744-133134583078117/AnsiballZ_command.py && sleep 0'
                  ├─ansible-playboo,9745 /usr/bin/ansible-playbook playbook-filters-demo7.yml
                  │   # 子进程执行剧本,并向目标主机发起远程 SSH 连接,执行 Python 脚本
                  │   └─ssh,9802 -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User="root" -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/65ae441d83 -tt bj-huawei-hecs-1 /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1678103105.16-9745-120153618180627/AnsiballZ_command.py && sleep 0'
                  ├─ansible-playboo,9747 /usr/bin/ansible-playbook playbook-filters-demo7.yml
                  │   # 子进程执行剧本,并向目标主机发起远程 SSH 连接,执行 Python 脚本
                  │   └─ssh,9801 -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User="root" -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/17827b13d8 -tt bj-tencent-lhins-1 /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1678103105.16-9747-104995554240311/AnsiballZ_command.py && sleep 0'
                  └─{ansible-playboo},974

在大规模场景下,PUSH 执行性能会比较,所以 ansible-pull 的作用就体现出来了,它主要有以下特点

  • 执行任务、剧本时不通过主控节点,而是通过远程 Git 仓库
  • 触发方式通常为 Crontab 或 其他

接下来,看具体示例,创建 ansible-pull-demo 仓库并拉取到本地

bash
$ git clone ansible-pull-demo

常见以下文件,local.yml 文件名是预定义的

bash
$ cat > local.yml << EOF
---
- hosts: all
  remote_user: root
  gather_facts: no
  tasks:
    - name: "echo datetime to file"
      shell: "echo $(date +'%Y-%m-%d %H:%M:%S') >> /tmp/ansible-pull-demo.txt"
    - name: "sleep"
      shell: "sleep 120"

EOF

主机清单

bash
$ cat > host << EOF
[local]
127.0.0.1
EOF

提交推送远程

bash
$ git add *
$ git commit -m "ansible pull demo"
$ git push origin master

接下来执行 git-pull 执行前需确保 本机 SSH 公钥已存储至远程 Git 仓库 或 Git 用户设置中

bash
$ ansible-pull -vvvv -U git@e.coding.net:LotusChing/ansible/ansible-pull-demo.git -i hosts
$ pstree -aspl 12308 
systemd,1 --system --deserialize 17
  └─sshd,1618 -D
      └─sshd,9190
          └─zsh,9193
              └─bash,9291
                  └─ansible-pull,12308 /usr/bin/ansible-pull -vvvv -U git@e.coding.net:LotusChing/ansible/ansible-pull-demo.git -i hosts
                      └─ansible-playboo,12367 /usr/bin/ansible-playbook -c local -vvvv /root/.ansible/pull/bj-huawei-hecs-1/local.yml -t all -l localhost,bj-huawei-hecs-1,127.0.0.1 -i hosts
                          ├─ansible-playboo,12407 /usr/bin/ansible-playbook -c local -vvvv /root/.ansible/pull/bj-huawei-hecs-1/local.yml -t all -l localhost,bj-huawei-hecs-1,127.0.0.1 -i hosts
                          │   └─sh,12421 -c /usr/bin/python /root/.ansible/tmp/ansible-tmp-1678105005.07-12407-197308829181260/AnsiballZ_command.py && sleep 0
                          │       └─python,12422 /root/.ansible/tmp/ansible-tmp-1678105005.07-12407-197308829181260/AnsiballZ_command.py
                          │           └─sleep,12423 120
                          └─{ansible-playboo},12375

总结一下,应用场景很少,大致了解下即可

六、ansible-config

编辑管理 ansible 配置,主要用途有三个,查看配置文件、DUMP 配置、查看所有参数配置

bash
$ ansible-config view
$ ansible-config dump
$ ansible-config list

七、ansible-inventory

查看主机清单信息

以文本图形展示主机(组)列表

bash
$ ansible-inventory --export --graph
@all:
  |--@bj_server:
  |  |--bj-huawei-hecs-1
  |--@db_server:
  |  |--sz-aliyun-ecs-1
  |--@dev:
  |  |--sz-aliyun-ecs-1
  |--@ecs:
  |  |--ecs-1.aliyun.sz
  |  |--huawei
  |--@maintenance:
  |  |--sz-aliyun-ecs-1
  |--@prod:
  |  |--bj-huawei-hecs-1
  |--@sz_server:
  |  |--sz-aliyun-ecs-1
  |--@ungrouped:
  |--@web_server:
  |  |--bj-huawei-hecs-1

展示指定清单文件中的主机列表

bash
$ ansible-inventory -i /etc/ansible/hosts --export --list
{
    "_meta": {
        "hostvars": {
            "ecs-1.aliyun.sz": {
                "ansible_host": "sz-aliyun-ecs-1"
            },
            "huawei": {
                "ansible_host": "bj-huawei-hecs-1"
            }
        }
    },
    "all": {
        "children": [
            "bj_server",
            "db_server",
            "dev",
            "ecs",
            "maintenance",
            "prod",
            "sz_server",
            "ungrouped",
            "web_server"
        ]
    },
    "bj_server": {
        "hosts": [
            "bj-huawei-hecs-1"
        ]
    },
    "db_server": {
        "hosts": [
            "sz-aliyun-ecs-1"
        ]
    },
    "dev": {
        "hosts": [
            "sz-aliyun-ecs-1"
        ]
    },
    "ecs": {
        "hosts": [
            "ecs-1.aliyun.sz",
            "huawei"
        ],
        "vars": {
            "server_type": "ecs"
        }
    },
    "maintenance": {
        "hosts": [
            "sz-aliyun-ecs-1"
        ]
    },
    "prod": {
        "hosts": [
            "bj-huawei-hecs-1"
        ]
    },
    "sz_server": {
        "hosts": [
            "sz-aliyun-ecs-1"
        ]
    },
    "web_server": {
        "hosts": [
            "bj-huawei-hecs-1"
        ]
    }
}

八、ansible-valut

Ansible Valut


文章作者: Da
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Da !