cpp
语法格式
ansible pattern [-i inventory] -m module -a argument
pattern 资产选择器
-i 指定资产清单文件的位置
-m 指定本次 Ansible ad-hoc 要执行的模块。可以类比成 SHELL 中的命令。
-a 模块的参数。可以类比成 SHELL 中的命令参数
Ansible Ad-Hoc(临时命令)是一次性、无持久化的 Ansible 命令行操作,用于快速执行简单的单条任务(如批量执行命令、拷贝文件、重启服务),无需编写 Playbook 剧本,是 Ansible 最基础、最常用的交互式使用方式。
1.查看模块文档
cpp
列举出所有的核心模块和附加模块
# ansible-doc -l
查询某个模块的使用方法
# ansible-doc modulename
查询某个模块的使用方法,比较简洁的信息
# ansible-doc -s modulename
2.常用模块
2.1Command&Shell模块
Command模块
默认使用的模块,无需指定
cpp
[root@Ansible-Manager ~]# touch hosts
[root@Ansible-Manager ~]# vim hosts
[root@Ansible-Manager ~]# cat hosts
[dbservers]
192.168.0.201
[webservers]
192.168.0.202
[root@Ansible-Manager ~]# ansible all -i hosts -a "echo 'hello'"
192.168.0.202 | CHANGED | rc=0 >>
hello
192.168.0.201 | CHANGED | rc=0 >>
hello
[root@Ansible-Manager ~]# ansible all -i hosts -a "hostname -i"
192.168.0.201 | CHANGED | rc=0 >>
fe80::20c:29ff:fe9c:616b%ens32 192.168.0.201 192.168.122.1
192.168.0.202 | CHANGED | rc=0 >>
fe80::20c:29ff:fee5:c0a4%ens32 192.168.0.202 192.168.122.1
[root@Ansible-Manager ~]#
Shell模块
cpp
[root@Ansible-Manager ~]# ansible all -i hosts -m shell -a "hostname -i"
192.168.0.201 | CHANGED | rc=0 >>
fe80::20c:29ff:fe9c:616b%ens32 192.168.0.201 192.168.122.1
192.168.0.202 | CHANGED | rc=0 >>
fe80::20c:29ff:fee5:c0a4%ens32 192.168.0.202 192.168.122.1
[root@Ansible-Manager ~]#
区别
- shell 模块可以执行 SHELL 的内置命令和特性(比如管道符)。
- command 模块无法执行 SHELL 的内置命令和特性。
2.2Script模块
cpp
[root@Ansible-Manager ~]# echo "touch /tmp/testfile" > a.sh
[root@Ansible-Manager ~]# cat a.sh
touch /tmp/testfile
[root@Ansible-Manager ~]# ansible webservers -i hosts -m script -a "/root/a.sh"
192.168.0.202 | CHANGED => {
"changed": true,
"rc": 0,
"stderr": "Shared connection to 192.168.0.202 closed.\r\n",
"stderr_lines": [
"Shared connection to 192.168.0.202 closed."
],
"stdout": "",
"stdout_lines": []
}
[root@Ansible-Manager ~]# ansible webservers -i hosts -m shell -a "ls -l /tmp/"
192.168.0.202 | CHANGED | rc=0 >>
total 0
-rw-r--r-- 1 root root 0 Feb 28 16:27 a.conf
drwx------ 2 root root 41 Feb 28 19:04 ansible_command_payload_E1WMFM
drwx------ 3 root root 17 Feb 28 15:26 systemd-private-9694a1c5e86e4f459cf4f5ca1be13886-bolt.service-qIgwrS
drwx------ 3 root root 17 Feb 28 15:26 systemd-private-9694a1c5e86e4f459cf4f5ca1be13886-chronyd.service-j31iWd
drwx------ 3 root root 17 Feb 28 15:26 systemd-private-9694a1c5e86e4f459cf4f5ca1be13886-colord.service-noRaQI
drwx------ 3 root root 17 Feb 28 15:26 systemd-private-9694a1c5e86e4f459cf4f5ca1be13886-cups.service-5df7w8
drwx------ 3 root root 17 Feb 28 15:26 systemd-private-9694a1c5e86e4f459cf4f5ca1be13886-rtkit-daemon.service-CoVTO7
-rw-r--r-- 1 root root 0 Feb 28 19:03 testfile
drwx------ 2 root root 6 Feb 28 15:26 vmware-root_6161-1949770682
[root@Ansible-Manager ~]#
2.3Copy模块
常用参数:
- src 指定拷贝文件的源地址
- dest 指定拷贝文件的目标地址
- backup 拷贝文件前,若原目标文件发生了变化,则对目标文件进行备份
- owner 指定新拷贝文件的所有者
- group 指定新拷贝文件的所有组
- mode 指定新拷贝文件的权限
复制文件
cpp
[root@Ansible-Manager ~]# touch /tmp/nginx.repo
[root@Ansible-Manager ~]# vim /tmp/nginx.repo
[root@Ansible-Manager ~]# cat /tmp/nginx.repo
5432154321543215432154321
[root@Ansible-Manager ~]# ansible webservers -i hosts -m copy -a "src=/tmp/nginx.repo dest=/tmp/nginx.repo"
192.168.0.202 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "7796e02efdebbb04dba01e77dd56732d5006233b",
"dest": "/tmp/nginx.repo",
"gid": 0,
"group": "root",
"md5sum": "aaf717fff28f5bf1420e51f94dbc4f03",
"mode": "0644",
"owner": "root",
"size": 26,
"src": "/root/.ansible/tmp/ansible-tmp-1772363993.93-100940-223404070296429/source",
"state": "file",
"uid": 0
}
[root@Ansible-Manager ~]# ansible webservers -i hosts -m shell -a "ls /tmp/"
192.168.0.202 | CHANGED | rc=0 >>
a.conf
ansible_command_payload_4xhEoA
nginx.repo
systemd-private-9694a1c5e86e4f459cf4f5ca1be13886-bolt.service-qIgwrS
systemd-private-9694a1c5e86e4f459cf4f5ca1be13886-chronyd.service-j31iWd
systemd-private-9694a1c5e86e4f459cf4f5ca1be13886-colord.service-noRaQI
systemd-private-9694a1c5e86e4f459cf4f5ca1be13886-cups.service-5df7w8
systemd-private-9694a1c5e86e4f459cf4f5ca1be13886-rtkit-daemon.service-CoVTO7
testfile
vmware-root_6161-1949770682
[root@Ansible-Manager ~]#
复制文件并备份
cpp
webservers主机上的文件已经修改
[root@Ansi-Node02 ~]# vim /tmp/nginx.repo
[root@Ansi-Node02 ~]# cat /tmp/nginx.repo
5432154321543215432154321
ansible
[root@Ansi-Node02 ~]#
加上backup参数会产生备份
[root@Ansible-Manager ~]# ansible webservers -i hosts -m copy -a "src=/tmp/nginx.repo dest=/tmp/nginx.repo backup=yes"
192.168.0.202 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"backup_file": "/tmp/nginx.repo.22408.2026-02-28@19:27:38~",
"changed": true,
"checksum": "7796e02efdebbb04dba01e77dd56732d5006233b",
"dest": "/tmp/nginx.repo",
"gid": 0,
"group": "root",
"md5sum": "aaf717fff28f5bf1420e51f94dbc4f03",
"mode": "0644",
"owner": "root",
"size": 26,
"src": "/root/.ansible/tmp/ansible-tmp-1772364455.57-103088-217827481798404/source",
"state": "file",
"uid": 0
}
[root@Ansible-Manager ~]#
指定属主、属组、权限
cpp
[root@Ansible-Manager ~]# ansible all -i hosts -m copy -a "src=/tmp/nginx.repo dest=/tmp/nginx.repo owner=nobody group=nobody mode=0755"
192.168.0.202 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "7796e02efdebbb04dba01e77dd56732d5006233b",
"dest": "/tmp/nginx.repo",
"gid": 99,
"group": "nobody",
"mode": "0755",
"owner": "nobody",
"path": "/tmp/nginx.repo",
"size": 26,
"state": "file",
"uid": 99
}
192.168.0.201 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "7796e02efdebbb04dba01e77dd56732d5006233b",
"dest": "/tmp/nginx.repo",
"gid": 99,
"group": "nobody",
"md5sum": "aaf717fff28f5bf1420e51f94dbc4f03",
"mode": "0755",
"owner": "nobody",
"size": 26,
"src": "/root/.ansible/tmp/ansible-tmp-1772364807.95-105227-68886312465789/source",
"state": "file",
"uid": 99
}
[root@Ansible-Manager ~]#
主机1上
[root@Ansi-Node01 ~]# ls -l /tmp/nginx.repo
-rwxr-xr-x 1 nobody nobody 26 Feb 28 19:33 /tmp/nginx.repo
[root@Ansi-Node01 ~]#
2.4Repository模块
name 参数:
- 模块通过这个参数定位
.repo文件中[epel]开头的配置段; - 注意:
name必须与.repo中仓库段的名称完全一致(区分大小写),否则模块无法匹配。
baseurl + description 参数:
baseurl指定了 EPEL 源的具体地址;description是可选的「备注信息」,最终会写入.repo文件的name=字段
state 参数:
state=absent表示「删除name=epel对应的仓库条目」;- 若省略
state,模块默认使用state=present,即「创建 / 更新该仓库条目」。
安装epel源
cpp
[root@Ansible-Manager ~]# ansible dbservers -i hosts -m yum_repository -a "name=epel baseurl='https://download.fedoraproject.org/pub/epel/$releasever/$basearch/' description='EPEL YUM repo'"
192.168.0.201 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"repo": "epel",
"state": "present"
}
[root@Ansible-Manager ~]#
[root@Ansi-Node01 ~]# cd /etc/yum.repos.d/
[root@Ansi-Node01 yum.repos.d]# ls
bak CentOS-Base.repo CentOS-Base.repo.backup epel.repo
[root@Ansi-Node01 yum.repos.d]# cat epel.repo
[epel-debuginfo]
name = Extra Packages for Enterprise Linux 7 - $basearch - Debug
baseurl = http://mirrors.aliyun.com/epel/7/$basearch/debug
failovermethod = priority
enabled = 0
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
gpgcheck = 0
[epel-source]
name = Extra Packages for Enterprise Linux 7 - $basearch - Source
baseurl = http://mirrors.aliyun.com/epel/7/SRPMS
failovermethod = priority
enabled = 0
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
gpgcheck = 0
[epel]
baseurl = https://download.fedoraproject.org/pub/epel///
name = EPEL YUM repo
[root@Ansi-Node01 yum.repos.d]#
删除epel源
cpp
[root@Ansible-Manager ~]# ansible dbservers -i hosts -m yum_repository -a "name=epel state=absent"
192.168.0.201 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"repo": "epel",
"state": "absent"
}
[root@Ansi-Node01 yum.repos.d]# cat epel.repo
[epel-debuginfo]
name = Extra Packages for Enterprise Linux 7 - $basearch - Debug
baseurl = http://mirrors.aliyun.com/epel/7/$basearch/debug
failovermethod = priority
enabled = 0
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
gpgcheck = 0
[epel-source]
name = Extra Packages for Enterprise Linux 7 - $basearch - Source
baseurl = http://mirrors.aliyun.com/epel/7/SRPMS
failovermethod = priority
enabled = 0
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
gpgcheck = 0
2.5Yum模块
state的值:安装present、installed,最新版本latest,移除absent、removed
安装nginx
cpp
[root@Ansible-Manager ~]# ansible webservers -i hosts -m yum -a "name=nginx state=present"
[root@Ansi-Node02 ~]# nginx -v
nginx version: nginx/1.20.1
安装软件包组,一定要加@符号
cpp
[root@Ansible-Manager ~]# ansible webservers -i hosts -m yum -a "name='@Development tools' state=present"
2.6Systemd模块
常用参数:
- daemon_reload 重新载入 systemd,扫描新的或有变动的单元
- enabled 是否开机自启动 yes|no
- name 必选项,服务名称,比如 httpd vsftpd
- state 对当前服务执行启动、停止、重启、重新加载等操作(started,stopped,restarted,reloaded)
重新加载系统信息
cpp
[root@Ansible-Manager ~]# ansible webservers -i hosts -m systemd -a "daemon_reload=yes"
启动nginx服务
cpp
[root@Ansible-Manager ~]# ansible webservers -i hosts -m systemd -a "name=nginx state=started"
关闭nginx服务
cpp
[root@Ansible-Manager ~]# ansible webservers -i hosts -m systemd -a "name=nginx state=stopped"
2.7Group模块
常用参数:
- name 组名称,必须的
- system 是否为系统组,yes/no,默认是 no
- state 删除或这创建,present/absent,默认是 present
cpp
[root@Ansible-Manager ~]# ansible dbservers -i hosts -m group -a "name=db_admin"
192.168.0.201 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"gid": 1003,
"name": "db_admin",
"state": "present",
"system": false
}
[root@Ansi-Node01 yum.repos.d]# tail /etc/group
tcpdump:x:72:
stapusr:x:156:
stapsys:x:157:
stapdev:x:158:
admin:x:1000:
nginx:x:982:
testusr1:x:1001:
testusr2:x:1002:
apache:x:48:
db_admin:x:1003:
2.8User模块
创建用户,设置密码
cpp
[root@Ansible-Manager ~]# pass=$(echo "123456" | openssl passwd -1 -stdin)
[root@Ansible-Manager ~]# echo $pass
$1$JkfsI9QN$OtJ0ux2weIPLuukXKWqZV/
[root@Ansible-Manager ~]#
[root@Ansible-Manager ~]# ansible all -i hosts -m user -a"name=foo password=${pass}"
192.168.0.201 | 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": 1003
}
192.168.0.202 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"comment": "",
"create_home": true,
"group": 1003,
"home": "/home/foo",
"name": "foo",
"password": "NOT_LOGGING_PASSWORD",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 1003
}
[root@Ansi-Node02 ~]# tail /etc/passwd
avahi:x:70:70:Avahi mDNS/DNS-SD Stack:/var/run/avahi-daemon:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
admin:x:1000:1000:admin:/home/admin:/bin/bash
nginx:x:988:982:Nginx web server:/var/lib/nginx:/sbin/nologin
testusr1:x:1001:1001::/home/testusr1:/bin/bash
testusr2:x:1002:1002::/home/testusr2:/bin/bash
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
foo:x:1003:1003::/home/foo:/bin/bash
[root@Ansi-Node02 ~]# tail /etc/shadow
avahi:!!:20496::::::
postfix:!!:20496::::::
ntp:!!:20496::::::
tcpdump:!!:20496::::::
admin:$1$F/3i33GV$oCZ.GRJQicRzn3KAa.ois.:20496:0:99999:7:::
nginx:!!:20496::::::
testusr1:!!:20497:0:99999:7:::
testusr2:!!:20497:0:99999:7:::
apache:!!:20497::::::
foo:$1$JkfsI9QN$OtJ0ux2weIPLuukXKWqZV/:20512:0:99999:7:::
创建用户,生成密钥对
cpp
[root@Ansible-Manager ~]# ansible all -i hosts -m user -a"name=yangge generate_ssh_key=yes ssh_key_type=ecdsa"
192.168.0.201 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"comment": "",
"create_home": true,
"group": 1005,
"home": "/home/yangge",
"name": "yangge",
"shell": "/bin/bash",
"ssh_fingerprint": "256 SHA256:1ro5MZOvLnEhU1cYKW7s2KNv05wQsYG/glqBODojZqg ansible-generated on Ansi-Node01 (ECDSA)",
"ssh_key_file": "/home/yangge/.ssh/id_ecdsa",
"ssh_public_key": "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCvoVMN0Qsb1XRfehhyKyMjEGc8MtoEponit8fl4FwYwuEP+UgUDpcN6ja/LCe6c0mi5AXtm0do1FBmiO93UXgQ= ansible-generated on Ansi-Node01",
"state": "present",
"system": false,
"uid": 1004
}
192.168.0.202 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"comment": "",
"create_home": true,
"group": 1004,
"home": "/home/yangge",
"name": "yangge",
"shell": "/bin/bash",
"ssh_fingerprint": "256 SHA256:hfKGEUiQHre1mhdbIJ0CfRPereBLtBuqaCs7ax8CfRI ansible-generated on Ansi-Node02 (ECDSA)",
"ssh_key_file": "/home/yangge/.ssh/id_ecdsa",
"ssh_public_key": "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLz4ViZQLCaTgRTqsum3F0GXmCAWgbKxMOWwtrnursC3IGPauvZ6bOwrKy79Ux+QR0IJjwAprFLasDBm2Z9oH54= ansible-generated on Ansi-Node02",
"state": "present",
"system": false,
"uid": 1004
}
[root@Ansi-Node01 yum.repos.d]# ls /home/yangge/.ssh
id_ecdsa id_ecdsa.pub
创建用户,设置过期时间
cpp
[root@Ansible-Manager ~]# ansible dbservers -i hosts -m user -a "name=tom expires=$(date +%s -d 20260302) groups=db_admin append=yes"
192.168.0.201 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"comment": "",
"create_home": true,
"group": 1006,
"groups": "db_admin",
"home": "/home/tom",
"name": "tom",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 1005
}
[root@Ansi-Node01 yum.repos.d]# id tom
uid=1005(tom) gid=1006(tom) groups=1006(tom),1003(db_admin)
[root@Ansi-Node01 yum.repos.d]# tail /etc/shadow
ntp:!!:20496::::::
tcpdump:!!:20496::::::
admin:$1$rOFKGEDn$iPOFohbDt5CDIui2pc3cS1:20496:0:99999:7:::
nginx:!!:20496::::::
testusr1:!!:20497:0:99999:7:::
testusr2:!!:20497:0:99999:7:::
apache:!!:20497::::::
foo:$1$JkfsI9QN$OtJ0ux2weIPLuukXKWqZV/:20512:0:99999:7:::
yangge:!!:20512:0:99999:7:::
tom:!!:20512:0:99999:7::20513:
2.9file模块
file 模块主要用于远程主机上的文件操作。
cpp
// 创建一个文件
# ansible all -i hosts -m file -a "path=/tmp/foo.conf state=touch"
// 改变文件所有者及权限
# ansible all -i hosts -m file -a "path=/tmp/foo.conf owner=nobody group=nobody mode=0644"
// 创建一个软连接
# ansible all -i hosts -m file -a "src=/tmp/foo.conf dest=/tmp/link.conf state=link"
// 创建一个目录
# ansible all -i hosts -m file -a "path=/tmp/testdir state=directory"
// 取消一个连接
# ansible all -i hosts -m file -a "path=/tmp/link.conf state=absent"
// 删除一个文件
# ansible all -i hosts -m file -a "path=/tmp/foo.conf state=absent"
2.10cron模块
等同于Linux中的计划任务
cpp
// 新建一个 CRON JOB 任务
# ansible all -i hosts -m cron -a "name='create new job' minute='0' job='ls -alh > /dev/null'"
// 删除一个 CRON JOB 任务,删除时,一定要正确指定job 的name参数,以免误删除。
# ansible all -i hosts -m cron -a "name='create new job' state=absent"
// 验证cron
[root@localhost ~]# crontab -l
#Ansible: create new job
0 * * * * ls -alh > /dev/null
2.11debug模块
debug 模块主要用于调试时使用,通常的作用是将一个变量的值打印出来。
cpp
[root@localhost ~]# ansible all -i hosts -m debug -a "var=role" -e "role=web"
192.168.95.131 | SUCCESS => {
"role": "web"
}
192.168.95.132 | SUCCESS => {
"role": "web"
}
[root@localhost ~]# ansible all -i hosts -m debug -a "msg='role is {{role}}'" -e "role=web"
192.168.95.132 | SUCCESS => {
"msg": "role is web"
}
192.168.95.131 | SUCCESS => {
"msg": "role is web"
}
2.12template模块
用法其实和 copy 模块基本一样,template 模块的强大之处就是使用变量替换,就是可以把传递给 Ansible 的变量的值替换到模板文件中。
cpp
1. 建立一个 template 文件,名为 hello_world.j2
# cat hello_world.j2
Hello {{var}} !
2. 执行命令,并且设置变量 var 的值为 world
# ansible all -i hosts -m template -a "src=hello_world.j2 dest=/tmp/hello_world.world" -e "var=world"
3. 在被控主机上验证
# cat /tmp/hello_world.world
Hello world !
2.13lineinfile模块
对管理节点的文件中符合正则表达式的最后一行删除或替换
cpp
删除被控节点文件里的某一条内容
[root@localhost ~]# ansible dbservers -i hosts -m lineinfile -a "path=/etc/sudoers regexp='^%wheel' state=absent"
192.168.95.131 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"backup": "",
"changed": true,
"found": 1,
"msg": "1 line(s) removed"
}
替换某一行
[root@localhost ~]# ansible dbservers -i hosts -m lineinfile -a "path=/etc/selinux/config regexp='^SELINUX=' line='SELINUX=disabled' state=present"
192.168.95.131 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"backup": "",
"changed": true,
"msg": "line replaced"
}