使用Ansible ad-hoc命令示例
概述
Ansible中的ad hoc命令用于根据需要执行临时或者仅一次所需的任务或者操作。
换句话说,这些是用户希望即时执行但又不想保存以备后用的任务。
Ansible模块可以使用
ansible
命令来调用使用ansible-doc -l获取模块列表,并使用ansible-doc <modulename>获取有关模块选项的信息
使用-m指定要使用的模块
命令模块是默认模块,不必指定
在本节中,我将使用默认列表文件,即/etc/ansible/hosts
,其中添加了受管节点的主机名。由于我们使用的是默认列表文件,因此我不会在临时命令中指定列表文件的位置。当我们到达Ansible Inventory一章时,我们可以了解有关库存的更多信息,在该章中,我们将了解静态和动态库存文件。
语法:
ansible [-i INVENTORY] [server] [-m MODULE] {-a MODULE_OPTIONS}
临时命令示例示例
让我们在两个托管主机上使用ansible执行一些临时命令。在此示例中,我们将收集托管主机的正常运行时间
[ansible@controller ~]$ansible all -m shell -a uptime server1 | CHANGED | rc=0 >> 15:36:00 up 53 min, 4 users, load average: 0.00, 0.00, 0.00 server2 | CHANGED | rc=0 >> 15:36:00 up 53 min, 3 users, load average: 0.00, 0.00, 0.00
我们将看到Ansible忠实地依次登录到每台计算机并运行uptime
命令,返回当前的正常运行时间输出:
同样,我们可以执行任何其他命令
[ansible@controller ~]$ansible all -m shell -a "free -m" server2 | CHANGED | rc=0 >> total used free shared buff/cache available Mem: 815 176 248 12 391 497 Swap: 0 0 0 server1 | CHANGED | rc=0 >> total used free shared buff/cache available Mem: 815 181 243 12 391 492 Swap: 0 0 0
Ansible如何与模块一起使用
在上一节中,我们执行了某些临时命令,但是"后端会发生什么?"?
Ansible从列表文件中获取主机列表。由于我们未提供任何列表文件,因此ansible将从默认位置(即/etc/ansible/hosts)收集主机列表。
然后,使用SSH将" ansible"并行连接到各个托管主机
接下来,我们将使用命令传递的模块将被推送到远程节点。如果
-m
没有提供模块名称,则默认情况下将命令视为该模块。这个各自的"模块"用于在托管主机上执行任务
执行完这些模块后,将从托管主机中删除这些模块。
让我们验证一下:
从托管主机中删除任何~/.ansible目录(每次ansible在托管主机上执行任务时都会创建此目录)。我将验证server2
上的行为
[ansible@server-2 ~]$rm -rf ~/.ansible
" ansible"用户的主目录的现有内容:
[ansible@server-2 ~]$ls -al total 44 drwx------ 3 ansible ansible 4096 Sep 19 16:41 . drwxr-xr-x. 12 root root 20480 Apr 3 23:00 .. -rw------- 1 ansible ansible 37 Sep 19 16:18 .bash_history -rw-r--r-- 1 ansible ansible 18 Jan 11 2019 .bash_logout -rw-r--r-- 1 ansible ansible 141 Jan 11 2019 .bash_profile -rw-r--r-- 1 ansible ansible 312 Jan 11 2019 .bashrc drwx------ 2 ansible ansible 4096 Jan 29 2017 .ssh
现在,我们将从控制器执行一个临时命令到此server2
。
[ansible@controller ~]$ansible server2 -m shell -a "free -m" server2 | CHANGED | rc=0 >> total used free shared buff/cache available Mem: 815 176 248 12 391 497 Swap: 0 0 0
执行完后,我们可以验证在server2上是否已创建~/.ansible目录。
[ansible@server-2 ~]$tree ~/.ansible/ /home/ansible/.ansible/ └── tmp 1 directory, 0 files
但是此目录中没有"文件"。那是因为将shell模块复制到server2
上,执行任务,然后ansible删除了该模块。
要将模块保留在托管主机上,可以通过设置ANSIBLE_KEEP_REMOTE_FILES = 1
为相应的命令启用此配置。在此示例中,我们重新运行相同的命令:
[ansible@controller ~]$ANSIBLE_KEEP_REMOTE_FILES=1 ansible server2 -m shell -a "free -m" server2 | CHANGED | rc=0 >> total used free shared buff/cache available Mem: 815 176 248 12 391 497 Swap: 0 0 0
现在,让我们验证服务器上的.ansible目录。
[ansible@server-2 ~]$tree ~/.ansible/ /home/ansible/.ansible/ └── tmp └── ansible-tmp-1600514171.7996144-28879-119129181135603 └── AnsiballZ_command.py 2 directories, 1 file
这次模块被保留并且未被删除。因此,现在我们应该熟悉如何使用模块在托管主机上执行任务。
控制并行执行的主机数(分叉)
我们已经在"可配置"部分中介绍了这一部分,但让我们通过一些示例进行讨论。
在
ansible.cfg
中,我们有一个选项来定义使用forks对客户端主机执行的并行任务的数量。" forks"的默认值为5,但是我们可以根据可用的系统资源和网络带宽来增加它。
出于演示目的,我们可以在5台服务器上执行默认派生值即5的命令,并且我们将观察到ansible同时执行并打印所有5个主机的执行输出
我们可以在
ansible.cfg
中减少/增加该值,或者使用-f <val>`,因此ansible将执行远程任务。就我而言,因为我只有两个托管主机,所以我正在用
-f 1
执行ansible。
[ansible@controller ~]$ansible all -m shell -a "free -m" -f 1 server1 | CHANGED | rc=0 >> total used free shared buff/cache available Mem: 815 181 242 12 391 492 Swap: 0 0 0 server2 | CHANGED | rc=0 >> total used free shared buff/cache available Mem: 815 176 248 12 391 497 Swap: 0 0 0
首先在server1
上执行,然后在server2
上执行(显然我无法通过代码段向我们展示)。
将文件从Ansible Engine传输到受管节点
在本部分中,我们将使用带有ansible ad-hoc命令的复制模块将文件从Ansible引擎复制到受管节点。
将文件从ansible引擎传输到受管节点的语法是:
ansible [-i INVENTORY] [server] -m copy -a "src=/source/file/path dest=/dest/path"
我将为演示创建一个空文件:
[ansible@controller ~]$touch /tmp/ctrl_file
接下来,我们将使用复制模块将此文件传输到server2
:
[ansible@controller ~]$ansible server2 -m copy -a "src=/tmp/ctrl_file dest=/home/ansible/"
验证server2上的文件
[ansible@server-2 ~]$ls -l total 0 -rw-rw-r-- 1 ansible ansible 0 Sep 19 19:10 ctrl_file
在这个例子中,我们将"添加一些文本"到我们刚刚应付到" server2"的文件中。要添加一些数据,我们使用content = <TEXT>的复制模块。其中将" TEXT"替换为我们要与dest =一起添加的数据
[ansible@controller ~]$ansible server2 -m copy -a "content='Hello, My name is hynman' dest=~/ctrl_file"
验证服务器上的内容
[ansible@server-2 ~]$cat ctrl_file Hello, My name is hynman
使用复制模块获取所有支持的选项的列表
将文件从受管节点下载到控制器节点
在前面的部分中,我们将文件从Ansible引擎传输到了受管节点,现在在本部分中,我们将执行相反的操作。因此,我们将文件从受管节点下载到ansible引擎节点。
我们将在这里使用fetch模块来代替复制,下面是syntax
:
ansible [-i INVENTORY] [server] -m fetch -a "src=/source/file/path dest=/dest/path"
我将在server2
上创建一个虚拟文件。
[ansible@server-2 ~]$touch /tmp/demo.txt
现在,我们将在Ansible用户的主目录下的Ansible引擎上获取此文件
[ansible@controller ~]$ansible server2 -m fetch -a "src=/tmp/demo.txt dest=~/"
该命令的输出
如果我们观察到此命令的输出
"changed": true "dest": "/home/ansible/server2/tmp/demo.txt",
现在,获取的过程与复制模块几乎没有什么不同。如果我们检查ansible用户的主文件夹,现在我们将找到一个新目录" server2"
[ansible@controller ~]$ls -al total 64 drwx------ 5 ansible ansible 4096 Sep 20 10:26 . drwxr-xr-x. 15 root root 20480 Aug 30 15:40 .. drwx------ 4 ansible ansible 4096 Sep 19 16:10 .ansible -rw------- 1 ansible ansible 145 Sep 19 13:30 .bash_history -rw-r--r-- 1 ansible ansible 18 Jan 11 2019 .bash_logout -rw-r--r-- 1 ansible ansible 141 Jan 11 2019 .bash_profile -rw-r--r-- 1 ansible ansible 312 Jan 11 2019 .bashrc drwxrwxr-x 3 ansible ansible 4096 Sep 20 10:26 server2 drwx------ 2 ansible ansible 4096 Sep 19 14:58 .ssh -rw------- 1 ansible ansible 10318 Sep 20 10:25 .viminfo
该目录是通过访存操作创建的。我们可以检查此目录的目录结构:
[ansible@controller ~]$tree server2/ server2/ └── tmp └── demo.txt 1 directory, 1 file
因此,"提取"模块已将文件复制到" server2"目录下同一路径" tmp/demo.txt"中。现在,我确定大多数用户只需要一个文件时,就不希望下载整个目录结构。
为了克服这个问题,我们将使用flat = yes,它将仅获取文件而不是所有目录结构。
让我们通过添加flat = yes来尝试相同的命令
[ansible@controller ~]$ansible server2 -m fetch -a "src=/tmp/demo.txt dest=~/flat=yes"
该命令的输出
验证文件是否已下载
[ansible@controller ~]$ls -l demo.txt -rw-rw-r-- 1 ansible ansible 0 Sep 20 10:36 demo.txt
在远程服务器(受管节点)上本地复制文件
在本节中,我将共享临时命令以在受管节点上创建文件或者目录,即源和目标都来自同一受管节点。这可以在执行任何配置更改之前备份任何配置文件。我们将使用相同的复制模块及其语法,此外,我们需要remote_src = true才能在受管节点上本地工作。
在此示例中,我将在server1上执行操作,将~/demo.txt复制到同一服务器上的/tmp/demo-2.txt。
[ansible@controller ~]$ansible server1 -m copy -a "src=~/demo.txt dest=/tmp/demo-2.txt remote_src=yes" server1 | CHANGED => { "ansible_facts": { "discovered_interpreter_python": "/usr/libexec/platform-python" }, "changed": true, "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709", "dest": "/tmp/demo-2.txt", "gid": 1000, "group": "ansible", "md5sum": "d41d8cd98f00b204e9800998ecf8427e", "mode": "0664", "owner": "ansible", "size": 0, "src": "/home/ansible/demo.txt", "state": "file", "uid": 1000 }
因此该命令似乎成功,我们可以在server1上验证文件
[ansible@controller ~]$ssh server1 "ls -l /tmp/demo-2.txt" -rw-rw-r-- 1 ansible ansible 0 Sep 27 10:09 /tmp/demo-2.txt
创建或者删除文件和目录
在本节中,我将共享临时命令以在受管节点上创建文件或者目录,即源和目标都来自同一受管节点。这可以在执行任何配置更改之前备份任何配置文件。我们将使用相同的复制模块及其语法,此外,我们需要remote_src = true
在受管节点上创建文件
我们将使用以下syntax
来创建一个新文件:
ansible [-i INVENTORY] [server] -m file -a "path=</path/to/file.txt state=touch>"
现在,我们将使用此语法在ansible引擎上在server2上创建一个新文件/tmp/demo_1.txt
。
[ansible@controller ~]$ansible server2 -m file -a "path=/tmp/demo_1.txt state=touch"
我的controller
节点的输出:
该命令已成功执行。我们可以看到一些已应用于新文件的默认值,例如gid
,group
,mode
,owner
等。我们还可以灵活地为新文件定义mode
。我们创建的。
使用自定义参数创建文件
在这个例子中,我还为这个新文件定义了一个模式值,即755:
[ansible@controller ~]$ansible server2 -m file -a "path=/tmp/demo_2.txt state=touch mode=755"
我的controller
节点的输出:
现在,文件以模式" 0755"创建。
从受管节点中删除文件
要删除文件,我们将使用缺少模块并带有以下syntax
:
ansible [-i INVENTORY] [server] -m file -a "path=</path/to/file.txt state=absent>"
因此,我们将使用absent
而不是touch
,并且将删除提供的path =
文件。让我们使用以下语法删除我们之前创建的文件之一:
[ansible@controller ~]$ansible server2 -m file -a "path=/tmp/demo_2.txt state=absent"
我的控制器节点的输出:
在受管节点上创建目录
我们也可以使用文件模块来创建一个新目录,但语法略有不同,如下所示:
ansible [-i INVENTORY] [server] -m file -a "path=</path/to/dir state=directory>"
其中我们使用" state = directory"代替" touch"来在受管节点上创建目录。让我们在示例中使用它:
[ansible@controller ~]$ansible server2 -m file -a "path=/tmp/dir_1.txt state=directory"
我的controller
节点的输出:
因此,该目录是在server2
上创建的
[ansible@server-2 ~]$ls -l /tmp/dir_1.txt/ total 0
默认情况下,将考虑来自server2的umask值来创建目录,但是再次创建目录时,我们可以像先前对文件所做的那样修改modes和permission。
删除目录
我们将再次使用absent
作为文件模块的状态,通过以下syntax
删除目录:
ansible [-i INVENTORY] [server] -m file -a "path=</path/to/dir state=absent>"
让我们使用以下语法删除上面示例中刚刚创建的dir_1
:
[ansible@controller ~]$ansible server2 -m file -a "path=/tmp/dir_1.txt state=absent"
我的controller
节点的输出:
:
现在,验证目录是否已从" server2"中删除
[ansible@server-2 ~]$ls -l /tmp/dir_1.txt/ ls: cannot access '/tmp/dir_1.txt/': No such file or directory
以root权限执行命令
到目前为止,在所有示例中,我们都是以ansible用户身份执行的。但是,如果我们尝试执行需要root级特权的命令,该怎么办?让我们尝试使用fdisk -l命令检查可用的分区和存储设备,该命令只能以root用户身份执行:
[ansible@controller ~]$ansible server2 -m command -a "fdisk -l" server2 | CHANGED | rc=0 >> fdisk: cannot open /dev/xvda: Permission denied
我们遇到了"权限被拒绝"错误,因为fdisk -l命令只能以root用户身份执行。在这种情况下,我们必须使用-b或者--become来以sudo特权执行命令。如果我们还记得在ansible配置阶段,我们已经为ansible
用户赋予了'sudo完全权限'。
让我们尝试使用--become
执行相同的命令
[ansible@controller ~]$ansible server2 -m command -a "fdisk -l" --become server2 | CHANGED | rc=0 >> Disk /dev/xvda: 10 GiB, 10737418240 bytes, 20971520 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes/512 bytes I/O size (minimum/optimal): 512 bytes/512 bytes Disklabel type: dos Disk identifier: 0xa323d5eb Device Boot Start End Sectors Size Id Type /dev/xvda1 2048 4095 2048 1M 83 Linux /dev/xvda2 * 4096 20971486 20967391 10G 83 Linux
这次命令已成功执行。如果我们已将sudo配置为提示输入密码,则该命令将再次失败,要求输入sudo密码,因为默认情况下--- become将不会提示输入任何密码。在这种情况下,我们可以使用--ask-become-pass和--become来提示输入root用户的sudo密码。
[ansible@controller ~]$ansible server2 -m command -a "fdisk -l" --become --ask-become-pass BECOME password: server2 | CHANGED | rc=0 >> Disk /dev/xvda: 10 GiB, 10737418240 bytes, 20971520 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes/512 bytes I/O size (minimum/optimal): 512 bytes/512 bytes Disklabel type: dos Disk identifier: 0xa323d5eb Device Boot Start End Sectors Size Id Type /dev/xvda1 2048 4095 2048 1M 83 Linux /dev/xvda2 * 4096 20971486 20967391 10G 83 Linux
在此命令中,ansible提示输入" BECOME password",如果密码正确,则该命令已执行。
我们可以使用--become与ansible ad-hoc命令一起执行任何根级任务。
使用yum模块处理软件包
由于我们使用的是RHEL/CentOS 8环境,因此我们将使用yum模块作为我使用rpm的模块,但是如果我们使用的是Ubuntu/Debian,则应使用apt作为模块。
用于在受管节点上安装软件包的"语法":
ansible [-i INVENTORY] [server] -m yum -a "name=<pkg> state=<present/latest/absent>"
让我们使用此语法在server2上安装git rpm
[ansible@controller ~]$ansible server2 -m yum -a "name=git state=latest" server2 | FAILED! => { "ansible_facts": { "discovered_interpreter_python": "/usr/libexec/platform-python" }, "changed": false, "msg": "This command has to be run under the root user.", "results": [] }
该命令失败,因为我们试图以ansible用户身份安装rpm,而只能使用root特权来执行该命令。因此,我们必须在此命令中使用--become
:
[ansible@controller ~]$ansible server2 -m yum -a "name=git state=latest" --become server2 | CHANGED => { "ansible_facts": { "discovered_interpreter_python": "/usr/libexec/platform-python" }, "changed": true, "msg": "", "rc": 0, "results": [ "Installed: perl-libnet-3.11-3.el8.noarch", "Installed: perl-Digest-1.17-395.el8.noarch", "Installed: perl-Mozilla-CA-20150104-7.el8.noarch", "Installed: perl-Digest-MD5-2.55-396.el8.x86_64", "Installed: perl-Net-SSLeay-1.88-1.el8.x86_64", "Installed: perl-Error-1:0.17025-2.el8.noarch", "Installed: perl-Data-Dumper-2.167-399.el8.x86_64", "Installed: perl-Encode-4:2.97-3.el8.x86_64", "Installed: perl-Getopt-Long-1:2.50-4.el8.noarch", "Installed: perl-HTTP-Tiny-0.074-1.el8.noarch", "Installed: perl-MIME-Base64-3.15-396.el8.x86_64", "Installed: perl-Git-2.18.4-2.el8_2.noarch", "Installed: perl-TermReadKey-2.37-7.el8.x86_64", "Installed: perl-Pod-Escapes-1:1.07-395.el8.noarch", "Installed: perl-Pod-Perldoc-3.28-396.el8.noarch", "Installed: perl-Pod-Simple-1:3.35-395.el8.noarch", "Installed: perl-Pod-Usage-4:1.69-395.el8.noarch", "Installed: perl-Storable-1:3.11-3.el8.x86_64", "Installed: perl-Term-ANSIColor-4.06-396.el8.noarch", "Installed: perl-Term-Cap-1.17-395.el8.noarch", "Installed: perl-IO-Socket-IP-0.39-5.el8.noarch", "Installed: perl-Text-ParseWords-3.30-395.el8.noarch", "Installed: perl-IO-Socket-SSL-2.066-4.el8.noarch", "Installed: perl-podlators-4.11-1.el8.noarch", "Installed: perl-URI-1.73-3.el8.noarch", "Installed: git-2.18.4-2.el8_2.x86_64", "Installed: git-core-2.18.4-2.el8_2.x86_64", "Installed: git-core-doc-2.18.4-2.el8_2.noarch" ] }
这次命令执行成功,并且git和所有其他依赖项一起安装。在server2
上验证相同:
[ansible@server-2 ~]$rpm -q git git-2.18.4-2.el8_2.x86_64
要"安装具有特定版本的软件包",可以使用" state = present",而"删除转速"则可以使用" state = absent"
以其他用户身份执行临时命令
在本节中,我们将学习如何以不同的用户身份执行Ansible ad-hoc命令。默认情况下,当我们使用ansible
执行命令时,它将使用与我们调用ansible
命令相同的用户名。在本例中,由于我们以" ansible"身份登录并执行了所有临时命令,因此默认情况下,ansible用户将用于SSH连接。
如果我们需要连接到受管节点并以其他用户身份执行命令,则必须将-u或者--user以及ansible命令一起使用。
我将在server3
上创建一个新的用户hynman并分配一个密码:
[root@server3 ~]# useradd hynman [root@server3 ~]# passwd hynman
现在我们还没有为用户hynman配置基于密码的登录,因此我们将使用密码以" hynman"用户身份连接到" server3"。我们将在"使用密码(而非密码)使用受管节点"一章中了解有关基于密码的登录的更多信息。
如果我们使用whoami参数执行ansible命令,则应该为我们提供在server3
上执行命令的用户名。
[ansible@controller ~]$ansible server3 -m command -a "whoami" --ask-pass SSH password: server3 | CHANGED | rc=0 >> ansible
因此,这里使用ansible用户执行whoami
。
类似地,现在我将使用--user hynman并执行相同的命令:
[ansible@controller ~]$ansible server3 -m command -a "whoami" --user hynman --ask-pass SSH password: server3 | CHANGED | rc=0 >> hynman
现在我们可以看到该命令是使用hynman用户执行的。因此,现在我们可以在不同的即席命令中使用此语法,并以不同的用户身份执行任务。