RCHE 学习指南(一)
原文:Red Hat Certified Engineer (RHCE) Study Guide
协议:CC BY-NC-SA 4.0
一、理解 Ansible 和红帽 RHCE
Red Hat 的企业 Linux 解决方案和随后的认证计划一直处于领先地位。对于 Linux 管理员来说,认证的顶峰一直是 RHCE,即红帽认证工程师。随着 Red Hat 收购 Ansible,他们再次引领潮流,将配置管理作为管理工作和新 RHCE 的重点。企业要赚钱,变得更有效率;通过使用 Ansible 这样的配置管理系统,一个管理员现在可以做十个管理员一样的工作。重要的是,你要成为那十分之一的人,你要在 Ansible 中学习和认证。
Note
“ansible”这个词是由作家厄休拉·K·勒·古恩在她 1966 年的小说《罗卡农的世界》中首次使用的。作为“answerable”一词的缩写,它指的是可以通过星际距离向管理系统发送消息的虚拟设备。Red Hat 的 Ansible 可能无法跨越星际距离工作,但它确实可以管理通常位于地球上的设备。
在第一章中,我将带您踏上 Ansible 的 RHCE 之旅,并向您介绍红帽认证和 Ansible 产品。如果你想跟随你自己的实验室系统,让你获得所有重要的实践,这对我来说将是令人惊奇和荣幸的。理解了这种需求,我将解释我在整本书中使用的系统,以及你完成自己的练习至少需要什么。你当然可以用这本书作为学习指南,但更重要的是,你可以用这本书来学习 Ansible。我在这里给你的信息不仅适用于考试,也适用于现实生活。可以理解的是,考试将只关注适用于您的托管设备的 Red Hat Enterprise Linux。利用其他 Linux 发行版,我也使用 Ubuntu 系统,即使我们没有到达星际距离,也允许你利用 Ansible 的真正力量。
红帽和 Ansible
位于北卡罗来纳州罗利的红帽公司在 2015 年收购了 Ansible。Ansible 最初由 Michael DeHaan 编写,是一个无代理配置管理系统,可用于管理 Linux、Unix、Microsoft Windows 和托管网络系统。从安装了 Ansible 的系统 Ansible controller 管理您的房地产,意味着您可以更轻松地管理更多系统。Ansible 记录并强制执行配置,是确保您满足企业和法规遵从性要求的完美之选。在一个充满不确定性的世界里,拥有每次都能以指定的配置可靠地快速部署系统的敏捷性绝对是一种天赋。Ansible 是免费开源的;部署 Ansible 来管理您的系统并提高您的效率是没有成本的。
基于 Linux 的其他配置管理系统包括:
-
PuppetLabs 的木偶
-
来自 chef.io 的厨师
-
盐堆中的盐
红帽认证
正如我前面提到的,Red Hat 处于 Linux 认证的最前沿,拥有最令人满意的可信度和认可度。参加考试将以实际的方式测试您的知识,为您提供实时系统来配置到所需的状态。认证从 RHCSA 开始;通过之后,你就可以坐 RHCE 了。
瑞沙
红帽认证之旅的起点是红帽认证系统管理员,通常称为 RHCSA。测试您的 Linux 管理技能,目前在 Red Hat Enterprise Linux 8 中,您可以向自己和世界证明您是最优秀的。您需要演示文件系统、用户、权限、网络等的管理。您可以通过为期九天的课堂培训获得这些技能,这些培训基于为期五天的课程 RH124,以及随后为期四天的课程 RH134。有了这些知识,你将会在考试中证明你的技能。
断续器
最新版本的红帽认证工程师于 2019 年推出,已经获得 RHCSA 认证的潜在候选人在与 Ansible 和红帽企业 Linux 8 的比赛中发挥他们的技能。他们通过将目标系统配置为考试开发者所设计的理想状态来实现这一点。你的任务变得更简单,因为你将从本书或为期四天的 RH294 课程课堂培训中获得必要的技能。使用 Ansible,您可以快速地将受管设备配置到所需的状态,通常是通过称为剧本的 YAML 文件,或者偶尔是在 Ansible 控制器的命令行执行的特别命令。
实验室系统
在本书中,我们将使用 CentOS 8,它是 Red Hat Enterprise Linux 的免费重建版。在考试中,你会被要求使用红帽,但 CentOS 和红帽是直接可比的。除了这个基于 Red Hat 的发行版,我们还将使用一个基于 Debian 的发行版,Ubuntu 18.04。这可以让你更多地了解 Ansible,以及我们如何轻松地将多个发行版集成到我们的 Ansible 管理中。Ansible 和大多数配置管理系统与底层操作系统无关。我们要求在不考虑如何实现的情况下进行配置。利用系统变量或事实,我们可以从 os_family 事实中确定 os,并修改任何操作以满足目标 OS 的需求,例如不同的包或服务名。如果您可以使用多个发行版,您对 Ansible 的学习将会得到加强,但是如果您受到可用系统的限制,您必须至少拥有一个 CentOS 8 系统。
Note
我们用的是两个 CentOS 8 系统和一个 Ubuntu 18.04 系统。
这些系统可以采取任何形式;您只需要拥有对它们的完全管理权限。这些可以是物理系统、您托管的虚拟系统,或者在云中托管的虚拟系统。由于这本书将在几个月内完成,我将使用托管在 MacOS 的 VMware Fusion 中的内部虚拟机,而不是云。如果你能在几周内完成你的学业,使用基于云的系统是一个好主意。事实是,所使用的虚拟化引擎与 Ansible 无关。你需要使用 CentOS 8 作为你的 Ansible 控制器;这是唯一一个需要添加软件的系统,因为 Ansible 不需要被管理设备上的代理。您将从控制器管理的每个设备都需要能够通过 TCP 端口 22(SSH)上的网络被控制器访问。理想情况下,您的实验室环境应该将受管设备托管在同一网络上,但这不是必需的。
在我的每个实验室系统上,我将总是创建一个名为 tux 的帐户;该帐户应添加到发行版的管理员组中。这是 CentOS 的轮组和 Ubuntu 的须藤组。
在 CentOS 8 上安装 Ansible
你将被期望在考试中使用红帽企业版 Linux 8;在这里,我们使用的是它的重建版本(例如,8 度音程)。我们只需要在 CentOS 8 系统上安装 Ansible。Ubuntu 系统不需要安装额外的软件。这个系统成为我们的可靠控制器,很有可能成为您在企业中的 Linux 工作站。我们将使用未安装 GUI 的 CentOS 8 服务器。如果您要开始实验室的全新安装,对于课程的大部分时间来说,最低限度地安装带有 1GB RAM 和 20GB 磁盘的 CentOS 8 服务器就足够了。
首先,在我们将用作 Ansible 控制器的 CentOS 8 系统上安装 EPEL(Enterprise Linux 的额外产品)存储库。我们就是在这个系统上安装 Ansible 的,它是唯一一个需要明确安装 Ansible 软件的系统。
$ sudo yum install -y epel-release
$ sudo yum update -y epel-release
Listing 1-1Adding the EPEL Repository on the CentOS 8 Controller System
安装然后更新将确保我们拥有最新版本的 EPEL 库。较新的版本通常存在于存储库中。对于 CentOS,我们可以从这个存储库安装 Ansible。如果使用 Red Hat,必须通过 subscription manager 启用存储库。这不是你在考试中可能会涉及到的任何事情。我敢肯定,不得不处理候选人订阅不是红帽会想参与只是为了考试。
接下来我们可以安装 Ansible。这不是一项困难的任务;让我展示给你看。
$ yum install -y ansible
Listing 1-2Install Ansible on CentOS
安装 Ansible 后,我们可以花点时间检查一下我们安装的版本。
$ ansible --version
ansible 2.9.15
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/tux/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3.6/site-packages/ansible
executable location = /usr/bin/ansible
python version = 3.6.8 (default, Apr 16 2020, 01:36:27) [GCC 8.3.1 20191121 (Red Hat 8.3.1-5)]
Listing 1-3Printing the Version of Ansible
查看输出,我们可以看到 EPEL 存储库的版本是 2.9.15。在考试中,你最有可能使用 2.8.x,但应该没有什么不同。
在 Ubuntu 18.04 上安装 Ansible
重要的是,或者至少我觉得是重要的是,我们也要学习如何在另一个发行版上安装 Ansible。尽管 Ubuntu 不会在考试中使用,但在现实世界中,你可能想在一个发行版而不是基于 Red Hat 的发行版上使用 Ansible。我将指导您在 Ubuntu 18.04 上安装 Ansible,但在本课程中,我们将只使用 CentOS 8 系统作为控制器。在余下的课程中,Ubuntu 系统将仍然是一个被管理的节点,不会在这些节点上安装 Ansible 包。
首先,Ansible 在标准的 Ubuntu 库内;但是,这是一个较旧的版本,显示为版本 2.5。虽然这样可以,但并不是真的可取。我们可以直接从 Ansible 添加软件库,以便稍后安装一些东西。在基于 Debian 的系统中,这些额外的库是 PPAs 或个人包档案。
$ sudo apt install ansible sshpass
Listing 1-5Install Ansible Where We Add sshpass Also
$ sudo apt update
$ sudo apt install software-properties-common
$ sudo apt-add-repository --yes --update ppa:ansible/ansible
Listing 1-4Adding the PPA to Ubuntu
您也可以将此信息复制到可回复的文档中: https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-ansible-on-ubuntu 。
这将安装 Ansible 自己的最新版本。检查版本,我们看到这是目前在 EPEL 相同的版本。
$ ansible --version
ansible 2.9.15
config file = /etc/ansible/ansible.cfg
configured module search path = [u'/home/tux/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python2.7/dist-packages/ansible
executable location = /usr/bin/ansible
python version = 2.7.17 (default, Sep 30 2020, 13:38:04) [GCC 7.5.0]
Listing 1-6Checking the Version Install on Ubuntu
记住,我们将使用 CentOS 8 系统作为控制器;这台 Ubuntu 主机和另一台 CentOS 8 系统将是被管理的设备。在这个阶段,我提醒您,如果您缺少对系统的访问,那么只使用控制器来运行大多数任务是可能的。但是,如果我们可以用一个命令配置多个系统,总体效果会更令人印象深刻。如果你能运行两三个系统,那就更好了。
摘要
你知道吗?你太棒了。你现在知道 Ansible 是什么了,你明白你成为一个 RHCE,一个认证的行政神的道路了!最重要的是,你知道你会通过大量的练习达到目的。我猜你是如此热情,以至于你已经在建立你的三个实验室系统了。是的,没错:两个 CentOS 8 系统和一个 Ubuntu 18.04 系统。没有一个系统需要 GUI 桌面,这也意味着每个系统上的资源非常少。老实说,1GB 的 RAM 和 20GB 的磁盘对于每个系统来说,在控制台模式下运行是绰绰有余的。对于一个章节,我们将需要控制器上的 2GB 内存。
您现在应该已经在 CentOS 8 系统上安装了 Ansible。这将作为您的责任控制器。Ansible 是一个无代理的配置管理系统,其优势在于无需向您管理的设备添加支持软件或客户端。您还了解了如果没有 CentOS 8,可以在其他系统上安装 Ansible。无论哪个系统是控制器,Ansible 的行为都是一样的。在考试中,你应该熟悉使用 CentOS 8 或 RHEL 8。
二、将 Ansible 用于配置
Ansible 的配置文件控制系统配置实用程序 Ansible 如何在被管理设备上运行。这可能包括权限提升的方式以及连接到受管理设备时使用的用户帐户。这些需要在你参与的每个项目中都是一样的吗?个人管理员或开发人员应该控制他们自己的配置吗?这些都是很好的问题,也是我们将在这里尝试回答的问题。让我们看看配置的内容,什么可以进入一个 Ansible 配置文件,以及可以进行的配置的层次结构和它们的搜索顺序。
Note
Ansible 的有效配置可以从命令 ansible - version 中确定。从您将为项目执行其他 Ansible 命令的目录中运行此命令。
Ansible 配置层次
与 Ansible 一起安装的默认 Ansible 配置文件的完整路径是: /etc/ansible/ansible.cfg 。Ansible 只需要安装在 Ansible 控制器主机系统,我们的主 CentOS 8 盒子上。通过阅读 version 选项的完整输出,我们可以看到基于当前工作目录和 shell 变量的有效 Ansible 配置。
$ pwd
/home/tux
$ ansible --version
ansible 2.9.15
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/tux/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3.6/site-packages/ansible
executable location = /usr/bin/ansible
python version = 3.6.8 (default, Apr 16 2020, 01:36:27) [GCC 8.3.1 20191121 (Red Hat 8.3.1-5)]
Listing 2-1Listing the Current and Default Location for the Ansible Configuration
紧接着 Ansible 版本号,我们看到了配置路径指令。这个显示为 /etc/ansible/ansible.cfg 。如前所述,这是文件的默认位置。它也是后备位置和配置层次结构的最底层。在 /etc 结构中拥有一个中心位置将会是一个非常规范 Ansible 配置。需要拥有控制器的管理权限来修改 /etc 中的文件,您不会期望每个人都编辑这个文件。如果没有其他文件可以使用,那么这些设置对于控制器上的每个 Ansible 项目都是相同的。现在,我无法反驳这一点,因为我不知道你正在进行的 Ansible 项目;然而,通常需要更大的灵活性,并且通常每个项目的配置比集中配置更可取。就责任而言,权力下放是王道。
在最先发现 – 最先应用的基础上应用有效可行配置。请务必注意,只能有一个配置处于活动状态并被应用,并且这些配置不会累积。搜索顺序显示在下面的项目符号列表中,从列表的顶部到底部进行搜索。效率最低的配置是列表底部的 /etc/ansible/ansible.cfg 。
-
ANSIBLE_CONFIG :如果设置了环境变量 ANSIBLE_CONFIG ,则使用该配置。默认选项用于任何未设置的配置选项。这种默认行为在所有配置中都很常见。
-
ansible.cfg :如果当前工作目录(CWD)中有一个 ansible.cfg 文件,并且 ANSIBLE_CONFIG 环境变量没有设置,那么使用的就是这个文件。
-
~/.ansible.cfg :如果没有检测到之前列出的配置,ansible 将在当前用户的主目录中检查一个名为 .ansible.cfg 的隐藏文件。如果该文件存在,那么它将成为层次结构中的第三个选择。对于用户来说,这是一个很好的选择,可以作为所有用户项目的默认选项,除了那些需要稍微调整的项目。那些需要调整的可以将配置文件添加到项目目录中;或者,正如您将了解到的,变量可以设置为覆盖某些选项,就像 Ansible 剧本中的设置一样。因此,有很多选项可以根据需要调整配置。
-
/etc/ansi ble/ansi ble . CFG:没有其他配置或没有检测到其他配置的默认文件。文件本身只包含注释,这意味着文件中没有有效的设置。但是不要绝望;这将导致默认设置应用于所有设置。文件本身并没有被浪费,它为您可能想要实现的配置文件提供了很好的文档。
作为如何构建层次结构的简单演示,我们可以从配置树的底部开始向上将文件添加到它们的位置。显然,树的底部已经就位,正如我们在最初的ansible --version输出中已经看到的。默认的 ansible.cfg 和 ansible 一起安装。
只有这个默认文件存在,您可以肯定它将被使用。使用 awesome 命令grep,我们可以过滤结果,只看到我们感兴趣的行。
$ ansible --version | grep 'config file'
config file = /etc/ansible/ansible.cfg
Listing 2-2Listing the Default Configuration Location
当用户的主目录中存在隐藏的 Ansible 文件时,如果后面的层次结构中不存在其他文件,它将是有效文件。通过添加 $HOME/.ansible.cfg 文件,我们可以看到我们如何开始提升层次结构。
$ touch ~/.ansible.cfg
$ ansible --version | grep 'config file'
config file = /home/tux/.ansible.cfg
Listing 2-3Adding a Configuration to the Home Directory
通过将一个 ansible.cfg 文件添加到当前工作目录,我们可以看到它接管了有效的设置。在没有 ANSIBLE_CONFIG 环境变量的情况下,CWD 中的 ansible.cfg 是 ansible 命令的有效配置。在下面的代码清单中,您将看到在我们的主目录中创建了一个新目录。我们进入新创建的目录,并创建新的空文件 ansible.cfg 。虽然这个目录是任何 Ansible 命令的工作目录,但是在没有变量的情况下,这个文件用于配置。
$ mkdir $HOME/ansible
$ cd $HOME/ansible
$ touch ansible.cfg
$ ansible --version | grep 'config file'
config file = /home/tux/ansible/ansible.cfg
Listing 2-4Adding a Configuration to the Current Directory
Important
对于 Ansible 安全性来说,绝不从全局可写的目录中加载配置文件是非常重要的。如果一个目录是全局可写的,(其中 others 拥有写权限),另一个用户可能会有意或无意地将一个恶意的 ansible.cfg 文件添加到您的工作目录中。
为了演示我们可能面临的安全问题,我们现在将更改刚刚创建的 $HOME/ansible 目录的权限,添加全局可写权限。一旦我们证明了理论是正确的,我们就恢复目录上的权限,使 Ansible 能够从目录中读取配置。
$ cd $HOME/ansible
$ chmod -v 777 $HOME/ansible.
mode of '/home/tux/ansible' changed from 0775 (rwxrwxr-x) to 0777 (rwxrwxrwx)
$ ansible --version | grep 'config file'
[WARNING]: Ansible is being run in a world writable directory (/home/tux/ansible), ignoring it as an ansible.cfg source. For more
information see https://docs.ansible.com/ansible/devel/reference_appendices/config.html#cfg-in-world-writable-dir
config file = /home/tux/.ansible.cfg
$ chmod -v 775 $HOME/ansible
mode of '/home/tux/ansible' changed from 0777 (rwxrwxrwx) to 0775 (rwxrwxr-x)
$ ansible --version | grep 'config file'
config file = /home/tux/ansible/ansible.cfg
Listing 2-5Test Ansible Security
在配置层次结构的最顶层,我们有一个环境变量, ANSIBLE_CONFIG 。这是配置界的大老板,她说什么很重要;她说话,Ansible 听!
该变量可以在用户的登录脚本中设置,也可以在命令行中动态配置。如果它是由管理员在登录脚本中设置的,那么值得将该变量设置为只读,从而消除用户更改该变量的任何机会。例如,如果我们想强制配置用户家中的 ansible.cfg 文件,我们可以实现变量。
$ touch $HOME/ansible.cfg
$ declare -xr ANSIBLE_CONFIG=$HOME/ansible.cfg
$ ansible --version | grep 'config file'
config file = /home/tux/ansible.cfg
$ declare -xr ANSIBLE_CONFIG=$HOME/my.cfg
-bash: declare: ANSIBLE_CONFIG: readonly variable
$ unset ANSIBLE_CONFIG
-bash: unset: ANSIBLE_CONFIG: cannot unset: readonly variable
Listing 2-6Using the Variable to Set the Configuration Location and Viewing Read-Only Variables
Note
选项 -x 到声明设置一个环境变量,(对所有命令可用),选项-r 将变量设置为只读。作为只读变量,不能取消设置或更改。现在,我知道你会习惯于出口的命令;我们可以使用 export 使变量对环境可用,并使用 readonly 命令使变量只读。然而,使用 declare 命令让我们能够在一个命令执行中设置两个选项。
从示例中我们可以看到,当我们遍历层次结构时,我们使用新的配置并忽略以前使用的配置,例如默认的 ansible.cfg 。我们还可以看到,作为管理员,我们可以通过理解在 bash shell 中使用declare命令来强制使用变量的位置。
目前,我们不想使用这个变量;正如我们已经看到的,我们不能取消设置,但是我们可以注销并重新登录到系统中。我们没有在登录脚本中设置它,所以这将有效地清除变量。我们还将从主目录—中删除 ansible.cfg 文件,而不是隐藏文件,只是删除 $HOME/ansible.cfg 。
$ exit
Log back in as tux
$ rm $HOME/ansible.cfg
$ cd $HOME; ansible --version | grep 'config file'
config file = /home/tux/.ansible.cfg
Listing 2-7Cleaning the Environment, We Should See the Hidden File as the Effective Configuration When Executed with Home as the Working Directory
打印 Ansible 配置
即使我们还没有进行任何配置设置,我们仍然能够打印有效文件的内容,该文件将是空的。我们还可以打印有效设置,即默认设置。为此,我们可以使用命令ansible-config,它有令人惊叹的三个子命令:
-
ansible-config view:打印当前有效可行配置的内容。 -
ansible-config dump:打印生效设置,由生效文件中的显式设置和未设置选项的默认设置组成。 -
ansible-config list:这充分详细地描述了可以通过变量或通过配置文件或剧本中的指令进行的设置。
很容易就能看到这一点,我将向你们展示。不要忘记在您自己的实验室系统上努力工作,您对这些命令的实践越多,它们在您的脑海中就越清晰。考试也是基于实践的,这意味着实践经验对考试和你自己的成功都很重要。
$ cd
$ ansible-config view
Listing 2-8Printing the Current Configuration File Content; This Should Be Empty
视图子命令仅打印当前配置的有效设置;任何注释行都不会被打印。我们可以通过重命名 $HOME/.ansible.cfg 来测试这一点。这将使文件 /etc/ansible/ansible.cfg 再次成为有效的配置,因为我们回到了默认文件。即使文件不是空的,每一行都有注释,所以不会打印任何内容。
$ mv $HOME/.ansible.cfg $HOME/.ansible.old
$ ansible --version | grep 'config file'
config file = /etc/ansible/ansible.cfg
$ ansible-config view
$ head -n 15 /etc/ansible/ansible.cfg
# config file for ansible -- https://ansible.com/
# ===============================================
# nearly all parameters can be overridden in ansible-playbook
# or with command line flags. ansible will read ANSIBLE_CONFIG,
# ansible.cfg in the current working directory, .ansible.cfg in
# the home directory or /etc/ansible/ansible.cfg, whichever it
# finds first
[defaults]
# some basic default values...
#inventory = /etc/ansible/hosts
#library = /usr/share/my_modules/
Listing 2-9Printing the Default Configuration
配置采用 INI 文件的形式,这意味着配置选项被组合在方括号中的节头中。默认文件中的节头没有注释,所以很容易单独打印。这也是使用正则表达式的很好的练习,我们可以和我们的好朋友grep一起使用。我们在查询中使用的正则表达式元字符在下面的列表中列出并解释:
-
^:行开始于 -
我们实际上是在寻找以左括号开始的行。我们需要去掉括号,因为它会被解释为正则表达式中一个范围的开始。
-
.*:正则表达式中的句号代表任意字符,星号代表前面字符的任意数量。这样,我们可以说括号可以包含任何数量的任何字符。 -
再次强调,我们必须避开右括号,因为我们希望它被当作文字而不是正则表达式元字符来读。
-
使用
grep,我们希望打印以方括号内的标题名称指示的部分标题开始的行。
$ grep -E '^\[.*\]' /etc/ansible/ansible.cfg
[defaults]
[inventory]
[privilege_escalation]
[paramiko_connection]
[ssh_connection]
[persistent_connection]
[accelerate]
[selinux]
[colors]
[diff]
Listing 2-10Cataloging the Headers
因此,我们现在已经对配置文件和ansible-config命令有了一些了解。到目前为止,我们只看到了视图子命令。我们现在必须继续,看看转储子命令。这向我们显示了基于显式设置的当前配置,以及那些未设置和使用默认选项的配置。
$ ansible-config dump | head
ACTION_WARNINGS(default) = True
AGNOSTIC_BECOME_PROMPT(default) = True
ALLOW_WORLD_READABLE_TMPFILES(default) = False
ANSIBLE_CONNECTION_PATH(default) = None
ANSIBLE_COW_PATH(default) = None
ANSIBLE_COW_SELECTION(default) = default
ANSIBLE_COW_WHITELIST(default) = ['bud-frogs', 'bunny', 'cheese', 'daemon', 'default', 'dragon', 'elephant-in-snake', 'elephant', 'eyes', 'hellokitty', 'kitty', 'luke-koala', 'meow', 'milk', 'moofasa', 'moose', 'ren', 'sheep', 'small', 'stegosaurus', 'stimpy', 'supermilker', 'three-eyes', 'turkey', 'turtle', 'tux', 'udder', 'vader-koala', 'vader', 'www']
ANSIBLE_FORCE_COLOR(default) = False
ANSIBLE_NOCOLOR(default) = False
ANSIBLE_NOCOWS(default) = False
Listing 2-11Listing the Current Effective Settings
有效配置文件中当前没有设置选项。我们看到的用 dump 子命令打印的每个选项将显示相应的默认配置值。这通过在配置名称后使用*(默认)*来显示。我们只列出了前十行,但是在我们创建自己的定制配置之前,每个设置都将是自己的默认设置。
尽管 dump 子命令在查看当前有效设置的能力方面非常出色,但是它并没有为配置设置提供任何帮助或解释。为此,我们需要使用列表子命令。来,我们来看看;但是我们将过滤输出,只查看一个设置。输出是冗长的,非常冗长,因此过滤单个设置将更容易看到和理解。
$ ansible-config list | grep -A8 DEFAULT_REMOTE_USER
DEFAULT_REMOTE_USER:
default: null
description: [Sets the login user for the target machines, 'When blank it uses the connection plugin''s default, normally the user currently executing Ansible.']
env:
- {name: ANSIBLE_REMOTE_USER}
ini:
- {key: remote_user, section: defaults}
name: Login/Remote User
Listing 2-12List All Configuration Settings with Documentation
选项名为DEFAULT _ REMOTE _ USER;这没有默认值,但是当前用户将与 Linux 插件一起使用。可以使用环境变量 ANSIBLE_REMOTE_USER 设置该值,或者使用 defaults 部分标题中的键 remote_user 从配置值设置该值。如果设置了变量,它将优先于配置文件。
Note
这些命令对你很有帮助,因为它们可以在考试中使用。所以,在需要的时候利用这一点,并确保你练习了这些命令,这样你在考试时就会很流利。
创建基本的 Ansible 配置文件
现在,到了这个阶段,您一定渴望开始自己的配置。你的声音已经被听到,这就是你现在要开始学习的。通过在我们的主目录和. ansible.cfg 文件中进行设置,如果没有在工作目录中进行设置,这些可以作为我们自己的默认设置。首先,我们恢复之前重命名的 .ansible.cfg 文件。
$ cd ; mv .ansible.old .ansible.cfg
$ ls -la .ansible.cfg
-rw-rw-r--. 1 tux tux 0 Nov 16 14:42 .ansible.cfg
Listing 2-13Restoring the .ansible.cfg file in Our Home Directory
我们现在可以设置一些配置选项,我们可能希望在多个 Ansible 项目之间共享这些选项。这些设置可能最适合在主目录的 .ansible.cfg 中进行配置。我们首先通过从默认文件中复制它们来确保我们有正确的节标题。
Note
以这种方式添加节头消除了可能出现的打字错误,并且我们有未使用的节也没有关系。
$ grep -E '^\[.*\]' /etc/ansible/ansible.cfg > $HOME/.ansible.cfg
$ vim $HOME/.ansible.cfg
[defaults]
remote_user = ansible ; we will later create this account
inventory = $HOME/inventory ; list of remote hosts
[inventory]
[privilege_escalation]
become = true ; user rights will be elevated
become_method = sudo ; by using sudo
[paramiko_connection]
[ssh_connection]
[persistent_connection]
[accelerate]
[selinux]
[colors]
[diff]
Listing 2-14Creating an Ansible Configuration
Note
开始一个新行的注释可以是分号 (#) 或分号*(;)*。放置在配置行末尾的行内注释和对该行其余部分的注释必须使用分号,就像我们在本例中使用的那样。
随着我们全新的配置就位并等待使用,我们将能够使用之前的ansible-config命令进行更多的演示。同样重要的是,永远不要假设我们输入到文件中的内容是正确的;一点点测试不会伤害任何人。
$ ansible-config view
[defaults]
remote_user = ansible
inventory = $HOME/inventory
[inventory]
[privilege_escalation]
become = true
become_method = sudo
[paramiko_connection]
[ssh_connection]
[persistent_connection]
[accelerate]
[selinux]
[colors]
[diff]
Listing 2-15Viewing the Configuration
我们必须小心使用这个命令,因为绝对没有检查我们使用的节头或提供的键或值。只要文件与 INI 文件格式匹配,就会打印出来。用dump子命令检查有效设置非常有用,尤其是当我们用--only-changed选项过滤时。加油;我带你去看。
$ ansible-config dump --only-changed
DEFAULT_BECOME(/home/tux/.ansible.cfg) = True
DEFAULT_BECOME_METHOD(/home/tux/.ansible.cfg) = sudo
DEFAULT_HOST_LIST(/home/tux/.ansible.cfg) = ['/home/tux/inventory']
DEFAULT_REMOTE_USER(/home/tux/.ansible.cfg) = ansible
Listing 2-16Viewing Settings Changed from the Default
输出现在也确认了 Ansible 的设置是有效的和可用的。如果键或标题未被识别,则它不会改变任何内容,并且节标题或设置无效。如果我们发现我们在输出中没有看到我们正在寻找的一个或多个选项,很可能我们的胖手指在某种程度上妨碍了完美。
摘要
我认为目前的情况是,你正在成为一个可靠的超级英雄。是的,你——正式成为了一个可靠的超级英雄。但也许我们需要更多地关注考试;毕竟,这是赚大钱的地方。你很快就能通过考试;只需看一看事实,并开始理解您现在可以配置 Ansible 了。您知道应用的配置层次。从上往下开始,我们首先搜索:
-
ANSIBLE_CONFIG
-
ansible.cfg 在 CWD,只要该目录不是全局可写的
-
$HOME/.ansible.cfg
-
/etc/ansible/ansible.cfg
不仅如此,在本章中,您还学习了如何查看和打印配置。首先,您学会了使用ansible --version命令打印配置路径,使用专用命令ansible-config打印设置。我们有三个子命令:查看、转储和列表,其中ansible-config dump --only-changed可能是最有用的,也是我个人最喜欢的。是的,我的确有胖手指!
当创建我们自己的定制配置时,我们可能会使用注释。在一个新行的开始,我们可以用#或#来注释整个行;。然而,如果我们需要注释一行的其余部分,我们只能使用分号。我们使用这些知识在我们的主目录中创建了一个简单的配置。因此,当然,文件名被隐藏并创建为 .ansible.cfg ,并添加我们可以跨项目使用的设置,用特定项目自己的基于项目的配置或环境变量覆盖它们。现在,我们可以继续深入了解如何创建我们在配置中已经引用过的主机清单。
三、创建一个 Ansible 库存
在我们的 CentOS 8 控制器上使用 Ansible 时,我们可以通过主机列表来定位我们想要直接管理的主机。该列表可作为选项提供给ansible命令。当然,我们可以做得更好。我们希望创建一个基于文件的持久主机列表,而不是临时列表。该列表是一个 Ansible 清单,在该文件中,我们还可以根据地理位置、功能或操作系统来定义组,这样就可以轻松地确定特定的目标主机。无论我们是直接在命令行中工作还是从剧本中工作,清单文件都提供了我们定位主机时所需的一致性。
Note
我知道我们还没有推出 Playbook,但别担心,我们很快就会推出。同时,剧本是以 YAML 格式编写的文本文件,它描述了应该在受管设备上执行的任务:如果您愿意,可以说是要完成的工作的清单。
创建库存
在前一章中已经为 Ansible 创建了一个配置,我们几乎可以肯定地继续使用 Ansible 管理主机列表和创建清单中的下一个任务。在 Ansible 配置中,库存文件位置的项目名称是 DEFAULT_HOST_LIST 。使用grep,我们可以使用ansible-config list的输出显示该设置的文档帮助。
$ ansible-config list | grep -A10 DEFAULT_HOST_LIST
DEFAULT_HOST_LIST:
default: /etc/ansible/hosts
description: Comma separated list of Ansible inventory sources
env:
- {name: ANSIBLE_INVENTORY}
expand_relative_paths: true
ini:
- {key: inventory, section: defaults}
name: Inventory Source
type: pathlist
yaml: {key: defaults.inventory}
Listing 3-1Gaining Help in the Host List or Inventory
我们应该从输出中注意到的第一件事是这个键的默认值;就是文件 /etc/ansible/hosts 。虽然这个文件没有包含任何有效的条目,但是每一行都被注释了,它确实提供了很好的有用的例子。当您刚接触库存和库存组时,这是一个很好的起点。默认文件是 INI 格式,但是,正如我们将在后面看到的,如果我们愿意,我们也可以使用 YAML 格式的库存文件。为了显示这个文件中的例子而不显示其他注释行,我们可以查找以双注释开头的行;除此之外,我们可以通过将输出传送到tr命令来删除显示的注释。当我们在命令行中变得有创造力的欲望压倒一切时,我们可以额外使用命令tee将输出显示到屏幕上,并填充我们自己的库存文件。我会给你看,但前提是你要保证在你自己的系统上练习。
$ grep '^##' /etc/ansible/hosts | tr -d '##' | tee ~/inventory
green.example.com
blue.example.com
192.168.100.1
192.168.100.10
[webservers]
alpha.example.org
beta.example.org
192.168.1.100
192.168.1.110
www[001:006].example.com
[dbservers]
db01.intranet.mydomain.net
db02.intranet.mydomain.net
10.25.1.56
10.25.1.57
db-[99:101]-node.example.com
Listing 3-2Listing the Default Inventory File to Populate Our Own Inventory
Note
如果您想更好地理解前面管道中的命令,那么就构建这些命令。首先列出不带过滤器的文件:
$ cat /etc/ansible/hosts
$ grep '^##' /etc/ansible/hosts
$ grep '^##' /etc/ansible/hosts | tr -d '##'
$ grep '^##' /etc/ansible/hosts | tr -d '##' | tee ~/inventory
$ cat ~/inventory
没有任何工作,(这总是一个很好的开始),我们现在有了一个可以练习的小组清单。对我们来说,这是理解库存文件和我们可以用来查询库存的相关工具的良好开端。我猜所使用的 IP 地址不符合您的网络,它们肯定也不符合我的主机,所以在我们查询清单的初始练习之后,我们将替换这个文件,或者至少是它的内容。
查询库存条目
我们有两个命令可以用来打印库存文件中的条目。这些命令包括ansible命令以及特定的ansible-inventory命令。除了我们在文件中明确定义的任何组之外,我们还有两个内置组:
-
all :是的,你猜对了,组 all 是指清单文件中包含的所有主机。
-
:未分组组是指不包含在文件中任何特定清单组的主机。
在 Ansible 的大部分时间里,你可能都会用到这个组。我们经常希望针对所有主机;毕竟,这就是为什么你把它们添加到清单中。到目前为止,我从未有过针对未分组组的需求,但是还有时间!首先,让我们确保我们在用户的主目录中工作,我们将检查正在使用的 Ansible 配置。我们希望确保我们使用的库存文件被设置为 $HOME/inventory 。
$ ansible --version | grep 'config file'
config file = /home/tux/.ansible.cfg
$ ansible-config dump --only-changed
DEFAULT_BECOME(/home/tux/.ansible.cfg) = True
DEFAULT_BECOME_METHOD(/home/tux/.ansible.cfg) = sudo
DEFAULT_HOST_LIST(/home/tux/.ansible.cfg) = ['/home/tux/inventory']
DEFAULT_REMOTE_USER(/home/tux/.ansible.cfg) = ansible
Listing 3-3Verify the Ansible Configuration
Note
如果您没有看到相同的配置,那么您可以花点时间回顾一下上一章,在那里我们创建了配置文件 ~/.ansible.cfg 。我向你保证,这本书不会消失;我们会在这里等你回来。
使用 Ansible 列出清单主机
准备好 Ansible 配置文件,并确保使用的清单文件是我们创建的文件,我们就可以开始了。列出清单中所有主机的最简单方法是使用ansible命令。使用内置组 all ,每个主机都会被列出。
$ ansible all --list-hosts
hosts (21):
green.example.com
blue.example.com
192.168.100.1
192.168.100.10
alpha.example.org
beta.example.org
www001.example.com
www002.example.com
www003.example.com
db-99-node.example.com
db-100-node.example.com
db-101-node.example.com
Listing 3-4Listing Hosts with the Ansible Command, Some Output Is Trimmed to Reduce Space Used in This Book, Thereby Not Just Saving Trees but Saving Your Eyes!
我们还可以列出组及其成员;使用 webservers 组而不是 all 组演示了这一点。
$ ansible webservers --list-hosts
hosts (10):
alpha.example.org
beta.example.org
192.168.1.100
192.168.1.110
www001.example.com
www002.example.com
www003.example.com
www004.example.com
www005.example.com
www006.example.com
Listing 3-5List Specific Groups with Ansible
如果您还记得,我们有两个内置组。我们已经看到了所有清单主机的列表,现在我们可以看到组未分组,即未包含在命名组中的主机。
$ ansible ungrouped --list-hosts
hosts (4):
green.example.com
blue.example.com
192.168.100.1
192.168.100.10
Listing 3-6Listing Hosts That Do Not Exist in Any Named Group
使用 Ansible-Inventory 列出主机
尽管ansible命令非常简单,但是随着课程的进行,我们将开始意识到仅列出主机是有局限性的。通常我们还需要查看库存变量。正是在这样的时候,当我们的需求变得更加复杂时,我们可以依靠ansible-inventory命令。同样,和以前一样,我们可以从列出清单中的所有主机开始。默认的输出是 JSON 格式的,但是我包含了用 YAML 打印的选项,因为它不太冗长。
$ ansible-inventory --list --yaml
all:
children:
dbservers:
hosts:
10.25.1.56: {}
10.25.1.57: {}
db-100-node.example.com: {}
db-101-node.example.com: {}
ungrouped:
hosts:
blue.example.com: {}
green.example.com: {}
webservers:
hosts:
www001.example.com: {}
www002.example.com: {}
www003.example.com: {}
Listing 3-7Listing All Hosts with the ansible-inventory Command, Some Output Is Trimmed to Reduce Space Used
我们可以看到每台主机的大括号;这是可以显示库存变量的地方,如果设置了库存变量的话。我们目前没有使用任何变量,但是我可以向您展示ansible-inventory命令在列出这些变量时是多么有用。以我自己系统上的一个工作配置为例,我可以首先列出所有带有ansible的主机,然后列出ansible-inventory。
Note
以下命令在我的内部 Ansible 控制器上运行,该控制器用于部署 AWS 系统。这些条目目前不在您自己的清单中,但我们将很快在实验室清单中使用变量。
$ ansible all --list-hosts
hosts (1):
3.8.123.144
$ ansible-inventory --list --yaml
all:
children:
redhat: {}
suse:
hosts:
3.8.123.144:
admin_group: sudo
ansible_user: ec2-user
ubuntu: {}
ungrouped: {}
Listing 3-8Listing Inventory with Variables, First with ansible and Then ansible-inventory
我们可以看到主机已经配置了两个变量: admin_group 和 ansible_user 。在创建需要管理系统的用户时,将使用 admin_group 变量;该组可能因 Linux 发行版的不同而不同。有些分配使用组轮,有些使用组轮。在 AWS 中,您应该连接的默认用户帐户根据图像创建者的不同而不同;在 openSUSE 中,它是 ec2 用户,在 CentOS 中,它是 centos 用户帐户。通过实现一个变量,我们能够满足不同的账户。Ansible 中的变量帮助我们处理这些不同的需求,作为管理员,能够看到变量将帮助我们调试 Ansible 命令和剧本执行的问题。
回到 CentOS 8 控制器作为我们的实验室系统,我们可以使用ansible-inventory列出一个组中的主机,就像我们使用ansible一样。
$ ansible-inventory --graph --yaml dbservers
@dbservers:
|--10.25.1.56
|--10.25.1.57
|--db-100-node.example.com
|--db-101-node.example.com
|--db-99-node.example.com
|--db01.intranet.mydomain.net
|--db02.intranet.mydomain.net
Listing 3-9Listing Group Membership with ansible-inventory
添加主机和组条目
当我们将主机添加到清单文件时,我们可以使用可解析的主机名或 IP 地址。我们还可以为主机名或 IP 地址添加范围。
# To add www1.example.com, www2.example.com, www3.example.com
www[1:3].example.com
# To add a range of IP addresses
192.168.1.[1:5]
Listing 3-10Adding Ranges to the Ansible Inventory
加入群组时,群组名称将会加入区段标题。例如,要为伦敦添加一个组,我们可以将下面一行添加到清单文件中。组的成员应该列在组部分标题的下面。
[London]
Listing 3-11Adding a Group to the Ansible Inventory
我们还可以充分利用库存文件中的嵌套组。嵌套组是在其他组中列出的组。例如,如果我们在清单中定义了一个伦敦组和布里斯托尔组,我们可以将这些组嵌套在英国组中。关键字 children 用于表示成员是嵌套组。
[London]
server1
server3
[Bristol]
server2
server4
[UK:children]
Bristol
London
Listing 3-12Using Nested Groups in the Ansible Inventory
发现网络上的主机
如果您正在为 VMWare 使用内部 NAT 网络,那么您将知道在该网络上运行的主机数量有限。如果像我一样,您在 NAT 网络上运行的唯一虚拟机是您在本课程中想要的三台主机,那么我们可以创造一些奇迹。我们可以通过端口扫描来做到这一点,首先我们需要安装端口扫描器nmap。
$ sudo yum install -y nmap
Listing 3-13Installing the Port Scanner nmap on the Controller
使用 NAT 网络上的端口扫描器,我们可以检测既在网络上运行又在侦听 TCP 端口 22(SSH 端口)的主机。我们将需要 SSH 端口从 Ansible 连接。扫描网络时,请确保您输入了网络的网络地址,但是如果您没有得到授权,请不要扫描网络!
Important
在一些公司中,网络扫描可能会触发警报,因为网络扫描可能是对网络和服务器资源进行网络攻击的前兆。如果这不是您自己的个人网络,您必须事先获得运行扫描的书面协议。我们在示例中运行的命令没有任何危险,但是我们当然会在网络上发现可以被视为识别的服务。
$ sudo nmap -Pn -p 22 -n 172.16.120.0/24 --open -oG -
Nov 18 16:51:36 2020 as: nmap -Pn -p 22 -n --open -oG - 172.16.120.0/24
Host: 172.16.120.185 () Status: Up
Host: 172.16.120.185 () Ports: 22/open/tcp//ssh///
Host: 172.16.120.188 () Status: Up
Host: 172.16.120.188 () Ports: 22/open/tcp//ssh///
Host: 172.16.120.161 () Status: Up
Host: 172.16.120.161 () Ports: 22/open/tcp//ssh///
# Nmap done at Wed Nov 18 16:51:41 2020 -- 256 IP addresses (6 hosts up) scanned in 5.57 seconds
Listing 3-14Scanning the Network for SSH Servers
我们启动的端口扫描有几个选项,旨在根据我们的需求提供最佳输出。选项如下所示:
-
-Pn:不要一开始就探测主机看它是否启动。由于我们发现的是单个端口,这不会降低扫描速度,而且可能会更准确。 -
-p 22:仅扫描端口 22;我们默认为 TCP。 -
在我的例子中,我们正在扫描 NAT 网络。
-
--open:仅列出端口打开时的结果,而不是过滤或关闭。 -
-oG -:我们让输出更容易被grep等命令过滤;最后一个破折号表示我们将输出发送到屏幕 STDOUT。
我们看到的结果是可以的,但是,如果您还记得,我们希望从这个输出中创建一个库存文件。这意味着我们需要排除输出中显示的其余数据。我们可以选择命令awk来过滤我们想要的行和我们想要的确切字段。我们希望查找包含 22/open 的行,并且我们希望只返回第二个字段,即网络上主机的 IP 地址。
$ sudo nmap -Pn -p22 -n 172.16.120.0/24 --open -oG - | awk '/22\/open/{ print $2 }'
172.16.120.185
172.16.120.188
172.16.120.161
Listing 3-15Extracting IP Addresses
最后一步是,一旦我们在屏幕上验证了输出,就将它发送到库存文件;如果看起来没问题,就把它发送到 $HOME/inventory 文件。为了确保世界和库存文件一切正常,我们用ansible-inventory列出了文件的内容。
$ sudo nmap -Pn -p22 -n 172.16.120.0/24 --open -oG - | awk '/22\/open/{ print $2 }' | tee $HOME/inventory
$ ansible-inventory --list --yaml
all:
children:
ungrouped:
hosts:
172.16.120.161: {}
172.16.120.185: {}
172.16.120.188: {}
Listing 3-16Dynamically Creating Our Own Inventory
库存变量
在本章的最后,我们将设置可以与清单一起使用的主机和组变量。变量可以直接添加到标准 INI 风格清单中;然而,当从清单中抽象出来并存储在单独的文件中时,这些变量变得更加清晰。这使得库存文件不那么密集,变量更加模块化。
默认情况下,Ansible 使用本机 OpenSSH 连接到受管设备。OpenSSH 在基于 Linux 和 Unix 的系统上更受欢迎,因为它支持 ControlPersist、Kerberos 身份验证和存储在 ~/中的选项。ssh/config 等跳转主机设置。如果您的控制器系统使用不支持 ControlPersist 的旧版本 OpenSSH,Ansible 将回退到名为 paramiko 的 OpenSSH Python 实现。其他连接方法也是可用的,例如用于 Microsoft Windows 系统的 WinRM。在管理控制器本身时,我们可能还想跳过 SSH 的使用;我们可以使用本地连接。为此,我们可以使用一个分配给控制器主机的变量。正如我们前面提到的,这可以在 INI 文件清单上设置。您需要确定控制器的 IP 地址,以便能够将变量添加到主机。
$ cd
$ ip -4 addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
inet 172.16.120.161/24 brd 172.16.120.255 scope global dynamic noprefixroute ens33
valid_lft 1425sec preferred_lft 1425sec
$ sed -Ei 's/(172.16.120.161)/\1 ansible_connection=local/' inventory
$ ansible-inventory --yaml --host 172.16.120.161
ansible_connection: local
Listing 3-17Determine the Controller IP and Configure Variable for Local Connection
虽然这确实有效,而且我们可以给这个主机添加更多的变量,但是你会发现你的库存变得更加密集,不那么容易阅读。将库存与变量分开是一种更整洁的工作方式。让我们使用sed来恢复我们刚刚添加到清单中的设置。
$ cd
$ sed -Ei 's/\<ansible_connection=local\>//' inventory
$ ansible-inventory --yaml --host 172.16.120.161
{}
Listing 3-18Reverting the Inventory
所选主机的大括号现在是空的,表示没有主机变量。我们现在将创建两个子目录;这些需要在与清单文件相同的目录中创建—在本例中,是我们的用户帐户的主目录。创建目录后,一个用于主机,一个用于组,我们可以为需要配置变量的主机或组添加 YAML 文件。
$ cd
$ mkdir {host,group}_vars
$ echo "ansible_connection: local" > host_vars/172.16.120.161
$ ansible-inventory --yaml --host 172.16.120.161
ansible_connection: local
Listing 3-19Separate Inventory and Variables
Note
分离的变量文件为 YAML 格式;键用一个:和一个<space>与它们的值分隔开。我们可以在这个新创建的文件中看到它:ansible_connection: local,而在 INI 清单文件中,键/值对使用了=符号,所以是ansible_connection=local。
摘要
我被你的进步惊呆了。您现在能够有效地配置 Ansible 清单,即我们可以管理的主机列表。不仅如此,您还能够创建用于主机或组的变量。这真的很神奇,你会发现 Ansible 的这个强大基础真的很有用。
在学习本章的过程中,我们还学习了更多的命令行技巧,这些技巧可以帮助我们加快 bash shell 的使用。首先,我们添加了一些sed例子来动态编辑文件。流编辑器sed非常有用,工作方式类似于grep;使用sed,我们可以编辑文件,而不仅仅是过滤输出。除了在本章中使用sed和grep之外,我们还看了这两个命令的老大哥awk。其次,在为变量文件创建目录时,我们通过使用大括号用一个命令创建了两个目录。命令mkdir {host,group}_vars将扩展为mkdir host_vars; mkdir group_vars。这些快捷方式可以让你在命令行上更快,时间在考试中至关重要。
您还可以使用ansible --list-hosts命令和ansible-inventory命令来查询库存。如果您只是需要列出组或所有主机,那么使用前一个命令。后一个命令非常适合列出与主机或组相关的变量。**
四、使用临时命令和 Ansible 准备
我开始感到你越来越不耐烦了;是的,它甚至超越了我们之间的时间和距离。您希望学习 Ansible 并获得该产品的大量实践经验。好吧,我有好消息告诉你;你不必再等了。我们即将释放迄今为止对你们隐藏的灼热力量。您将学习如何通过在 Ansible 控制器上执行单个命令来并行配置三个实验系统。临时命令允许我们直接进入 Ansible,而不需要剧本文件。矛盾的是,这使得它们既好又坏。临时命令很好,因为它们可以在需要时快速执行。它们之所以不好,是因为我们执行的命令缺乏与 YAML 剧本相关的可重复的正确属性。有了剧本,文件就是需要执行的任务的持久清单,既记录了配置又实现了任何配置管理系统的涅槃:重复正确。使用特别的命令,我们可以很容易地省略一个必需的配置参数,但是使用剧本不会发生这种情况。每次执行剧本时,我们都会得到相同的结果。我猜你现在已经理解了与 Ansible 相关的术语“ad hoc”。这些命令将根据需要运行,不一定需要重复。
测试 Ansible
尽管我们一直像谚语中的蜜蜂一样忙于创建配置和库存,但我们还没有看到 Ansible 在工作。简单的配置更改是特别命令的核心,它们是通过我们的好朋友ansible命令来执行的。这些命令中最简单的是 Ansible ping 模块。它不是网络 ICMP ping,而是使用 ansible_connect 方法进行连接,以发现受管设备上是否存在 Python 解释器。连接通常是 SSH,但是正如我们所看到的,我们已经将控制器设置为使用本地连接。通过检查我们是否可以在托管主机上针对我们的库存主机运行 ping 模块,我们将能够检查一切是否正常工作,并纠正可能出现的问题。然后,我们可以在本课程的剩余时间里继续将系统配置为所需的状态。
Note
我们假设你作为 tux 用户帐户登录到控制器,他能够使用sudo作为根用户运行所有命令。 tux 账户也应该存在于被管理设备上,并且能够使用sudo运行命令。
$ ANSIBLE_REMOTE_USER=tux ansible all -k -K -m ping
SSH password:
BECOME password[defaults to SSH password]:
172.16.120.188 | FAILED! => {
"msg": "Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this. Please add this host's fingerprint to your known_hosts file to manage this host."
}
172.16.120.185 | FAILED! => {
"msg": "Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this. Please add this host's fingerprint to your known_hosts file to manage this host."
}
172.16.120.161 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
Listing 4-1Testing Ansible with the Python Ping Module
绿色好,红色不太好。我们还需要添加一堆交换机,在托管主机的配置完成后,我们可以忽略这些交换机。我们将很快介绍开关,但首先让我们纠正我们看到的错误。对我来说,控制器 IP 地址显示连接成功;失败的是我们使用 SSH 的远程系统。如果我们读了这条信息,我们就能看到原因。是的,不要像被车头灯照着的兔子一样呆立不动——阅读错误信息!我们之前没有使用 SSH 连接到远程系统,也没有将它们的公钥存储为 SSH known_hosts 。这里我们可以采取两种方法:或者使用ssh-keyscan来收集远程密钥,或者,正如我们将要做的,我们可以选择禁用主机密钥检查。在我们的实验室环境中,这是明智的选择。让我们将配置复制到我们的 CWD 中,并根据我们的需要进行调整。
$ mkdir -p $HOME/ansible/setup
$ cd !$
cd $HOME/ansible/setup
$ cp ~/.ansible.cfg .
$ ansible --version | grep 'config file'
config file = /home/tux/ansible/setup/ansible.cfg
Listing 4-2Overwriting
the Effective Ansible Configuration
Note
我们使用了!$变量来表示在命令行中使用的最后一个参数,以使更改目录变得更加容易和快速。
编辑这个文件既可以纠正我们看到的错误,又可以提高命令执行的效率。在原始文件中,我们设置了提升特权的选项;对于 ping 模块,我们不需要以 root 身份运行,这意味着我们不需要输入 sudo 密码(-K)。我们还在原始文件中使用远程帐户 ansible ,这个文件我们还没有创建。选择在配置中修改此设置意味着我们不需要用变量覆盖设置。我们不能忘记,我们也应该纠正错误;添加值为 false 的密钥 host_key_checking 将解决这个问题。我们应该编辑文件,使它看起来像这样。对文件所做的更改会突出显示。
[defaults]
remote_user = tux
inventory = $HOME/inventory
host_key_checking = false
[inventory]
[privilege_escalation]
become = false
become_method = sudo
Listing 4-3Modified
$HOME/ansible/setup/ansible.cfg
使用设置become = false也有优势;我们可以使用选项–b来提升特殊命令的权限,但不能反过来。不进行不必要的升级有利于使系统更加安全。用我们喜欢的文本编辑器编辑完文件后,我们就可以开始了。现在,让我们在重新运行 Ansible ping 之前测试一下设置是否有效。
$ ansible-config dump --only-changed
DEFAULT_BECOME(/home/tux/ansible/setup/ansible.cfg) = False
DEFAULT_BECOME_METHOD(/home/tux/ansible/setup/ansible.cfg) = sudo
DEFAULT_HOST_LIST(/home/tux/ansible/setup/ansible.cfg) = ['/home/tux/inventory']
DEFAULT_REMOTE_USER(/home/tux/ansible/setup/ansible.cfg) = tux
HOST_KEY_CHECKING(/home/tux/ansible/setup/ansible.cfg) = False
$ ansible all -k -m ping
...
Listing 4-4Testing the Configuration Is Effective and Running the Ping
我们现在应该有三个成功的绿色输出,每个主机一个。如果你和我一样使用的是 Ubuntu 18.04 系统,你会看到一个警告,说 Python 解释器被检测为 /usr/bin/python ,而不是 /usr/bin/python3 。我们可以通过添加一个清单变量来设置 Ubuntu 18.04 主机使用 Python 3 来解决这个问题,但我们将回到这个问题。让我们首先确保我们理解在特别命令的命令行执行中使用的选项。以下列表显示了一些命令选项:
-
all:该参数指定我们要定位的清单中的组。我们在这里使用的是 all 组。 -
-k:提示输入 SSH 密码。我们稍后将使用基于密钥的身份验证,因此可以省略这个选项。 -
-K:我们已经忽略了这一点,因为我们不需要升级 ping 模块就能成功。如果我们确实需要升级,理想情况下,用户帐户可以无密码访问 sudo。我们将用一个特别的命令对此进行配置。 -
-b:使用权限提升,即使在配置中没有设置。 -
-m:要执行的 Ansible Python 模块的名称。我们使用 ping 模块。 -
这里不使用或不需要。我们可以并且经常需要向模块提供参数,这是通过-a 选项提供的。
实施可变库存组
从之前 Ubuntu 18.04 系统生成的关于 Python 解释器的警告中,我们可以开始看到对组的需求。尽管我们可以为该主机设置一个主机变量,但将来我们可能会招募更多的 18.04 主机,现在添加组将节省将来的工作。根据组设置所需的变量将更容易理解,并且可能更准确,因为不会忘记主机。在此阶段,我们将为 CentOS 主机和一个 Ubuntu 组创建组。我们还将为 18.04 主机创建一个组,因为 20.04 版本的 Ubuntu 可能不需要相同的变量设置。我们将使用仿生的 18.04 主机的代号作为组名。组名不应以数字开头。我们可以将仿生组嵌套在 Ubuntu 组中。正如我们现在看到的,这很容易实现。确保您可以确定 Ubuntu 系统的 IP 地址,以便将其添加到正确的组中。
$ cat $HOME/inventory
[bionic]
172.16.120.188
[centos]
172.16.120.161
172.16.120.185
[ubuntu:children]
bionic
$ ansible bionic --list-hosts
hosts (1):
172.16.120.188
$ ansible ubuntu --list-hosts
hosts (1):
172.16.120.188
$ ansible centos --list-hosts
hosts (2):
172.16.120.161
172.16.120.185
Listing 4-5Modify Your $HOME/inventory File to Include Groups
设置组启用了在即席命令中独立定位这些组的机制。我们不必只使用组 all 。我们还可以使用组来设置所需的变量。在下面,我们首先列出没有配置变量的 bionic 组,然后设置变量,并重新列出该组,然后只针对 Ubuntu 组重新运行 ping 命令。
Note
我们仍然可以用命令ansible-inventory使用--host选项来引用一个组名。
$ ansible-inventory --host bionic --yaml
{}
$ echo "ansible_python_interpreter: /usr/bin/python3" > $HOME/group_vars/bionic
$ ansible-inventory --host bionic --yaml
ansible_python_interpreter: /usr/bin/python3
$ ansible ubuntu -k -m ping
SSH password:
172.16.120.188 | SUCCESS => {
"changed": false,
"ping": "pong"
}
Listing 4-6Implementing Group Variables to Resolve Ad Hoc Issues
使用 CentOS 和 Ubuntu 可以让我们发现在单一发行版中看不到的问题。这一点在实验室系统介绍中提到过,但值得重复一遍,因为我们已经研究了在 Ansible 部署中常见的不同环境中有用的其他选项。这也让我们在课程的早期巩固了对组和嵌套组的理解。
为 Ansible 准备用户帐户
实际情况是,在受管系统上使用一个专用帐户来负责操作,可以提高配置更改的透明度和安全性。到目前为止,我们已经使用了 tux 帐户,我们将需要继续使用这个帐户,直到我们为 Ansible 创建专用帐户。
创建用户
我们将使用用户模块直接创建账户。我们将需要参数来创建用户,使我们能够演示-a选项。我们将用户密码设置为一个参数,它需要是一个加密的散列。我们在创建帐户之前生成这个散列。将加密的密码存储在一个变量中后,我们可以运行这个特别的命令,需要通过使用选项-b来提升特权。我们还需要分别使用选项-k和-K提示 SSH 密码和 sudo 密码。创建帐户后,我们可以确认条目存在于 passwd 和 shadow 数据库中。
$ user_password=$(openssl passwd -6 Password1)
$ ansible all -kKbm user -a "name=ansible password=$user_password"
...
$ getent passwd ansible
ansible:x:1001:1001::/home/ansible:/bin/bash
$ sudo getent shadow ansible
ansible:$6$li9wmHhZW/TUHYeX$WzH596QutESoI5j3GYqoqnkSLlN.9VxdnMt5aix7SX18AE.1.3rH25quQU1wLrtg3zwXCNNdlQ8Bm6CenJenL/:18586:0:99999:7:::
Listing 4-7Using an Ad Hoc Command to Create the Dedicated Ansible User Account
允许无密码的 Sudo 访问
使用新创建的帐户时,我们需要提升权限,而不需要添加密码。这有助于简化操作,尤其是当我们希望安排 Ansible 命令在无人值守的情况下运行时。在我们的 Linux 系统上,将一个文件添加到/ etc/sudoes.d/ 目录中,将允许在没有密码的情况下访问sudo。我们创建一个本地文件,然后使用 Ansible copy 模块分发它。我们可以在分发文件之前验证它,以保持 sudoers 子系统的完整性。
$ cd ~/ansible/setup
$ echo "ansible ALL=(root) NOPASSWD: ALL" > ansible
$ sudo visudo -cf ansible
ansible: parsed OK
$ ansible all -bkK -m copy -a "src=ansible dest=/etc/sudoers.d/ansible"
Listing 4-8Allowing Access Without a Password to sudo
SSH 密钥认证
我们也倾向于使用基于密钥的身份验证,使帐户更加健壮,并减少执行命令时的交互需求。我们需要为我们的用户帐户 tux 生成一个 SSH 密钥对。公钥将需要被分发到远程系统和责任账户。从 tux 用户到 ansible 用户帐户的认证不需要 SSH 的密码。我们将用来分发密钥的 Ansible 模块是 authorized_key 模块,但是首先我们需要为 tux 生成密钥对。
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/tux/.ssh/id_rsa):
Enter passphrase (empty for no passphrase): (leave blank)
Enter same passphrase again: (leave blank)
Your identification has been saved in /home/tux/.ssh/id_rsa.
Your public key has been saved in /home/tux/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:BdooIqc1yJbyIlEC20U/Xqxvx4k7AGqWasvWeTj64wo tux@controller
The key's randomart image is:
+---[RSA 3072]----+
|o=*o+ .. |
+----[SHA256]-----+
$ ansible all -bkKm authorized_key -a "user='ansible' state="present" \
key='{{ lookup('file','/home/tux/.ssh/id_rsa.pub')}}'"
...
Listing 4-9Establishing Key-Based Authentication
三个系统的输出应该是黄色的,表示已经发生了变化。我们几乎完成了我们的配置。不过,我想知道你是否能够想到一个我们仍然可能会遇到的问题。
嗯,这很简单,需要做两个改变。我们需要告诉 Ansible 使用我们已经创建的专用 ansible 账户,我们需要确保控制器允许无密码sudo访问,在那里我们使用来自 tux 用户的本地连接。
配置最终更改
$ cd ~/ansible/setup ; cp ansible tux
$ sed -i s/ansible/tux/ tux
$ sudo visudo -cf tux
tux: parsed OK
$ sudo cp tux /etc/sudoers.d/tux
$ sed -i s/tux/ansible/ ansible.cfg
Listing 4-10Configuring
the tux Account on the Controller System and Reverting the ansible.cfg
我们现在可以通过重新执行之前分发 SSH 密钥的ansible命令来测试这一点。我们现在应该能够排除所有的密码提示。重新执行该命令不会导致任何问题,Ansible 是幂等的,这意味着我们可以多次运行相同的特别命令,并且只有在我们不满足所需的配置状态时才会进行更改。我们应该看到每台主机的绿色输出,表明我们符合配置。
$ ansible all -bm authorized_key -a "user='ansible' state="present" \
key='{{ lookup('file','/home/tux/.ssh/id_rsa.pub')}}'"
...
Listing 4-11Testing Access Without Interaction
获取模块帮助
我们在这一章做了很大的改进,使用了 ping 、 copy 、 user 和 authorized_key 模块。问题是:你怎么知道存在什么模块,它们接受什么参数?这是另一个简单但很棒的问题,我们可以用ansible-doc命令来回答。在下面的代码示例中,我们首先看到如何打印所有模块;我们还对它们进行了计数,以查看我们正在使用的版本中有超过 3000 个。之后,我们将获得本章介绍的用户模块的帮助。
$ ansible-doc user
Listing 4-13Gain Help on a Specific Ansible Module
$ ansible-doc --list
...
$ ansible-doc --list | wc -l
3387
Listing 4-12List All Ansible Modules
当获得关于某个模块的帮助时,您可以搜索示例以获得如何使用该模块的实用指南。它们包括 YAML 剧本的例子,但是这些很容易适应命令行。我们可以通过添加和删除另一个用户来扩展我们在命令行上使用特殊命令的实践。
$ ansible all -bm user -a "name=fred"
Listing 4-14Adding a Supplementary Test User to Our Systems
当使用用户模块时,如果我们没有指定键,状态被假定为存在。下面的命令与前面的清单相同,我们没有定义状态。那么输出将是绿色的,表明我们符合配置;用户存在。
$ ansible all -bm user -a "name=fred state=present"
Listing 4-15Explicitly Setting the State in the User Module
当删除用户帐户时,我们将状态设置为不存在。我们还可以用 remove=true 键/值对设置删除他们的主目录和邮件假脱机文件。
$ ansible all -bm user -a "name=fred state=absent remove=true"
Listing 4-16Removing a User Account and Their Home Directory
摘要
我们现在真的能使用 Ansible 了,而且用得很好。你已经能够看到你的系统管理有多有效。我们轻松地在三个系统上创建了新帐户,并添加了 sudo 文件和我们的身份验证密钥。我们执行的每个命令都是在三台受管主机上执行的。即使使用我们需要实现的配置,使用 Ansible 执行这些任务也会比单独配置每台主机更快。这仅仅是开始;从这一点开始,收益将呈指数级增长。
在本章中,我们将组添加到我们的清单中,并为 Ubuntu 18.04 的仿生组添加了变量ansi ble _ python _ interpreter。我们还使用带有 false 值的密钥 host_key_checking 在 Ansible 配置中轻松地自动接受 SSH 主机密钥。在我们经常连接到新主机的情况下,例如在敏捷开发运维环境中,该设置是必不可少的。配置和库存就绪后,我们很快就能够扩展 Ansible 的知识,在使用用户模块创建 Ansible 使用的专用帐户之前,我们可以使用 ping 模块检查受管设备上的 Python 解释器。这个用户需要在没有密码的情况下访问 sudo,在使用 authorized_key 模块允许对我们添加的用户帐户进行基于密钥的认证之前,我们使用 copy 模块将 sudoers 文件传送到每个主机。
在本章的最后,我真的希望你正在用 Ansible 建立你对配置管理的信心。不要忘记你的自信是通过实践建立起来的。因此,请在您自己的实验室中练习这些命令,并研究使用ansible-doc命令可以获得的帮助。
五、编写 YAML 和基本剧本
Ansible 中的即席命令棒极了;看看我们是如何轻松地跨所有三个系统创建用户帐户的。然而,我们不能让自己过于沉浸在我们所取得的成就中;临时命令是我们旅程的一部分,但不是目的地。因此,让我们庆祝我们已经取得的成就,但不要休息太久;我们将继续前进,开始理解剧本和 YAML 的基础知识。首先要了解的是缩略词本身:YAMLAin tMarkupLlanguage。它是用于数据处理的,而不是一种标记语言。要理解和掌握的下一个也是最重要的特性是使用重要的前导空格。一个元素的缩进级别决定了它与文件中其他元素的关系。
在本章中,您将学习编写剧本,并受益于每次执行都正确的可重复命令。我们将学习调整 YAML 文件的文本编辑器,帮助我们创建正确的 YAML 语法。我们还将先睹为快,看看 Linux 上的图形编辑器和 Microsoft Visual Studio 代码。记住,Ansible 控制器很可能是一个 Linux 工作站,这使得使用 GUI 编写 YAML 非常合理。请系好安全带,因为我们即将开始你的下一阶段学习。
编写简单的 YAML 剧本
在创建剧本时,我们需要意识到,与 Python 文件一样,我们处理的文件格式中,前导空格非常重要,并且有一定的意义。为了保持文件格式的整洁,我们不使用任何形式的括号来分组相关的代码元素;我们使用缩进级别。你可以查看一个文件,每个元素看起来都与前面的元素对齐。如果一行使用制表符作为缩进,而另一行使用八个空格,则它们不在同一缩进级别。最好使用明确的空格,而不是制表符;使用 tab 键仍然很方便,所以能够配置您的文本编辑器将制表符视为空格是一个很好的学习设置。首先,我们必须了解剧本是由什么组成的。
剧本的要素
我们知道剧本是 YAML 文件,但是它们到底包含什么,尤其是它们必须包含什么?我亲爱的朋友和读者,这很简单:一个剧本将包含至少一个剧本,一个剧本将包含至少一个任务。一个 Ansible 任务与我们可以从命令行执行的每个单独的特别命令相关:
-
剧本 :包含一个或多个剧本的 YAML 格式文本文件
-
Play :该游戏将包含一个或多个任务,稍后我们将看到其他元素,如处理程序。
-
任务 :一个任务将代表一个带有参数的模块的执行,参数可以是可选的,也可以是强制的,就像我们使用特殊命令一样。我们在剧本任务中使用的模块与在特殊命令中使用的 Python 模块完全相同。这是优于竞争系统(如 SaltStack 的盐)的一个优势。与状态模块相比,Salt 使用不同的模块进行远程执行(特别命令),在状态模块中,Salt 状态文件与剧本相当。
我们的第一本剧本
学习剧本结构的最好方法是实际上弄脏我们的键盘并开始写一个。这比仅仅讨论剧本应该是什么样子要好得多。因此,让我们利用第一本剧本,看看如何安装软件,这是我们以前没有使用过的特殊命令。我们将继续为项目使用 $HOME/ansible/setup 目录。请使用您最喜欢使用的命令行文本编辑器。我的是vim,但是我们稍后会考虑定制vim和nano来很好地配合 YAML。
$ cd $HOME/ansible/setup
$ ansible-config dump --only-changed
DEFAULT_BECOME(/home/tux/ansible/setup/ansible.cfg) = False
DEFAULT_BECOME_METHOD(/home/tux/ansible/setup/ansible.cfg) = sudo
DEFAULT_HOST_LIST(/home/tux/ansible/setup/ansible.cfg) = ['/home/tux/inventory']
DEFAULT_REMOTE_USER(/home/tux/ansible/setup/ansible.cfg) = ansible
HOST_KEY_CHECKING(/home/tux/ansible/setup/ansible.cfg) = False
$ vim software.yml
---
- name: My first play
hosts: all
become: true
tasks:
- name: Install software
package:
name: bash-completion
state: present
...
Listing 5-1Creating Our First Playbook
在我们创建的文本文件中,我们有一个包含一个行动的剧本。反过来,这个剧本只包含了一个任务。
-
剧本 :剧本就是文档本身。YAML 文档可以选择以三个破折号
---开始,相应的文档结束标记是三个点...。遗憾的是,我们没有提到三个朋友,这可能会更有趣一点。一个剧本将包含至少一个剧本。这些表示为一个列表。YAML 的清单项目用单个破折号-表示。 -
播放 :在每个播放中我们设置可选名称。虽然是可选的,但我强烈建议添加一个名称,既有助于记录文件,也有助于诊断。游戏名作为控制台输出的一部分打印出来。在与播放
name键相同的级别,我们为个人播放设置了其他键。这些键必须与name键处于相同的缩进级别。也就是说,用来自name的字母n而不是破折号排队。我们可以看到name、hosts、become、tasks都是这部剧被纵向排列的元素。在该行动中,hosts键用于将库存主机指向目标。become键用于强制提升特权,类似于将-b选项用于特殊命令。最后,我们有了tasks字典。与存储单个值的标准 YAML 键不同,YAML 字典包含多个键和值,或者像在本例中一样,以单个任务的形式存储键/值对的列表。 -
任务 :生活在
tasks字典下,每个任务需要缩进到同一字典内其他任务的同一级别。我们使用 vim 默认的八个空格作为缩进。每个任务都是任务字典中的一个列表项,因此可选的任务名称以破折号和空格开头。在这个任务中,我们使用了packageAnsible Python 模块,类似于我们在一个特别命令中用-m选项指定的模块名称。该模块是一个 YAML 字典,包含一组键/值对。这些键需要缩进以显示它们与包字典的关系。我们与用于缩进这些键的八个空格保持一致。对于模块,我们引用要处理的包的名称和状态。这里我们规定它应该以状态present安装。我们可以使用absent、present或latest来确保包是最新的。
Note
虽然我们有一个yum模块和一个apt模块,但是使用package模块来管理软件允许 Ansible 不知道操作系统选择最合适的打包程序。尽管我们努力使可翻译剧本不可知,但我们确实需要注意包名,它可能因操作系统而异。群体变量可以帮助我们克服这些差异。在这种情况下,该包在 CentOS 和 Ubuntu 上具有相同的名称。
现在,在这个阶段,您应该在 $HOME/ansible/setup 目录下创建剧本文件 software.yml 。仔细观察我们提到的缩进层次。一旦创建了文件,就可以执行它。您可以选择在执行之前检查语法。我们现在使用的是ansible-playbook命令,而不是我们在临时执行中使用的ansible命令。
$ $ ansible-playbook software.yml --syntax-check
playbook: software.yml
$ ansible-playbook software.yml
PLAY [My first play]
TASK [Gathering Facts]
ok: [172.16.120.185]
ok: [172.16.120.188]
ok: [172.16.120.161]
TASK [Install software]
ok: [172.16.120.188]
ok: [172.16.120.161]
changed: [172.16.120.185]
Listing 5-2Checking Playbook Syntax and Executing Playbooks, Executed from $HOME/ansible/setup
从剧本执行的输出中,我们可以看到,在两个 CentOS 主机上,没有必要进行任何更改。然而,该软件包并不存在,因此安装在 Ubuntu 系统上。所有主机现在都处于所需状态。我们可以对这些主机再次执行相同的剧本,没有一个系统需要安装软件。仔细观察,我们现在可以开始理解为什么我们不应该把游戏或任务的名字看作是可选的。它有助于记录文件和可解析的输出。但是,但是,挺住,还有两个任务;我们只创建了一个任务,却执行了两个任务。Ansible 在玩什么呢!啊,好问题,我很高兴你注意到了。收集事实任务将收集我们可以在游戏中使用的关于被管理设备的事实或信息。这可能是为了决定应该执行哪些任务的逻辑,或者是为了在任务中包含一些事实,例如系统的主机名。如果我们在游戏中不使用事实,我们可以使用游戏键gather_facts: false禁用该任务。
Tip
禁用事实收集将会加快剧本的执行速度,当单个剧本不需要事实时,这是值得的。在一个剧本中有多个剧本将意味着多次执行 gather_facts 任务,每个剧本一个,如果没有禁用的话。当使用多个剧本时,尝试将需要事实的任务组合到一个剧本中。
使用事实扩展剧本
我不知道你的感受,但是我对 Ansible 能为我节省多少时间感到兴奋不已。安装软件对我来说至关重要。我在 AWS 上运行许多在线培训课程。能够部署全新的、干净的 AWS 系统,并为它们配置特定课程所需的包,这是非常宝贵的。我经常为一门课程配置十个或更多的系统,我使用 Ansible 是因为它是无代理的,可以毫不费力地在新系统上工作。顺便提一下,我个人的控制器是一个树莓 Pi,在我英国彼得伯勒的家庭办公室里,它总是开着。
我们可以开始扩展剧本,添加第二个任务来简单地显示托管系统的主机名。为此,我们可以使用debug模块。此外,不要对此过于沮丧;虽然本身没什么用,但是我们可以习惯用事实。
---
- name: My first play
hosts: all
become: true
tasks:
- name: Install software
package:
name: bash-completion
state: present
- name: Show hostname
debug:
msg: "This host is {{ ansible_hostname }}"
...
Listing 5-3Using Facts in the Playbook
使用debug模块和msg键,我们能够将文本打印到控制台上显示的输出中。变量(包括事实)必须包含在双引号文本字符串中,并用双括号括起来,如代码所示。对于执行此操作的每台主机,我们将看到静态文本和被管理设备的主机名都打印在控制器上。这两个任务都将运行,但只有显示主机名任务会导致动作,因为第一个任务中的软件已经安装。
$ ansible-playbook software.yml
TASK [Show hostname]
ok: [172.16.120.161] => {
"msg": "This host is controller"
}
ok: [172.16.120.185] => {
"msg": "This host is client"
}
ok: [172.16.120.188] => {
"msg": "This host is ubuntu"
}
Listing 5-4Viewing Abbreviated Output from the Debug Module
要列出主机上的所有事实,我们可以使用一个特别的命令和setup模块。这也可以从剧本中运行,但最适合提供快速、一次性参考的临时命令。由于以下示例过于冗长,我们已经将其输出排除在外,但是请务必在您自己的系统上运行该命令并查看输出。
$ ansible ubuntu -m setup
Listing 5-5Listing All Facts from the Targeted Group or Host
安装多个软件包
如果像我一样,您有每个系统都需要的自己喜欢的软件,您会希望确保它们无处不在。我们可以通过软件包模块的单个任务执行来安装许多软件包,创建一个软件包名称列表。我们现在将编辑该文件,以便在初始任务中包含更多的包。
$ cat $HOME/inventory
$ vim software.yml
---
- name: My first play
hosts: all
become: true
tasks:
- name: Install software
package:
name:
- bash-completion
- vim
- tree
- nano
state: present
- name: Show hostname
debug:
msg: "This host is {{ ansible_hostname }}"
...
$ ansible-playbook software.yml
TASK [Install software]
changed: [172.16.120.161]
changed: [172.16.120.188]
changed: [172.16.120.185]
Listing 5-6Installing More Than One Package
Note
以这种方式使用多个包名,我们只使用底层包一次,它类似于yum install curl vim tree,等等。如果我们使用多个任务,额外的时间和资源被使用,因为它变得类似于运行yum install curl、yum install tree,等等。我们能够让剧本及其执行更加高效。
改进文本编辑器
当我们看着我们一直在处理的发展中的 YAML 文件时,屏幕和书本上显示它所需的空间都在增加。这很大程度上是因为默认的缩进级别为八个空格。如果我们把这个值设置得小一点,YAML 文件会更容易处理,同时我们还可以使用其他选项来加快编辑速度。首先,我们将看看如何定制nano文本编辑器,因为 CentOS 中的默认设置不允许对 YAML 文件提供任何帮助。对软件. yml 文件的最终编辑应该已经在所有需要的系统上安装了nano。我们将创造一个*。nanorc* 文件放在控制器上我们自己的主目录中。
$ nano $HOME/.nanorc
set autoindent
set tabsize 2
set tabstospaces
Listing 5-7Creating the $HOME/.nanorc Control File for the Nano Text Editor
通过配置这个控制文件,我们可以使用 return 键返回到之前的缩进层次,从而有效地编辑 YAML 文件。我们将 tab 键设置为使用两个空格,并将制表符转换为空格保存。我们可以通过在我们之前使用的安装目录中创建一个简单的测试手册来测试它。使用列表时,autoindent选项会将光标返回到列表项目破折号使用的级别。我们将需要使用 tab 键缩进两个空格,与列表项对齐,而不是与破折号对齐。这将更改缩进级别,以便下次使用 return 键时将光标定位在正确的级别。这是在 YAML 文件中使用两个空格的制表位的一个原因。
$ cd $HOME/ansible/setup; vim nano.yml
---
- name: Ping
hosts: all
gather_facts: false
tasks:
- name: Ping hosts
ping:
...
Listing 5-8Sample Playbook File to Test .nanorc
使用vim,你可能会发现你在编辑时得到了一些帮助。默认情况下,结果可能不是最好的,但我们可以做出巨大的改进。回到我们的主目录,我们可以编辑*。vimrc* 文件,它看起来类似于所示的例子:
$ vim $HOME/.vimrc
set tabstop=2 shiftwidth=2 expandtab autoindent
set cursorcolumn cursorline
Listing 5-9Sample $HOME/.vimrc File for Editing YAML Files
第一行提供了与前面非常相似的效果。我们之前创建的 nanorc 文件。新的第二行。vimrc 文件在缩进量很大的任何格式中都非常有用,比如我们将要使用的 YAML 剧本。我们突出显示光标所在的垂直列,当前水平线带有下划线。您可能会发现,根据所实现的终端仿真,列突出显示可能在您的终端上不起作用。默认 PuTTY 终端不支持该设置,但可以调整。如果你知道用这些新设置编辑文件时应该看到什么,那就太好了。因此,不考虑费用,the great folk at Apress 包含了以下屏幕截图。这张截图是在编辑之前的 nano.yml 文件时拍摄的。突出显示的列当前位于任务列表中,即破折号。我们可以使用这个可视化工具来确保在整个 YAML 文件中正确使用一致的缩进。随着 YAML 变长,这一功能变得越来越有用。
图 5-1
用 vim 和 new 编辑 YAML 文件。vimrc 文件
全图形用户界面
如果我们为控制器使用图形桌面,我们可以利用非常强大的 ide,集成开发环境来编辑剧本。使用在标准存储库中包含微软的 Visual Studio 代码的 Ubuntu 桌面,并编辑之前展示的剧本,我们可以看到 IDE 的一些好处。
图 5-2
在 Visual Studio 代码中编辑 YAML 文件
摘要
当我们将这一章带入我们专业知识的红色天空时,我们应该回忆起这一章将如何塑造我们的成功和职业生涯。通过 Ansible 剧本,我们可以为新部署和持续合规部署创建可重复的配置。它们以 YAML 格式编写,既记录了配置,又强制符合您描述的需求。
YAML 文档的特征包括用三个破折号显示的文档页眉和用三个句点显示的文档页脚。
-
---:用三条虚线显示的 YAML 文件标题 -
类似地,在 YAML,三个句点表示文档页脚。
YAML 的列表用单个破折号显示。我们已经看到了与剧本中的剧本列表和剧本中的任务列表一起使用的列表。我们还看到,我们可以安装多个带有包名列表的包。
package:
name:
- bash-completion
- vim
- tree
- nano
state: present
Listing 5-10List of Package Names Used with the Ansible Package Module
通过使用 YAML 文件,我们已经了解了理解和格式化缩进层次以将相关项目组合在一起的必要性。将我们的文本编辑器配置为将制表符存储为空格有助于确保我们能够更容易、更准确地进行这些配置。
我们使用带有语法检查选项的ansible-playbook命令,而不是使用ansible命令来执行剧本。这两个命令使用相同的清单和可解析配置。考虑将您配置文件设置为不提升权限,因为从一个 Ansible play 中提升权限很容易。通常最重要的提醒是查看 ansible-doc 命令模块的文档,并找出它们的示例部分。
六、使用 Ansible 剧本管理用户
虽然我们已经用一个特别的命令创建了一个新用户,但是我们还没有用剧本做同样的事情。在剧本定义中创建用户意味着那些特别的步骤变得更加规范,并且将在每次执行时以相同的方式发生,重要的是,没有遗漏。即使我们的三台主机拥有专用的 Ansible 用户帐户,我们也可以在新系统上线时使用剧本以一致的方式供应它们。
在本章中,您将学习编写剧本来添加和删除用户,甚至了解我们如何使用一个剧本来创建和删除用户,并使用逻辑来控制执行哪个任务。我们将重新讨论组变量,以迎合 Ubuntu 和 CentOS 之间的差异,并花时间研究用户密码如何工作,以及单向密码哈希加密和可以解密的加密机制之间的差异。作为一门 RHCE 课程,我不希望你错过重要的安全知识,你会从中受益。
管理用户的剧本
我们将从 CWD 开始,保留在 $HOME/ansible/setup 目录中,并开始开发一个通用的剧本来管理一般用户。稍后,我们将开始剧本的工作,以创建专用的 Ansible 用户帐户,从 CLI 以特别的方式复制我们之前所做的工作。与往常一样,文档对于您在考试环境中的学习和快速参考变得至关重要。如果你只是在考试的时候使用文档,那么不要期望很快或熟练地获得帮助。相信我,现在积累丰富的经验将在考试中获得巨大的回报。花点时间阅读由user模块提供的选项的完整列表,您将对如何使用该模块来满足自己的需求有一个大致的了解。
用户模块帮助
首先,你能不能迁就我一下,为了你自己的利益,研究一下在微软视窗操作系统中创建用户时应该使用的模块;这在user模块帮助中列出。一旦你做到了这一点,我希望你能进一步研究帮助,以确定如何为用户禁用基于密码的身份验证。
$ ansible-doc user
Listing 6-1Researching User Module Documentation
Note
阅读完整的用户模块帮助不会花费太多时间。投资于你自己,阅读可用的选项。不过,作为一个提示,用于 Windows 的模块显示在第一段中。要禁用密码,请阅读 password_lock 键上的帮助。你可以用这个和其他选项来练习,观察它们的行为。
创建一致的用户帐户
我们已经为 Ansible 帐户创建了一个专用的用户帐户,但是它是否在每个系统中以相同的方式创建?我猜你的答案要么是“我不知道”,要么是“我想是的”。 " 嗯,那还不够好是吧;是还是不是?这些账户应该是相同的,并且它们很可能在相同的操作系统中是相同的。我们使用了两个发行版,在我们同时使用 CentOS 和 Ubuntu 的地方,一致性不太可能相同,除非为用户设置了每个选项。为了演示用户缺省值的变化,让我们运行一个新的模块来访问 shell,运行一个带有参数的命令。我们可以通过为用户列出 /etc/passwd 文件的第七个字段来列出与每个帐户相关联的 shell。
$ cd $HOME/ansible/setup
$ ansible all -m shell -a "getent passwd ansible | cut -f7 -d:"
172.16.120.161 | CHANGED | rc=0 >>
/bin/bash
172.16.120.188 | CHANGED | rc=0 >>
/bin/sh
172.16.120.185 | CHANGED | rc=0 >>
/bin/bash
Listing 6-2Listing the Default Shell for the Ansible User
这里我们必须做一点检测工作,将 IP 地址引用到操作系统,但是两个 CentOS 主机使用 /bin/bash ,Ubuntu 使用 /bin/sh 。创建一个 Ansible 剧本来配置用户的 shell 将会修改现有的用户,只替换需要更新的字段。
$ vim user.yml
---
- name: Manage User Account
hosts: all
become: true
gather_facts: false
tasks:
- name: Create User
user:
name: ansible
shell: /bin/bash
state: present
...
Listing 6-3Ensuring a Consistent Shell Within a New Playbook
目前在这个剧本中,我们只设置了名称,默认Shell,以及状态键。我们可以省略 state 键,因为 present 是这个模块的默认值,但是我们为什么要这样做呢?包括这个,虽然不是必需的,但是提供了更好的文档,并且使用了 14 次额外的击键(如果我可以计算的话)。执行这个剧本将修改 Ubuntu 中的用户帐户,其中 bash 目前不是用户的默认 shell。
$ ansible-playbook user.yml
TASK [Create User]
ok: [172.16.120.161]
changed: [172.16.120.188]
ok: [172.16.120.185]
$ ansible all -m shell -a "getent passwd ansible | cut -f7 -d:"
172.16.120.161 | CHANGED | rc=0 >>
/bin/bash
172.16.120.188 | CHANGED | rc=0 >>
/bin/bash
172.16.120.185 | CHANGED | rc=0 >>
/bin/bash
Listing 6-4Setting the Default Shell, Ensuring Consistency Across Distributions
我们可以很快理解如何使用剧本可以给我们更准确和一致的结果。即使我们可以使用特殊命令设置完全相同的选项,但是当需要更多选项时,它们就变得不那么方便了。
使用可变循环控件
我们之前也看到了如何在一个任务中指定多个包名。但是,该选项在用户模块中不可用。考虑为什么会这样,我们必须理解底层命令:yum允许多个包,但是useradd不允许多个用户。我们可以使用 Ansible 中的loop控件来克服这个限制。loop是任务的一部分,而不是模块的一部分,与其他任务项对齐。特殊变量项用作用户模块名称键的值;通过迭代loop控件来填充项变量。
$ cd $HOME/ansible/setup
$ vim user.yml
---
- name: Manage User Account
hosts: all
become: true
gather_facts: false
tasks:
- name: Create User
user:
name: "{{ item }}"
shell: /bin/bash
state: present
loop:
- user1
- user2
- user3
...
$ ansible-playbook user.yml
TASK [Create User]
changed: [172.16.120.188] => (item=user1)
changed: [172.16.120.188] => (item=user2)
changed: [172.16.120.161] => (item=user1)
changed: [172.16.120.185] => (item=user1)
changed: [172.16.120.188] => (item=user3)
changed: [172.16.120.161] => (item=user2)
changed: [172.16.120.185] => (item=user2)
changed: [172.16.120.161] => (item=user3)
changed: [172.16.120.185] => (item=user3)
Listing 6-5Creating Many Users, Edit the Existing Playbook to Support Three New Users
从剧本执行的输出中,我们可以清楚地看到每个系统上三个帐户的创建,为了更加清晰起见,这个输出被稍微简化了。Ansible loop控件可以用于任何模块,是你自己军械库中的一个真正的工具。
删除用户
只需点击几个键,我们就可以轻松地修改剧本,删除那些相同的用户。我们现在将快速删除这些用户帐户,然后继续研究如何更有创造性地使用变量。
$ vim user.yml
---
- name: Manage User Account
hosts: all
become: true
gather_facts: false
tasks:
- name: Delete User
user:
name: "{{ item }}"
state: absent
remove: true
loop:
- user1
- user2
- user3
...
$ ansible-playbook user.yml
Listing 6-6Deleting Users Using Playbooks
Note
如果您还记得第四章中的,remove: true用于确保用户的主目录以及相关的邮件假脱机和 cron 文件被删除。
在剧本中使用变量和逻辑
回到管理单个用户,我们可以学习如何在创建和删除用户方面变得更有创造性。通过不将用户名硬编码到剧本中,我们可以允许更大的灵活性。我们可能还需要选择创建或删除帐户。运行时传递给剧本的变量可以很容易地允许这种情况发生,您很快就会了解到这一点。
在下面编辑过的剧本中,你会注意到我在单个剧本中添加了两个任务。作为每个任务的一部分,我已经添加了一个读取user_create变量的when子句。请注意,我们没有将变量括在大括号中,因为变量是需要变量的子句的参数。每个任务的用户名来自另一个变量。使用选项-e将这两个变量传递给ansible-playbook命令。
$ vim user.yml
---
- name: Manage User Account
hosts: all
become: true
gather_facts: false
tasks:
- name: Create User
user:
name: "{{ user_name }}"
shell: /bin/bash
state: present
when: user_create == 'yes'
- name: Delete User
user:
name: "{{ user_name }}"
state: absent
remove: true
when: user_create == 'no'
...
$ ansible-playbook -e user_create=yes -e user_name=mary user.yml
$ ansible-playbook -e user_create=no -e user_name=mary user.yml
Listing 6-7Building Logic and Choice into the Playbook
使用正确的变量执行剧本使我们拥有了在敏捷开发运维工作环境中经常需要的选择和灵活性。
管理用户密码
为用户设置密码时,我们需要提供密码的加密散列,就像我们需要使用底层的useradd命令一样。密码散列是加密的密码,但是散列是不能被解密的单向加密。我觉得了解这些散列的身份验证是如何工作的以及我们在/etc/shadow 文件中看到的加密密码的元素对您是有用的。
密码元素
存储在/etc/shadow 文件中的密码包含三个元素,允许根据密码哈希进行身份验证。这些元素用美元符号分隔。我们可以使用getent命令为用户提取阴影信息。
-
加密算法 :密码的第一个元素紧跟在第一个 $ 之后,第二个之前。这里我们有值
6,表示我们使用 SHA512 加密来创建散列。值5将使用 SHA256,1 表示较弱的 MD5。 -
盐 :这是一个加盐的密码,意思是给密码增加了一个随机性。SALT 是一个 16 字节的文本字符串,应该随机生成。这里使用的盐紧接在第二个之前。值为:
li9wmHhZW/TUHYeX。SALT 与输入的密码和加密算法相结合来创建密码哈希。如果盐不随机化,密码系统就被削弱了。可能会看到具有相同密码值的用户,可能是默认密码未被更改的帐户。 -
哈希 :最后的密码跟在第三个 $ 符号后面。这里显示的哈希是:
WzH596QutESoI5j3GYqoqnkSLlN.9VxdnMt5aix7SX18AE.1.3rH25quQU1wLrtg3zwXCNNdlQ8Bm6CenJenL/。使用相同的明文密码、SALT 和算法将创建完全相同的哈希,这是密码的加密形式。
$ sudo getent shadow ansible | cut -f2 -d:
$6$li9wmHhZW/TUHYeX$WzH596QutESoI5j3GYqoqnkSLlN.9VxdnMt5aix7SX18AE.1.3rH25quQU1wLrtg3zwXCNNdlQ8Bm6CenJenL/
Listing 6-8Listing a User’s Password
认证用户
密码散列是安全的,因为它使用不可逆转的加密机制。为了对用户进行身份验证,我们必须将根据输入并使用的密码创建的哈希与来自存储的密码和相同算法的 SALT 进行比较。盐和算法都没有加密。我们可以在下面的例子中看到这一点;首先,我们展示了当相同的密码与默认随机生成的 SALT 组合时,每次都会创建一个唯一的散列。然后,我们使用相同的 SALT 值,呈现给我们的是一致的散列。这就是身份验证的工作方式:通过检查是否创建了相同的哈希。
Note
为了简化输出,我们使用 MD5 提供的 128 位加密,而不是更安全的 SHA512 的 512 位。这纯粹是为了减少较小按键所需的显示空间,实际上不会使用。
$ openssl passwd -1 Password1
$1$/EX4F4Hi$YxXViUagixN9DYZ2LvtBM/
$ openssl passwd -1 Password1
$1$7y2QB7Xk$aBdYTlO5vHFY0T61luJeU0
$ openssl passwd -salt 7y2QB7Xk -1 Password1
$1$7y2QB7Xk$aBdYTlO5vHFY0T61luJeU0
Listing 6-9Using OpenSSL to Demonstrate Authentication
通过使用存储的密码中的相同 SALT,如果我们输入相同的密码,产生的散列将是相同的。
在剧本中生成密码
在 Ansible Playbook 中生成密码利用了一个 Python 函数password_hash。这很简单,通过user模块的帮助文档中的 URL 链接来演示。这里的大问题是在他们的例子中使用了静态文本 SALT。这不是您想要做的,因为它将为相同的给定密码创建相同的散列。他们的例子也使用了一个特别的命令,但是这可以很容易地调整到剧本风格。使用debug模块打印到屏幕上,我们可以看到生成的 hash。在下面的例子中,我们展示了一个 Ansible 例子,然后对它进行了调整以使用一个随机 SALT。和以前一样,出于输出简洁的原因,我们将使用 MD5 而不是更安全的 SHA512:
-
例 1 :使用 mysecret 的静盐
-
例 2 :使用相同的静态盐,我们可以看到产生了相同的 hash
-
例 3 :使用随机盐实际上更简单,通过排除
password_hash函数的第二个参数。这将为输入的密码生成一个唯一的哈希。
$ ansible ubuntu -m debug -a "msg={{ 'mypassword' | password_hash('md5', 'mysecret') }}"
172.16.120.188 | SUCCESS => {
"msg": "$1$mysecret$E0Xe5aWuqhm5pgpi4Epcy/"
}
$ ansible ubuntu -m debug -a "msg={{ 'mypassword' | password_hash('md5', 'mysecret') }}"
172.16.120.188 | SUCCESS => {
"msg": "$1$mysecret$E0Xe5aWuqhm5pgpi4Epcy/"
}
$ ansible ubuntu -m debug -a "msg={{ 'mypassword' | password_hash('md5') }}"
172.16.120.188 | SUCCESS => {
"msg": "$1$.GAXnycZ$CZGGRTWc..KKqFijwWJpW1"
}
Listing 6-10Using Python to Generate Password Hashes, First with Static SALT and then Random SALT Values
在这个阶段,将这一点添加到剧本中对我们来说是小菜一碟。在本书的后面,您将看到如何保护明文密码值,该值将存储在 YAML 文件中。除了将password键添加到剧本之外,我们还将添加键update_password,这样我们就可以避免重置已更改密码的用户的密码。我们只想设置新用户密码的默认值。
$ vim user.yml
---
- name: Manage User Account
hosts: all
become: true
gather_facts: false
tasks:
- name: Create User
user:
name: "{{ user_name }}"
shell: /bin/bash
state: present
password: "{{ 'Password1' | password_hash('sha512') }}"
update_password: on_create
when: user_create == 'yes'
- name: Delete User
user:
name: "{{ user_name }}"
state: absent
remove: true
when: user_create == 'no'
...
Listing 6-11Setting Passwords with Playbooks for New User Accounts
现在,我们可以通过使用单一剧本来创建或删除帐户,以及了解管理用户密码的最佳方式,来管理我们的用户帐户。我认为我们已经准备好用剧本创建 Ansible 托管主机的初始设置了。
使用剧本创建托管主机设置
也许你想知道为什么我们在项目中使用目录名 setup 。当我们可以创建一个单一的剧本来运行 Ansible 控制器和受管主机的初始配置时,我们已经发展到了这一步。在新剧本*$ HOME/ansi ble/setup/setup . yml*中,我们将分阶段构建它,代表我们之前作为特别命令运行过的构建块。
第一个任务是在控制器上为我们自己的用户帐户生成一个 SSH 密钥对。我一直在用用户账号 tux 。这个键只在控制器上需要,我们在游戏的主机键中指定这个键,以前我们使用组 all 。 hosts 键的值应该是一个字符串,因为我们使用的是 IP 地址,所以需要用引号括起来以避免误解。可以使用表示登录用户帐户的 shell 变量 $USER 将用户名自动传递给剧本。假设我们还没有设置无密码sudo访问,我们恢复到提示输入sudo密码。
$ vim setup.yml
---
- name: Manage User Account
hosts: "172.16.120.161"
become: true
gather_facts: false
tasks:
- name: Update User
user:
name: "{{ user_name }}"
state: present
generate_ssh_key: true
...
$ ansible-playbook -K -e user_name=$USER setup.yml
BECOME password:
Listing 6-12Ensuring an SSH Key Pair Exists for the Operator User Account
我想你会同意,这开始看起来非常好;当然,密钥对将会就位,因此不需要做任何更改。接下来,我们要确保我们拥有无密码的访问权限。首先确保你有文件 $HOME/ansible/setup/tux ,作为允许 tux 访问sudo而不需要密码的sudo文件。确保文件中使用的名称代表您在控制器上使用的用户帐户。
$ cat $HOME/ansible/setup/tux
tux ALL=(root) NOPASSWD: ALL
Listing 6-13The tux sudo File
准备好文件后,我们可以将任务添加到 setup.yml 中的现有游戏中。
$ vim setup.yml
---
- name: Manage User Account
hosts: "172.16.120.161"
become: true
gather_facts: false
tasks:
- name: Update User
user:
name: "{{ user_name }}"
state: present
generate_ssh_key: true
- name: Password-less access for operator
copy:
src: tux
dest: /etc/sudoers.d/tux...
$ ansible-playbook -K -e user_name=$USER setup.yml
Listing 6-14Adding the Task to Allow sudo Access Without Password on the Controller
在我们的控制器上,这个文件已经存在,所以我们应该满足当前的配置。
下一步是将 Ansible 的专用帐户部署到受管设备。我们将需要一个新的剧本,允许我们指定主机组 all 。该剧还将允许我们将 remote_user 键设置为 tux 键,而不是参照 ansible 用户帐户对 Ansible 配置进行更改。此外,我们将为新用户配置组成员身份,使其成为正确管理组的成员。这将需要对库存变量进行调整,并允许对库存命令进行大量检查。
$ echo "admin_group: sudo" >> ~/group_vars/ubuntu
$ echo "admin_group: wheel" >> ~/group_vars/centos
$ ansible-inventory --yaml --list
all:
children:
centos:
hosts:
172.16.120.161:
admin_group: wheel
ansible_connection: local
172.16.120.185:
admin_group: wheel
ubuntu:
children:
bionic:
hosts:
172.16.120.188:
admin_group: sudo
ansible_python_interpreter: /usr/bin/python3
ungrouped: {}
$ vim setup.yml
---
- name: Manage User Account
hosts: "172.16.120.161"
become: true
gather_facts: false
tasks:
- name: Update User
user:
name: "{{ user_name }}"
state: present
generate_ssh_key: true
- name: Password-less access for operator
copy:
src: tux
dest: /etc/sudoers.d/tux
- name: Manage Dedicated Ansible Account
hosts: all
become: true
gather_facts: false
remote_user: tux
tasks:
- name: Create Ansible Account
user:
name: ansible
state: present
groups: "{{ admin_group }}"
password: "{{ 'Password1' | password_hash('sha512') }}"
update_password: on_create
comment: Dedicated Ansible Devops Account
shell: bin/bash
...
$ ansible-playbook -Kk -e user_name=$USER setup.yml
SSH password:
BECOME password[defaults to SSH password]:
Listing 6-15Creating the Inventory Variables and the new Dedicated Account
虽然我们确实满足了配置需求,但剧本现在真的出现了。有了剧本,我们可以以完全一致的方式配置新主机,而无需额外的工作。接下来,我们现在可以使用authorized_key模块对专用帐户启用 SSH 验证。
$ vim setup.yml
---
- name: Manage User Account
hosts: "172.16.120.161"
become: true
gather_facts: false
tasks:
- name: Password-less access for operator
copy:
src: tux
dest: /etc/sudoers.d/tux
- name: Manage Dedicated Ansible Account
hosts: all
become: true
gather_facts: false
remote_user: tux
tasks:
- name: Create Ansible Account
user:
name: ansible
state: present
groups: "{{ admin_group }}"
password: "{{ 'Password1' | password_hash('sha512') }}"
update_password: on_create
comment: Dedicated Ansible Devops Account
shell: /bin/bash
- name: Install Local User Key
authorized_key:
user: ansible
state: present
manage_dir: true
key: "{{ lookup('file', '/home/tux/.ssh/id_rsa.pub') }}"
...
$ ansible-playbook -Kk -e user_name=$USER setup.yml
SSH password:
BECOME password[defaults to SSH password]:
Listing 6-16Enabling Key-Based Authentication, Assuming We Are Using the tux Account on the Controller
最后一步是为专用的 Ansible 帐户添加对sudo的无密码访问。对我们来说,我们已经有了账户的文件。我们只需要把最后一个任务加到第二个剧本里。完整的剧本显示在下面的代码块中。
$ vim setup.yml
---
- name: Manage User Account
hosts: "172.16.120.161"
become: true
gather_facts: false
tasks:
- name: Update User
user:
name: "{{ user_name }}"
state: present
generate_ssh_key: true
- name: Password-less access for operator
copy:
src: tux
dest: /etc/sudoers.d/tux
- name: Manage Dedicated Ansible Account
hosts: all
become: true
gather_facts: false
remote_user: tux
tasks:
- name: Create Ansible Account
user:
name: ansible
state: present
groups: "{{ admin_group }}"
password: "{{ 'Password1' | password_hash('sha512') }}"
update_password: on_create
comment: Dedicated Ansible Devops Account
shell: /bin/bash
- name: Install Local User Key
authorized_key:
user: ansible
state: present
manage_dir: true
key: "{{ lookup('file', '/home/tux/.ssh/id_rsa.pub') }}"
- name: Password-less access for ansible account
copy:
src: ansible
dest: /etc/sudoers.d/ansible
...
$ ansible-playbook -Kk -e user_name=$USER setup.yml
Listing 6-17The Final setup.yml
现在,我们已经完整地记录了使用特殊命令运行的步骤。不仅如此;这些命令是可重复的和正确的,因为它们现在被记录在剧本中。从现在开始,我们可以放弃密码提示,因为我们已经确保了正确的 SSH 密钥认证和对权限提升的无密码访问。
摘要
哇,这是我所有的话。现在已经创建了 setup.yml 剧本,我们可以轻松地添加新的受管主机,而不用担心它们是否准确包含在受管主机中。一切都将像在现有主机上一样进行配置。我们在这一章集中讨论了用户管理,到最后,除了创建了一个真正令人敬畏的 YAML 剧本,你还学到了一大堆。
从 Ansible 中的循环控件开始,我们看到了如何用一个任务管理多个帐户,这导致了 when 子句,我们可以用它来检查一个变量,以确定该任务是否应该运行。这些变量可以是系统中的事实,也可以是传递给 Ansible 的事实。在创建用户时,我们不得不讨论密码和密码散列。这些是无法解密的单向加密文件。我们演示了在密码无法解密的情况下身份验证是如何工作的,这里使用的openssl命令是一个有用的工具。
然后,我们将之前用于配置受管设备的临时命令转移到剧本中,以记录设置,并允许我们轻松引入新的受管主机,而无需记住所需的每个临时命令。这对您来说确实是一个里程碑,您创建的剧本将在您自己的项目中证明对您有用。
七、使用变量和事实
我们已经通过之前使用和学习的例子触及了变量和事实。在本章中,我们可以通过调查可在受管设备上收集的事实来巩固这些知识。这包括检索 IP 地址、主机名和完全限定的域名等项目。以上都是可以在剧本中作为变量使用的事实,要么在子句中控制执行,要么作为键的值。我们将扩展到目前为止所使用的库存变量,以考虑到产品(如 Apache HTTPD 服务器)中出现的包和服务名差异。到本章结束时,您将使用一个任务在 CentOS 和 Ubuntu 上安装 Apache。
收集事实
事实由剧本自动收集,除非通过 gather_facts 键禁用。执行 Ansible Python 模块设置来收集这些事实。从命令中,我们可以看到使用特别命令和设置模块的系统事实。在第一个示例中,我们在过滤第二个示例中的结果之前显示所有事实。
$ ansible all -m setup
$ ansible all -m setup -a "filter='*_distribution_*'"
Listing 7-1Displaying Facts
过滤器通过使用通配符来表示字符范围,就像在命令行 shell 中使用文件通配符一样。当不需要完整正则表达式的能力时,这可能是管道输出到grep的有用替代方法。
打印操作系统信息
在剧本中工作,我们可以显式地运行设置模块,但是只要我们没有用gather_facts: false禁用事实收集,我们将能够使用每个事实作为变量。现在,我们将创建一个新的项目目录,开始查看 CentOS 和 Ubuntu 系统之间的软件升级。
$ mkdir $HOME/ansible/upgrade ; cd $HOME/ansible/upgrade
$ ansible --version | grep 'config file'
config file = /home/tux/.ansible.cfg
$ ansible-config dump --only-changed
DEFAULT_BECOME(/home/tux/.ansible.cfg) = True
DEFAULT_BECOME_METHOD(/home/tux/.ansible.cfg) = sudo
DEFAULT_HOST_LIST(/home/tux/.ansible.cfg) = ['/home/tux/inventory']
DEFAULT_REMOTE_USER(/home/tux/.ansible.cfg) = ansible
$ vim upgrade.yml
---
- name: Upgrade Systems
hosts: all
become: true
gather_facts: true
tasks:
- name: Print Host Details
debug:
msg: "{{ item }}"
loop:
- "{{ ansible_hostname }}"
- "{{ ansible_distribution }}"
- "{{ ansible_distribution_version }}"
...
$ ansible-playbook upgrade.yml
Listing 7-2Creating New Ansible Project to Print OS Details
Note
我们在这里使用了循环操作符,但是您也可以打印一条包含所有变量的消息。对我们来说,我们可以检查一下循环操作符,减少超长行使用的页面宽度。在这一章的后面,我们将看看如何在键中折叠需要的长行。
升级系统
恰好我的 CentOS 8 客户端系统使用的是 8.0,而不是目前可用的 8.2。我确信这只是因为安装客户端操作系统时使用了 ISO 文件,但这也表明在您的环境中使用过时的系统是多么容易。我们可以也将会充分利用这些事实来控制哪些系统需要更新。在以下任务中,我们仅在不等于 8.2 的 CentOS 主机上执行。变量ansi ble _ distribution _ version将它存储为一个文本值,我们将在比较中使用它。回到 upgrade.yml 剧本,我们可以先删除打印变量的原始任务,然后再添加新任务来运行包更新。如果你愿意,欢迎你保留第一个任务;我们这里开门营业。不再需要第一个任务;但是,如果您希望添加新任务并保留原来的任务,剧本仍然有效。
Tip
一个当子句可以变得相当长,我们可以理解。通过使用折叠操作符>,我们能够在不影响子句本身的情况下跨越多行。不要忘记将折叠线缩进到从句内部的两个空格处。
$ vim upgrade.yml
---
- name: Upgrade Systems
hosts: all
become: true
gather_facts: true
tasks:
- name: Upgrade CentOS
package:
name: "*"
state: latest
when: >
ansible_distribution == "CentOS" and
ansible_distribution_version != "8.2"
...
$ ansible-playbook upgrade.yml
Listing 7-3Updating CentOS Hosts
首次运行本剧本将更新 CentOS 8 客户端系统。第二次运行它,不需要更新,因为两个系统都是正确的最新版本。
更新 Ubuntu 系统,可以研究最新的 18.04 版本,目前是 18.04.5 。我们需要深入研究 ansible_lsb.descripton 变量来了解这一点。以下特别命令演示了 ansible_lsb 数组,该数组在 Ubuntu 系统上默认可用。
$ ansible ubuntu -m setup -a "filter=ansible_lsb*"
172.16.120.188 | SUCCESS => {
"ansible_facts": {
"ansible_lsb": {
"codename": "bionic",
"description": "Ubuntu 18.04.5 LTS",
"id": "Ubuntu",
"major_release": "18",
"release": "18.04"
}
},
"changed": false
}
Listing 7-4Decting the Full Ubuntu Version
Note
如果我们需要 CentOS 主机上的 ansible_lsb 数组,我们将安装包 redhat-lsb-core 。我们不需要这个包,所以没有安装。
将 CentOS 和 Ubuntu 条件组添加到剧本中现有的 when 子句中,我们将有一个可以更新两组主机的任务。为了控制每组条件的处理,我们用括号将相关元素分组,并用逻辑 OR 操作符将两组带括号的条件组组合起来。编辑后的剧本可供您创建和练习。
$ vim upgrade.yml
---
- name: Upgrade Systems
hosts: all
become: true
gather_facts: true
tasks:
- name: Upgrade Older Systems
package:
name: "*"
state: latest
when: >
(ansible_distribution == "CentOS" and
ansible_distribution_version != "8.2") or
(ansible_distribution == "Ubuntu" and
ansible_lsb.description != "Ubuntu 18.04.5 LTS")
...
Listing 7-5Upgrading Both Ubuntu and CentOS in a Single Task
我们已经看到,变量可以从库存、从-e选项中读取,事实也可以从系统中读取;然而,我们也可以在剧本本身中定义变量。在这样的例子中,这些变量对我们特别有用。在剧本中尽早定义版本号,可以在发布新版本时根据需要轻松查看和编辑。看看下面例子中更新的剧本,我相信你会明白的。
$ vim ugrade.yml
---
- name: Upgrade Systems
hosts: all
become: true
gather_facts: true
vars:
- ubuntu_version: "Ubuntu 18.04.5 LTS"
- centos_version: "8.2"
tasks:
- name: Upgrade Older Systems
package:
name: "*"
state: latest
when: >
(ansible_distribution == "CentOS" and
ansible_distribution_version != centos_version) or
(ansible_distribution == "Ubuntu" and
ansible_lsb.description != ubuntu_version)
...
Listing 7-6Setting Variables Inside the Playbook
能够检查剧本中的当前版本集,并在文件顶部轻松更新它是非常方便的,有助于记录强制版本,并且非常容易编辑。我强烈推荐执行剧本来检查你自己的打字已经是典范了!4 个小时的考试飞逝而过,你越快写出准确的 YAML,你为考试做的准备就越充分。
安装 Apache
正如我们所看到的,使用包模块,而不是 yum 或 apt ,有助于 Ansible 和我们的剧本保持所有重要的不可知的对操作系统的态度,在所有支持的平台上工作。然而,我们不能迎合包名的差异,这就是库存变量可以帮助我们的地方。
Important
虽然包模块非常有用,但是它的简单性是有代价的。对于打包模块,只有很少的选项,因为它必须跨许多不同的打包程序工作。使用底层的 apt 或 yum 模块将为您提供更多的功能,同时失去通用模块的不可知论性质。理解通用模块和特定模块之间的差异非常重要。针对包和 yum 模块的快速ansible-doc可以帮助你理解。
我们现在将创建一个新项目来部署 Apache web 服务器。CentOS 上的包名是 httpd,Ubuntu 上的包名是 apache2。首先,让我们更新库存变量。
$ echo "apache_pkg: httpd" >> ~/group_vars/centos
$ echo "apache_pkg: apache2" >> ~/group_vars/ubuntu
$ ansible-inventory --yaml --list
all:
children:
centos:
hosts:
172.16.120.161:
admin_group: wheel
ansible_connection: local
apache_pkg: httpd
172.16.120.185:
admin_group: wheel
apache_pkg: httpd
ubuntu:
children:
bionic:
hosts:
172.16.120.188:
admin_group: sudo
ansible_python_interpreter: /usr/bin/python3
apache_pkg: apache2
ungrouped: {}
Listing 7-7Updating the Ansible Inventory Variables to Support Apache Installation
我们也可以选择打印特定主机的变量;这将包括在主机和组级别定义的变量。如果我们选择控制器,我们将能够看到这种行为,因为它是目前唯一具有主机特定变量集和组变量的系统。
$ ansible-inventory --yaml --host 172.16.120.161
admin_group: wheel
ansible_connection: local
apache_pkg: httpd
Listing 7-8Listing Variables Associated with a Specific Host
设置并确认了库存变量之后,我们现在可以继续安装 Apache 的新项目了。
$ mkdir $HOME/ansible/apache
$ cd $HOME/ansible/apache
$ vim simple_apache.yml
---
- name: Install Apache
hosts: all
become: true
gather_facts: false
tasks:
- name: Install Apache Package
package:
name: "{{ apache_pkg }}"
state: present
...
$ ansible-playbook simple_apache.yml
TASK [Install Apache Package]
changed: [172.16.120.185]
changed: [172.16.120.161]
changed: [172.16.120.188]
Listing 7-9Creating the New Apache Project
通过这个简单的任务,我们能够在三个系统上安装 Apache。有了好的计划,再加上好的软件和好的管理员,我们就能够战胜摆在我们面前的最艰巨的挑战。我们还需要提醒自己,我们实际上只是安装了软件,并没有配置服务。当我们开始学习剩余的章节时,这就会到来。
摘要
我们现在是可变因素和事实的主人。读到这一章的结尾,你应该为自己的进步感到高兴和自豪。设置模块可用于显示来自我们管理的设备的事实。使用模块的过滤器参数,我们能够钻取我们需要研究的特定项目。如果在游戏中启用了gather_facts,设置模块将自动运行,使变量可供您使用。
当子句需要用双引号括起来并放在双括号内时,在之外使用的变量是很好的度量:
name: "{{ ansible_package }}"
与 when 子句一起使用的变量不需要以同样的方式加引号,但文本字符串需要加引号:
when: ansible_distribution == "CentOS"
这些变量可能来自许多地方。本章使用了库存变量、播放变量以及事实。发现这些变量后,我们能够看到它们在允许灵活执行方面变得多么有用。利用 when 子句允许条件求值来决定一个任务是否执行。我们使用逻辑操作符OR和逻辑操作符AND构建了一个复杂的子句。当子句变长时,我们使用折叠操作>,允许子句中有多个缩进行。
我们还使用本模块复习了我们以前使用过的不想忘记的命令。
$ ansible --version | grep "config file"
$ ansible-config dump --only-changed
$ ansible-inventory --list --yaml
$ ansible-inventory --host 172.16.120.161 --yaml
$ ansible ubuntu -m setup -a "filter=*lsb*"
Listing 7-10Commands Reviewed in This Chapter