前言
官方介绍:针对一组主机定义并运行单个任务“剧本”工具;个人理解ansible 是Ansible-hoc功能的程序入口,即简单临时命令;
命令格式
ansible [group|host] [options] # group:组名,可以使用all来表示所有组与主机 # host:主机名或者主机地址,多主机用逗号隔开 # options:ansible程序选项
选项
常用实例
指定模块来进行操作远程主机
[[email protected] ~]# ansible 192.168.10.170 -m ping 192.168.10.170 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" }
通过-m选项调用ping模块,对被管理端执行ping操作
向指定的模块传递参数
[[email protected] ~]# ansible 192.168.10.170 -m shell -a "ip addr" 192.168.10.170 | CHANGED | rc=0 >> 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 52:54:00:b5:b5:32 brd ff:ff:ff:ff:ff:ff inet 192.168.10.170/24 brd 192.168.10.255 scope global noprefixroute dynamic ens3 valid_lft 23730sec preferred_lft 23730sec inet6 fe80::2f78:3130:6efe:4b3a/64 scope link noprefixroute valid_lft forever preferred_lft forever
通过-a传递的参数必须是-m指定模块能传递的参数;如果是shell或者command模块调用的执行命令,尽量用绝对路径操作
设置远程连接最大超时时间
比如你确定公司环境正常不会发生网络问题,ssh反向解析已经关闭;正常连接在几秒之内,这个时候如果服务或者主机连接时间特别长,或者你觉得根本就用不到那么长时间,是不是可以将连接超时的时间缩小或者放大?
[[email protected] ~]# ansible 192.168.10.170 -T 20 -m command -a "ip addr" 192.168.10.170 | UNREACHABLE! => { "changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 192.168.10.170 port 22: Connection refused", "unreachable": true }
个人感觉这条指令作用不大
权限提升操作
比如现在公司都用的统一普通账户进行运维的工作,你要做批量部署或者批量管理,这个时候ansible临时命令应该用哪些方式及参数?
[[email protected] ~]# ansible 192.168.10.170 -m shell -a "ls -lah /root/" -u test1 192.168.10.170 | FAILED | rc=2 >> ls: cannot open directory /root/: Permission deniednon-zero return code [[email protected]-master01 ~]# ansible 192.168.10.170 -m shell -a "ls -lah /root/" -u test1 -b --become-method sudo -K BECOME password: 192.168.10.170 | CHANGED | rc=0 >> total 28K dr-xr-x---. 5 root root 185 Mar 8 20:16 . dr-xr-xr-x. 17 root root 224 Jan 18 14:36 .. -rw-------. 1 root root 1.4K Jan 18 14:37 anaconda-ks.cfg drwx------ 3 root root 17 Mar 3 14:51 .ansible drwxr-xr-x 2 root root 85 Mar 8 19:22 .ansible_async -rw-------. 1 root root 514 Mar 8 20:14 .bash_history -rw-r--r--. 1 root root 18 Dec 29 2013 .bash_logout -rw-r--r--. 1 root root 176 Dec 29 2013 .bash_profile -rw-r--r--. 1 root root 176 Dec 29 2013 .bashrc -rw-r--r--. 1 root root 100 Dec 29 2013 .cshrc drwx------ 2 root root 29 Mar 8 19:15 .ssh -rw-r--r--. 1 root root 129 Dec 29 2013 .tcshrc [[email protected]-master01 ~]# ansible 192.168.10.170 -m shell -a "ls -lah /root/" -u test1 -b --become-method su --become-user root -K BECOME password: 192.168.10.170 | CHANGED | rc=0 >> total 28K dr-xr-x---. 5 root root 185 Mar 8 20:16 . dr-xr-xr-x. 17 root root 224 Jan 18 14:36 .. -rw-------. 1 root root 1.4K Jan 18 14:37 anaconda-ks.cfg drwx------ 3 root root 17 Mar 3 14:51 .ansible drwxr-xr-x 2 root root 85 Mar 8 19:22 .ansible_async -rw-------. 1 root root 514 Mar 8 20:14 .bash_history -rw-r--r--. 1 root root 18 Dec 29 2013 .bash_logout -rw-r--r--. 1 root root 176 Dec 29 2013 .bash_profile -rw-r--r--. 1 root root 176 Dec 29 2013 .bashrc -rw-r--r--. 1 root root 100 Dec 29 2013 .cshrc drwx------ 2 root root 29 Mar 8 19:15 .ssh -rw-r--r--. 1 root root 129 Dec 29 2013 .tcshrc
详情示例
第一次执行ansible的时候并没有成功,因为我是用test1用户去进行/root目录内容的查看,当然无法查看;
第二次执行ansible的时候成功了,采用的是sudo的方式进行权限的提升
第三次执行ansible的时候也成功了,采用的是su的方式进行权限的提升
参数讲解:
- -u:指定被管理端用来远程的用户,我这里是test1
- -b:开启权限提升模式,如果不用-b的话,后面的这些参数其实都没什么意义。
- --become-method:指定采用何种方式进行权限的提升,方式有很多,常用的是su及sudo
- --become-user:指定提升权限的用户,我想一般也只有提升权限的模式是su的时候才会用到吧,当然眼界仅限于我个人
- -K:提升权限时需要输入的密码,如果你是sudo,并在被管理端的配置文件中设置了nopasswd,这里就不需要这个选项啦
设置程序最大远程执行时间
[[email protected] ~]# ansible 192.168.10.170 -m shell -a "sleep 15" -B 5 -P 2 -T 2 192.168.10.170 | FAILED | rc=-1 >> async task did not complete within the requested time - 5s [[email protected]-master01 ~]# ansible 192.168.10.170 -m shell -a "sleep 4" -B 5 -P 2 -T 2 192.168.10.170 | CHANGED => { "ansible_job_id": "660579276690.8952", "changed": true, "cmd": "sleep 4", "delta": "0:00:04.009900", "end": "2020-03-08 20:33:39.079987", "finished": 1, "rc": 0, "start": "2020-03-08 20:33:35.070087", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": [] }
上面可以看到,我第一次将最大超时时间设置成5S、sleep 15;ansible告诉我最大请求反馈时间是5S。第二次我将sleep 改成4秒就正常了。上述参数选项说明如下:
- -B:设置程序最大远程执行时间,并且放入后台执行。
- -P:设置执行过程的轮询时间,这个参数是配合-B参数使用的;这里每2秒检查一下任务的执行进度
- -T:设置ssh连接超时时间,意味着2秒如果没有连接上,任务失败。
异步并行任务
多台机器需要同时远程操作时,可以用-f来进行ansible任务的fork;
[[email protected] ~]# ansible 192.168.10.* -m shell -a "sleep 4" -f 3 192.168.10.170 | CHANGED | rc=0 >> 192.168.10.186 | CHANGED | rc=0 >> [[email protected]-master01 ~]# ansible 192.168.10.* -m shell -a "sleep 4" -f 1 192.168.10.186 | CHANGED | rc=0 >> 192.168.10.170 | CHANGED | rc=0 >>
这里显示不明白,因为这里显示不出时间的先后,这里打字说明一下情况吧;第一次-f指定fork最大数3时,表示最多有3个节点可以并行执行,第二次-f指定fork数最大数是1是,表示只能一个节点一个节点的执行,但是这里要注意一下,如果你多个并行任务执行,ansible读取地址执行任务时在最大连接范围内可能不分先后,这个并行执行的先后顺序要注意一下
压缩输出参数
[[email protected] ~]# ansible 192.168.10.170 -m shell -a "ip addr" -o 192.168.10.170 | CHANGED | rc=0 | (stdout) 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000\n link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\n inet 127.0.0.1/8 scope host lo\n valid_lft forever preferred_lft forever\n inet6 ::1/128 scope host \n valid_lft forever preferred_lft forever\n2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000\n link/ether 52:54:00:b5:b5:32 brd ff:ff:ff:ff:ff:ff\n inet 192.168.10.170/24 brd 192.168.10.255 scope global noprefixroute dynamic ens3\n valid_lft 40946sec preferred_lft 40946sec\n inet6 fe80::2f78:3130:6efe:4b3a/64 scope link noprefixroute \n valid_lft forever preferred_lft forever [[email protected]-master01 ~]# ansible 192.168.10.170 -m shell -a "ip addr" 192.168.10.170 | CHANGED | rc=0 >> 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 52:54:00:b5:b5:32 brd ff:ff:ff:ff:ff:ff inet 192.168.10.170/24 brd 192.168.10.255 scope global noprefixroute dynamic ens3 valid_lft 40941sec preferred_lft 40941sec inet6 fe80::2f78:3130:6efe:4b3a/64 scope link noprefixroute valid_lft forever preferred_lft forever
实例详情
这里可以看出,使用-o选项之后,ansible默认把系统默认的\n符号转换成了普通字符串\n;减少了输出的空内容
详细输出参数
有时候你想知道连接的详情,就通过-v或者N个-v选项来进行查看;例如:ansible的版本、配置文件、模块搜索路径、使用的python默认模块、python版本、调用的Inventory文件及连接的详情等等。
[[email protected] ~]# ansible 192.168.10.170 -m shell -a "ip addr" -vvv ansible 2.9.3 config file = /etc/ansible/ansible.cfg configured module search path = [u‘/root/.ansible/plugins/modules‘, u‘/usr/share/ansible/plugins/modules‘] ansible python module location = /usr/lib/python2.7/site-packages/ansible executable location = /usr/bin/ansible python version = 2.7.5 (default, Oct 30 2018, 23:45:53) [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)] Using /etc/ansible/ansible.cfg as config file host_list declined parsing /etc/ansible/hosts as it did not pass its verify_file() method script declined parsing /etc/ansible/hosts as it did not pass its verify_file() method auto declined parsing /etc/ansible/hosts as it did not pass its verify_file() method Parsed /etc/ansible/hosts inventory source with ini plugin META: ran handlers <192.168.10.170> ESTABLISH SSH CONNECTION FOR USER: None <192.168.10.170> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/a9efa3225b 192.168.10.170 ‘/bin/sh -c ‘"‘"‘echo ~ && sleep 0‘"‘"‘‘ <192.168.10.170> (0, ‘/root\n‘, ‘‘) <192.168.10.170> ESTABLISH SSH CONNECTION FOR USER: None <192.168.10.170> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/a9efa3225b 192.168.10.170 ‘/bin/sh -c ‘"‘"‘( umask 77 && mkdir -p "` echo /root/.ansible/tmp/ansible-tmp-1583671586.84-124750328385027 `" && echo ansible-tmp-1583671586.84-124750328385027="` echo /root/.ansible/tmp/ansible-tmp-1583671586.84-124750328385027 `" ) && sleep 0‘"‘"‘‘ <192.168.10.170> (0, ‘ansible-tmp-1583671586.84-124750328385027=/root/.ansible/tmp/ansible-tmp-1583671586.84-124750328385027\n‘, ‘‘) <192.168.10.170> Attempting python interpreter discovery <192.168.10.170> ESTABLISH SSH CONNECTION FOR USER: None <192.168.10.170> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/a9efa3225b 192.168.10.170 ‘/bin/sh -c ‘"‘"‘echo PLATFORM; uname; echo FOUND; command -v ‘"‘"‘"‘"‘"‘"‘"‘"‘/usr/bin/python‘"‘"‘"‘"‘"‘"‘"‘"‘; command -v ‘"‘"‘"‘"‘"‘"‘"‘"‘python3.7‘"‘"‘"‘"‘"‘"‘"‘"‘; command -v ‘"‘"‘"‘"‘"‘"‘"‘"‘python3.6‘"‘"‘"‘"‘"‘"‘"‘"‘; command -v ‘"‘"‘"‘"‘"‘"‘"‘"‘python3.5‘"‘"‘"‘"‘"‘"‘"‘"‘; command -v ‘"‘"‘"‘"‘"‘"‘"‘"‘python2.7‘"‘"‘"‘"‘"‘"‘"‘"‘; command -v ‘"‘"‘"‘"‘"‘"‘"‘"‘python2.6‘"‘"‘"‘"‘"‘"‘"‘"‘; command -v ‘"‘"‘"‘"‘"‘"‘"‘"‘/usr/libexec/platform-python‘"‘"‘"‘"‘"‘"‘"‘"‘; command -v ‘"‘"‘"‘"‘"‘"‘"‘"‘/usr/bin/python3‘"‘"‘"‘"‘"‘"‘"‘"‘; command -v ‘"‘"‘"‘"‘"‘"‘"‘"‘python‘"‘"‘"‘"‘"‘"‘"‘"‘; echo ENDFOUND && sleep 0‘"‘"‘‘ <192.168.10.170> (0, ‘PLATFORM\nLinux\nFOUND\n/usr/bin/python\n/usr/bin/python2.7\n/usr/libexec/platform-python\n/usr/bin/python\nENDFOUND\n‘, ‘‘) <192.168.10.170> ESTABLISH SSH CONNECTION FOR USER: None <192.168.10.170> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/a9efa3225b 192.168.10.170 ‘/bin/sh -c ‘"‘"‘/usr/bin/python && sleep 0‘"‘"‘‘ <192.168.10.170> (0, ‘{"osrelease_content": "NAME=\\"CentOS Linux\\"\\nVERSION=\\"7 (Core)\\"\\nID=\\"centos\\"\\nID_LIKE=\\"rhel fedora\\"\\nVERSION_ID=\\"7\\"\\nPRETTY_NAME=\\"CentOS Linux 7 (Core)\\"\\nANSI_COLOR=\\"0;31\\"\\nCPE_NAME=\\"cpe:/o:centos:centos:7\\"\\nHOME_URL=\\"https://www.centos.org/\\"\\nBUG_REPORT_URL=\\"https://bugs.centos.org/\\"\\n\\nCENTOS_MANTISBT_PROJECT=\\"CentOS-7\\"\\nCENTOS_MANTISBT_PROJECT_VERSION=\\"7\\"\\nREDHAT_SUPPORT_PRODUCT=\\"centos\\"\\nREDHAT_SUPPORT_PRODUCT_VERSION=\\"7\\"\\n\\n", "platform_dist_result": ["centos", "7.6.1810", "Core"]}\n‘, ‘‘) Using module file /usr/lib/python2.7/site-packages/ansible/modules/commands/command.py <192.168.10.170> PUT /root/.ansible/tmp/ansible-local-84126c23rI/tmpPTfumZ TO /root/.ansible/tmp/ansible-tmp-1583671586.84-124750328385027/AnsiballZ_command.py <192.168.10.170> SSH: EXEC sftp -b - -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/a9efa3225b ‘[192.168.10.170]‘ <192.168.10.170> (0, ‘sftp> put /root/.ansible/tmp/ansible-local-84126c23rI/tmpPTfumZ /root/.ansible/tmp/ansible-tmp-1583671586.84-124750328385027/AnsiballZ_command.py\n‘, ‘‘) <192.168.10.170> ESTABLISH SSH CONNECTION FOR USER: None <192.168.10.170> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/a9efa3225b 192.168.10.170 ‘/bin/sh -c ‘"‘"‘chmod u+x /root/.ansible/tmp/ansible-tmp-1583671586.84-124750328385027/ /root/.ansible/tmp/ansible-tmp-1583671586.84-124750328385027/AnsiballZ_command.py && sleep 0‘"‘"‘‘ <192.168.10.170> (0, ‘‘, ‘‘) <192.168.10.170> ESTABLISH SSH CONNECTION FOR USER: None <192.168.10.170> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/a9efa3225b -tt 192.168.10.170 ‘/bin/sh -c ‘"‘"‘/usr/bin/python /root/.ansible/tmp/ansible-tmp-1583671586.84-124750328385027/AnsiballZ_command.py && sleep 0‘"‘"‘‘ <192.168.10.170> (0, ‘\r\n{"changed": true, "end": "2020-03-08 20:46:27.852608", "stdout": "1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000\\n link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\\n inet 127.0.0.1/8 scope host lo\\n valid_lft forever preferred_lft forever\\n inet6 ::1/128 scope host \\n valid_lft forever preferred_lft forever\\n2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000\\n link/ether 52:54:00:b5:b5:32 brd ff:ff:ff:ff:ff:ff\\n inet 192.168.10.170/24 brd 192.168.10.255 scope global noprefixroute dynamic ens3\\n valid_lft 40845sec preferred_lft 40845sec\\n inet6 fe80::2f78:3130:6efe:4b3a/64 scope link noprefixroute \\n valid_lft forever preferred_lft forever", "cmd": "ip addr", "rc": 0, "start": "2020-03-08 20:46:27.843091", "stderr": "", "delta": "0:00:00.009517", "invocation": {"module_args": {"creates": null, "executable": null, "_uses_shell": true, "strip_empty_ends": true, "_raw_params": "ip addr", "removes": null, "argv": null, "warn": true, "chdir": null, "stdin_add_newline": true, "stdin": null}}}\r\n‘, ‘Shared connection to 192.168.10.170 closed.\r\n‘) <192.168.10.170> ESTABLISH SSH CONNECTION FOR USER: None <192.168.10.170> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/a9efa3225b 192.168.10.170 ‘/bin/sh -c ‘"‘"‘rm -f -r /root/.ansible/tmp/ansible-tmp-1583671586.84-124750328385027/ > /dev/null 2>&1 && sleep 0‘"‘"‘‘ <192.168.10.170> (0, ‘‘, ‘‘) 192.168.10.170 | CHANGED | rc=0 >> 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 52:54:00:b5:b5:32 brd ff:ff:ff:ff:ff:ff inet 192.168.10.170/24 brd 192.168.10.255 scope global noprefixroute dynamic ens3 valid_lft 40845sec preferred_lft 40845sec inet6 fe80::2f78:3130:6efe:4b3a/64 scope link noprefixroute valid_lft forever preferred_lft forever META: ran handlers META: ran handlers
实例详情
日志输出目录参数
[[email protected] ~]# ansible 192.168.10.170 -m shell -a "ip addr" -t /opt/file 192.168.10.170 | CHANGED | rc=0 >> 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 52:54:00:b5:b5:32 brd ff:ff:ff:ff:ff:ff inet 192.168.10.170/24 brd 192.168.10.255 scope global noprefixroute dynamic ens3 valid_lft 39575sec preferred_lft 39575sec inet6 fe80::2f78:3130:6efe:4b3a/64 scope link noprefixroute valid_lft forever preferred_lft forever
-t参数会在指定的目录生成输出文件,文件内容是按照压缩格式输出的,如果目录不存在会创建目录,并且在目录下面按照IP地址生成文件
排除主机或者组
[[email protected] ~]# ansible all -m ping -l test2 192.168.10.186 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } [[email protected]-master01 ~]# ansible all -m ping -l 192.168.10.170 192.168.10.170 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" }
-l或者--limit就是在一个指定组中或者一个地址范围中要限定的内容;比如:192.168.10.0/24网段的主机,test1组中有192.168.1.1及192.168.10.1,我现在只想让192.168.10.1去执行内容,其他的不想让他执行。这个地方跨网段测试没试过,日后留意
参考内容:
- Ansile 权威指南
- https://docs.ansible.com/
原文地址:https://www.cnblogs.com/guge-94/p/12444988.html