Ansible-2-OpenStack-管理手册-全-
Ansible 2 OpenStack 管理手册(全)
原文:
zh.annas-archive.org/md5/F107565E531514C473B8713A397D43CB译者:飞龙
前言
随着 OpenStack 开始被视为更主流的云平台,构建后对其进行操作的挑战变得更加突出。虽然所有云任务都可以通过 API 或 CLI 工具逐个执行,但这并不是处理更大规模云部署的最佳方式。现在明显需要更多自动化的方法来管理 OpenStack。大多数组织都在寻求改善业务敏捷性的方法,并意识到仅仅拥有一个云是不够的。只有通过某种形式的自动化才能改善应用部署、减少基础设施停机时间和消除日常手动任务。OpenStack 和 Ansible 将帮助任何组织弥合这一差距。OpenStack 提供的许多基础设施即服务功能,再加上 Ansible 这种易于使用的配置管理工具,可以确保更完整的云实施。
无论您是 OpenStack 的新手还是经验丰富的云管理员,本书都将帮助您在设置好 OpenStack 云后进行管理。本书充满了真实的 OpenStack 管理任务,我们将首先逐步介绍这些工作示例,然后过渡到介绍如何使用最流行的开源自动化工具之一 Ansible 来自动化这些任务的说明。
Ansible 已经成为开源编排和自动化领域的市场领导者。它也是使用 Python 构建的,与 OpenStack 类似,这使得二者易于结合。利用现有和/或新的 OpenStack 模块的能力将使您能够快速创建您的 playbook。
我们将从简要介绍 OpenStack 和 Ansible 开始,重点介绍一些最佳实践。接下来,每个后续章节的开头都将让您更加熟悉处理云操作员管理任务,如创建多个用户/租户、管理容器、自定义云配额、创建实例快照、设置主动-主动区域、运行云健康检查等。最后,每个章节都将以逐步教程结束,介绍如何使用 Ansible 自动化这些任务。作为额外的奖励,完全功能的 Ansible 代码将在 GitHub 上发布,供您在审阅章节时参考和/或以供以后审阅时参考。
将本书视为一次 2 合 1 的学习体验,深入了解基于 OpenStack 的云管理知识以及了解 Ansible 的工作原理。作为读者,您将被鼓励亲自动手尝试这些任务。
本书涵盖内容
第一章 OpenStack 简介,提供了 OpenStack 及构成该云平台的项目的高层概述。本介绍将为读者介绍 OpenStack 组件、概念和术语。
第二章 Ansible 简介,详细介绍了 Ansible 2.0,其特性和建立坚实起步基础的最佳实践。此外,它还将介绍为什么利用 Ansible 来自动化 OpenStack 任务是最简单的选择。
第三章 创建多个用户/租户,指导读者手动在 OpenStack 中创建用户和租户的过程,以及在使用 Ansible 自动化此过程时需要考虑的创建。
第四章 自定义云配额,让您了解配额是什么,以及它们如何用于限制您的云资源。它向读者展示了如何在 OpenStack 中手动创建配额。之后,它解释了如何使用 Ansible 自动化此过程,以便一次处理多个租户的任务。
第五章, 快照您的云,教您如何在 OpenStack 内手动创建云实例的快照,以及如何使用 Ansible 自动化此过程。它探讨了一次性对一个租户内的所有实例进行快照的强大功能。
第六章, 迁移实例,介绍了在传统的 OpenStack 方法中迁移选择实例到计算节点的概念。然后,它演示了自动化此任务所需的步骤,同时将实例分组,并展示了 Ansible 在处理此类任务时可以提供的其他选项。
第七章, 管理云上的容器,带领读者了解如何自动化构建和部署在 OpenStack 云上运行的容器的一些策略。现在有几种方法可用,但关键是自动化该过程,使其成为可重复使用的功能。对于每种方法,本章展示了如何成功地使用 OpenStack 完成这些构建块。
第八章, 设置主动-主动区域,详细审查了设置主动-主动 OpenStack 云区域的几个用例。有了这些知识,您将学会如何自动化部署到您的云。
第九章, 盘点您的云,探讨了读者如何使用一个 Ansible playbook 动态盘点所有 OpenStack 云用户资源。它指导他们收集必要的指标以及如何将这些信息存储以供以后参考。这对于云管理员/操作员来说是一个非常强大的工具。
第十章, 使用 Nagios 检查您的云健康状况,演示了如何手动检查云的健康状况以及如何利用 Ansible 设置 Nagios 和必要的检查来监视您的云的一些有用提示和技巧。Nagios 是领先的开源监控平台之一,并且与 OpenStack 和 Ansible 非常配合。
您需要为本书做好准备
要真正从本书中受益,最好部署或访问使用 openstack-ansible(OSA)构建的 OpenStack 云,该云运行 Newton 版本或更高版本。OSA 部署方法提供了一个环境,可以安装 OpenStack 和 Ansible。
如果您计划部署其他任何 OpenStack 发行版,您仍然只需要运行 OpenStack Newton 版本或更高版本。此外,您需要在相同节点上或您的工作站上安装 Ansible 版本 2.1 或更高版本。
另外,如果您计划添加或编辑 GitHub 存储库中找到的任何 Ansible playbooks/roles,拥有良好的文本编辑器,如 TextWrangler、Notepad++或 Vim,将非常有用。
这本书是为谁写的
如果您是基于 OpenStack 的云操作员和/或基础架构管理员,已经具有基本的 OpenStack 知识,并且有兴趣自动化管理功能,那么这本书正是您在寻找的。通过学习如何自动化简单和高级的 OpenStack 管理任务,您将把您的基本 OpenStack 知识提升到一个新的水平。拥有一个运行良好的 OpenStack 环境是有帮助的,但绝对不是必需的。
惯例
本书中,您将找到许多文本样式,用于区分不同类型的信息。以下是这些样式的一些示例以及它们的含义解释。
文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄显示如下:"我们可以从我们创建的名为create-users-env的角色开始。"
代码块设置如下:
- name: User password assignment
debug: msg="User {{ item.0 }} was added to {{ item.2 }} project, with the assigned password of {{ item.1 }}"
with_together:
- userid
- passwdss.stdout_lines
- tenantid
当我们希望引起您对代码块的特定部分的注意时,相关的行或项目会以粗体显示:
- name: User password assignment
debug: msg="User {{ item.0 }} was added to {{ item.2 }} project, with the assigned password of {{ item.1 }}"
with_together:
- userid
**- passwdss.stdout_lines**
- tenantid
任何命令行输入或输出都是这样写的:
**$ source openrc**
**$ openstack user create --password-prompt <username>**
新术语和重要单词以粗体显示。您在屏幕上看到的单词,例如菜单或对话框中的单词,会在文本中以这种方式出现:“通过Horizon仪表板下的Images选项卡查看它们。”
注意
警告或重要说明会以这种方式出现在框中。
提示
提示和技巧会以这种方式出现。
第一章:介绍 OpenStack
这一章将作为 OpenStack 和构成这个云平台的项目的高层概述。建立关于 OpenStack 的清晰基础非常重要,以便描述 OpenStack 组件、概念和术语。一旦概述完成,我们将过渡到讨论 OpenStack 的核心特性和优势。最后,本章将以两个工作示例结束,介绍如何通过应用程序接口(API)和命令行界面(CLI)使用 OpenStack 服务。
-
OpenStack 概述
-
审查 OpenStack 服务
-
OpenStack 支持组件
-
特性和优势
-
工作示例:列出服务
OpenStack 概述
简单来说,OpenStack 可以被描述为一个开源的云操作平台,可以用来控制数据中心中的大型计算、存储和网络资源池,所有这些都通过一个由 API、CLI 和/或 Web 图形用户界面(GUI)仪表板控制的单一界面进行管理。OpenStack 提供给管理员的能力是控制所有这些资源,同时还赋予云消费者通过其他自助服务模型来提供这些资源的能力。OpenStack 是以模块化方式构建的;该平台由许多组件组成。其中一些组件被认为是核心服务,是构建云所必需的,而其他服务是可选的,只有在符合个人用例时才需要。
OpenStack 基金会
早在 2010 年初,Rackspace 只是一个专注于通过名为Fanatical Support的服务和支持提供技术托管的公司。该公司决定创建一个开源云平台。
OpenStack 基金会由自愿成员组成,受委任的董事会和基于项目的技术委员会管理。合作发生在一个六个月的、基于时间的主要代码发布周期内。发布名称按字母顺序排列,并参考 OpenStack 设计峰会将举行的地区。每个发布都包含一个称为OpenStack 设计峰会的东西,旨在建立 OpenStack 运营商/消费者之间的合作,让项目开发人员进行实时工作会话,并就发布项目达成一致。
作为 OpenStack 基金会的成员,您可以积极参与帮助开发任何 OpenStack 项目。没有其他云平台允许这样的参与。
要了解更多关于 OpenStack 基金会的信息,您可以访问网站www.openstack.org。

审查 OpenStack 服务
深入了解 OpenStack 作为一个项目的核心内容,就是审查构成这个云生态系统的服务。需要记住的一件事是,关于 OpenStack 服务,每个服务都会有一个官方名称和与之相关的代码名称。代码名称的使用在社区中变得非常流行,大多数文档都会以这种方式引用服务。熟悉代码名称对于简化采用过程很重要。
另一件需要记住的事是,每个服务都是作为 API 驱动的 REST 网络服务开发的。所有操作都是通过 API 执行的,从而实现了最大的消费灵活性。即使在使用 CLI 或基于 Web 的 GUI 时,幕后也会执行和解释 API 调用。
从 Newton 发布版开始,OpenStack 项目包括六个所谓的核心服务和十三个可选服务。这些服务将按发布顺序进行审查,以展示整体服务时间表。该时间表将展示 OpenStack 项目整体的自然进展,同时也显示了它现在肯定已经准备好用于企业。
OpenStack 社区最近提供的一个重要补充是项目导航器的创建。项目导航器旨在成为 OpenStack 项目的消费者的实时指南,旨在分享每个服务的社区采用情况、成熟度和年龄。就个人而言,这个资源被发现非常有用和信息丰富。导航器可以在 OpenStack 基金会网站上找到,www.openstack.org/software/project-navigator。
OpenStack 计算(代号 Nova)
集成在发布版:Austin
核心服务
这是 OpenStack 平台的第一个,也是最重要的服务部分。Nova 是提供与用于管理计算资源的底层 hypervisor 的桥梁。
注意
一个常见的误解是 Nova 本身是一个 hypervisor,这简直是不正确的。Nova 是一种 hypervisor 管理器,能够支持许多不同类型的 hypervisor。
Nova 将负责调度实例的创建、实例的大小选项、管理实例位置,以及如前所述,跟踪云环境中可用的 hypervisor。它还处理将您的云分隔成名为cells、regions和可用区域的隔离组的功能。
OpenStack 对象存储(代号 Swift)
集成在发布版:Austin
核心服务
这项服务也是 OpenStack 平台的第一个服务之一。Swift 是 OpenStack 云提供对象存储服务的组件,能够存储宠字节的数据,从而提供高可用性、分布式和最终一致的对象/块存储。对象存储旨在成为静态数据的廉价、成本效益的存储解决方案,例如图像、备份、存档和静态内容。然后,这些对象可以通过标准的 Web 协议(HTTP/S)从对象服务器流式传输到发起 Web 请求的最终用户,或者从最终用户流式传输到对象服务器。Swift 的另一个关键特性是所有数据都会自动复制到集群中,从而实现高可用性。存储集群可以通过简单地添加新服务器来实现水平扩展。
OpenStack 镜像服务(代号 Glance)
集成在发布版:Bextar
核心服务
这项服务是在第二个 OpenStack 发布版中引入的,它负责管理/注册/维护 OpenStack 云的服务器镜像。它包括上传或导出 OpenStack 兼容的镜像的能力,并存储实例快照以供以后用作模板/备份。Glance 可以将这些镜像存储在各种位置,例如本地和/或分布式存储,例如对象存储。大多数 Linux 内核发行版已经提供了可用于下载的 OpenStack 兼容镜像。您还可以从现有服务器创建自己的服务器镜像。支持多种图像格式,包括 Raw、VHD、qcow2、VMDK、OVF 和 VDI。
OpenStack Identity(代号 Keystone)
集成在发布版:Essex
核心服务
这项服务是在第五个 OpenStack 发布中引入的。Keystone 是内置在您的 OpenStack 云中的身份验证和授权组件。它的关键作用是处理用户、租户和所有其他 OpenStack 服务的创建、注册和管理。在搭建 OpenStack 云时,Keystone 将是第一个要安装的组件。它有能力连接到 LDAP 等外部目录服务。Keystone 的另一个关键特性是它是基于基于角色的访问控制(RBAC)构建的。这使得云运营商能够为云消费者提供对各个服务功能的不同基于角色的访问。
OpenStack 仪表板(代号 Horizon)
集成版本:Essex
这项服务是第五个 OpenStack 发布中引入的第二项服务。Horizon 为云运营商和消费者提供了一个基于 Web 的 GUI,用于控制他们的计算、存储和网络资源。OpenStack 仪表板运行在Apache和Django REST 框架之上。这使得它非常容易集成和扩展,以满足您的个人用例。在后端,Horizon 还使用本机 OpenStack API。Horizon 的基础是为了能够为云运营商提供对其云状态的快速整体视图,以及为云消费者提供一个自助服务的云资源配置门户。
提示
请记住,Horizon 可以处理大约 70%的可用 OpenStack 功能。要利用 100%的 OpenStack 功能,您需要直接使用 API 和/或为每项服务使用 CLI。
OpenStack 网络(代号 Neutron)
集成版本:Folsom
核心服务
这项服务可能是您的 OpenStack 云中除 Nova 之外第二强大的组件。
OpenStack Networking 旨在提供可插拔、可扩展和 API 驱动的系统,用于管理网络和 IP 地址。
这个引用直接摘自 OpenStack Networking 文档,最好地反映了 Neutron 背后的目的。Neutron 负责在 OpenStack 云中创建您的虚拟网络。这将涉及创建虚拟网络、路由器、子网、防火墙、负载均衡器和类似的网络功能。Neutron 是使用扩展框架开发的,允许集成额外的网络组件(物理网络设备控制)和模型(平面、第 2 层和/或第 3 层网络)。已经创建了各种特定于供应商的插件和适配器,以与 Neutron 配合使用。这项服务增加了 OpenStack 的自助服务功能,消除了网络方面成为使用云的障碍。
作为 OpenStack 中最先进和强大的组件之一,Neutron 有一整本书专门介绍它。
OpenStack 块存储(代号 Cinder)
集成版本:Folsom
核心服务
Cinder 是为您的 OpenStack 云提供块存储服务的组件,利用本地磁盘或附加存储设备。这意味着您的实例可以使用持久的块级存储卷。Cinder 负责管理和维护创建的块卷,附加/分离这些卷,以及备份创建。Cinder 的一个显着特点是其能够同时连接到多种类型的后端共享存储平台。这种能力范围还可以延伸到利用简单的 Linux 服务器存储。作为额外的奖励,服务质量(QoS)角色可以应用于不同类型的后端。扩展了使用块存储设备以满足各种应用需求的能力。
OpenStack 编排(代号 Heat)
集成版本:Havana
这是第八个 OpenStack 版本中引入的两项服务之一。Heat 提供了对您的 OpenStack 云资源的编排能力。它被描述为 OpenStack 编排计划的主要项目。这意味着 OpenStack 还将有额外的自动化功能。
内置编排引擎用于自动化应用和其组件的提供,称为堆栈。一个堆栈可能包括实例、网络、子网、路由器、端口、路由器接口、安全组、安全组规则、自动扩展规则等等。Heat 利用模板来定义一个堆栈,并以标准标记格式 YAML 编写。您将听到这些模板被称为HOT(Heat Orchestration Template)模板。
OpenStack 遥测(代号 Ceilometer)
集成在版本中:哈瓦那
这是第八个 OpenStack 版本中引入的两项服务之一。Ceilometer 将云使用和性能统计数据集中存储到一个集中的数据存储中。这种能力成为云运营商的关键组成部分,因为它提供了对整个云的清晰度量标准,可以用来做出扩展决策。
提示
您可以选择将数据存储后端设置为 Ceilometer。这些选项包括 MongoDB、MySQL、PostgreSQL、HBase 和 DB2。
OpenStack 数据库(代号 Trove)
集成在版本中:冰雪屋
Trove 是为您的 OpenStack 云提供数据库服务的组件。这种能力包括提供可伸缩和可靠的关系型和非关系型数据库引擎。这项服务的目标是消除需要理解数据库安装和管理的负担。有了 Trove,云消费者可以通过利用服务 API 来提供数据库实例。Trove 支持在 Nova 实例中的多个单租户数据库。
提示
目前支持的数据存储类型包括 MySQL、MongoDB、Cassandra、Redis 和 CouchDB。
OpenStack 数据处理(代号 Sahara)
集成在版本中:朱诺
Sahara 是为您的 OpenStack 云提供数据处理服务的组件。这种能力包括能够提供一个专门处理大量分析数据的应用集群。可用的数据存储选项包括Hadoop和/或Spark。这项服务还将帮助云消费者抽象出安装和维护这种类型集群的复杂性。
OpenStack 裸金属提供(代号 Ironic)
集成在版本中:基洛
这项服务一直是 OpenStack 项目中最受期待的组件之一。Ironic 提供了在 OpenStack 云中从物理裸金属服务器进行提供的能力。它通常被称为裸金属虚拟化 API,并利用一组插件来实现与裸金属服务器的交互。这是最新引入 OpenStack 家族的服务,仍在开发中。
其他可选服务
还有一些处于早期成熟阶段的其他服务,稍后会列出。一些服务的范围和深度仍在确定中,因此最好不要在这里可能误传它们。更重要的是,当这些新服务准备就绪时,它们将为您的 OpenStack 云增加的能力的深度。
| 代号 | 服务 |
|---|---|
| Zaqar | 消息服务 |
| 马尼拉 | 共享文件系统 |
| 指定 | DNS 服务 |
| 巴比肯 | 密钥管理 |
| 马格南 | 容器 |
| 穆拉诺 | 应用目录 |
| 国会 | 治理 |
OpenStack 支持的组件
与任何传统应用程序非常相似,有一些关键的核心组件对其功能至关重要,但不一定是应用程序本身。在基本的 OpenStack 架构中,有两个核心组件被认为是云的核心或骨干。OpenStack 功能需要访问基于 SQL 的后端数据库服务和AMQP(高级消息队列协议)软件平台。就像任何其他技术一样,OpenStack 也有基本支持的参考架构供我们遵循。从数据库的角度来看,常见的选择将是 MySQL,而默认的 AMQP 软件包是RabbitMQ。在开始 OpenStack 部署之前,这两个依赖关系必须安装、配置和正常运行。
还有其他可选的软件包,也可以用来提供更稳定的云设计。关于这些管理软件和更多 OpenStack 架构细节的信息可以在以下链接找到docs.openstack.org/arch-design/generalpurpose-architecture.html。
特点和优势
OpenStack 的强大已经得到了许多企业级组织的验证,因此吸引了许多领先的 IT 公司的关注。随着这种采用的增加,我们肯定会看到消费量的增加和额外的改进功能。现在,让我们回顾一些 OpenStack 的特点和优势。
完全分布式架构
OpenStack 平台内的每个服务都可以分组和/或分离,以满足您的个人用例。正如前面提到的,只有核心服务(Keystone、Nova 和 Glance)需要具有功能的云。所有其他组件都可以是可选的。这种灵活性是每个管理员对于基础设施即服务(IaaS)平台都在寻求的。
使用商品硬件
OpenStack 被设计成可以适应几乎任何类型的硬件。底层操作系统是 OpenStack 的唯一依赖。只要 OpenStack 支持底层操作系统,并且该操作系统在特定硬件上受支持,您就可以开始了!没有购买 OEM 硬件或具有特定规格的硬件的要求。这为管理员提供了另一种部署灵活性。一个很好的例子是让你的旧硬件在数据中心中得到新的生命,成为 OpenStack 云中的一部分。
水平或垂直扩展
轻松扩展您的云是 OpenStack 的另一个关键特性。添加额外的计算节点就像在新服务器上安装必要的 OpenStack 服务一样简单。扩展 OpenStack 服务控制平面也使用相同的过程。与其他平台一样,您也可以向任何节点添加更多的计算资源作为另一种扩展的方法。
满足高可用性要求
如果按照文档中的最佳实践实施,OpenStack 能够证明满足其自身基础设施服务的高可用性(99.9%)要求。
计算隔离和多数据中心支持
OpenStack 的另一个关键特性是支持处理计算虚拟化隔离和支持跨数据中心的多个 OpenStack 区域的能力。计算隔离包括分离由虚拟化程序类型、硬件相似性和/或 vCPU 比率区分的多个虚拟化程序池的能力。
支持多个 OpenStack 区域的能力,这是在数据中心之间安装具有共享服务(如 Keystone 和 Horizon)的完整 OpenStack 云的关键功能,有助于维护高度可用的基础设施。这种模式简化了整体云管理,允许单一视图管理多个云。
强大的基于角色的访问控制
所有 OpenStack 服务都允许在向云消费者分配授权时使用 RBAC。这使得云操作员能够决定云消费者允许的特定功能。例如,可以授予云用户创建实例的权限,但拒绝上传新的服务器镜像或调整实例大小选项的权限。
工作示例-列出服务
因此,我们已经介绍了 OpenStack 是什么,构成 OpenStack 的服务以及 OpenStack 的一些关键特性。展示 OpenStack 功能和可用于管理/管理 OpenStack 云的方法的工作示例是非常合适的。
再次强调,OpenStack 管理、管理和消费服务可以通过 API、CLI 和/或 Web 仪表板来完成。在考虑一定程度的自动化时,通常不涉及 Web 仪表板的最后选项。因此,在本书的其余部分,我们将专注于使用 OpenStack API 和 CLI。
列出 OpenStack 服务
现在,让我们看看如何使用 OpenStack API 或 CLI 来检查云中可用的服务。
通过 API
使用 OpenStack 服务的第一步是对 Keystone 进行身份验证。您必须始终首先进行身份验证(告诉 API 您是谁),然后根据您的用户被允许执行的预定义任务来接收授权(API 接受您的用户名并确定您可以执行的任务)。该完整过程最终会提供给您一个认证令牌。
提示
Keystone 可以提供四种不同类型的令牌格式:UUID、fernet、PKI 和 PKIZ。典型的 UUID 令牌如下所示53f7f6ef0cc344b5be706bcc8b1479e1。大多数人不使用 PKI 令牌,因为它是一个更长的字符串,更难处理。使用 fernet 令牌而不是 UUID 有很大的性能优势,因为不需要持久性。建议在云中设置 Keystone 以提供 fernet 令牌。
以下是一个请求安全令牌的认证请求示例。使用 cURL 进行 API 请求是与 RESTful API 交互的最简单方法。使用 cURL 和各种选项,您可以模拟类似于使用 OpenStack CLI 或 Horizon 仪表板的操作:
**$ curl -d @credentials.json -X POST -H "Content-Type: application/json"
http://127.0.0.1:5000/v3/auth/tokens | python -mjson.tool**
提示
由于凭证字符串相当长且容易错误操作,建议使用 cURL 的-d @<filename>功能部分。这允许您将凭证字符串插入文件中,然后通过引用文件将其传递到 API 请求中。这个练习与创建客户端环境脚本(也称为 OpenRC 文件)非常相似。在 API 请求的末尾添加| python -mjson.tool可以使 JSON 输出更容易阅读。
凭证字符串的示例如下所示:
{
"auth": {
"identity": {
"methods": [
"password"
],
"password": {
"user": {
"name": "admin",
"domain": {
"id": "default"
},
"password": "passwd"
}
}
}
}
}
提示
下载示例代码
下载代码包的详细步骤在本书的前言中提到。
该书的代码包也托管在 GitHub 上:github.com/PacktPublishing/OpenStack-Administration-with-Ansible-2。我们还有其他代码包,可以在github.com/PacktPublishing/找到。请查看!
当示例针对 Keystone API 执行时,它将返回一个认证令牌。该令牌实际上是在响应的 HTTP 标头中返回的。该令牌应该用于所有后续的 API 请求。请记住,令牌会过期,但传统上,令牌被配置为从创建时间戳开始的最后 24 小时。
如前所述,令牌可以在 API 响应消息的 HTTP 标头中找到。HTTP 标头属性名称为X-Subject-Token:
HTTP/1.1 201 Created
Date: Tue, 20 Sep 2016 21:20:02 GMT
Server: Apache
**X-Subject-Token: gAAAAABX4agC32nymQSbku39x1QPooKDpU2T9oPYapF6ZeY4QSA9EOqQZ8PcKqMT2j5m9uvOtC9c8d9szObciFr06stGo19tNueHDfvHbgRLFmjTg2k8Scu1Q4esvjbwth8aQ-qMSe4NRTWmD642i6pDfk_AIIQCNA**
Vary: X-Auth-Token
x-openstack-request-id: req-8de6fa59-8256-4a07-b040-c21c4aee6064
Content-Length: 283
Content-Type: application/json
一旦您获得了身份验证令牌,您就可以开始制作后续的 API 请求,以请求有关您的云的信息和/或执行任务。现在我们将请求您的云中可用的服务列表:
**$ curl -X GET http://127.0.0.1:35357/v3/services -H
"Accept: application/json" -H "X-Auth-
Token: 907ca229af164a09918a661ffa224747" | python -mjson.tool**
通过这个 API 请求的输出将是在您的云中注册的所有服务的完整列表,按照名称、描述、类型、ID以及是否活跃的方式。输出的摘要看起来类似于以下代码:
{
"links": {
"next": null,
"previous": null,
"self": "http://example.com/identity/v3/services"
},
"services": [
{
"description": "Nova Compute Service",
"enabled": true,
"id": "1999c3a858c7408fb586817620695098",
"links": {
"...
},
"name": "nova",
"type": "compute"
},
{
"description": "Cinder Volume Service V2",
"enabled": true,
"id": "39216610e75547f1883037e11976fc0f",
"links": {
"...
},
"name": "cinderv2",
"type": "volumev2"
},
...
通过 CLI
之前使用 API 时应用的所有基本原则也适用于使用 CLI。主要区别在于使用 CLI 时,您只需要创建一个带有您的凭据的 OpenRC 文件,并执行定义的命令。CLI 在后台处理 API 调用的格式,获取令牌以进行后续请求,并格式化输出。
与之前一样,首先您需要对 Keystone 进行身份验证,以获得安全令牌。首先通过源化您的 OpenRC 文件,然后执行service-list命令来完成此操作。下一个示例将更详细地演示。现在 Keystone 服务有两个活跃版本,版本 2.0 和 3.0,您可以选择希望激活的版本来处理身份验证/授权。
这是一个名为openrc的 OpenRC 文件 v2.0 的示例:
# To use an OpenStack cloud you need to authenticate against keystone.
export OS_ENDPOINT_TYPE=internalURL
export OS_USERNAME=admin
export OS_TENANT_NAME=admin
export OS_AUTH_URL=http://127.0.0.1:5000/v2.0
# With Keystone you pass the keystone password.
echo "Please enter your OpenStack Password: "
read -sr OS_PASSWORD_INPUT
export OS_PASSWORD=$OS_PASSWORD_INPUT
OpenRC 文件 v3.0 将类似于这样:
# *NOTE*: Using the 3 *Identity API* does not necessarily mean any other
# OpenStack API is version 3\. For example, your cloud provider may implement
# Image API v1.1, Block Storage API v2, and Compute API v2.0\. OS_AUTH_URL is
# only for the Identity API served through keystone.
export OS_AUTH_URL=http://172.29.238.2:5000/v3
# With the addition of Keystone we have standardized on the term **project**
# as the entity that owns the resources.
export OS_PROJECT_ID=5408dd3366e943b694cae90a04d71c88
export OS_PROJECT_NAME="admin"
export OS_USER_DOMAIN_NAME="Default"
if [ -z "$OS_USER_DOMAIN_NAME" ]; then unset OS_USER_DOMAIN_NAME; fi
# unset v2.0 items in case set
unset OS_TENANT_ID
unset OS_TENANT_NAME
# In addition to the owning entity (tenant), OpenStack stores the entity
# performing the action as the **user**.
export OS_USERNAME="admin"
# With Keystone you pass the keystone password.
echo "Please enter your OpenStack Password: "
read -sr OS_PASSWORD_INPUT
export OS_PASSWORD=$OS_PASSWORD_INPUT
# If your configuration has multiple regions, we set that information here.
# OS_REGION_NAME is optional and only valid in certain environments.
export OS_REGION_NAME="RegionOne"
# Don't leave a blank variable, unset it if it was empty
if [ -z "$OS_REGION_NAME" ]; then unset OS_REGION_NAME; fi
一旦创建并源化 OpenRC 文件,您就可以开始使用 CLI 执行诸如请求服务列表之类的命令。看下面的工作示例:
**$ source openrc**
**$ openstack service list**
输出将类似于这样:

摘要
自本书第一版以来,OpenStack 在企业中的采用已经开始蓬勃发展。许多大公司,如沃尔玛、宝马、大众、AT&T 和康卡斯特,都已经分享了他们的成功故事,并继续支持 OpenStack。我希望本章可能已经解答了您对 OpenStack 的任何疑问,甚至可能打消了您听到的任何谣言。
现在我们将过渡到学习有关 Ansible 以及为什么将其与 OpenStack 结合使用是一个很好的组合。
第二章:介绍 Ansible
本章将作为 Ansible 2.0 和构成这个开源配置管理工具的组件的高级概述。我们将介绍 Ansible 组件的定义及其典型用途。此外,我们将讨论如何为角色定义变量,并为 playbooks 定义/设置有关主机的事实。接下来,我们将过渡到如何设置您的 Ansible 环境以及定义用于运行 playbooks 的主机清单的方法。然后,我们将介绍 Ansible 2.0 中引入的一些新组件,名为Blocks和Strategies。我们还将讨论作为 Ansible 框架的一部分的云模块。最后,本章将以一个 playbook 的工作示例结束,该示例将确认使用 Ansible 所需的主机连接。将涵盖以下主题:
-
Ansible 2.0 概述。
-
什么是 playbooks、roles 和 modules?
-
设置环境。
-
变量和事实。
-
定义清单。
-
块和策略。
-
云集成。
Ansible 2.0 概述
Ansible 以其最简单的形式被描述为基于 Python 的开源 IT 自动化工具,可用于配置\管理系统,部署软件(或几乎任何东西),并为流程提供编排。这些只是 Ansible 的许多可能用例中的一部分。在我以前作为生产支持基础设施工程师的生活中,我希望有这样的工具存在。我肯定会睡得更多,头发也会少得多。
关于 Ansible,总是让我印象深刻的一点是,开发人员的首要目标是创建一个提供简单性和最大易用性的工具。在一个充满复杂和错综复杂的软件的世界中,保持简单对大多数 IT 专业人员来说是非常重要的。
保持简单的目标,Ansible 通过安全外壳(SSH)完全处理主机的配置/管理。绝对不需要守护程序或代理。您只需要在运行 playbooks 的服务器或工作站上安装 Python 和一些其他软件包,很可能已经存在。老实说,没有比这更简单的了。
与 Ansible 一起使用的自动化代码组织成名为 playbooks 和 roles 的东西,这些东西以 YAML 标记格式编写。Ansible 遵循 playbooks/roles 中的 YAML 格式和结构。熟悉 YAML 格式有助于创建您的 playbooks/roles。如果您不熟悉,不用担心,因为很容易掌握(这一切都是关于空格和破折号)。
playbooks 和 roles 采用非编译格式,如果熟悉标准的 Unix\Linux 命令,代码非常容易阅读。还有一个建议的目录结构,以创建 playbooks。这也是我最喜欢的 Ansible 功能之一。使能够审查和/或使用由其他人编写的 playbooks,几乎不需要任何指导。
注意
强烈建议在开始之前查看 Ansible playbook 最佳实践:docs.ansible.com/playbooks_best_practices.html。我还发现整个 Ansible 网站非常直观,并且充满了很好的示例,网址为docs.ansible.com。
我从 Ansible playbook 最佳实践中最喜欢的摘录位于内容组织部分。对如何组织您的自动化代码有清晰的理解对我非常有帮助。playbooks 的建议目录布局如下:
group_vars/
group1 # here we assign variables to particular groups
group2 # ""
host_vars/
hostname1 # if systems need specific variables, put them here
hostname2 # ""
library/ # if any custom modules, put them here (optional)
filter_plugins/ # if any custom filter plugins, put them here
(optional)
site.yml # master playbook
webservers.yml # playbook for webserver tier
dbservers.yml # playbook for dbserver tier
roles/
common/ # this hierarchy represents a "role"
tasks/ #
main.yml # <-- tasks file can include smaller files if
warranted
handlers/ #
main.yml # <-- handlers file
templates/ # <-- files for use with the template resource
ntp.conf.j2 # <------- templates end in .j2
files/ #
bar.txt # <-- files for use with the copy resource
foo.sh # <-- script files for use with the script resource
vars/ #
main.yml # <-- variables associated with this role
defaults/ #
main.yml # <-- default lower priority variables for this role
meta/ #
main.yml # <-- role dependencies
现在是时候深入研究 playbooks、roles 和 modules 的组成部分了。这是我们将分解每个组件独特目的的地方。
什么是 playbooks、roles 和 modules?
您将创建的用于由 Ansible 运行的自动化代码被分解为分层级。想象一个金字塔,有多个高度级别。我们将从顶部开始首先讨论 playbooks。
Playbooks
想象一下,playbook 是金字塔的最顶部三角形。playbook 承担了执行角色中包含的所有较低级别代码的角色。它也可以被视为对创建的角色的包装器。我们将在下一节中介绍角色。
Playbooks 还包含其他高级运行时参数,例如要针对哪些主机运行 playbook,要使用的根用户,和/或者 playbook 是否需要作为sudo用户运行。这只是您可以添加的许多 playbook 参数中的一部分。以下是 playbook 语法的示例:
---
# Sample playbooks structure/syntax.
- hosts: dbservers
remote_user: root
become: true
roles:
- mysql-install
提示
在前面的示例中,您会注意到 playbook 以---开头。这是每个 playbook 和角色的标题(第 1 行)都需要的。另外,请注意每行开头的空格结构。最容易记住的方法是每个主要命令以破折号(-)开头。然后,每个子命令以两个空格开头,并重复代码层次结构越低的部分。随着我们走过更多的例子,这将开始变得更有意义。
让我们走过前面的例子并分解各部分。playbook 的第一步是定义要针对哪些主机运行 playbook;在这种情况下,是dbservers(可以是单个主机或主机列表)。下一个区域设置了要在本地、远程运行 playbook 的用户,并启用了以sudo执行 playbook。语法的最后一部分列出了要执行的角色。
前面的示例类似于您将在接下来的章节中看到的其他 playbook 的格式。这种格式包括定义角色,允许扩展 playbooks 和可重用性(您将发现大多数高级 playbooks 都是这样结构的)。通过 Ansible 的高度灵活性,您还可以以更简单的整合格式创建 playbooks。这种格式的示例如下:
---
# Sample simple playbooks structure/syntax
- name: Install MySQL Playbook
hosts: dbservers
remote_user: root
become: true
tasks:
- name: Install MySQL
apt: name={{item}} state=present
with_items:
- libselinux-python
- mysql
- mysql-server
- MySQL-python
- name: Copying my.cnf configuration file
template: src=cust_my.cnf dest=/etc/my.cnf mode=0755
- name: Prep MySQL db
command: chdir=/usr/bin mysql_install_db
- name: Enable MySQL to be started at boot
service: name=mysqld enabled=yes state=restarted
- name: Prep MySQL db
command: chdir=/usr/bin mysqladmin -u root password 'passwd'
现在我们已经回顾了 playbooks 是什么,我们将继续审查角色及其好处。
Roles
下降到 Ansible 金字塔的下一级,我们将讨论角色。描述角色最有效的方式是将 playbook 分解为多个较小的文件。因此,不是将多个任务定义在一个长的 playbook 中,而是将其分解为单独的特定角色。这种格式使您的 playbooks 保持简单,并且可以在 playbooks 之间重复使用角色。
提示
关于创建角色,我个人收到的最好建议是保持简单。尝试创建一个执行特定功能的角色,比如只安装一个软件包。然后可以创建第二个角色来进行配置。在这种格式下,您可以反复重用最初的安装角色,而无需为下一个项目进行代码更改。
角色的典型语法可以在这里找到,并且应放置在roles/<角色名称>/tasks目录中的名为main.yml的文件中:
---
- name: Install MySQL
apt: name="{{ item }}" state=present
with_items:
- libselinux-python
- mysql
- mysql-server
- MySQL-python
- name: Copying my.cnf configuration file
template: src=cust_my.cnf dest=/etc/my.cnf mode=0755
- name: Prep MySQL db
command: chdir=/usr/bin mysql_install_db
- name: Enable MySQL to be started at boot
service: name=mysqld enabled=yes state=restarted
- name: Prep MySQL db
command: chdir=/usr/bin mysqladmin -u root password 'passwd'
角色的完整结构在本章的 Ansible 概述部分中找到的目录布局中确定。在接下来的章节中,我们将通过工作示例逐步审查角色的其他功能。通过已经涵盖了 playbooks 和角色,我们准备好在本次会话的最后一个主题中进行讨论,即模块。
模块
Ansible 的另一个关键特性是它带有可以控制系统功能的预定义代码,称为模块。模块直接针对远程主机执行,或通过 playbooks 执行。模块的执行通常需要您传递一组参数。Ansible 网站(docs.ansible.com/modules_by_category.html)对每个可用的模块和传递给该模块的可能参数进行了很好的文档记录。
提示
每个模块的文档也可以通过执行ansible-doc <module name>命令来通过命令行访问。
在 Ansible 中始终推荐使用模块,因为它们被编写为避免对主机进行请求的更改,除非需要进行更改。当针对主机多次重新执行 playbook 时,这非常有用。模块足够智能,知道不要重新执行已经成功完成的任何步骤,除非更改了某些参数或命令。
值得注意的另一件事是,随着每个新版本的发布,Ansible 都会引入额外的模块。就个人而言,Ansible 2.0 有一个令人兴奋的新功能,这就是更新和扩展的模块集,旨在简化您的 OpenStack 云的管理。
回顾之前共享的角色示例,您会注意到使用了各种模块。再次强调使用的模块,以提供进一步的清晰度:
---
- name: Install MySQL
apt: name="{{ item }}" state=present
with_items:
- libselinux-python
- mysql
- mysql-server
- MySQL-python
- name: Copying my.cnf configuration file
template: src=cust_my.cnf dest=/etc/my.cnf mode=0755
- name: Prep MySQL db
command: chdir=/usr/bin mysql_install_db
- name: Enable MySQL to be started at boot
service: name=mysqld enabled=yes state=restarted
...
另一个值得一提的功能是,您不仅可以使用当前的模块,还可以编写自己的模块。尽管 Ansible 的核心是用 Python 编写的,但您的模块几乎可以用任何语言编写。在底层,所有模块在技术上都返回 JSON 格式的数据,因此允许语言的灵活性。
在本节中,我们能够涵盖 Ansible 金字塔的前两个部分,即 playbooks 和 roles。我们还回顾了模块的使用,即 Ansible 背后的内置功能。接下来,我们将转入 Ansible 的另一个关键功能-变量替换和收集主机信息。
设置环境
在您开始尝试使用 Ansible 之前,您必须先安装它。没有必要复制所有已经在docs.ansible.com/上创建的出色文档来完成这个任务。我鼓励您访问以下网址,并选择您喜欢的安装方法:docs.ansible.com/ansible/intro_installation.html。
提示
如果您在 Mac OS 上安装 Ansible,我发现使用 Homebrew 更简单和一致。有关使用 Homebrew 的更多详细信息,请访问brew.sh。使用 Homebrew 安装 Ansible 的命令是brew install ansible。
升级到 Ansible 2.0
非常重要的一点是,为了使用 Ansible 2.0 版本的新功能,您必须更新 OSA 部署节点上运行的版本。目前在部署节点上运行的版本是 1.9.4 或 1.9.5。似乎每次都有效的方法在这里进行了概述。这部分有点实验性,所以请注意任何警告或错误。
从部署节点执行以下命令:
**$ pip uninstall -y ansible**
**$ sed -i 's/^export ANSIBLE_GIT_RELEASE.*/export
ANSIBLE_GIT_RELEASE=${ANSIBLE_GIT_RELEASE:-"v2.1.1.0-1"}/' /opt/
openstack-ansible/scripts/bootstrap-ansible.sh**
**$ cd /opt/openstack-ansible**
**$ ./scripts/bootstrap-ansible.sh**
新的 OpenStack 客户端认证
随着新的python-openstackclient的推出,CLI 还推出了os-client-config库。该库提供了另一种为云提供/配置认证凭据的方式。Ansible 2.0 的新 OpenStack 模块通过一个名为 shade 的包利用了这个新库。通过使用os-client-config和 shade,您现在可以在名为clouds.yaml的单个文件中管理多个云凭据。在部署 OSA 时,我发现 shade 会在$HOME/.config/openstack/目录中搜索这个文件,无论playbook/role和 CLI 命令在哪里执行。clouds.yaml文件的工作示例如下所示:
# Ansible managed:
/etc/ansible/roles/openstack_openrc/templates/clouds.yaml.j2 modified
on 2016-06-16 14:00:03 by root on 082108-allinone02
clouds:
default:
auth:
auth_url: http://172.29.238.2:5000/v3
project_name: admin
tenant_name: admin
username: admin
password: passwd
user_domain_name: Default
project_domain_name: Default
region_name: RegionOne
interface: internal
identity_api_version: "3"
使用这种新的认证方法极大地简化了创建用于 OpenStack 环境的自动化代码。您可以只传递一个参数--os-cloud=default,而不是在命令中传递一系列认证参数。Ansible OpenStack 模块也可以使用这种新的认证方法,您将注意到在接下来的章节中,大多数示例都将使用这个选项。有关os-client-config的更多详细信息,请访问:docs.openstack.org/developer/os-client-config。
提示
安装 shade 是使用 Ansible OpenStack 模块 2.0 版本所必需的。Shade 将需要直接安装在部署节点和实用程序容器上(如果您决定使用此选项)。如果在安装 shade 时遇到问题,请尝试使用-pip install shade-isolated命令。
变量和事实
任何曾经尝试创建某种自动化代码的人,无论是通过bash还是Perl脚本,都知道能够定义变量是一个重要的组成部分。与其他编程语言一样,Ansible 也包含变量替换等功能。
变量
首先,让我们首先定义变量的含义和在这种情况下的使用,以便了解这个新概念。
变量(计算机科学),与值相关联的符号名称,其关联值可能会更改
使用变量允许您在自动化代码中设置一个符号占位符,您可以在每次执行时替换值。Ansible 允许以各种方式在 playbooks 和 roles 中定义变量。在处理 OpenStack 和/或云技术时,能够根据需要调整执行参数至关重要。
我们将逐步介绍一些设置 playbook 中变量占位符的方法,如何定义变量值,以及如何将任务的结果注册为变量。
设置变量占位符
如果您想在 playbooks 中设置一个变量占位符,您可以添加以下语法:
- name: Copying my.cnf configuration file
template: src=cust_my.cnf dest={{ CONFIG_LOC }} mode=0755
在前面的示例中,CONFIG_LOC变量是在先前示例中指定的配置文件位置(/etc/my.cnf)的位置添加的。在设置占位符时,变量名必须用{{ }}括起来,如前面的示例所示。
定义变量值
现在,您已经将变量添加到了 playbook 中,您必须定义变量值。可以通过以下方式轻松完成:
**$ ansible-playbook base.yml --extra-vars "CONFIG_LOC=/etc/my.cnf"**
或者您可以在 playbook 中直接定义值,在每个角色中包含它们,或者将它们包含在全局 playbook 变量文件中。以下是三种选项的示例。
通过在 playbook 中添加vars部分,直接定义变量值:
---
# Sample simple playbooks structure/syntax
- name: Install MySQL Playbook
hosts: dbservers
...
vars:
CONFIG_LOC: /etc/my.cnf
...
通过在角色的vars/目录中创建一个名为main.yml的变量文件,在每个角色中定义变量值,其中包含以下内容:
---
CONFIG_LOC: /etc/my.cnf
定义全局 playbook 中的变量值,首先要在 playbook 目录的根目录下的group_vars/目录中创建一个特定主机的变量文件,内容与前面提到的完全相同。在这种情况下,变量文件的名称必须与hosts文件中定义的主机或主机组名称相匹配。
与之前的示例一样,主机组名称是dbservers;因此,在group_vars/目录中将创建一个名为dbservers的文件。
注册变量
有时会出现这样的情况,您希望捕获任务的输出。在捕获结果的过程中,您实质上是在注册一个动态变量。这种类型的变量与我们迄今为止所涵盖的标准变量略有不同。
以下是将任务的结果注册到变量的示例:
- name: Check Keystone process
shell: ps -ef | grep keystone
register: keystone_check
注册变量值数据结构可以以几种格式存储。它始终遵循基本的 JSON 格式,但值可以存储在不同的属性下。就我个人而言,有时我发现盲目确定格式很困难。这里给出的提示将为您节省数小时的故障排除时间。
提示
要在运行 playbook 时查看和获取已注册变量的数据结构,可以使用debug模块,例如将其添加到前面的示例中:- debug: var=keystone_check。
事实
当 Ansible 运行 playbook 时,它首先会代表您收集有关主机的事实,然后执行任务或角色。有关主机收集的信息将从基本信息(如操作系统和 IP 地址)到详细信息(如硬件类型/资源)等范围。然后将捕获的详细信息存储在名为 facts 的变量中。
您可以在 Ansible 网站上找到可用事实的完整列表:docs.ansible.com/playbooks_variables.html#information-discovered-from-systems-facts。
提示
您可以通过将以下内容添加到 playbook 中来禁用事实收集过程:gather_facts: false。默认情况下,除非禁用该功能,否则会捕获有关主机的事实。
快速查看与主机相关的所有事实的一种方法是通过命令行手动执行以下操作:
**$ ansible dbservers -m setup**
事实还有很多其他用途,我鼓励您花些时间在 Ansible 文档中进行审阅。接下来,我们将更多地了解我们金字塔的基础,即主机清单。如果没有要运行 playbook 的主机清单,您将白白为自动化代码创建。
因此,为了结束本章,我们将深入探讨 Ansible 如何处理主机清单,无论是静态还是动态格式。
定义清单
定义一组主机给 Ansible 的过程称为清单。可以使用主机的完全限定域名(FQDN)、本地主机名和/或其 IP 地址来定义主机。由于 Ansible 使用 SSH 连接到主机,因此可以为主机提供任何机器可以理解的别名。
Ansible 期望inventory文件以 INI 格式命名为 hosts。默认情况下,inventory文件通常位于/etc/ansible目录中,并且如下所示:
athena.example.com
[ocean]
aegaeon.example.com
ceto.example.com
[air]
aeolus.example.com
zeus.example.com
apollo.example.com
提示
就我个人而言,我发现默认的inventory文件的位置取决于安装 Ansible 的操作系统。基于这一点,我更喜欢在执行 playbook 时使用-i命令行选项。这允许我指定特定的hosts文件位置。一个工作示例看起来像这样:ansible-playbook -i hosts base.yml。
在上面的示例中,定义了一个单个主机和一组主机。通过在inventory文件中定义一个以[ ]括起来的组名,将主机分组到一个组中。在前面提到的示例中定义了两个组ocean和air。
如果您的inventory文件中没有任何主机(例如仅在本地运行 playbook 的情况下),您可以添加以下条目来定义本地主机,如下所示:
[localhost]
localhost ansible_connection=local
您可以在inventory文件中为主机和组定义变量。有关如何执行此操作以及其他清单详细信息,请参阅 Ansible 网站上的docs.ansible.com/intro_inventory.html。
动态清单
由于我们正在自动化云平台上的功能,因此审查 Ansible 的另一个很棒的功能似乎是合适的,即动态捕获主机/实例清单的能力。云的主要原则之一是能够通过 API、GUI、CLI 和/或通过自动化代码(如 Ansible)直接按需创建实例。这个基本原则将使依赖静态inventory文件几乎成为一个无用的选择。这就是为什么您需要大量依赖动态清单。
可以创建动态清单脚本,以在运行时从云中提取信息,然后再利用这些信息执行 playbooks。Ansible 提供了功能来检测inventory文件是否设置为可执行文件,如果是,将执行脚本以获取当前时间的清单数据。
由于创建 Ansible 动态清单脚本被认为是更高级的活动,我将引导您到 Ansible 网站(docs.ansible.com/intro_dynamic_inventory.html),因为他们在那里有一些动态清单脚本的工作示例。
幸运的是,在我们的情况下,我们将审查使用openstack-ansible(OSA)存储库构建的 OpenStack 云。OSA 带有一个预构建的动态清单脚本,可用于您的 OpenStack 云。该脚本名为dynamic_inventory.py,可以在位于root OSA deployment文件夹中的playbooks/inventory目录中找到。在接下来的章节中,您将看到如何利用这个动态inventory文件的工作示例。稍后将给出如何使用动态inventory文件的简单示例。
首先,手动执行动态inventory脚本,以熟悉数据结构和定义的组名(假设您在root OSA deployment目录中):
**$ cd playbooks/inventory**
**$ ./dynamic_inventory.py**
这将在屏幕上打印类似于以下内容的输出:
...
},
"compute_all": {
"hosts": [
"compute1_rsyslog_container-19482f86",
"compute1",
"compute2_rsyslog_container-dee00ea5",
"compute2"
]
},
"utility_container": {
"hosts": [
"infra1_utility_container-c5589031"
]
},
"nova_spice_console": {
"hosts": [
"infra1_nova_spice_console_container-dd12200f"
],
"children": []
},
...
接下来,有了这些信息,现在您知道,如果要针对实用程序容器运行 playbook,您只需执行以下命令即可:
**$ ansible-playbook -i inventory/dynamic_inventory.py playbooks/base.yml -l
utility_container**
在本节中,我们将介绍 Ansible 2.0 版本中添加的两个新功能。这两个功能都为 playbook 中的任务分组或执行添加了额外的功能。到目前为止,在创建更复杂的自动化代码时,它们似乎是非常好的功能。现在我们将简要回顾这两个新功能。
Blocks
块功能可以简单地解释为一种逻辑上将任务组合在一起并应用自定义错误处理的方法。它提供了将一组任务组合在一起的选项,建立特定的条件和特权。可以在此处找到将块功能应用于前面示例的示例:
---
# Sample simple playbooks structure/syntax
- name: Install MySQL Playbook
hosts: dbservers
tasks:
- block:
- apt: name={{item}} state=present
with_items:
- libselinux-python
- mysql
- mysql-server
- MySQL-python
- template: src=cust_my.cnf dest=/etc/my.cnf mode=0755
- command: chdir=/usr/bin mysql_install_db
- service: name=mysqld enabled=yes state=restarted
- command: chdir=/usr/bin mysqladmin -u root password 'passwd'
when: ansible_distribution == 'Ubuntu'
remote_user: root
become: true
有关如何实现 Blocks 和任何相关错误处理的更多详细信息,请参阅docs.ansible.com/ansible/playbooks_blocks.html。
策略
策略功能允许您控制主机执行 play 的方式。目前,默认行为被描述为线性策略,即所有主机在移动到下一个任务之前都会执行每个任务。截至今天,存在的另外两种策略类型是 free 和 debug。由于策略被实现为 Ansible 的一种新类型插件,可以通过贡献代码轻松地添加更多策略。有关策略的更多详细信息可以在docs.ansible.com/ansible/playbooks_strategies.html找到。
在 playbook 中实施策略的一个简单示例如下:
---
# Sample simple playbooks structure/syntax
- name: Install MySQL Playbook
hosts: dbservers
strategy: free
tasks:
...
注意
当您需要逐步执行 playbook/role 以查找诸如缺少的变量、确定要提供的变量值或找出为什么可能会偶尔失败等内容时,新的调试策略非常有帮助。这些只是一些可能的用例。我绝对鼓励您尝试这个功能。以下是有关 playbook 调试器的更多详细信息的 URL:docs.ansible.com/ansible/playbooks_debugger.html。
云集成
由于云自动化是本书的主题和最重要的内容,因此突出显示 Ansible 2.0 提供的许多不同的云集成是合理的。这也是我立即爱上 Ansible 的原因之一。是的,其他自动化工具也可以与许多云提供商进行集成,但我发现有时它们无法正常工作或者不够成熟。Ansible 已经超越了这个陷阱。并不是说 Ansible 已经覆盖了所有方面,但它确实感觉大多数都覆盖了,这对我来说最重要。
如果您尚未查看 Ansible 可用的云模块,请立即花点时间查看docs.ansible.com/ansible/list_of_cloud_modules.html。不时地回来查看,我相信您会惊讶地发现更多的模块已经添加进来了。我为我的 Ansible 团队感到非常自豪,他们一直在跟进这些模块,并且让编写自动化代码更加容易。
特别针对 OpenStack,在 2.0 版本中已经添加了大量新的模块到 Ansible 库。详细列表可以在docs.ansible.com/ansible/list_of_cloud_modules.html#openstack找到。您会注意到,从本书的第一个版本到现在,最大的变化将集中在尽可能多地使用新的 OpenStack 模块上。
摘要
让我们在探索动态inventory脚本功能方面暂停一下,并在接下来的章节中继续构建。
就我个人而言,我非常期待进入下一章,我们将一起创建我们的第一个 OpenStack 管理 playbook。我们将从一个相当简单的任务开始,即创建用户和租户。这还将包括审查在为 OpenStack 创建自动化代码时需要牢记的一些自动化考虑。准备好了吗?好的,让我们开始吧!
第三章:创建多个用户/项目
我们终于到达了本书的部分,我们将动手创建我们的第一个 OpenStack 管理 playbook。为您的 OpenStack 云创建用户和项目实际上是设置云供用户使用的第一步。因此,从这里开始是很好的。我们将首先逐步介绍如何手动执行此操作,然后过渡到创建具有角色的 playbook 以完全自动化。在创建 playbook/role 时,我将尝试强调可能的问题以及您可以使用 Ansible 实现它的灵活方式。本章将涵盖以下主题:
-
创建用户和项目
-
自动化考虑
-
编写 playbook 和 roles
-
Playbook 和角色审查
创建用户和项目
尽管作为云操作员/管理员创建新用户和项目似乎是一个微不足道的任务,但如果要求创建 10、20 或 50 个用户和 5、10 或 20 个项目,它确实会成为一个负担。首先创建用户(具有相应的复杂安全密码),然后为用户创建项目,最后将用户链接到该项目并为该用户分配适当的角色。
想象一遍又一遍地这样做。无聊!作为任何管理员,您学到的第一件事是:找出您的日常任务是什么,然后确定如何尽快/轻松地完成它们。这正是我们要在这里做的事情。
手动创建用户和项目
进一步演示前面概述的步骤,我们将演示用于创建用户和项目的命令。
注意
出于简单起见,我们将仅使用 OpenStack CLI 演示手动命令。
创建用户
在 OpenStack 中创建用户涉及向身份服务(Keystone)发送请求。Keystone 请求可以通过首先使用 OpenRC 文件或通过在命令中传递--os-cloud认证参数来执行(稍后的第二个示例中显示)。接下来,您需要负责提供命令所需的参数值,例如用户名和密码。请参阅以下示例:
**$ source openrc**
**$ openstack user create --password-prompt <username>**
或者我们也可以使用这个:
**$ openstack --os-cloud=<cloud name> user create --password-prompt
<username>**
输出将类似于这样:

创建项目
如前所述,项目(以前称为租户)是云中的一个隔离区域,您可以在其中分配用户。该用户可以仅限于该项目,也可以允许访问多个项目。创建项目的过程类似于前面提到的创建用户的过程。一旦您使用 OpenRC 文件或在每个命令中传递认证参数,就可以继续执行 CLI 命令。假设 OpenRC 文件已经被加载,请参阅以下示例:
**$ openstack --os-cloud=<cloud name> project create
--description="<project description>" <project name>**
输出将类似于这样:

为用户分配角色和项目访问权限
仍然使用 Keystone 服务,您将为刚刚创建的用户指定一个特定的角色(用户权限)到指定的项目。基本 OpenStack 云带有默认角色:admin和_member_。您还可以创建自定义角色。您需要角色和要分配给用户的项目的名称。如果 OpenRC 文件仍然被加载,请参阅以下示例。对于此命令,屏幕上不会打印任何输出:
**$ openstack role add --user=<username> --project=<project name> <role name>**
到目前为止,您已经手动创建了一个用户和一个项目,并将该用户分配给了该项目的一个角色。让我们继续审查围绕自动化前面提到的所有步骤的一些考虑。
自动化考虑
将手动任务转化为自动化脚本的想法,无论使用哪种自动化工具,都需要做出一些基本框架决策。这是为了保持代码的一致性,并允许其他人轻松采用您的代码。您是否曾经尝试使用其他人创建的脚本,而他们没有代码标准?这很令人困惑,您会浪费时间试图理解他们的方法。
在我们的情况下,我们将提前做出一些框架决定并保持一致性。在我们开始审查考虑因素以设置我们的框架决策之前,我最大的免责声明是:
注意
有许多方法可以使用 Ansible 自动化 OpenStack 的任务;本书中展示的方法只是我个人发现成功的一种方式,当然不是唯一的方式。playbooks/roles 旨在成为您可以用作或调整/改进个人用例的工作示例。
既然这样说了,让我们继续吧。
全局定义变量还是每个角色
这个话题可能看起来不够重要,但实际上,使用 Ansible 时,您有比通常更多的选择。考虑到这一点,您将不得不决定如何在角色中定义变量。
Ansible 遵循变量定义层次结构。您可以选择在全局范围内定义放置在 playbook/role 中的变量的值,将其分配给一组主机或仅在特定角色中本地定义。在全局范围内定义值意味着所有 playbooks/roles 都可以使用该值并将其应用于一组主机。相反,如果您将值设置为本地角色,角色将首先从这里获取变量。
全局定义的变量值将在 playbook 的group_vars/目录中的文件中定义。文件名必须与hosts文件中设置的组名匹配。请参考第二章中的定义变量值部分,回顾这个过程,Ansible 简介。这种方法的优点是您可以一次设置变量值,并使您的 playbooks/roles 重复使用该值。这简化了整体定义变量和根据需要更新值的任务。这种方法的负面影响是,如果您希望重用变量名称并希望为每个角色提供不同的值。这就是另一种选择的作用。
在角色中本地定义变量值允许重用变量名称并能够为该变量定义不同的值。通过我的实验,我发现在角色中本地定义变量似乎是最佳选择。我创建角色的整体方法是尽可能简单地创建角色并完成单个管理任务。尽量不要将多个管理任务合并到一个角色中。保持角色简单可以使角色可重用,并符合 Ansible 的最佳实践。
因此,我们在这里做出的第一个框架决定是在角色中本地定义变量值。现在我们可以继续下一个考虑/决策点,即是否使用 OpenStack API 或 CLI 来执行管理命令。
OpenStack API 还是 CLI?
再次,这个决定在高层面上可能看起来不重要。决定使用 OpenStack API 还是 CLI 可能会极大地改变创建 playbooks/roles 的整体结构和方法。在第一章中,OpenStack 简介,我们介绍了 OpenStack API 和 CLI 之间的区别。
一个应该引起注意的事情是,CLI 在使用 Ansible 时更容易使用和编码。请记住,CLI 仍然在幕后执行 API 命令,处理所有令牌和 JSON 解释工作。这允许功能上零损失。
我们宣布的第二个框架决定是在调用 OpenStack 云时使用 Ansible 提供的本机 OpenStack 模块。唯一偏离这一决定的情况是,如果没有可用的模块来处理我们需要编码的任务,我们将使用 CLI 命令。通过这个决定,我们还选择使用第二章中提到的clouds.yaml文件来存储我们的凭据。
现在最后一个考虑是决定从哪里执行 playbooks。
在哪里运行 Ansible
我的下一个声明可能有点显而易见,但 playbooks 需要在安装了 Ansible 的工作站/服务器上执行。既然我们已经解决了这个问题,让我们探索一下我们的选择:
-
我的第一个建议是不要直接从任何 OpenStack 控制器节点运行 playbooks。控制器节点已经有很多工作要做,只需保持 OpenStack 运行,无需增加额外负担。
-
另一个选择是在您的环境中从某种集中式的 Ansible 服务器执行 playbooks。虽然这是一个完全可行的选择,但我有一个更好的选择给你。
由于我是openstack-ansible(OSA)部署 OpenStack 的忠实粉丝和倡导者,开箱即用的 playbooks/roles 将使用 OSA 提供的一些出色功能。我的最后一句话可能看起来有点离题,但很快就会更有意义。
运行 OSA 的最大特点之一是内置的动态清单脚本。这个功能消除了您在hosts文件中保持 OpenStack 服务位置清单的负担。为了从这个功能中受益,您需要从 OSA 部署服务器执行 playbooks/roles。从大局上来看,将所有 Ansible playbooks/roles(部署和管理脚本)放在一起是有意义的。
这是最佳选择的另一个令人信服的原因是,OSA 部署服务器已经设置好,可以与 LXC 容器通信,OpenStack 服务就位于其中。当您想要使用 Ansible 进行 OpenStack 服务配置更改时,这一点变得非常重要。
我想要强调 OSA 的最后一个特性是,它带有一个专门用于管理您的 OpenStack 云的容器,称为utility容器。该容器已安装并准备好使用每个 OpenStack 服务 CLI 包。是的,这是您需要担心的一件小事。这是我喜欢 OSA 的主要原因之一。
现在我们有了最后的框架决定,即从 OSA 部署服务器执行 playbooks,以充分利用 OSA 为我们提供的所有功能(这感觉就对了)。现在我们都掌握了大量的好信息和编码框架,我们唯一剩下的就是创建我们的第一个 playbook 和 roles。
编写 playbooks 和 roles
在开始之前,我们应该先回顾本章的开头。我们概述了在 OpenStack 云中创建用户和项目的步骤。这里,它们再次出现,供快速参考:
-
创建用户(附带复杂安全密码)
-
为用户创建项目
-
将用户链接到项目,并为该用户分配适当的角色
解决的第一步是处理流程中的用户创建部分。在 OpenStack 中创建用户是一个简单的任务,那么为什么不添加一些管理风格呢。创建用户的过程中的一部分是为该用户分配一个适当的密码。我们将把这作为创建用户的角色的一部分,并将该用户分配给项目。
创建 playbook 时,我通常从创建角色开始,以处理所需的管理任务。该角色将包含针对 OpenStack 云的所有可执行代码。Playbook 将包含要针对的主机(在本例中,将是实用容器)、要执行的角色以及其他执行设置。处理此管理任务的角色将被命名为create-users-env。
我们 playbook 的目录结构将开始看起来像这样:
base.yml # master playbook for user creation
group_vars/
util_container # assign variable values for this host group
hosts # static host inventory file
roles/
create-users-env # user/project creation role
tasks/
main.yml # tasks file for this role
vars/
main.yml # variables associated with this role
由于我们将从角色任务文件组装开始,让我们在create-users-env/tasks目录中创建main.yml文件。该文件的初始内容如下:
---
- name: Install random password generator package
apt: name={{item}} state=present
with_items:
- apg
- name: Random generate passwords
command: apg -n {{ pass_cnt }} -M NCL -q
register: passwdss
- name: Create users
os_user:
cloud: "{{CLOUD_NAME}}"
state: present
name: "{{ item.0 }}"
password: "{{ item.1 }}"
domain: default
with_together:
- "{{userid}}"
- "{{passwdss.stdout_lines}}"
现在我们可以更详细地讨论刚刚添加到角色中的前三个任务。第一个任务为使用apg包设置了使用apg包的基础,该包生成几个随机密码:
- name: Install random password generator package
apt: name={{item}} state=present
with_items:
- apg
由于在第二个任务中,我们将使用apg包为我们生成密码,因此我们必须确保它已安装在执行 playbook/角色的主机上。Ansible 的apt模块是管理 Debian/Ubuntu 软件包的非常有用的工具。使用{{item}}参数值定义模块,允许我们循环遍历稍后在with_items语句中列出的多个软件包。在这种特殊情况下,这并不需要,因为我们只安装一个软件包,但同时也不会对我们造成伤害。接下来是第二个任务:
- name: Random generate passwords
command: apg -n {{ pass_cnt }} -M NCL -q
register: passwdss
现在第二个任务将使用 Ansible 的命令模块执行apg包。
提示
命令模块将是在使用 Ansible 时最常用的模块之一。它基本上可以处理执行任何命令/包,但不能处理使用 shell 变量和特定于 shell 的操作的命令,例如:<、>、|和&。
使用命令模块,我们传递了带有特定参数-n {{ pass_cnt }} -M NCL -q的apg命令。大多数参数都是apg的标准选项,除了定义的变量{{ pass_cnt }}。设置此参数允许我们从为该角色设置的变量文件(位于create-users-env/vars目录中)中调整生成的密码数量。我们将很快查看变量文件。此任务的最后一步是将apg命令的输出注册到名为passwdss的变量中。稍后将在此角色中使用此变量。
添加到角色的第三个任务现在将在您的 OpenStack 云中创建用户。再次看到,使用os_user模块,我们将执行 Keystone 命令以创建具有认证参数的用户:
- name: Create users
os_user:
cloud: "{{CLOUD_NAME}}"
state: present
name: "{{ item.0 }}"
password: "{{ item.1 }}"
domain: default
with_together:
- "{{userid}}"
- "{{passwdss.stdout_lines}}"
在任务中,我们还将定义一些要使用的变量:
{{ item.0 }} # variable placeholder used to set the usernames from the list
defined in the userid variable
{{ item.1 }} # variable placeholder used to read in the output from the apg
command found within the passwdss variable registered earlier
提示
将变量放在命令中,可以让您创建具有核心代码的角色,而无需每次使用时都更新。只需更新变量文件比不断修改角色任务要简单得多。
此任务的另一个特殊部分是使用with_together Ansible 循环命令。此命令允许我们循环遍历分别设置的变量值,并按照定义的顺序将它们配对在一起。由于密码是随机的,我们不在乎哪个用户得到哪个密码。
现在我们在角色中有了用户创建代码,下一步是创建用户的项目。下面显示了接下来的两个任务:
- name: Create user environments
os_project:
cloud: "{{CLOUD_NAME}}"
state: present
name: "{{ item }}"
description: "{{ item }}"
domain_id: default
enabled: True
with_items: "{{tenantid}}"
- name: Assign user to specified role in designated environment
os_user_role:
cloud: "{{CLOUD_NAME}}"
user: "{{ item.0 }}"
role: "{{ urole }}"
project: "{{ item.1 }}"
with_together:
- "{{userid}}"
- "{{tenantid}}"
这个第一个任务将使用os-project模块创建项目。项目名称和描述将来自tenantid变量。接下来的任务将使用urole变量设置的角色值,将我们之前创建的用户分配给这个新创建的项目。
您会注意到这些任务与之前用于创建用户的任务非常相似,并且使用类似的 Ansible 参数。正如您所看到的,它将开始形成一个重复的模式。这确实有助于简化代码的创建。
角色的最后一个任务部分将简单地提供已创建用户及其对应密码的输出。这一步将为您(作为云操作员)提供一个非常简单的输出,其中包含您需要保存和/或传递给云消费者的所有信息。虽然这一步不是完成整体管理任务所必需的,但它很好。请参阅以下任务:
- name: User password assignment
debug: msg="User {{ item.0 }} was added to {{ item.2 }} project, with the assigned password of {{ item.1 }}"
with_together:
- userid
- passwdss.stdout_lines
- tenantid
在这个任务中,我们将使用debug模块来显示我们手动设置或使用registerAnsible 命令动态设置的变量的输出。输出将看起来像这样:

信不信由你,你刚刚创建了你的第一个 OpenStack 管理角色。为了支持这个角色,我们现在需要创建与之配套的变量文件。位于create-users-env/vars目录中的变量文件名为main.yml,在结构上与任务文件非常相似。
提示
请记住,变量文件中定义的值是为了在每次执行正常的日常使用之前进行更改的。
以下示例中显示的值只是工作示例。让我们来看一下:
---
pass_cnt: 10
userid: [ 'mrkt-dev01', 'mrkt-dev02', 'mrkt-dev03', 'mrkt-dev04', 'mrkt-dev05', 'mrkt-dev06', 'mrkt-dev07', 'mrkt-dev08', 'mrkt-dev09', 'mrkt-dev10' ]
tenantid: [ 'MRKT-Proj01', 'MRKT-Proj02', 'MRKT-Proj03', 'MRKT-Proj04', 'MRKT-Proj05', 'MRKT-Proj06', 'MRKT-Proj07', 'MRKT-Proj08', 'MRKT-Proj09', 'MRKT-Proj10' ]
urole: _member_
让我们花点时间来分解每个变量。摘要如下:
pass_cnt # with the value of 10, we would be creating 10 random passwords
with apg
userid # the value is a comma delimited list of users to loop through
when executing the user-create Keystone command
tenanted # the value is a comma delimited list of tenant names to loop
through when executing the tenant-create Keystone command
urole # with the value of _member_, the user would be assigned the
member role to the tenant created
这基本上总结了创建变量文件所涉及的内容。现在我们可以继续进行这个 playbook 的基础,并创建名为base.yml的主 playbook 文件,它位于 playbook 目录的 root 目录中。base.yml文件的内容将是:
---
# This playbook used to demo OpenStack Juno user, role and project features.
- hosts: util_container
remote_user: root
become: true
roles:
create-users-env
该文件的摘要如下:
hosts # the host or host group to execute the playbook against
remote_user # the user to use when executing the playbook on the
remote host(s)
become # will tell Ansible to become the above user on the
remote host(s)
roles # provide a list of roles to execute as part of
this playbook
在完成 playbook 并使其准备好执行之前,还有最后两个需要注意的地方,即创建主机清单文件和全局变量文件。在这种情况下,我们使用静态主机清单文件来保持简单,但在未来的章节中,我们将使用 OSA 动态清单文件。因为我们使用静态清单文件,所以我们必须发现实用容器的名称和/或 IP 地址。
这可以通过在任何控制节点上运行以下命令来完成:
**$ lxc-ls -fancy**
然后,在输出中查找类似于突出显示的项目:

然后,将实用容器的 IP 地址添加到 hosts 文件中,如下所示:
[localhost]
localhost ansible_connection=local
[util_container]
172.29.236.199
最后但并非最不重要的是,然后您将在group_vars/目录中创建全局变量文件。请记住,该文件的名称必须与主 playbook 中定义的主机或主机组的名称相匹配。由于我们称主机组为util_container,因此必须将变量文件命名为完全相同的名称。util_container全局变量文件的内容将是:
# Here are variables related globally to the util_container host group
CLOUD_NAME: default
提示
专业提示
在远程执行系统命令时,始终创建/使用自动化服务帐户。永远不要使用内置的管理员和/或您个人的帐户来执行该系统的命令。使用服务帐户可以简化故障排除和系统审核。
猜猜...你成功了!我们刚刚完成了我们的第一个 OpenStack 管理 playbook 和 role。让我们通过快速回顾刚刚创建的 playbook 和 role 来完成本章。
审查 playbooks 和 roles
直奔主题,我们可以从我们创建的名为create-users-env的角色开始。位于create-users-env/tasks目录中的完成角色和名为main.yml的文件如下所示:
---
- name: Install random password generator package
apt: name={{item}} state=present
with_items:
- apg
- name: Random generate passwords
command: apg -n {{ pass_cnt }} -M NCL -q
register: passwdss
- name: Create users
os_user:
cloud: "{{CLOUD_NAME}}"
state: present
name: "{{ item.0 }}"
password: "{{ item.1 }}"
domain: default
with_together:
- "{{userid}}"
- "{{passwdss.stdout_lines}}"
- name: Create user environments
os_project:
cloud: "{{CLOUD_NAME}}"
state: present
name: "{{ item }}"
description: "{{ item }}"
domain_id: default
enabled: True
with_items: "{{tenantid}}"
- name: Assign user to specified role in designated environment
os_user_role:
cloud: "{{CLOUD_NAME}}"
user: "{{ item.0 }}"
role: "{{ urole }}"
project: "{{ item.1 }}"
with_together:
- "{{userid}}"
- "{{tenantid}}"
- name: User password assignment
debug: msg="User {{ item.0 }} was added to {{ item.2 }} tenant, with the assigned password of {{ item.1 }}"
with_together:
- userid
- passwdss.stdout_lines
- tenantid
该角色的对应变量文件名为main.yml,位于create-users-env/vars目录中,如下所示:
---
pass_cnt: 10
userid: [ 'mrkt-dev01', 'mrkt-dev02', 'mrkt-dev03', 'mrkt-dev04', 'mrkt-dev05', 'mrkt-dev06', 'mrkt-dev07', 'mrkt-dev08', 'mrkt-dev09', 'mrkt-dev10' ]
tenantid: [ 'MRKT-Proj01', 'MRKT-Proj02', 'MRKT-Proj03', 'MRKT-Proj04', 'MRKT-Proj05', 'MRKT-Proj06', 'MRKT-Proj07', 'MRKT-Proj08', 'MRKT-Proj09', 'MRKT-Proj10' ]
urole: _member_
接下来,位于 playbook 目录的 root 目录中的名为base.yml的主 playbook 文件将如下所示:
---
# This playbook used to demo OpenStack Juno user, role and project features.
- hosts: util_container
remote_user: root
become: true
roles:
create-users-env
接下来,我们创建了hosts文件,它也位于playbook目录的root目录中。
[localhost]
localhost ansible_connection=local
[util_container]
172.29.236.199
最后,我们通过创建名为util_container的全局变量文件,将其保存到playbook目录的group_vars/目录中,将这个 playbook 全部完成:
# Here are variables related globally to the util_container host group
CLOUD_NAME: default
正如之前承诺的,我觉得为您提供完全可用的 Ansible playbook 和 role 非常重要。您可以直接使用它们,或者作为创建新/改进的 Ansible 代码的跳板。代码可以在 GitHub 存储库中找到,github.com/os-admin-with-ansible/os-admin-with-ansible-v2。
现在当然,我们必须测试我们的工作。假设您已经克隆了之前提到的 GitHub 存储库,从部署节点测试 playbook 的命令如下:
**$ cd os-admin-with-ansible-v2**
**$ ansible-playbook -i hosts base.yml**
摘要
现在看,这并不那么糟糕,对吧?Ansible 确实在简化自动化 OpenStack 管理任务所需的工作方面做得很好。您现在可以一次又一次地重复使用该角色,将创建用户和项目的时间缩短到几分钟。这种时间投资是非常值得的。
在本章中,我们通过 API 和 CLI 在 OpenStack 中创建了用户和项目。我们了解了基本的自动化考虑。我们还开发了 Ansible playbook 和 role 来自动化用户和项目的创建。
有了这个良好的基础,我们准备继续进行下一个管理任务,即定制您的云配额。下一章将包括对配额的一般理解以及它们在您的 OpenStack 云中的使用方式。然后我们将过渡到手动创建配额的练习,最后讲解如何使用 Ansible 自动化这项任务。我们在第四章中见!
第四章:定制您的云配额
现在我们已经解决了创建我们的第一个 OpenStack 管理 Playbook,是时候进入下一个任务了。我们将要涵盖的下一个任务是如何定制云中的项目配额。这通常是为云消费者设置新项目/租户的过程中的下一步。我们将首先逐步介绍如何手动执行此操作,然后过渡到创建具有角色的 Playbook,以完全自动化它:
-
定义和创建配额
-
自动化考虑
-
编写 Playbook 和角色
-
Playbook 和角色审查
定义和创建配额
什么是配额?在 OpenStack 中,您可以在租户/项目或用户级别上设置配额,以限制允许的资源消耗。计算服务(Nova)管理配额值并强制执行它们。作为云操作员,这是 OpenStack 提供的另一个重要功能。配额允许您控制云的整体系统容量。您可能会问,为什么不只设置一个默认配额,让每个项目都使用它?我们将根据特定用例逐步介绍这种方法可能有效或无效的原因。还值得一提的是,块存储服务(Cinder)也具有设置配额的能力。
由于我们现在知道您可以设置配额,让我们回顾一下可以受限制的资源以及默认值是什么。以下表格描述了可以设置的配额类型:
| 配额名称 | 定义的数量 |
|---|---|
| 实例 | 每个项目中允许的实例 |
| 内核 | 每个项目中允许的实例内核 |
| RAM(MB) | 每个实例中允许的 RAM 兆字节 |
| 浮动 IP | 每个项目中允许的浮动 IP |
| 固定 IP | 每个项目中允许的固定 IP |
| 元数据项 | 每个实例中允许的元数据项 |
| 注入文件 | 每个项目中允许的注入文件 |
| 注入文件内容字节 | 每个注入文件中允许的内容字节 |
| 密钥对 | 每个项目中允许的密钥对 |
| 安全组 | 每个项目中允许的安全组 |
| 安全组规则 | 每个安全组中允许的规则 |
| 服务器组 | 每个项目中允许的服务器组 |
| 服务器组成员 | 每个项目中允许的服务器组成员 |
正如您所见,有很多选项可以应用限制。作为云操作员,您希望充分利用这些选项,以便在每个项目的基础上进行调整。采用这种方法可以优化您的云使用,从本质上延伸您的资源,同时只提供所需的资源。作为管理员,我讨厌看到浪费的资源挂在那里,如果有更好的控制措施,它们可以用于其他用途。配额作为相反的方法,也是保持云消费者不会耗尽所有云资源的概念。
是的,调整配额的过程确实需要努力(也就是额外的工作)。因此,设置全局默认配额值的概念变得流行起来。要查看默认配额值,您将执行以下命令:
**$ openstack --os-cloud=<cloud name> quota show <project name>**
输出将如下所示:

提示
每当您希望将配额值设置为无限制时,请将该值设置为-1。这告诉 Nova 允许该资源在该项目或全局范围内不受限制。
现在,让我们专注于如何使用 CLI 手动调整配额值。出于简单起见,我们将仅使用 OpenStack CLI 演示手动命令。
手动创建配额
准确地说,您只能更新全局配额或特定租户/项目的配额设置的值。您无法创建新的配额;只能更新值。列出、更新和重置配额涉及向计算服务(Nova)发送请求。
就像每个 OpenStack 服务一样,你必须首先通过在第一章中讨论的 OpenRC 文件进行认证,OpenStack 简介。然后,你需要为你希望更新的配额提供值(参考前面提到的表格以获取你的选项)。现在,让我们看下面的例子:
**$ source openrc**
**$ openstack quota set <project name> --instances=<value>
--cores=<value>**
一旦执行了命令,屏幕上不会有任何输出。然后你可以执行quota show命令来确认更新。
一个真实的工作示例可能是这样的:
**$ openstack quota show admin**
请记住,前面的示例只显示了更新项目的实例和核心配额。还有其他可以更新的配额值。
设置默认配额
如果你只想设置所有租户/项目和用户都将被分配的默认配额,那么这个过程会有点不同。Nova 也管理默认的配额分配。设置默认配额在你希望快速创建一个带有自动内置控制的租户/项目或用户时非常有用。
最糟糕的情况莫过于错误地创建了一个没有资源限制的项目,然后在你意识到之前,该项目的消费者已经耗尽了你的云。云旨在给消费者一种无限的印象。实际上,我们都知道这是不可能的;一切都在某种程度上有限制。根据我的经验,如果你给一个用户 20 个 vCPU,如果允许的话,他们会全部使用完。设置云资源限制对于云操作者来说非常重要。
稍后会给出更新云的默认配额的命令。这个命令可以在认证后执行,就像前面的例子一样。配额选项与更新项目或特定用户的配额相同。请再次参考前面提到的表格以获取你的选项。以下是一个例子:
**$ openstack quota set <quota class name> --ram=<value>
--security-groups=<value>**
与前面的命令的主要区别之一是,你必须提供 Nova 所谓的“配额”类。配额类是 Nova 区分默认配额和你可能设置的自定义配额的方式。假设未来的 Nova 版本将包括创建额外的配额类的功能。目前,你只能更新唯一可用的配额类,即名为default的配额类。
命令的工作示例可能是这样的:
**$ openstack quota set default --ram=-1 --security-groups=30**
请记住,无论你将默认的配额值设置为多少,每个项目或用户最初都会配置为这个值。
重置配额值
可能会有一天,你可能希望重新开始并重置为项目或用户设置的配额。幸运的是,在 OpenStack 中这是一个简单的过程。你可以使用 Nova 的quota-delete命令。这将删除自定义配额并将其重置为默认配额。参见以下示例:
**$ nova quota-delete --tenant=<tenant-id> [--user=<user-id>]**
使用前面的命令,你可以提供要将配额恢复为默认值的租户 ID 或用户 ID。
自动化考虑
在创建这个角色时,除了我们在前一章中讨论的内容之外,我只需要做出一个自动化决定。所有其他考虑都延续了下来。
因为 Nova 配额命令允许传递多个选项而没有相互依赖,我们必须想出一种方法,既不限制角色的灵活性,又不需要直接对角色进行不断的更新。Ansible 通过允许将变量作为哈希传递来做出这样的决定。在变量文件中,你可以为每个项目或用户定义选项,并让任务循环遍历每个项目/用户以使用这些选项。
我保证这是我最后一次做出这样的声明,但我觉得强调这一点很重要:
注意
有许多方法可以使用 Ansible 自动化 OpenStack 的任务,本书中展示的方法只是我个人发现成功的一种方式,当然不是唯一的方式。这些剧本/角色旨在成为您可以直接使用或调整/改进以适应个人用例的工作示例。
就像上次一样,既然已经说了,让我们继续创建这个角色。
编写剧本和角色
我们现在将创建一个角色,允许我们一次更新单个和/或多个项目的配额。更新配额是一个相对简单的两步过程。第一步是记录您希望更新配额的租户 ID 或用户 ID。然后,第二步是实际更新配额。
由于在本示例中我们只是创建一个角色,我们可以从角色目录中的main.yml文件开始,该目录名为adjust-quotas/tasks。该文件开头的内容将如下所示:
---
- name: Adjust tenant quotas
command: openstack --os-cloud="{{ CLOUD_NAME }}"
quota set "{{ item.1 }}" "{{ item.0 }}"
with_together:
- "{{qoptions}}"
- "{{tenantname}}"
就像我们在本章前面审查的手动命令一样,您必须从稍后我们将审查的变量文件中提供您希望调整的配额选项和租户名称。同样,我们使用with_together命令循环遍历两个变量,将值配对在一起。
以下是任务中定义的变量的进一步细分:
{{ item.0 }} # variable placeholder used to set the quota options to update
{{ item.1 }} # variable placeholder used to set the project name
当执行角色时,在这种特定情况下不会生成任何输出。如果您想要提供输出以确认任务成功执行,可以将quota show命令作为角色中的附加任务添加。这将如下所示:
- name: Confirm tenant quota update
command: openstack --os-cloud="{{ CLOUD_NAME }}"
quota show "{{ item.0 }}"
with_items: "{{tenantname}}"
您现在已经完成了第二个 OpenStack 管理角色。为了支持这个角色,我们现在需要创建与之配套的变量文件。变量文件名为main.yml,将位于adjust-quotas/vars目录中。
提示
请记住,变量文件中定义的值是打算在每次执行前进行更改以进行正常的日常使用。
以下示例中显示的值只是工作示例。让我们来看一下:
---
qoptions: [ '--cores 30', '--instances 20', '--cores 20', '--instances 20', '--cores 20' ]
tenantname: [ 'MRKT-Proj01', 'MRKT-Proj02', 'MRKT-Proj02', 'MRKT-Proj03', 'MRKT-Proj03' ]
让我们花点时间来分解每个变量。总结如下:
qoptions # this is where you declare the quota options you wish to update, each
set of options and values are encapsulated within single quotes
comma delimited; there is no limit on the number of options that can
be added
tenantname # the value is a comma delimited list of tenant names you wish
to update quotas for
现在我们的变量文件已经创建,我们可以继续创建主剧本文件。就像在上一章中一样,文件将被命名为quota-update.yml并保存到剧本目录的根目录中。quota-update.yml文件的内容将是:
---
# This playbook used to demo OpenStack Juno quota updates.
- hosts: util_container
remote_user: root
become: true
roles:
adjust-quotas
该文件的摘要如下:
hosts # the host or host group to execute the playbook against
remote_user # the user to use when executing the playbook on the remote host(s)
become # will tell Ansible to become the above user on the remote host(s)
roles # provide a list of roles to execute as part of this playbook
现在只剩下填充我们的主机清单文件和全局变量文件。由于我们在上一章中已经创建了这些文件,所以没有必要重复这个过程。之前定义的值将保持不变。以下是这些文件的配置快速回顾。
剧本目录根目录中的主机文件是:
[localhost]
localhost ansible_connection=local
[util_container]
172.29.236.199
group_vars/目录中的全局变量文件是:
# Here are variables related globally to the util_container host group
CLOUD_NAME: default
好的,现在我们完成了两个管理剧本和角色。和往常一样,我们将以快速审查刚刚创建的剧本和角色结束本章。
审查剧本和角色
要开始,我们可以从我们创建的名为create-users-env的角色开始。完成的角色和文件,名为main.yml,位于adjust-quotas/tasks目录中,看起来像这样:
---
- name: Adjust tenant quotas
command: openstack --os-cloud="{{ CLOUD_NAME }}"
quota set "{{ item.1 }}" "{{ item.0 }}"
with_together:
- "{{qoptions}}"
- "{{tenantname}}"
该角色对应的变量文件,名为main.yml,位于adjust-quota/vars目录中,将如下所示:
---
qoptions: [ '--cores 30', '--instances 20', '--cores 20', '--instances 20', '--cores 20' ]
tenantname: [ 'MRKT-Proj01', 'MRKT-Proj02', 'MRKT-Proj02', 'MRKT-Proj03', 'MRKT-Proj03' ]
接下来,位于playbook目录的root中的主剧本文件,名为quota-update.yml,将如下所示:
---
# This playbook used to demo OpenStack Juno quota updates.
- hosts: util_container
remote_user: root
become: true
roles:
adjust-quotas
接下来,我们创建了主机文件,也位于playbook目录的root目录中:
[localhost]
localhost ansible_connection=local
[util_container]
172.29.236.199
最后,我们通过创建名为util_container的全局变量文件来包装这个剧本,将其保存到剧本的group_vars/目录中:
# Here are variables related globally to the util_container host group
CLOUD_NAME: default
注意
完整的代码集可以在以下 GitHub 存储库中再次找到:github.com/os-admin-with-ansible/os-admin-with-ansible-v2。
现在当然,我们必须测试我们的工作。假设您已经克隆了前面提到的 GitHub 存储库,从部署节点测试 playbook 的命令如下:
**$ cd os-admin-with-ansible-v2**
**$ ansible-playbook -i hosts quota-update.yml**
摘要
作为 OpenStack 操作员,配额将是您关注的重点,因此,能够简化该流程的任何努力都将是有益的。Ansible 是简化重复任务的关键。就像在上一章中一样,您可以将此角色与其他角色结合使用多次。这就是为什么您希望尽可能将角色设计为基本通用任务的原因。
本章涵盖的一些内容包括定义 OpenStack 中配额的概念。然后,我们利用这些知识学习了如何使用 OpenStack CLI 更新项目/用户的配额。我们应用了一些关于为什么要使用默认云配额以及如何适当地更新它们的基本原则。接下来,我们回顾了如何重置任何自定义配额。最后,我们开发了我们自己的 Ansible playbook 和角色,以自动更新自定义项目/用户配额。
现在让我们继续进行下一章,我们将承担云快照的管理任务。如果您想将实例用作金标本和/或保留实例的备份,那么拍摄实例快照的功能是一个强大的工具。了解如何在云操作员级别处理这种任务非常有益。下一章将介绍如何手动创建快照,介绍一次性快照项目中所有实例的功能,然后当然还包括如何使用 Ansible 自动化该任务。我们继续到第五章 快照您的云。
第五章:快照您的云
在本章中,我们将涵盖使用内置于计算服务(Nova)的 OpenStack 能力创建实例备份和/或快照的任务。当采用真正的云方法,即水平扩展和一次性资源的方法时,您会发现在利用快照与传统备份相比时有很大的用处。尽管这很好,但最佳实践是了解每种能力和适当用例。我们将首先逐步介绍如何手动创建备份或快照,然后过渡到创建具有角色的 playbook,以完全自动化租户级别的操作。本章将涵盖以下主题:
-
定义备份和快照
-
手动创建备份和快照
-
恢复实例备份
-
自动化考虑
-
编写 playbook 和角色
-
Playbook 和角色的审查
定义备份和快照
从 OpenStack 的角度来看,备份和实例快照之间存在明显的区别。这些差异可能影响每个功能的使用。请记住,与真正的云行为保持一致,所有云资源都应该是可丢弃的。您可能会问这句话真正意味着什么。它只是意味着为了支持应用功能而创建的任何实例或卷(资源)都应该能够以某种自动化方式重新创建。灌输宠物与牛的类比。不再是试图让生病的虚拟机复活的日子。
销毁实例,重新创建,然后再次开始。这些原则消除了对实例备份的需求。话虽如此,仍会有一些情况下您可能希望备份实例。因此,让我们首先检查获取实例备份的能力。
OpenStack 计算服务(Nova)备份实例的功能就像任何传统备份过程一样。备份实例的目的是为了保留实例的当前状态,以便以后可能恢复。与任何其他后备过程一样;您可以确定备份类型和轮换计划。一些可能的备份类型参数可以是每日或每周。轮换计划将表示要保留的备份数。通过 Nova CLI 执行实例备份命令的工作示例如下:
**$ nova backup <instance><backup name><backup-type><rotation>**
**$ nova backup testinst bck-testinst weekly 5**
注意
完全透明地说,截至本书编写时,Nova备份功能尚未完全运行。此时的备份命令只是 Nova 中设置的一个挂钩,用于未来专门关注数据保护的 OpenStack 服务。OpenStack 数据保护服务,代号Raksha,将负责帮助自动化数据保护任务,如备份。Raksha 仍在开发中,并将出现在即将推出的 OpenStack 版本中。您可以在wiki.openstack.org/wiki/Raksha上阅读更多关于 Raksha 的信息。
现在我们可以继续讨论快照。Nova 获取实例快照的功能类似于备份,但是不是为了恢复目的而保留备份,而是由镜像服务(Glance)存储为图像模板。然后可以使用该图像模板创建与原始快照所在实例相同的其他实例。这就像制作实例的橡皮图章副本。
注意
请记住,对实例进行传统快照会暂时暂停实例,直到过程完成。如果您希望在不暂停实例的情况下进行快照,请查看docs.openstack.org/openstack-ops/content/snapshots.html上找到的实时快照功能详细信息。
我经常喜欢将快照过程比作制作服务器的黄金镜像,该镜像将用于构建其他服务器。所采取的步骤将完全相同。创建具有所需操作系统的实例,安装必要的软件包,进行建议的操作系统和应用程序安全调整,验证应用程序功能,然后创建快照。在不需要任何第三方软件的情况下即可随时使用快照功能,这确实是 OpenStack 提供的又一个强大工具。
通过 OpenStackClient CLI 执行实例快照命令的实际工作示例如下:
**$ openstack server image create
--name=<snapshot name> <instance>**
**$ openstack server image create
--name=snp-testinst testinst**
希望这有助于清晰地定义实例备份和快照之间的区别。现在让我们来看看使用 CLI 手动创建它们所需的步骤。
注意
为了简单起见,我们将仅使用 OpenStack CLI 演示手动命令。
手动创建备份和快照
如前所述,计算服务(Nova)负责创建实例备份和快照的任务。与每个 OpenStack 服务一样,您必须首先进行身份验证,可以通过获取第一章中讨论的 OpenRC 文件,OpenStack 简介或通过在命令中传递内联身份验证参数来进行身份验证。这两个任务分别需要提供不同的参数值才能成功执行命令。请参见后面给出的示例。
以下是使用 OpenRC 文件的实例“备份”:
**$ source openrc**
**$ nova backup <instance> <backup name>
<backup-type><rotation>**
以下是一个使用内联身份验证参数的实例“备份”:
**$ nova --os-username=<OS_USERNAME> --os-password=
<OS_PASSWORD> --os-tenant-
name=<OS_TENANT_NAME> --os-auth-url=<OS_AUTH_URL>
backup <instance><backup name>
<backup-type><rotation>**
执行命令后,不会将任何输出写回屏幕。然后您可以执行openstack image show命令来确认更新。
使用 OpenRC 文件的真实工作示例可能如下所示:
**$ source openrc**
**$ openstack server list**
**$ nova backup vm-with-vol-my_instance-v35vvbw67u7s
bck-vm-with-vol-my_instance-v35vvbw67u7s weekly 3**
然后openstack image list命令的输出将是:

使用前面提到的命令,您可以提供实例 ID 或名称。刚刚显示的示例使用了实例名称。在获取 OpenRC 文件后,执行openstack server list命令以记录您希望备份的实例 ID 或名称。一旦您获得了这些信息,就可以执行nova backup命令。
注意
镜像服务,代号 Glance,负责保留由云操作员手动上传的备份、快照和任何镜像的清单。要查看可用的清单,您将需要发出 Glance CLI 命令和/或通过Horizon仪表板下的Images选项卡查看它们。
以下是使用 OpenRC 文件的实例快照:
**$ source openrc**
**$ openstack server image create
--name=<snapshot name> <instance>**
以下是使用内联身份验证参数的实例快照:
**$ openstack --os-cloud=default server image create
--name=<snapshot name> <instance>**
执行命令后,不会将任何输出写回屏幕。然后您可以执行openstack image list命令来确认更新。
使用 OpenRC 文件的真实工作示例可能如下所示:
**$ source openrc**
**$ openstack server list**
**$ openstack server image create --name=snap-vm-
with-vol-my_instance-v35vvbw67u7s
vm-with-vol-my_instance-v35vvbw67u7s**
然后openstack image list命令的输出将是:

既然我们已经介绍了如何创建实例备份和快照,那么演示如何使用它们就显得很重要。特别是,我想专注于使用实例备份,因为我注意到在这个功能周围缺乏严重的文档。
恢复实例备份
尽管实例“备份”功能在计划任务/自动化方面并非 100%活跃,但您仍然可以使用实例备份将实例恢复到特定时间点。为了做到这一点,您将使用 Nova CLI 中的nova rebuild命令。该命令将指示实例关闭,使用引用的“备份”文件重新映像实例,然后重新启动实例。
通过 Nova CLI 执行nova rebuild命令的实际工作示例如下:
**$ nova rebuild <instance> <image name>**
**$ nova rebuild vm-with-vol-my_instance-v35vvbw67u7s
snap-vm-with-vol-my_instance-v35vvbw67u7s**
nova rebuild命令还有一些可选参数可以与命令一起传递。这些可选参数可以执行诸如重置管理员密码或更改实例名称等操作。我建议查看 OpenStack CLI 文档,该文档可以在docs.openstack.org/cli-reference/content/novaclient_commands.html#novaclient_subcommand_rebuild找到。
自动化考虑
自动化这个任务非常简单,不需要任何新的框架决策。我们之前审查的所有其他自动化决策都已经被采纳。
有一个值得强调的领域,当您使用 CLI 自动化 OpenStack 任务时,您可能也会面临。 CLI 的默认输出是漂亮打印(使用 Python prettytable模块),有时当您想要整理输出时并不那么漂亮。一些 CLI 命令允许特定格式,但如果命令不允许,您还有其他选择。这就是awk命令再次成为您非常亲密的盟友的地方。在下一节中,您将注意到awk命令的具体用法,以过滤我们在角色中需要的下一个任务的值。
感觉我们现在准备好继续创建下一个 playbook 和 role 了。
编写 playbooks 和 roles
我们现在将创建的 playbook 和 role 将允许您一次对单个租户内的所有实例进行快照。选择这个独特的任务是为了保持角色简单,不要使任务过于复杂。您也可以创建一个角色来对所有租户中的所有实例进行快照或备份,只需删除一个参数。很棒,对吧?好吧,感谢 Ansible。
在本章的开头,我们审查了如何进行实例备份和快照的过程。这是一个简单的两步过程。为了自动化这个任务,我们必须向过程添加一个额外的步骤。这一步将是获取我们计划从中获取快照的租户的租户 ID。因此,在大局中,将有三个步骤。步骤 1是记录您希望为其获取实例快照的租户 ID。步骤 2是现在列出来自租户的所有实例 ID。最后,步骤 3是实际获取实例快照。
由于在此示例中我们只创建了一个 role,因此我们可以从名为create-snapshot/tasks的 role 目录中的main.yml文件开始。该文件的初始内容如下:
---
- name: Retrieve tenantID
shell: openstack --os-cloud="{{ CLOUD_NAME }}"
project list | awk '/ "{{tenantname}}" / { print $2 }'
register: tenantid
使用awk命令和管道(|)符号提取租户 ID 的第一步非常简单。这种方法是您将在许多 OpenStack 文档中看到的。它允许您获取一个命令的输出并过滤出您想要保留的部分。首先,我们将执行项目列表命令,然后将使用过滤器,该过滤器将搜索通过名为tenantname的变量提供的租户名称,并最终输出原始project list命令的第二列值。然后,将使用名为tenantid的变量注册该最终输出。tenantname变量的定义方式与上一章相同。
请记住,这里使用shell模块,因为我们正在执行需要特定于 shell 的操作的命令。
下一个任务现在将列出来自租户的所有实例 ID。完成此操作的代码如下:
- name: Retrieve instance id from tenant
shell: openstack --os-cloud="{{ CLOUD_NAME }}"
server list --all-projects --project "{{ tenantid.stdout }}" | awk 'NR > 3 { print $2 }'
register: instid
这个任务与第一个任务非常相似,只是我们使用 OpenStackClient CLI 而不是列出实例并过滤掉所有前导或尾随字符的 ID。我发现当使用 Ansible 时,openstack server list命令对实例 ID/名称的提供方式非常具体。为了实现这一点,我决定使用awk命令的一个内置变量,名为NR。
awk中的NR变量(记录数)旨在为您提供被过滤内容的记录数或行号。反过来,NR变量可以用于集中研究某些行。在这里,我们使用该变量跳过 CLI 输出的前三行。此示例显示了正常输出:

然后,当添加awk命令awk 'NR > 3 { print $2 }'时,输出如下:

最后,现在我们有了实例列表,我们可以完成最后一个任务,即拍摄快照。执行此操作的代码如下:
- name: Create instance snapshot
command: openstack --os-cloud="{{ CLOUD_NAME }}"
server image create --name="{{ tenantname }}"-snap-"{{ item }}" "{{ item }}"
with_items: "{{instid.stdout_lines}}"
register: command_result
failed_when: "'_info' not in command_result.stderr"
就像在上一章中一样,使用模块定义{{item}}参数值允许我们循环遍历with_items语句中列出的多个软件包。还要记住,在 Ansible 中将值注册到变量后,需要查询 JSON 数据结构的stdout或stdout_lines部分。然后,我们重新利用了租户名称和实例 ID 来命名快照,以便将来轻松引用。快照名称本身可以是任何您想要的,我只是觉得这种命名约定最有意义。
在上述代码的最后两行中,必须添加register和failed_when,这是由于openstack server image create命令的输出。如果您想要提供输出以确认任务的成功执行,可以将openstack image list命令作为角色的附加任务,并将任务输出打印到屏幕上或保存在文件中。将输出打印到屏幕的示例如下:
- name: Confirm instance snapshot(s)
shell: openstack --os-cloud="{{ CLOUD_NAME }}"
image list --format value --column Name
register: snapchk
- name: Image list output
debug: msg="{{ item }}"
with_items: "{{snapchk.stdout_lines}}"
您现在已经完成了第三个 OpenStack 管理角色。为了支持此角色,我们现在需要创建与之配套的变量文件。名为main.yml的变量文件将位于create-snapshot/vars目录中。
提示
请记住,变量文件中定义的值是为了在正常的日常使用中在每次执行之前进行更改的。
对于此角色,只需要一个变量:
---
tenantname: MRKT-Proj01
此变量旨在成为需要拍摄实例快照的租户名称之一的单个值。
现在我们已经创建了变量文件,可以继续创建主要的 playbook 文件。该文件将命名为snapshot-tenant.yml,并保存在playbook目录的root目录中。
注意
playbook 和角色的名称可以是任何您选择的。这里提供了具体的名称,以便您可以轻松地跟踪并引用 GitHub 存储库中找到的完整代码。唯一的警告是,无论您决定如何命名角色,都必须在 playbook 中引用时保持统一。
snapshot-tenant.yml文件的内容将是:
---
# This playbook used to demo OpenStack Newton user, role, image and volume features.
- hosts: util_container
remote_user: root
become: true
roles:
- create-snapshot
该文件的摘要如下:
hosts # the host or host group to execute the playbook against
remote_user # the user to use when executing the playbook on the remote host(s)
become # will tell Ansible to become the above user on the remote host(s)
roles # provide a list of roles to execute as part of this playbook
现在只剩下填写我们的主机inventory文件和全局variable文件。由于我们已经在上一章中创建了这些文件,所以无需重复此过程。之前定义的值将保持不变。以下是这些文件配置的快速回顾。
playbook目录中root目录中的hosts文件是:
[localhost]
localhost ansible_connection=local
[util_container]
172.29.236.199
group_vars/目录中的全局变量文件是:
# Here are variables related globally to the util_container host group
CLOUD_NAME: default
完成了第三个管理 playbook 和 role 的出色工作!和往常一样,我们将以快速审查刚刚创建的 playbook 和 role 来结束本章。
审查 playbooks 和 roles
让我们立即开始检查我们创建的名为create-snapshot的 role。完成的 role 和文件名为main.yml,位于create-snapshot/tasks目录中,如下所示:
---
- name: Retrieve tenantID
shell: openstack --os-cloud="{{ CLOUD_NAME }}"
project list | awk '/ "{{tenantname}}" / { print $2 }'
register: tenantid
- name: Retrieve instance id from tenant
shell: openstack --os-cloud="{{ CLOUD_NAME }}"
server list --all-projects --project "{{ tenantid.stdout }}" | awk 'NR > 3 { print $2 }'
register: instid
- name: Create instance snapshot
command: openstack --os-cloud="{{ CLOUD_NAME }}"
server image create --name="{{ tenantname }}"-snap-"{{ item }}" "{{ item }}"
with_items: "{{instid.stdout_lines}}"
register: command_result
failed_when: "'_info' not in command_result.stderr"
对应的变量文件名为main.yml,位于create-snapshot/vars目录中,该角色的文件如下:
---
tenantname: MRKT-Proj01
接下来,位于 playbook 目录的root目录中的主 playbook 文件名为snapshot-tenant.yml,如下所示:
---
# This playbook used to demo OpenStack Newton user, role, image and volume features.
- hosts: util_container
remote_user: root
become: true
roles:
- create-snapshot
接下来,我们创建了hosts文件,也位于playbook目录的root目录中:
[localhost]
localhost ansible_connection=local
[util_container]
172.29.236.199
最后,创建全局变量文件名为util_container,并将其保存到 playbook 的group_vars/目录中,将完成 playbook:
# Here are variables related globally to the util_container host group
CLOUD_NAME: default
完整的代码集可以在 GitHub 存储库中再次找到github.com/os-admin-with-ansible/os-admin-with-ansible-v2。
在没有先测试我们的工作之前,我们无法结束本章。假设您已经克隆了前面的 GitHub 存储库,从部署节点测试 playbook 的命令如下:
**$ cd os-admin-with-ansible-v2**
**$ ansible-playbook -i hosts snapshot-tenant.yml**
总结
一旦开始使用 Ansible 创建 playbooks 和 roles,您会发现可以为许多不同的目的重复使用大量代码。在本章中,我们能够创建另一个与上一章非常相似的 role,但很快且轻松地包含一个完全不同的任务。始终记住尽可能将您的角色设计为尽可能基本的通用任务。我真诚地无法强调这一点。这可能是自动化某事所需的时间差异。
在本章中,我们定义并描述了实例备份和快照之间的区别。我们解释了使用 OpenStack CLI 手动创建备份和快照的过程。我们还回顾了如何使用实例backup的示例。然后,我们最终开发了用于自动创建指定租户内所有实例的快照的 Ansible playbook 和 role。我非常期待进入下一章,我们将在其中研究在计算节点之间迁移实例的过程。这肯定是您在管理 OpenStack 云时会遇到的管理任务。这也是一个颇具争议的话题,因为许多人要么不知道 OpenStack 中存在这个功能,要么不相信这个功能运行良好。在下一章中,我们将尝试通过演示如何手动迁移实例,然后进一步自动化来消除不必要的困惑。对于我们这些云操作员来说,下一章将是金子般的价值。您不想跳过下一章;它肯定是值得的。第六章,迁移实例,我们来了!
第六章:迁移实例
在本章中,我们将介绍使用内置在计算服务(Nova)中的 OpenStack 本机功能迁移实例的任务。如前所述,许多人并不知道这种功能的存在。在本章中,我们将通过演示如何手动迁移实例来证明这种功能。此外,我们将审查自动化此任务所需的步骤,并最终创建一个包含角色的 playbook,以完全自动化实例迁移到指定的计算节点。
本章将涵盖以下主题:
-
实例迁移
-
自动化考虑
-
编写 playbook 和角色
-
Playbook 和角色审查
实例迁移
每当提到实例迁移的话题时,通常会因为各种原因而引发一场激烈的讨论。因此,作为一个负责任的成年人,我将继续记录并说实例迁移并不完美。
它有其缺陷,可能有些古怪。无论是实时迁移还是非实时迁移,都对您的 OpenStack 云具有实际用途。在 OpenStack 中,您可以将实例从一个计算节点迁移到另一个计算节点。您可能这样做的原因之一是为了维护目的和/或在云中重新平衡资源利用率。此外,请记住,有多种方法可以清除计算节点以进行维护,我们将在第八章中更详细地介绍这一点,设置主动-主动区域。
注意
如前所述,OpenStack 计算服务(Nova)具有传统方法迁移实例和实例迁移的功能。
我们将首先检查传统迁移方法及其属性。
传统迁移方法将通过关闭该实例,将实例镜像/文件复制到下一个可用的计算节点,启动新节点上的实例,最后从原始节点中删除实例来移动实例。在这种方法中需要关注的区域是:
-
实例已关闭
-
实例镜像/文件将需要一些时间复制到新的计算节点
-
新的计算节点选择由 Nova Scheduler 完成;您不能在没有额外步骤的情况下分配一个
-
一旦复制完成,实例就会重新上线
正如您所注意到的,这种方法可能被一些人认为是侵入性的。关闭实例以移动它的想法通常不是虚拟化时代中的理想情景。请记住,我们处于一个新时代,云和可丢弃资源的时代。
由于资源是随时可用的,并且您有控制权来确定如何使用这些资源,应该没有问题将实例下线。对吗?是的,我知道要摆脱那种宠物心态可能需要一段时间,但您会做到的。如果情况允许,通常意味着您在分布在您的 hypervisors 上运行应用程序的实例方面做得很好,您可以非常容易地使用这种方法来迁移实例。
通过 OpenStackClient CLI 进行传统实例迁移命令的工作示例如下:
**$ openstack server migrate <instance>**
**$ openstack server migrate testinst**
另一种迁移方法是执行实时实例迁移。这种方法将消除之前描述的传统迁移过程中关闭实例的要求。而不是关闭实例,它被挂起(仍处于运行状态),同时实例被重新分配到新的计算节点。自Mitaka发布以来,已经取得了很大进展,以改进此功能。这些新增功能包括跟踪迁移进度的能力,暂停或取消正在进行的迁移以及排除某些附加卷的可能性。
为了利用实时迁移功能,还需要满足其他系统要求。这些要求如下:
-
您的计算节点之间必须存在某种共享或外部存储能力
-
使用实时迁移,您可以选择新的计算节点,但必须确保新节点具有新实例所需的资源
-
旧的和新的计算节点必须具有相同的 CPU;如果不是这种情况,Kilo 之前的 OpenStack 版本可能会遇到问题
列表中的第一个要求是最重要的,它值得进一步解释。附加存储要求可以通过以下三种不同方式进行满足:
-
满足需求的第一种方法是配置您的 hypervisor 以存储并访问共享存储以进行实例放置。这意味着实例存储在共享存储设备上,而不是在临时存储上。这可能涉及在计算节点上挂载 NFS 共享以用于存储实例,或通过光纤通道在计算节点之间共享 LUN,例如。
-
满足共享/外部存储要求的第二种方法可能是利用直接块存储,其中您的实例由基于镜像的根磁盘支持。
-
第三种和最后一种方法可能是来自卷存储功能的引导。这是您从 Cinder 基于卷引导实例的地方。当然,您需要在 OpenStack 云中启用和配置块存储服务(Cinder)。
注意
在 Nova 中使用实时迁移功能时的一个关键消息是,您的实例必须存在于某种共享/外部存储上,并且不能使用计算节点本地的临时存储。有关所需配置的更多详细信息,请访问docs.openstack.org/admin-guide/compute-configuring-migrations.html。
通过 Nova CLI 执行实例server migrate命令的工作示例如下:
**$ openstack server migrate --live=<new compute node> <instance>**
**$ openstack server migrate --live=compute01 testinst**
如前所述,实例迁移的整个概念可以从非常简单到极其复杂。希望您现在可以清楚地了解所需的内容以及实例迁移过程。现在让我们来检查使用 CLI 手动迁移实例的过程。
注意
出于简单起见,我们将仅使用 OpenStack CLI 演示手动命令。
手动迁移实例
计算服务(Nova)负责管理实例迁移过程。 Nova 在幕后将执行重新分配实例到新节点以及实例镜像/文件移动所需的所有步骤。与每个 OpenStack 服务一样,您必须首先进行身份验证,要么通过在第一章中讨论的 OpenRC 文件中进行源化,要么通过在命令中使用内联传递身份验证参数。这两个任务分别需要提供不同的参数值,以便成功执行命令。这里提到了示例。
使用 OpenRC 文件进行实例迁移:
**$ source openrc**
**$ openstack server migrate <instance>**
通过内联传递身份验证参数进行实例迁移:
**$ openstack --os-cloud=<cloud name> server migrate <instance>**
发出openstack server migrate命令后,我通常会跟上openstack server show命令,以报告实例迁移过程。这是我通常不会经常使用的东西,当自动化 OpenStack 任务时,这是显而易见的原因。由于迁移过程可能需要一些时间,而我们正在手动执行任务,因此有助于跟踪其进展。
另一种检查迁移的方法是使用传统的 Nova CLI 和nova migration-list命令。
使用 OpenRC 文件的实际工作示例可能如下所示:
**$ source openrc**
**$ openstack server list**
**$ openstack server migrate test-1ae02fae-93ca-4485-a797-e7f781a7a25b**
**$ nova migration-list**
nova migration-list命令的输出将类似于这样:

在之前的命令中提供的完整输出将根据之前执行的任何迁移而有所不同。要关注的关键信息是您刚刚尝试迁移的实例的迁移Status。状态将报告为migrating或finished。一旦状态更新为finished,您就可以确认实例的迁移。
迁移后,实例将默认处于VERIFY_RESIZE状态,无论您是否实际上调整了它的大小。

然后,您需要执行openstack server resize命令将实例恢复到ACTIVE状态。以下示例演示了这个任务:
**$ openstack server resize
--confirm test-1ae02fae-93ca-4485-a797-e7f781a7a25b**
到此为止,您可以开始了!您的实例已经迁移到一个新的计算节点,并且现在处于ACTIVE状态。对于我们中的一些人来说,已经习惯了接受传统的迁移过程,下一个问题通常是,为什么我不能使用 nova migrate 命令将实例迁移到特定的计算节点?我们将在下一节讨论这个问题。
将实例迁移到特定的计算节点
对于之前提到的问题,诚实而直接的答案是我不知道为什么没有包括这个功能。好消息是,就像 OpenStack 内的大多数事物一样,总是有办法让它按照您的意愿去做。
注意
请注意,下面概述的步骤是 100%的解决方法(中等脏的解决方法),在未经多层测试以确保预期功能的情况下,不应在生产环境中使用。
如前面的章节所述,您无法使用传统的迁移方法将实例迁移到特定的计算节点。这个选项实际上是不存在的(希望很快会改变)。但是,您可以通过禁用其他计算节点来欺骗 Nova 调度程序,将实例放置在选定的计算节点上。Nova 调度程序将别无选择,迁移实例到您选择的计算节点上。是的,在您的脑海中,您刚刚称呼我为白痴。不要担心,这在纸上听起来并不像是那么具有侵入性。
OpenStack 控制平面服务旨在报告分布式组件(如计算节点和/或 Cinder 节点)的状态。然后,接收到的报告存储在 OpenStack 数据库中,控制平面服务就知道特定节点是上线还是下线。同样,控制平面服务也可以强制报告节点的状态。
计算服务(Nova)是一个可以强制报告计算节点状态的示例服务。这只会在数据库中标记计算节点的上线或下线状态,实际上并不会对计算节点做任何物理操作。所有运行在这些计算节点上的实例将继续运行,节点的整体功能将保持不变。然而,在数据库中禁用节点的时间内,将阻止在该节点上创建新实例。如果您的 OpenStack 云非常繁忙且不使用分离的计算节点集,这种解决方法可能不是一个明智的选择。
由于其侵入性,这感觉像是一个完美的管理任务,可以尝试自动化。对于这样的任务,时间和准确性非常关键。浪费一分钟的时间可能导致无法在 OpenStack 云内部创建任意数量的新实例。对于这种性质的任务,自动化是王道。在接下来的几节中,我们将回顾自动化这个任务所需的步骤。
自动化考虑
这个任务也不需要做出任何新的框架决定。我们之前审查过的所有其他自动化决策都被延续了。
在我们开始之前,值得注意的是,当自动化像这样的任务(迁移实例并禁用计算节点)时,最好在迁移前后收集有关它们的详细信息。拥有这些详细信息将简化您需要时撤销更改的过程。是的,这将为您的角色增加额外的任务,使其稍微复杂一些,但仍然非常值得。
有了这些说法,我们现在准备继续创建我们的下一个剧本和角色。
编写剧本和角色
在本节中,我们将创建允许您使用传统的openstack server migrate命令将实例迁移到特定计算节点的剧本和角色。与迄今为止创建的其他任务不同,处理此任务实际上只有一种方法。我们将采取前两节中概述的步骤,自动化它们,以便您只需要提供一些变量值,然后执行一个命令。
本章开始讨论了实例迁移以及在 Nova 中处理此问题的两种选项:传统迁移和在线迁移。传统迁移过程实际上是一个一步过程,但为了正确自动化此任务,我们需要向该过程添加一些步骤。我们将不得不创建的任务的简要概述如下:
-
列出计算节点。
-
收集预迁移实例详细信息。
-
禁用除了我们希望实例迁移到的计算节点之外的所有计算节点。
-
迁移实例。
-
启用所有计算节点。
-
确认实例迁移。
-
收集迁移后实例的详细信息。
角色详细信息
由于在此示例中我们只创建一个角色,因此可以从角色目录中的instance-migrate/tasks中的main.yml文件开始。此文件的初始内容将如下所示:
---
- name: Retrieve hypervisor list
shell: openstack --os-cloud="{{ CLOUD_NAME }}"
hypervisor list | awk 'NR > 3' | awk '$4 != "{{ desthype }}"
{ print $4 }'
register: hypelist
检索 OpenStack 云中所有计算节点的完整列表的第一步非常容易,只需使用openstack hypervisor list命令。一旦获得这些结果,最好将输出精简为您所需的信息。同样,我们将使用awk命令和管道(|)符号来做到这一点。您会注意到这与我们在上一章中所做的方式类似。请记住,这里使用 shell 模块是因为我们正在执行需要特定于 shell 的操作的命令。
对于这个特定的任务,我们必须使用awk命令进行一些魔术操作:
**awk 'NR > 3' | awk '$4 != "{{ desthype }}" { print $4 }'**
它不仅会提取标准 CLI 输出的前三行,还会检查第四列并打印所有输出,除了与{{ desthype }}变量匹配的内容。然后将整理后的输出注册到名为hypelist的变量中。
下一个任务现在将收集预迁移实例详细信息,这些信息将在角色内稍后使用。完成此操作的代码如下:
- name: Collect pre-migration instance details
shell: openstack --os-cloud="{{ CLOUD_NAME }}"
server list --name "{{ instance }}" --long | awk 'NR > 3' | awk '{ print $16 }'
register: preinststat
对于这个任务,我们再次使用 OpenStackClient CLI 使用openstack server list命令提供实例详细信息。您也可以使用openstack server show命令列出实例详细信息。这两个命令之间的明显区别在于openstack server list命令可以选择在输出上显示附加字段。要执行此操作,请添加--long的可选参数。
在我们的特定情况下,我们想知道特定实例当前正在运行的计算节点。因此,我们需要确保openstack server list命令如下所示:
**openstack server list --name {{ instance }} --long**
第三个任务将是禁用您不希望实例迁移到的计算节点。请记住,我们只是在 Nova 中禁用计算节点,而不是物理上改变计算节点的状态。执行此操作的代码将如下所示:
- name: Disable unselected hypervisors
command: nova "{{ AUTH_S }}"
service-disable "{{ item }}" nova-compute --reason '{{ migreason }}'
with_items: "{{hypelist.stdout_lines}}"
通过使用nova service-disable命令,您可以告诉 Nova 在远程主机上禁用任何特定的与 Nova 相关的服务。为了让 Nova Scheduler 忽略/跳过计算节点,您需要禁用 nova-compute 服务。该命令还需要提供一个原因,如果需要的话,将存储在 Nova 数据库中以供以后参考。在这个任务中,我们将使用之前收集到的hypelist变量中存储的计算节点列表。
注意
请注意,我们不会禁用我们希望将实例迁移到的计算节点,因为我们已经将其从列表中过滤出来。
进入第四个任务,我们现在将执行实例迁移。在这一点上,只有您选择接收迁移实例的计算节点是启用的,关于openstack server migrate不需要做任何特殊的事情。支持代码请参见这里:
- name: Migrate instance
command: openstack --os-cloud="{{ CLOUD_NAME }}"
server migrate "{{ instance }}"
迁移完成后,我们需要立即重新启用被禁用的计算节点。我欣赏 OpenStack 的一点是,如果您被给予禁用某些东西的命令,通常也会给您一个重新启用它的命令。因此,我们只需执行nova service-enable命令。同样,我们将使用hypelist变量来提供要执行的计算节点列表。使用的代码如下:
- name: Enable the disabled hypervisors
command: nova "{{ AUTH_S }}"
service-enable "{{ item }}" nova-compute
with_items: "{{hypelist.stdout_lines}}"
现在迁移已经完成,并且计算节点都已启用,我们可以专注于完成实例迁移过程。实例迁移的最后一步是通知 Nova,您确认实例已经移动。乍一看,我可以不做这一步,但事后来看,某种确认确实是有意义的。此任务的代码可以在这里找到:
- name: Confirm instance migration
command: openstack --os-cloud="{{ CLOUD_NAME }}"
server resize --confirm "{{ instance }}"
最后两个任务将用于向运行 playbook 的个人提供对所做工作的可视确认。考虑这更多是一个自动化的故障安全,而不是一个要求。对于这样一个复杂的管理任务,总是一个很好的常规做法是输出一些关于系统上发生了什么变化的细节:
- name: Collect post-migration instance details
shell: openstack --os-cloud="{{ CLOUD_NAME }}"
server list --name "{{ instance }}" --long | awk 'NR > 3' | awk '{ print $16 " and has a status of " $10 }' | awk 'NR == 1'
register: postinststat
- name: Show instance location and status
debug: msg="{{ instance }} was migrated from {{ item.0 }} to {{ item.1 }}"
with_together:
- "{{preinststat.stdout_lines}}"
- "{{postinststat.stdout_lines}}"
这两个任务将首先收集迁移后实例的详细信息,然后使用从preinststat和postinststat变量收集到的信息在屏幕上输出变更的摘要。使用的摘要模板将是:
<实例已迁移>已从<计算节点>迁移到<计算节点>,状态为<实例当前状态>
提示
随意进入并进行更改以适应您的需求。这只是我的意见方法。保持简单,同时提供处理迁移时关心的相关细节,这样做感觉是正确的。在回顾 playbook 时,如果出现问题和/或实施不正确,您应该能够快速定位需要纠正的步骤。
变量细节
再次恭喜,您已经完成了第四个 OpenStack 管理角色。为了支持这个角色,我们现在需要创建与之配套的变量文件。变量文件名为main.yml,将位于instance-migrate/vars目录中。
提示
请记住,变量文件中定义的值是为了在正常的日常使用中在每次执行之前进行更改的。
对于这个角色,我们在变量方面保持了相当简单,只需要定义三个变量:
---
desthype: 021579-compute02
instance: testG-2c00131c-c2c7-4eae-aa90-981e54ca7b04
migreason: "Migrating instance to new compute node"
让我们花点时间来分解每个变量。总结如下:
desthype # this value would be the name of the compute node you wish
to migrate the instance to
instance # the name of the instance to be migrated
migreason: # a string encapsulated in quotes to explain the reason
for migrating the instance (keep the string brief)
Playbook 细节
完成变量文件后,我们可以继续创建主 playbook 文件。文件名为migrate.yml,保存在playbook目录的root目录中。
注意
playbook 和角色的名称可以是您选择的任何内容。这里提供了具体的名称,以便您可以轻松地跟踪并参考 GitHub 存储库中找到的完成代码。唯一的警告是,无论您决定如何命名角色,都必须在 playbook 中引用时保持统一。
migrate.yml文件的内容将是:
---
# This playbook used to migrate instance to specific compute node.
- hosts: util_container
remote_user: root
become: true
roles:
- instance-migrate
该文件的摘要如下:
hosts # the host or host group to execute the playbook against
remote_user # the user to use when executing the playbook on the remote host(s)
become # will tell Ansible to become the above user on the remote host(s)
roles # provide a list of roles to execute as part of this playbook
我们已经在两章前向主机清单文件和全局变量文件添加了内容,所以我们已经完成了这部分。之前定义的值将保持不变。以下是这些文件配置的快速回顾。
hosts文件位于 playbook 目录的 root 目录中:
[localhost]
localhost ansible_connection=local
[util_container]
172.29.236.199
group_vars/目录中的全局变量文件是:
# Here are variables related globally to the util_container host group
CLOUD_NAME: default
AUTH_S: --os-username {{ OS_USERNAME }} --os-password {{ OS_PASSWORD }} --os-project-name {{ OS_TENANT_NAME }} --os-domain-name {{ OS_DOMAIN_NAME }} --os-auth-url {{ OS_AUTH_URL }}
OS_USERNAME: admin
OS_PASSWORD: passwd
OS_TENANT_NAME: admin
OS_DOMAIN_NAME: default
OS_AUTH_URL: http://172.29.238.2:5000/v3
注意
警告
由于该文件的内容,它应该作为安全文件存储在您可能用来存储 Ansible playbooks/roles 的任何代码存储库中。获取这些信息可能会危及您的 OpenStack 云安全。
我们现在进展非常顺利,微笑,您做到了!希望到目前为止一切都变得更加清晰。保持我们的传统,我们将以快速回顾刚刚创建的 playbook 和 role 结束本章。
审查 playbook 和 role
让我们直接开始检查我们创建的 role,名为instance-migrate。位于instance-migrate/tasks目录中的已完成 role 和文件,名为main.yml,看起来是这样的:
---
- name: Retrieve hypervisor list
shell: openstack --os-cloud="{{ CLOUD_NAME }}"
hypervisor list | awk 'NR > 3' | awk '$4 != "{{ desthype }}" { print $4 }'
register: hypelist
- name: Collect pre-migration instance details
shell: openstack --os-cloud="{{ CLOUD_NAME }}"
server list --name "{{ instance }}" --long | awk 'NR > 3' | awk '{ print $16 }'
register: preinststat
- name: Disable unselected hypervisors
command: nova "{{ AUTH_S }}"
service-disable "{{ item }}" nova-compute --reason '{{ migreason }}'
with_items: "{{hypelist.stdout_lines}}"
- name: Migrate instance
command: openstack --os-cloud="{{ CLOUD_NAME }}"
server migrate "{{ instance }}"
- name: Enable the disabled hypervisors
command: nova "{{ AUTH_S }}"
service-enable "{{ item }}" nova-compute
with_items: "{{hypelist.stdout_lines}}"
- name: Confirm instance migration
command: openstack --os-cloud="{{ CLOUD_NAME }}"
server resize --confirm "{{ instance }}"
- name: Collect post-migration instance details
shell: openstack --os-cloud="{{ CLOUD_NAME }}"
server list --name "{{ instance }}" --long | awk 'NR > 3' | awk '{ print $16 " and has a status of " $10 }' | awk 'NR == 1'
register: postinststat
- name: Show instance location and status
debug: msg="{{ instance }} was migrated from {{ item.0 }} to {{ item.1 }}"
with_together:
- "{{preinststat.stdout_lines}}"
- "{{postinststat.stdout_lines}}"
该角色的对应变量文件,名为main.yml,位于instance-migrate/vars目录中,将如下所示:
---
desthype: 021579-compute02
instance: testG-2c00131c-c2c7-4eae-aa90-981e54ca7b04
migreason: "Migrating instance to new compute node"
接下来,位于playbook目录的root目录中的主 playbook 文件,名为migrate.yml,将如下所示:
---
# This playbook used to migrate instance to specific compute node.
- hosts: util_container
remote_user: root
become: true
roles:
- instance-migrate
接下来,我们创建了hosts文件,它也位于playbook目录的root目录中:
[localhost]
localhost ansible_connection=local
[util_container]
172.29.236.199
最后,创建名为util_container的全局变量文件,并将其保存到 playbook 的group_vars/目录中将完成 playbook:
# Here are variables related globally to the util_container host group
CLOUD_NAME: default
AUTH_S: --os-username {{ OS_USERNAME }} --os-password {{ OS_PASSWORD }} --os-project-name {{ OS_TENANT_NAME }} --os-domain-name {{ OS_DOMAIN_NAME }} --os-auth-url {{ OS_AUTH_URL }}
OS_USERNAME: admin
OS_PASSWORD: passwd
OS_TENANT_NAME: admin
OS_DOMAIN_NAME: default
OS_AUTH_URL: http://172.29.238.2:5000/v3
注意
完整的代码集可以在 GitHub 存储库中找到,github.com/os-admin-with-ansible/os-admin-with-ansible-v2。
我们终于来到了我最喜欢的部分,即测试我们的出色工作。幸运的是,我已经解决了所有的错误(眨眼)。假设您已经克隆了前面的 GitHub 存储库,从部署节点测试 playbook 的命令将如下所示:
**$ cd os-admin-with-ansible-v2**
**$ ansible-playbook -i hosts migrate.yml**
可以在此处查看 playbook 执行输出的示例:

摘要
很高兴完成了另一个涵盖现实生活中 OpenStack 管理职责的章节。您创建的 playbooks 和 roles 越多,您就能够通过简单地重用先前为其他目的创建的代码来更快地创建新代码。在本书结束之前,您将拥有一个不错的 playbooks/roles 集合,以供将来的 Ansible 自动化参考。
回顾本章时,您会回想起我们讨论了实例迁移是什么,以及为什么您会想要使用这个功能。我们回顾了两种可能的迁移方法:传统迁移和在线迁移。您学会了如何手动迁移实例,以及如何使用传统迁移将实例迁移到特定计算节点的解决方法。最后,我们创建了 Ansible playbook 和 role 来自动化这种解决方法。总体而言,实例的维护和在计算节点之间的移动不断改进。在某个时候,您将不需要使用本章提到的一些解决方法。敬请期待一些很棒的改进!
下一章是一个热门话题,因为我们许多人都在探索容器技术。特别是,我们关注如何在利用 OpenStack 云的同时消耗和使用容器。现在有几种方法可用,但关键是自动化这个过程,使其成为可重复使用的功能。在下一章中,我们将介绍每种方法,并展示如何成功地完成这一过程的构建模块。再拿一杯咖啡,做个快速伸展,让我们开始《第七章》在您的云上管理容器!
第七章:在您的云上管理容器
在本章中,我们将介绍当前最受讨论的技术方法之一,即容器。围绕容器的流行度不断增加,这是理所当然的,谁不希望有一种更容易部署应用程序和一种整合的方法来使用计算资源?我喜欢使用的最佳类比之一是,在谈论容器时,除了显而易见的船上的集装箱类比之外,想象把所有的代码放进一辆汽车或 SUV 中。然后一辆车辆载体出现在你家门口来接你的车辆。你的车辆的维护将几乎没有,因为车辆载体正在做所有的工作。你只需要担心确保车辆载体正常工作。这就是容器背后的原理,我们将深入探讨容器概念,还将学习如何利用 OpenStack 和/或 Ansible 来构建和部署它们。与我们通常的做法一样,当我们逐个部分进行时,我们将创建一些 Ansible 示例,以展示您可能如何管理各种不同的容器格式。本章将涵盖以下主题:
-
解释容器概念
-
构建和部署容器
-
使用 Ansible Container 构建容器
-
在 OpenStack 上部署 Kubernetes
-
使用 Ansible 管理 CoreOS 和 Docker
-
在 OpenStack 上部署 Nova LXD
-
审查 playbooks 和 roles
解释容器概念
我必须相信大多数对技术感兴趣的人已经知道容器化(又称容器)是什么,但在我假设错误的怪异机会中,开始解释它究竟是什么感觉是一个好主意。我会尽力不只是给出维基百科的定义,而是尽力为为什么容器模型是资源虚拟化的一个非常有用的补充提供一些实质性的意义。
随着传统虚拟化的开始,人们意识到我可以将我的服务器切成可消耗的块。不再需要将整个服务器专用于成为网络或应用程序服务器。随之而来的是云的采用,因为许多人开始意识到他们没有正确使用这些虚拟化资源。虚拟机闲置或拥有不需要的过多资源。云的一个主要卖点是你可以只使用你需要的资源,并且这些资源是可丢弃的,即使用后就丢弃。尽管这些技术使消耗计算资源变得更容易,但它们都没有真正帮助改善应用程序的部署方式。
记住你为什么需要那些虚拟机和实例,那就是为了运行应用程序。如果获取资源的速度更快,但部署新应用程序仍然需要几天,那有什么意义呢?在我看来,这就是容器化方法被设计的基础。开发人员和系统管理员(主要是系统管理员)希望有一种更有效的部署应用程序的方法。我个人还记得部署新应用程序或 API 的极其痛苦的过程。它包括尝试按照开发人员编写的部署文档进行部署,而这些开发人员很可能以前从未登录过服务器或管理过网络/应用程序服务器软件。让我们只说它充满了遗漏的步骤、错误的命令,并且永远无法考虑到可能需要的任何环境修改(例如依赖软件版本)。
快进到现在,你现在有更多的选择。现在有很多不同的容器技术,允许开发人员将应用程序打包到容器中,然后将其直接部署到你选择的容器平台。不再需要部署文档,不再需要凌晨 2 点的部署派对,最重要的是不再有部署错误。由于容器包含了应用程序的完整运行时环境,你只需要管理容器技术本身和它运行的操作系统。容器也很容易在环境之间或系统之间移动,因为唯一的依赖是运行相同容器技术的服务器。
现在你已经了解了一些关于容器的知识,你必须选择最适合你需求的平台。一些最流行的容器技术包括 Docker (www.docker.com),Kubernetes (kubernetes.io),CoreOS (coreos.com)和 LXC/LXD (linuxcontainers.org)。
所以在你问之前,你可能会想,由于容器相对较新,它是否可以被信任,容器化概念是否已被证明有效?答案是肯定的,因为容器并不是一个新概念。容器或容器化的概念已经存在了 10 年。第一个容器技术是 LXC,它已经成为 Linux 内核的一部分多年了。因此,我可以肯定地说它已经经过了测试,并且绝对是一个值得加入你的组织组合中的技术。
我们现在可以开始探索容器并制定如何在你的 OpenStack 云上自动构建和部署它们的旅程。我们旅程中需要走的第一步是构建我们的第一个容器。
构建和部署容器
在这一部分,我们将学习如何设计、构建和部署各种容器技术的容器。我们将在这里涵盖的主题包括:
-
使用 Ansible Container 构建容器
-
在 OpenStack 上部署 Kubernetes
-
使用 Ansible 管理 CoreOS 和 Docker
-
在 OpenStack 上部署 Nova LXD
如前所述,我们将首先学习如何使用我认为是最简单的容器工具 Ansible Container 构建我们的第一个容器。希望你也很兴奋,因为我肯定是,让我们开始吧!
使用 Ansible Container 构建容器
什么是 Ansible Container?
Ansible 将 Ansible Container 描述为"容器开发、测试和部署的终极工作流" - (
docs.ansible.com/ansible-container)
把它看作是一个工作流工具,它不仅可以帮助你构建 Docker 镜像,还可以使用 Ansible playbooks 编排它们和应用程序的部署。我会给你一点时间来整理一下自己。是的,我们的朋友 Ansible 又一次做到了,并提供了另一个很棒的工具放入我们的工具箱中。不再只依赖 Dockerfile。Ansible 带来的所有功能现在可以直接与构建、运行、部署,甚至将容器镜像推送到选择的注册表相结合。关于 Ansible Container 的所有信息都可以在这里找到:docs.ansible.com/ansible-container。
就像对待每个其他工具一样,Ansible 使得以 Ansible Container 为中心的焦点变得简单易用。就我个人而言,我能够在短短几个小时内安装它并部署我的第一个容器。Ansible Container 的关键功能之一是能够利用来自 Ansible Galaxy(galaxy.ansible.com/intro)的共享容器构建,以便快速设计您的容器映像。请记住,开源就是与社区分享。
自动化考虑
第一步是安装它,由于 Ansible 文档与众不同,我无需重新发明轮子。安装选项和详细信息可以在以下位置找到:docs.ansible.com/ansible-container/installation.html。在您运行它之后,我建议的下一步是查看此处的入门指南:docs.ansible.com/ansible-container/getting_started.html。
现在,我们将逐步介绍我创建的一个示例 Ansible Container 项目以供开始使用。对我来说,这是学习新技术的最佳方式。花些时间,动手尝试一下,然后变得更有见识。
步骤 1
使用 Ansible Container 项目开始就像创建一个新目录一样简单。创建新目录后,您需要进入该目录并执行 Ansible Container 初始化命令。这些命令的工作示例如下:
**$ mkdir elk-containers**
**$ cd elk-containers**
**$ ansible-container init**
命令的输出将类似于这样:

在所示的示例中,我们的项目将被命名为elk-containers,并将在同名的目录中初始化。现在您已经初始化了您的项目,您会发现 Ansible Container 文件被创建在一个名为ansible的目录中。您的项目的目录结构将如下所示:
ansible/
container.yml
main.yml
meta.yml
requirements.txt
requirements.yml
ansible.cfg
在这里创建的文件是提供一个起点的骨架文件。如果您检查两个最重要的文件container.yml和main.yml,它们将看起来像这样:
container.yml
version: "1"
services:
# Add your containers here, specifying the base image you want to build from
# For example:
#
# web:
# image: ubuntu:trusty
# ports:
# - "80:80"
# command: ['/usr/bin/dumb-init', '/usr/sbin/apache2ctl', '-D', 'FOREGROUND']
# dev_overrides:
# environment:
# - "DEBUG=1"
#
registries: {}
# Add optional registries used for deployment. For example:
# google:
# url: https://gcr.io
# namespace: my-cool-project-xxxxxx
main.yml
# This should be your Ansible playbooks to provision your containers.
# An inventory will be automatically created using the names of the services
# from your container.yml file.
# Add any roles or other modules you'll need to this directory too.
# For many examples of roles, check out Ansible Galaxy: https://galaxy.ansible.com/
#
---
- hosts: all
gather_facts: false
步骤 2
现在,我们可以手动配置我们的容器和/或利用 Ansible Galaxy 上托管的许多预打包的 Ansible Container 配置。在这里的示例中,我们将从 Ansible Galaxy 拉取并使用三种不同的配置。我们的示例项目将部署三个容器,这些容器将共同运行 ELK 堆栈(Elasticsearch,Logstash 和 Kibana)。
注意
在执行以下命令之前,请确保您已安装了 Ansible Container 和所有先决软件。有关详细信息,请参阅 Ansible Container 安装说明:docs.ansible.com/ansible-container/installation.html。
处理此的命令在此处提到;确保在执行时您在项目目录的root目录中:
**$ cd elk-containers**
**$ ansible-container install chouseknecht.kibana-container**
**$ ansible-container install chouseknecht.elasticsearch-container**
**$ ansible-container install chouseknecht.logstash-container**
命令的输出将类似于这样:

下载基础镜像后,Ansible Container 将其加载到一个虚拟容器中,其中包含所有可能的镜像依赖项,以准备构建它。

步骤 3
接下来,我们将回顾之前的ansible-container安装命令对我们的项目做了什么。如果我们现在查看我们的container.yml和main.yml文件,我们会注意到部署 ELK 堆栈到容器所需的所有自动化代码都在那里。让我们看看这些文件的变化:
container.yml
version: '1'
services:
kibana:
image: centos:7
ports:
- 5601:5601
user: kibana
links:
- elasticsearch
working_dir: /opt/kibana/bin
command: [./kibana]
elasticsearch:
image: centos:7
ports:
- 9200:9200
expose:
- 9300
restart: always
user: elasticsearch
working_dir: /usr/share/elasticsearch/bin
command: [./elasticsearch]
logstash:
image: centos:7
ports:
- 5044:5044
links:
- elasticsearch
restart: always
working_dir: /opt/logstash/bin
command: [./logstash, agent, -f, /etc/logstash/conf.d]
environment:
- JAVACMD=/usr/bin/java
# volumes:
# - your_configuration_volume:/etc/logstash/conf.d
# Add your containers here, specifying the base image you want to build from
# For example:
#
# web:
# image: ubuntu:trusty
# ports:
# - "80:80"
# command: ['/usr/bin/dumb-init', '/usr/sbin/apache2ctl', '-D', 'FOREGROUND']
# dev_overrides:
# environment:
# - "DEBUG=1"
#
registries: {}
# Add optional registries used for deployment. For example:
# google:
# url: https://gcr.io
# namespace: my-cool-project-xxxxxx
main.yml
- hosts: all
gather_facts: false
- hosts: kibana
roles:
- role: chouseknecht.kibana-container
kibana_host: 0.0.0.0
kibana_port: 5601
kibana_elasticsearch_url: http://elasticsearch:9200
kibana_index: .kibana
kibana_log_dest: stdout
kibana_logging_silent: false
kibana_logging_quiet: false
kibana_logging_verbose: true
- hosts: elasticsearch
roles:
- role: chouseknecht.elasticsearch-container
elasticsearch_network_host: 0.0.0.0
elasticsearch_http_port: 9200
elasticsearch_script_inline: true
elasticsearch_script_indexed: true
elasticsearch_data: /usr/share/elasticsearch/data
elasticsearch_logs: /usr/share/elasticsearch/logs
elasticsearch_config: /usr/share/elasticsearch/config
java_home: ''
- hosts: logstash
roles:
- role: chouseknecht.logstash-container
logstash_elasticsearch_hosts:
- http://elasticsearch:9200
logstash_listen_port_beats: 5044
logstash_local_syslog_path: /var/log/syslog
logstash_monitor_local_syslog: true
logstash_ssl_dir: /etc/pki/logstash
logstash_ssl_certificate_file: ''
logstash_ssl_key_file: ''
logstash_enabled_on_boot: yes
logstash_install_plugins:
- logstash-input-beats
现在我们需要检查的另一个文件是requirements.yml文件。由于我们使用预打包的配置,这些配置的链接将被添加到此文件中:
requirements.yml
- src: chouseknecht.kibana-container
- src: chouseknecht.elasticsearch-container
- src: geerlingguy.java
- src: chouseknecht.logstash-container
在这一点上,如果您需要调整变量、特定应用程序更改或添加额外的编排步骤,您可以选择对文件进行更改。最好的是,您也可以选择不进行任何更改。您可以构建和运行这个容器项目就像它是的那样。
步骤 4
在我们的最后一步中,我们将采用我们设计的内容,执行 Ansible Container 构建过程,并最终在本地部署这些容器。同样,对于我们的示例,我们不需要对容器设计文件进行任何更改。
构建过程非常强大,因为所有容器依赖关系和编排将被实现以创建容器映像。当您希望部署容器时,将使用这些映像。以下是用于构建我们的容器的命令:
**$ ansible-container build**
命令的输出片段将类似于这样:

最后,我们准备测试我们全新的容器。正如容器化世界所说,“只需发布它!”。使用 Ansible Container 在本地部署容器映像以测试它们是另一个非常合理的功能。您将使用ansible-container run命令将容器部署到您本地配置的 Docker Engine 安装中:
**$ ansible-container run -d**
运行后,命令的输出将类似于这样,我们可以通过执行docker ps命令来确认我们的容器部署:

正如你所看到的,我们在本地运行了三个容器,做得很好。我们成功了!我们的第一个容器已经设计、配置、构建和部署(在不到一个小时的时间内)。在我们继续之前,我们应该暂停或移除我们的容器。请使用以下命令来停止或移除您的容器:
**$ docker stop <container ID>**
**$ docker rm <container ID>**
在 OpenStack 上部署 Kubernetes
在撰写本文时,Kubernetes 已成为容器编排的市场选择,成为顶级 GitHub 项目之一,并成为管理容器的领先企业选择。Kubernetes 的一些高级功能包括能够执行滚动升级、零停机部署、管理大规模复杂工作负载以及开箱即用的高可用/容错。如果您希望在生产环境中管理容器集群,您绝对应该尝试一下 Kubernetes。
在这种情况下,经常出现的问题是,为什么我要在 OpenStack 等平台上运行 Kubernetes?许多人经常忘记 OpenStack 是一个虚拟化管理器,而不是虚拟化本身。OpenStack 使操作员能够管理许多不同类型的虚拟化管理器,容器编排软件只是另一种虚拟化管理器。在 OpenStack 中,您可以选择几种方式来管理和部署 Kubernetes 集群。可以通过 Magnum 来完成,这是 OpenStack 中的容器管理项目。另一种方法是使用 Heat 模板来将 Kubernetes 集群作为堆栈进行管理。最后,您可以使用一个名为 kargo 的 GitHub 项目,它允许您在许多不同的系统和云平台上使用 Ansible 部署 Kubernetes。
对于我们的示例,我们将涵盖最后一个选项,并使用 kargo 在我们的 OpenStack 云上部署 Kubernetes。尝试通过创建我们自己的 Ansible playbooks/roles 来部署 Kubernetes 似乎不是一个很好的时间利用。kargo 项目可以在以下网址找到:github.com/kubernetes-incubator/kargo。存储库中有说明,将指导您如何设置以运行设置 playbook。
注意
请记住,kargo 是一个开源项目,就像其他开源项目一样,它可能会发生变化。变化可能包括重新组织存储库布局、更改部署说明甚至废弃。在撰写本文时,该项目仍在运行。
OpenStack 特定的说明可以在这里找到:github.com/kubernetes-incubator/kargo/blob/master/docs/openstack.md。要开始,您需要将 kargo 存储库克隆到 OpenStack 云上的 Utility 容器中:
**$ git clone https://github.com/kubernetes-incubator/kargo.git**
**$ cd kargo**
自动化考虑
在大多数情况下,安装将顺利进行。我确实不得不调整两个小细节,以确保 playbooks 成功完成。第一个调整是在我的 OpenRC 文件中。正如您在说明中所注意到的,第二步是在运行设置 playbook 之前源化您的 OpenRC 文件。我的文件缺少 playbook 检查的两个参数;它是OS_TENANT_ID和OS_REGION_NAME参数。我的 OpenRC 文件的工作示例如下:
# Ansible managed: /etc/ansible/roles/openstack_openrc/templates/openrc.j2
export LC_ALL=C
# COMMON CINDER ENVS
export CINDER_ENDPOINT_TYPE=publicURL
# COMMON NOVA ENVS
export NOVA_ENDPOINT_TYPE=publicURL
# COMMON OPENSTACK ENVS
export OS_ENDPOINT_TYPE=publicURL
export OS_USERNAME=admin
export OS_PASSWORD=passwd
export OS_PROJECT_NAME=admin
export OS_TENANT_NAME=admin
**export OS_TENANT_ID=bcf04d870b4c469cb1728e71ef9a6422**
export OS_AUTH_URL=https://192.168.0.249:5000/v3
export OS_NO_CACHE=1
export OS_USER_DOMAIN_NAME=Default
export OS_PROJECT_DOMAIN_NAME=Default
export OS_INTERFACE=publicURL
**export OS_REGION_NAME=RegionOne**
# For openstackclient
export OS_IDENTITY_API_VERSION=3
export OS_AUTH_VERSION=3
我不得不做的另一个调整是调整如何拉取特定的 Kubernetes 依赖软件容器。容器存储库标签已更改,而 kargo 项目尚未更新。在项目中对roles/download/defaults/main.yml文件执行更新。原始文件的片段如下:
...
exechealthz_version: 1.1
exechealthz_image_repo: "gcr.io/google_containers/exechealthz-amd64"
exechealthz_image_tag: "{{ exechealthz_version }}"
hyperkube_image_repo: "quay.io/coreos/hyperkube"
**hyperkube_image_tag: "{{ kube_version }}_coreos.0"**
需要更改的文件如下所示:
...
exechealthz_version: 1.1
exechealthz_image_repo: "gcr.io/google_containers/exechealthz-amd64"
exechealthz_image_tag: "{{ exechealthz_version }}"
hyperkube_image_repo: "quay.io/coreos/hyperkube"
**hyperkube_image_tag: "v{{ kube_version }}_coreos.0"**
有了这两个更改,您所需要做的就是启动实例,作为 Kubernetes 主节点、etcd 和节点。实例可以是您希望的任何基于 Linux 的操作系统。您布置 Kubernetes 集群的方式取决于环境类型和最终用例。稳定的 Kubernetes 集群的参考架构是将两个实例作为主节点,三个实例作为 etcd,并利用 Ironic 来部署至少三个裸金属服务器作为节点。当然,出于测试目的,您可以将整个集群部署为 OpenStack 云上的实例。
下一步是配置您的清单文件,以包括您启动的实例,以充当您的 Kubernetes 集群。我的清单文件名为os-inventory。清单文件的工作示例如下:
[kube-master]
kubes-1
kubes-2
[etcd]
kubes-3
kubes-4
[kube-node]
kubes-5
kubes-6
kubes-7
[k8s-cluster:children]
kube-node
kube-master
etcd
信不信由你,您现在已经准备好运行设置 playbook 来部署您的 Kubernetes 集群了。要这样做的命令如下,请确保您在 kargo 存储库的root目录中:
**$ ansible-playbook -i inventory/os-inventory -b cluster.yml**
安装将运行一段时间,但最终您将拥有一个可用的 Kubernetes 集群进行实验。现在我们将转向另一种容器编排技术,并尝试如何使用 Ansible 来管理容器,同时利用 OpenStack。
使用 Ansible 管理 CoreOS 和 Docker
CoreOS 似乎是另一个很好的选择,可以在 OpenStack 上运行,因为它是:
一种专为集群部署设计的轻量级 Linux 操作系统,为您最关键的应用程序提供自动化、安全性和可伸缩性 – (
coreos.com/why/#cluster)
CoreOS 的重点是提供一个默认情况下具有集群意识的操作系统,使其非常适合容器技术等平台。Docker 也是一个明显的选择,用于实验容器,因为它是使容器再次流行的原因。此外,Docker 有各种各样的镜像可以随时拉取和部署。在我们的示例中,我们将审查一个非常简单的 playbook,它将在 CoreOS 上的容器中部署 ELK 堆栈。
自动化考虑
这个过程的第一步是启动至少三个具有至少 2GB 内存和稳定 CoreOS 镜像的 flavor 的实例。由于我喜欢使用 Heat 来做这样的事情,我使用了一个 Heat 模板来启动我的实例。我创建的模板可以在这里找到:github.com/wbentley15/openstack-heat-templates/tree/master/coreos。然后使用 Heat 部署堆栈的命令如下:
**$ heat stack-create coreos --template-file=heat-coreos-prod.yaml --
parameters="key-name=my-key;user-data=cloud-config-prod.yaml;
network=24b9b982-b847-4d0e-9088-61acbf92a37f"**

编写 playbooks 和角色
一旦您的 CoreOS 堆栈在线,您可以执行我们将要创建的 playbook。在本例中,所有任务将在名为base.yml的 playbook 中,该文件位于playbook目录的root目录中。该文件的初始内容将如下所示:
---
# This playbook deploys the ELK stack on CoreOS
- name: Bootstrap CoreOS
hosts: coreos
gather_facts: False
roles:
- defunctzombie.coreos-bootstrap
playbook 中的第一个任务对于运行诸如 Ansible 之类的软件包对目标 CoreOS 实例至关重要。由于 CoreOS 是一个最小的操作系统,它没有安装任何版本的 Python。我们知道,运行 Ansible 对主机的一个主要先决条件是安装 Python。为了规避这个限制,我们将使用一个名为defunctzombie.coreos-bootstrap的角色,在我们的 CoreOS 实例上安装pypy。我们将在稍后学习如何告诉 Ansible 在这些节点上找到我们的 Python 解释器。
您可以通过执行以下命令从 Galaxy 上拉取此角色:
**$ ansible-galaxy install defunctzombie.coreos-bootstrap**
接下来的两个任务将在 CoreOS 实例上设置环境,以便将 Docker 镜像作为容器运行。请注意,我们将固定docker-py和docker-compose软件包的版本;这是由于docker_image和docker_container模块的已知错误。一旦错误得到解决,这种依赖关系可以被移除,或者随着时间的推移,这些版本可能需要进行调整:
- name: Deploy ELK Stack
hosts: coreos
remote_user: core
become: false
tasks:
- name: Start etcd
service: name=etcd.service state=started
become: true
- name: Install docker-py
shell: /home/core/bin/pip install docker-py==1.9.0 docker-compose==1.8.0
最后剩下的任务将处理拉取 ELK 堆栈的 Docker 镜像,然后在您的 CoreOS 集群上启动这些容器:
- name: Pull Elasticsearch container
docker_image: name=elasticsearch
- name: Pull Kibana container
docker_image: name=kibana
- name: Pull Logstash container
docker_image: name=logstash
- name: Launch Elasticsearch container
docker_container:
name: elasticsearch-cont
image: elasticsearch
state: started
- name: Launch Kibana container
docker_container:
name: kibana-cont
image: kibana
state: started
- name: Launch Logstash container
docker_container:
name: logstash-cont
image: logstash
state: started
Docker 镜像是从hub.docker.com上的存储库中拉取下来,然后部署在托管 Docker 的 CoreOS 实例上。
我们这个例子的hosts文件又有点独特,因为我们不得不为 CoreOS 安装自定义的 Python 解释器。我们需要配置 Ansible 来使用这个替代的 Python 解释器。在下面的工作示例中,您会发现我们配置了 Ansible 来使用位于/home/core/bin/python的 Python 解释器和位于/home/core/bin/pip的 pip 软件包:
[coreos]
162.209.96.54
[coreos:vars]
ansible_ssh_user=core
ansible_python_interpreter=/home/core/bin/python
ansible_pip_interpreter=/home/core/bin/pip
在本章的后面,我们将通过再次审查这些 playbooks 和角色来结束,然后进行测试,以查看最终的结果。
部署 Nova LXD 在 OpenStack
最后,但肯定不是最不重要的,我们将以真正开始一切的容器选项 LXC 或者它的新的大哥 LXD 来结束本章。LXD 被描述为:
LXC 的容器“hypervisor”和一个新的用户体验 – (
www.ubuntu.com/cloud/lxd)
LXD 的三个主要组件是其系统范围的守护程序(lxd)、命令行客户端(lxc)和 OpenStack Nova 插件。正是这个插件使我们能够在 OpenStack 控制下将 LXD 作为 hypervisor 运行,并使用传统的 OpenStack 命令来启动容器。有了这样的东西,您可以在相同的控制平面下在单独的计算节点上运行实例和容器。LXD 进一步被描述为安全设计、可扩展、直观、基于镜像和具有执行实时迁移的能力。
幸运的是,Ansible 之神已经听到并回答了我们的祈祷。在openstack-ansible 项目(OSA)的 Newton 版本(以及以后),您现在可以将 LXD 作为 KVM 的替代 hypervisor 部署。现在只需在部署 OSA 云之前编辑两个配置文件即可。我们将概述这些更改,并演示如何使用 OpenStack 启动您的第一个 LXD 容器。
在开始之前,您应该知道在 OSA 上启用 LXD 的详细说明可以在这里找到:docs.openstack.org/developer/openstack-ansible-os_nova/。
自动化考虑
OSA 部署围绕部署节点上/etc/openstack_deploy目录中的三个主要配置文件展开。您需要编辑user_variables.yml和user_secrets.yml文件。从user_variables.yml文件开始,您需要将nova_virt_type变量设置为使用 LXD。一个工作示例如下:
# This defaults to KVM, if you are deploying on a host that is not KVM capable
# change this to your hypervisor type: IE "qemu", "lxc".
**nova_virt_type: lxd**
需要编辑的第二个文件是user_secrets.yml文件。您只需要为 LXD 信任提供密码。需要编辑的行的示例如下:
# LXD Options for nova compute
lxd_trust_password:
提示
如果您计划设置混合计算节点农场并希望拥有 KVM 和 LXD 主机。您需要编辑openstack_user_config.yml文件,并为每个主机设置nova_virt_type。如何配置的工作示例可以在前面的文档链接中找到。
现在您可以开始安装 OSA,知道您将能够启动 LXD 容器以及在 KVM 上运行的实例。安装完成后,您还需要完成最后一步。我们现在必须创建一个 LXD 兼容的镜像,该镜像将在您启动容器时使用。LXD 需要使用原始镜像,因此我们将下载符合这些要求的镜像。在 OSA 云的实用程序容器中,执行以下命令:
**$ wget http://cloud-images.ubuntu.com/trusty/current/
trusty-server-cloudimg-amd64-root.tar.gz**
**$ glance image-create --name=trusty-LXD --visibility=public --container-
format=bare --disk-format=raw
--file=trusty-server-cloudimg-amd64-root.tar.gz**

有了您的新镜像,现在您已经准备好启动您的第一个 LXD 容器了。LXD 容器的管理方式与在 KVM 上运行的实例类似。您可以通过 Horizon 仪表板或通过 OpenStack Client CLI 创建容器。在本例中,我们将使用 OpenStack Client 来创建容器。以下命令将创建您的容器:
**$ nova boot --image=<image name> --flavor=<flavor> --nic net-id=<network ID> --security-group=<security group> --min-count <number of containers> <container name>**
**$ nova boot --image=trusty-LXD --flavor=m1.small --nic net-id=eb283939-2c65-4ecb-9d9f-cbbea9bf252c --security-group default --min-count 3 first-lxd-container**
如果您的输出看起来与此类似,您可以将其视为成功:

然后,您可以执行openstack server list命令来验证您的新容器是否正在运行。

非常棒,我的朋友,你又做得很好!我知道我们涵盖了很多内容,但是你现在已经是一个老手了,所以不用担心。保持我们的传统,我们将以快速回顾我们所涵盖的内容以及下一章的预期结束本章。
审查 playbooks 和 roles
让我们直接开始检查我们之前创建的主 playbook,以部署名为ansible-coreos的 CoreOS 上的 Docker 容器。位于ansible-coreos目录的根目录中的已完成的 playbook 和文件名为base.yml,看起来是这样的:
---
# This playbook deploys the ELK stack on CoreOS
- name: Bootstrap CoreOS
hosts: coreos
gather_facts: False
roles:
- defunctzombie.coreos-bootstrap
- name: Deploy ELK Stack
hosts: coreos
remote_user: core
become: false
tasks:
- name: Start etcd
service: name=etcd.service state=started
become: true
- name: Install docker-py
shell: /home/core/bin/pip install docker-py==1.9.0 docker-compose==1.8.0
- name: Pull Elasticsearch container
docker_image: name=elasticsearch
- name: Pull Kibana container
docker_image: name=kibana
- name: Pull Logstash container
docker_image: name=logstash
- name: Launch Elasticsearch container
docker_container:
name: elasticsearch-cont
image: elasticsearch
state: started
- name: Launch Kibana container
docker_container:
name: kibana-cont
image: kibana
state: started
- name: Launch Logstash container
docker_container:
name: logstash-cont
image: logstash
state: started
我们从 Galaxy 中拉下来的相应角色位于ansible-coreos/roles/defunctzombie.coreos-bootstrap/tasks目录中,看起来是这样的:
- name: Check if bootstrap is needed
raw: stat $HOME/.bootstrapped
register: need_bootstrap
ignore_errors: True
- name: Run bootstrap.sh
script: bootstrap.sh
when: need_bootstrap | failed
- name: Check if we need to install pip
shell: "{{ansible_python_interpreter}} -m pip --version"
register: need_pip
ignore_errors: True
changed_when: false
when: need_bootstrap | failed
- name: Copy get-pip.py
copy: src=get-pip.py dest=~/get-pip.py
when: need_pip | failed
- name: Install pip
shell: "{{ansible_python_interpreter}} ~/get-pip.py"
when: need_pip | failed
- name: Remove get-pip.py
file: path=~/get-pip.py state=absent
when: need_pip | failed
- name: Install pip launcher
copy: src=runner dest=~/bin/pip mode=0755
when: need_pip | failed
最后,我们创建了hosts文件,也位于playbook目录的root目录中:
[coreos]
162.209.96.54
[coreos:vars]
ansible_ssh_user=core
ansible_python_interpreter=/home/core/bin/python
ansible_pip_interpreter=/home/core/bin/pip
注意
完整的代码集可以再次在以下 GitHub 存储库中找到:github.com/os-admin-with-ansible/os-admin-with-ansible-v2。
我们终于准备好尝试这个 playbook 了。假设您已经克隆了之前的 GitHub 存储库,则从部署节点测试 playbook 的命令如下:
**$ cd os-admin-with-ansible-v2**
**$ cd ansible-coreos**
**$ ansible-playbook -i hosts base.yml**
假设一切顺利,输出应该类似于以下截图中的片段:
通常,我还喜欢采取额外的步骤,通过在 CoreOS 实例上执行docker ps命令来验证容器是否正在运行。

摘要
穿过终点线确实感觉不错。我希望容器的原始力量能激励您开始在 OpenStack 云上部署它们。在传统虚拟机和实例之外拥有选择总是让人感觉良好。
在结束本章之前,让我们花一点时间回顾本章。我们从探索容器化的概念以及为什么它变得如此流行开始本章。您学会了如何使用 Ansible Container 来创建我们的第一个容器镜像和构建。我们审查了 kargo 项目,该项目使您能够使用 Ansible 在多个云平台上部署 Kubernetes,包括 OpenStack。接下来,我们演示了如何使用 Ansible 来管理在 OpenStack 上运行 Docker 集群的 CoreOS。最后,我们审查了部署 LXD 所需的配置更改与openstack-ansible项目。
下一章也将是非常有趣的一章,因为作为云运营商,您最终将不得不考虑扩展/缩小您的云足迹。OpenStack 具有有用的内置功能,使得扩展的过程相当容易和简单。在下一章中,我们将介绍设置活跃-活跃云区域的概念,然后通过自动化这项任务来减轻在需要时进行扩展的压力。如果您准备好迎接一些未知的领域,请前往第八章设置活跃-活跃区域!
第八章:设置 Active-Active 区域
在本章中,我们将重点介绍 OpenStack 的一个非常有用的内置功能。这将是能够集中管理多个可能在不同地理位置运行的 OpenStack 区域的能力。OpenStack 中的区域概念并不新鲜,但请问你是否曾经真正见过它的实现。在许多场合,我发现自己对完成这些步骤感到不清楚。今天是你将对这个问题有一个积极回答的日子。
稳定性和可用性目前是 OpenStack 社区中热门话题,我认为分享一个可行的用例来实现云高可用性是很好的。这将是云操作员可以设置的许多方式之一。正如我们可能已经知道的,OpenStack 可以满足许多高可用性要求。我们将简要回顾这些场景,然后转向为什么要使用这个功能。与之前的所有章节一样,我们将通过演示如何使用 Ansible 自动设置 Active-Active 云区域来完成本章。本章将涵盖以下主题:
-
回顾 OpenStack 高可用性场景
-
为什么要使用 Active-Active 云区域?
-
设置 Active-Active 云区域
-
创建和设置管理员区域
-
配置活动区域的身份验证
-
编写 playbooks 和 roles
-
回顾 playbook 和 roles
回顾 OpenStack 高可用性场景
这个话题恰好是我总是喜欢讨论的话题之一。高可用性(HA)和灾难恢复总是因为明显的原因在 IT 人员中引起非常情绪化的对话。可以说,你的命运掌握在手中,确保你的组织系统在灾难/故障发生时保持在线。在过去,本地系统、HA 和冷(未使用)灾难恢复站点已经足够了。云的当前灵活性现在为系统稳定性提供了新的更好的选择。不要满足于旧的解决方案。你有选择!
如前所述,有多种方法可以实现 OpenStack 的 HA。我们将概述三种可能成功的场景,并满足大多数组织的 HA 要求。以下是三种可能的场景,附加了图表以提供更多上下文:
-
多个数据中心:多个 OpenStack 区域跨越多个地理位置的数据中心
-
单数据中心:一个数据中心内有多个 OpenStack 区域
-
可用区:在一个数据中心内的单个 OpenStack 区域中使用成对的可用区

多个数据中心
我们将从三种情景中最复杂的情景开始。这种情景包括在多个数据中心部署多组 OpenStack 区域,并使它们作为一个云系统运行的概念。虽然这听起来复杂,但实际上并不像听起来那么困难。当将它们全部绑定在一起时,以及当您去支持/管理它们时,复杂性就会出现。这种模式不仅为您提供了跨数据中心的 HA(多个 Active-Active 区域),而且还在每个数据中心内提供了 HA(独立的 Active-Active 区域)。您必须经历多层故障才能使您的云下线。

单数据中心
与前面的情景类似,主要区别在于,它只限于单个数据中心。在这种情况下,您可以部署一组仅限于一个数据中心的 OpenStack Active-Active 区域。这种模型只会在运行区域的数据中心内提供高可用性。如果该数据中心发生火灾,您的云将彻底倒霉(SOL)。
如果没有其他选择,这种模型仍然可以避免完全的云故障。

可用区域
这种情景可能是最简单的选择,但肯定可以在提供客户级别的高可用性方面发挥作用。是的,如果您希望获得真正的灾难恢复设计,这种模型就不够了。通过利用多个 AZ,您可以使用反亲和性过滤器将实例分布在不同的计算节点上,从而提供客户级别的高可用性。
现在,让我们专注于我们之前描述的多数据中心模型的简化版本。我们将回顾为什么您可能有兴趣使用 Active-Active 区域方法。

为什么要使用 Active-Active 云区域?
除了能够积极使用多个 OpenStack 区域的纯粹令人敬畏之外,Active-Active 云区域方法还能最大限度地利用您的整体云投资。不再需要因为第二个站点不经常使用而进行灾难恢复测试。此外,您还获得了集中管理区域的额外好处。处处都是双赢的局面。
因此,让我们深入了解架构,以提供一个 OpenStack Active-Active 区域。以下图表以最简单的形式解释了架构:

上述架构的组件是:
-
两个独立的 OpenStack 云部署,也就是两个区域。在这个例子中,我们有A 区和B 区。这些区域运行核心 OpenStack 服务,除了 Keystone 和 Horizon。每个区域可以有任意数量的互补 AZ。
-
创建另一个专门用于托管 Keystone 和 Horizon 服务的 OpenStack 区域。这个区域可以被归类为 Admin 区域。
-
然后,A 区和 B 区将利用 Admin 区域来处理身份验证和 GUI Web 界面,通过集中用户、租户和项目管理/创建,并提供一个单一的 Web 仪表板来管理所有活动区域。
设置 Active-Active 云区域
实施这一过程相对简单,但确实需要特别注意细节。提前概述步骤非常有用,可以避免遗漏步骤。我还了解到,手动执行更改通常也不会有好结果。编辑服务配置文件的过程会导致错误修改,导致服务无法启动。不好!!!更不用说这会使实施过程时间变长三倍。首先,我们将手动回顾步骤,然后在接下来的部分,我们将学习如何尽可能自动化设置过程。我只能说感谢 Ansible!
在这一部分,我们将回顾设置 Active-Active OpenStack 云区域的手动步骤。以下是步骤的简要概述:
-
记录每个区域的端点并注意 URL。
-
在 Admin 区域创建服务用户帐户。
-
在 Admin 区域创建服务。
-
将每个区域的端点注册到 Admin 区域。
-
调整 Admin 区域的身份端点。
-
配置每个区域的服务,以便对 Admin 区域的身份验证服务进行身份验证,而不是本地区域的身份验证服务。
现在,让我们逐步通过这里显示的每个配置步骤,演示工作配置示例。
区域端点清单
这一步将是简单地查询您想要包括在主-主设置中的每个区域的端点。由于我们使用openstack-ansible(OSA)来部署我们的 OpenStack 云,您需要连接到每个区域的实用程序容器,以便使用 OpenStack CLI。一旦连接并源化 OpenRC 文件,命令将是:
**$ openstack endpoint list**
这个命令的输出应该类似于这样:

请记住,我们这里的重点是要注意可用的公共端点。
由于 openstack-ansible 将 OpenStack 服务安装到 LXC 容器中,您需要知道如何连接到每个容器以使用 CLI 并配置/维护服务。列出在控制平面服务器上运行的所有容器的 LXC 命令是lxc-ls -fancy,输出将类似于以下内容:

管理区域配置
接下来的步骤将涉及定制管理区域的安装和配置。这将是您的集中管理区域,只为身份验证请求提供服务。管理区域可以存在于与其他区域相同的数据中心,也可以存在于与其他区域完全不同的区域。显然,数据中心之间需要网络连接。请按照稍后给出的说明进行操作。
在管理区域创建服务用户帐户
在这一点上,您应该有一个仅运行身份服务(Keystone)和 Web 仪表板(Horizon)的运行中的管理区域。只有这两个服务应该存在并处于活动状态。由于我们希望使用管理区域来管理其他区域,您必须让它了解其他区域的服务和端点。这个过程从在管理区域上创建服务用户帐户开始:
- 对于这一步,我们将使用以下命令使用 CLI 创建服务用户帐户:
**$ openstack user create
--project <project reserved for services>
--password <user password> <user name>**
这个命令的一个工作示例看起来像这样:
**$ openstack user create --project service
--password passwd glance**
- 现在我们必须为刚刚创建的新用户分配一个具有适当权限的角色。完成此操作的 CLI 命令在这里:
**$ openstack role add --user <user name>
--project <project reserved for services> <role>**
这个命令的一个工作示例看起来像这样:
**openstack role add --user glance
--project service admin**
现在我们已经创建了服务用户帐户,我们可以过渡到在管理区域上注册新服务的下一步。
在管理区域创建服务
在这一步中,我们只是在管理区域为活动区域上运行的服务创建占位符。请记住,活动区域上运行其他核心服务,管理区域将为它们处理身份验证。然后管理区域必须知道这些服务。
使用以下命令在管理区域上注册服务:
**$ openstack service create --name <service name>
--description "<service description>" <service type>**
这个命令的一个工作示例看起来像这样:
**openstack service create --name glance
--description "Glance Image Service" image**
下一步将是在管理区域注册活动区域的端点。这一步需要一定的精度,因为端点 URL 是管理区域用来进行功能调用的。如果 URL 不正确或输入错误,服务将被管理区域视为已关闭。
将每个区域的端点注册到管理区域
注册活动区域端点的过程涉及使用我们之前开始的端点清单。这里的关键点是,您必须使用每个区域公共端点的 IP 地址。分配给公共端点的 IP 地址需要是公共 IP 地址(可通过互联网访问)或在每个数据中心之间可访问的内部 IP 地址。再次强调,管理区域将使用此 URL 进行服务调用,因此端点必须是可访问的。
您需要注册两种类型的端点:公共和内部。我在设置过程中发现了这个关键组件。一些 OpenStack 服务仅利用内部端点,而其他服务将使用公共端点。为了避免任何问题,我们将注册两者。从技术上讲,注册两者没有任何风险,这是一个好的做法。
注册服务的命令示例如下:
**$ openstack endpoint create --region <region name>
<service name> <service type> <endpoint url>**
一组命令的工作示例如下:
**$ openstack endpoint create --region alpha glance
internal
http://127.0.0.1:9292**
**$ openstack endpoint create --region alpha glance
public
http://127.0.0.1:9292**
前面的步骤需要为您希望加入 Admin 区域的每个活动区域重复执行。如前面的示例所示,我们将为Region A和Region B执行此步骤。
调整 Admin 区域的身份端点
设置 Admin 区域的最后一步是确保活动区域可以成功连接到那里运行的身份服务。之前分享的关于必须公开服务公共端点的原则在这里同样适用于 Keystone。每个云设置可能略有不同,因此并非所有云都需要此步骤。
为了评估是否需要进行此调整,请执行以下命令,并确定公共和管理员端点是否为 URL 配置了本地 IP 地址:
**$ openstack endpoint list --service identity**
如果输出看起来类似于这样,您必须在创建新的公共 IP 或数据中心之间可访问的 IP 地址后禁用公共和管理员端点。有关如何处理此问题的更多详细信息将在此处分享:

为了创建新的公共和管理员端点,然后禁用当前的端点,您将执行以下命令:
**# Add public Keystone endpoint**
**$ openstack endpoint create --region <region name>
keystone public <endpoint url>**
**# Add an additional admin Keystone endpoint**
**$ openstack endpoint create --region <region name>
keystone admin <endpoint url>**
**# Disable the original public Keystone endpoint
with the local IP address
configured (URL will have a non-routable address)**
**$ openstack endpoint set --disable <endpoint-id>**
**# Disable the original admin Keystone endpoint with
the local IP address configured
(URL will have a non-routable address)**
**$ openstack endpoint set --disable <endpoint-id>**
完成后,执行openstack endpoint list --service identity命令,输出应该类似于这样:

活动区域配置
本节将包括设置活动区域的步骤,这些区域将成为您的 Active-Active 云设计的一部分。这些区域运行核心 OpenStack 服务。在这一点上,我们已经设置了 Admin 区域,以便与这些活动区域进行通信。现在,我们必须配置核心服务,通过 Admin 区域进行身份验证,而不是使用本地身份服务(Keystone)。
在部署本地身份服务之前,您无法部署 OpenStack 云。身份服务必须是第一个安装的服务,因此将存在于活动区域。要使服务不使用本地身份服务,您必须重新配置每个服务。仅仅禁用本地身份服务是不够的。重新配置每个核心服务的过程包括编辑配置文件。如前所述,编辑服务配置文件会留下错误编辑的可能性,这可能导致该服务无法启动。
这就是您必须更聪明而不是更努力的地方。问问自己:有没有工具可以帮助完成这样的任务?是的,答案再次是 Ansible!Ansible 可以帮助大大减少打字错误,从而进行许多服务配置更改。在第二章中,介绍 Ansible,我们简要讨论了 Ansible 的临时命令。临时命令允许直接运行模块命令,而无需将任务包装成 playbook 或 role。
临时命令的基本示例如下:
**$ ansible <host> -m <module> -a <module arguments>**
在我们的情况下,我们需要连接到运行在控制平面上的特定容器,并更改该服务的配置文件。这需要针对在该活动区域上运行的每个核心服务重复进行。好消息是,我们可以利用 openstack-ansible 部署的动态清单部分来简化整个过程。让我们使用以下示例作为示例,展示如何完成这个过程。
在这个例子中,我们将尝试对 Alpha 区域的镜像服务(Glance)进行所需的更改。所以,我们知道的是:
-
您必须连接到 Glance 容器
-
使用
sed命令,我们需要利用 shell Ansible 模块 -
我们准备了一个
sed命令,将更改glance-api.conf文件中的auth_url值
现在,命令参数的进一步细分将是:
host = glance_container
module = shell
adhoc command = sed -i 's+^auth_url = <current IP>:35357+auth_url = http://<alpha region IP>:35357+' /etc/glance/glance-api.conf
注意
为了利用openstack-ansible 安装的动态清单功能,您必须从部署节点(用于部署该区域的节点)执行这些命令。此外,您必须在/opt/openstack-ansible/playbooks目录中执行这些命令。
命令的一个工作示例将如下所示:
**$ ansible glance_container -m shell -a "sed -i
's+^auth_url = http://172.30.238.2:35357+auth_url =
http://166.78.18.131:35357+' /etc/glance/glance-api.conf"**
您可以使用前面的原则对活动区域上的所有服务进行所需的更改。确保记住在更改配置文件后重新启动服务。
**$ ansible nova_scheduler_container -m service -a
"name=nova-scheduler state=restarted" **
编写 playbooks 和 roles
在本节中,我们将创建 playbooks 和 roles 来设置管理区域。我们还将概述设置 Active-Active 云所需的 Ansible 临时命令的其他步骤。在为这类事情创建 Ansible 自动化代码时,我通常喜欢创建多个任务,分解成单独的角色。这种格式允许您能够重用与其他 playbooks 创建的角色。最终,我们将得到两个 playbooks 和两个角色,以自动化设置管理区域的步骤。最后,我们将总结使用这些角色的 playbooks。
在本节的另一半中,我们还将概述设置 Active-Active 云所需的 Ansible 临时命令。您可以收集命令以创建 playbooks 和 roles。我觉得这将是几百行不必要的代码,所以我选择了起草命令并使用搜索和替换。
设置管理区域
我们将创建的第一个角色将包括配置管理区域所需的任务。文件的名称将是main.yml,位于名为config-admin-region/tasks的角色目录中。该文件的内容将如下所示:
---
- name: Create users
os_user:
cloud: "{{CLOUD_NAME}}"
state: present
name: "{{ item.0 }}"
password: "{{ item.1 }}"
default_project: "{{ servicesproject }}"
domain: default
with_together:
- "{{userid}}"
- "{{passwdss}}"
- name: Assign user to specified role in designated environment
os_user_role:
cloud: "{{CLOUD_NAME}}"
user: "{{ item.0 }}"
role: "{{ urole }}"
project: "{{ servicesproject }}"
with_together:
- "{{userid}}"
- name: Register the new services on the Admin region
shell: openstack --os-cloud="{{ CLOUD_NAME }}"
service create --name "{{ item.0 }}" --description "{{ item.1 }}" "{{ servicetype }}"
with_together:
- "{{userid}}"
- "{{descrip}}"
第一个任务将在管理区域创建服务用户帐户。然后,第二个任务将分配管理员角色给刚刚创建的用户。最后一个任务将在活动区域创建服务的占位符。
接下来要创建的角色将处理在管理区域内注册每个区域端点的任务。与前一个角色一样,文件的名称将是main.yml,位于名为register-endpoints/tasks的角色目录中。该文件的内容将如下所示:
---
- name: Register the region service endpoints on the Admin region
shell: openstack --os-cloud="{{ CLOUD_NAME }}"
service endpoint create --region "{{ item.1 }}" "{{ item.0 }}" "{{ item.2 }}" "{{ item.3 }}"
with_together:
- "{{endpointname}}"
- "{{regionname}}"
- "{{endpointtype}}"
- "{{endpointurl}}"
该角色只有一个任务,即使用服务端点create to register的 CLI 命令来注册端点。在这种情况下,我们使用了with_together参数,以便可以循环遍历四个变量定义的参数。这样,您可以只需调整变量值就可以重新运行 playbook。在我们的情况下,我们需要运行这个 playbook 两次,一次用于内部端点,一次用于公共端点。
为了支持这些角色,我们现在需要创建与之配套的变量文件。对于这两个角色,我们将使用角色定义的变量文件来简化一些事情。变量文件将存储在role目录中,另一个名为vars的目录中。该目录中的文件将命名为main.yml。
与名为config-admin-region的角色对应的变量文件的内容如下:
---
userid: [ 'glance', 'nova', 'neutron', 'heat' ]
passwdss: [ 'passwd', 'passwd', 'passwd', 'passwd' ]
descrip: [ 'Glance Image Service', 'Nova Compute Service', 'Neutron Network Service', 'Heat Orchestration Service' ]
servicetype: [ 'image', 'compute', 'network', 'orchestration' ]
servicesproject: service
urole: admin
与名为register-endpoints的角色对应的第二个变量文件的内容如下:
---
endpointname: [ 'glance', 'nova', 'neutron', 'heat' ]
regionname: alpha
endpointtype: internal
endpointurl: [ 'http://<alpha region IP>:9292', 'http://<alpha region IP>:8774/v2.1/%\(tenant_id\)s', 'http://<alpha region IP>:9696', 'http://<alpha region IP>:8004/v1/%\(tenant_id\)s' ]
请记住,变量文件中定义的值旨在在每次执行正常日常使用之前更改。
让我们花点时间来分解变量及其预期用途。总结如下:
userid # name of the user to create
passwdss # passwords for the users being created
descript # description for the service being registered
servicetype # type of service being registered
servicesproject # name of the project where the services user accounts are associated
urole # name of the role to associate with the user
endpointname # service name of the endpoint being registered
regionname # name of the region
endpointtype # the type of endpoint being registered
endpointurl # the url of the endpoint
变量文件完成后,我们可以继续创建主剧本文件。为了演示,我决定将剧本文件分成两个单独的文件。这完全是我的选择,可以合并成一个文件而不会出现问题。我觉得有两个单独的主剧本会更容易在需要注册多组端点时重新运行。以下是剧本文件的列表:
config-admin.yml
config-admin-region
register-endpoints.yml
register-endpoints
剧本和角色的名称可以是您选择的任何内容。这里提供了具体的名称,以便您可以轻松跟踪并引用 GitHub 存储库中找到的完成代码。唯一的警告是,无论您决定如何命名角色,在剧本中引用时必须保持统一。
设置活动区域
这是我们将使用 Ansible 临时命令完成配置的地方。如前所述,我们将利用 openstack-ansible 部署模型的动态清单功能来实现这一目标。这些命令将重新配置 OpenStack 服务以使用 Admin 区域进行身份验证。以下是您需要执行的命令片段,以重新配置每个区域上的核心服务,成为 Active-Active 区域设置的一部分。完整的命令列表可以在os-admin-with-ansible/os-admin-with-ansible-v2 Github 存储库中的root目录中的名为configure-region-authentication.txt的文件中找到。
## Glance
ansible glance_container -m shell -a "sed -i 's+^auth_url = http://172.30.238.2:35357+auth_url = http://<admin region IP>:35357+' /etc/glance/glance-api.conf"
ansible glance_container -m shell -a "sed -i 's+^auth_url = http://172.30.238.2:35357+auth_url = http://<admin region IP>:35357+' /etc/glance/glance-registry.conf"
ansible glance_container -m shell -a "sed -i 's+^auth_url = http://172.30.238.2:5000/v3+auth_url = http://<admin region IP>:5000/v3+' /etc/glance/glance-cache.conf"
ansible glance_container -m shell -a "sed -i 's+^auth_uri = http://172.30.238.2:5000+auth_uri = http://<admin region IP>:5000+' /etc/glance/glance-api.conf"
ansible glance_container -m shell -a "sed -i 's+^auth_uri = http://172.30.238.2:5000+auth_uri = http://<admin region IP>:5000+' /etc/glance/glance-registry.conf"
ansible glance_container -m shell -a "service glance-api restart"
ansible glance_container -m shell -a "service glance-registry restart"
我发现最好和最有效的方法是搜索占位符<admin region IP>并将其替换为与 Admin 区域关联的公共 IP 或内部 IP。您可以使用任何文本编辑器进行操作,并且可以设置为针对任何区域执行的命令。
大家做得很好!您刚刚配置了具有多个活动区域的 OpenStack 云。与往常一样,为了保持我们的传统,我们将以快速回顾刚刚创建的剧本和角色结束本章。
审查剧本和角色
让我们立即开始检查我们创建的角色。
完成的角色和文件名为main.yml,位于config-admin-region/tasks目录中,如下所示:
---
- name: Create users
os_user:
cloud: "{{CLOUD_NAME}}"
state: present
name: "{{ item.0 }}"
password: "{{ item.1 }}"
default_project: "{{ servicesproject }}"
domain: default
with_together:
- "{{userid}}"
- "{{passwdss}}"
- name: Assign user to specified role in designated environment
os_user_role:
cloud: "{{CLOUD_NAME}}"
user: "{{ item.0 }}"
role: "{{ urole }}"
project: "{{ servicesproject }}"
with_together:
- "{{userid}}"
- name: Register the new services on the Admin region
shell: openstack --os-cloud="{{ CLOUD_NAME }}"
service create --name "{{ item.0 }}" --description "{{ item.1 }}" "{{ servicetype }}"
with_together:
- "{{userid}}"
- "{{descrip}}"
完成的角色和文件名为main.yml,位于register-endpoints/tasks目录中,如下所示:
---
- name: Register the region service endpoints on the Admin region
shell: openstack --os-cloud="{{ CLOUD_NAME }}"
service endpoint create --region "{{ item.1 }}" "{{ item.0 }}" "{{ item.2 }}" "{{ item.3 }}"
with_together:
- "{{endpointname}}"
- "{{regionname}}"
- "{{endpointtype}}"
- "{{endpointurl}}"
相应的角色本地变量文件都命名为main.yml,并保存在角色的vars目录中:
# variables for config-admin-region
---
userid: [ 'glance', 'nova', 'neutron', 'heat' ]
passwdss: [ 'passwd', 'passwd', 'passwd', 'passwd' ]
descrip: [ 'Glance Image Service', 'Nova Compute Service', 'Neutron Network Service', 'Heat Orchestration Service' ]
servicetype: [ 'image', 'compute', 'network', 'orchestration' ]
servicesproject: service
urole: admin
# variables for register-endpoints
---
endpointname: [ 'glance', 'nova', 'neutron', 'heat' ]
regionname: alpha
endpointtype: internal
endpointurl: [ 'http://<alpha region IP>:9292', 'http://<alpha region IP>:8774/v2.1/%\(tenant_id\)s', 'http://<alpha region IP>:9696', 'http://<alpha region IP>:8004/v1/%\(tenant_id\)s' ]
接下来,我们创建了以下主剧本文件;所有文件都位于playbook目录的root目录中:
config-admin.yml:
---
# This playbook used to demo OpenStack Juno user, role and project
features.
- hosts: util_container
remote_user: root
become: true
roles:
- config-admin-region
register-endpoints.yml:
---
# This playbook used to demo OpenStack Juno user, role and project
features.
- hosts: util_container
remote_user: root
become: true
roles:
- register-endpoints
最后,我们创建了hosts文件,它也位于playbook目录的root目录中:
[localhost]
localhost ansible_connection=local
[util_container]
172.29.236.224
注意
完整的代码集可以再次在 GitHub 存储库中找到github.com/os-admin-with-ansible/os-admin-with-ansible-v2。
现在是有趣的部分,是时候测试我们的新 playbooks 和角色了。您还需要执行之前描述的额外的临时命令,以完全测试此功能。假设您已经克隆了之前提到的 GitHub 存储库,从部署节点测试 playbook 的命令将如下所示:
**$ ansible-playbook -i hosts config-admin.yml**
**$ ansible-playbook -i hosts register-endpoints.yml**
接下来,您将执行configure-region-authentication.txt文件中的命令,该文件位于playbook目录的root目录中。如果一切顺利,您将能够登录到管理区域的 Web 仪表板,并在页面顶部标题中单击项目名称时看到以下内容:

摘要
是的!您刚刚在 Active-Active 设计中设置了您的 OpenStack 云。您刚刚获得的灵活性和可靠性解决了大多数主流 HA 要求。在区域之间跳转并在一两次点击内分离应用程序资源会很有趣。在结束本章之前,让我们花点时间回顾一下本章。我们讨论了 OpenStack 提供的开箱即用的处理高可用性要求的好处。然后,我们过渡到您可能想要使用 Active-Active 云区域的一些可能原因。接下来,我们将介绍如何设置 Active-Active 云区域的步骤。最后,我们开发了 Ansible playbooks 和角色来自动设置管理区域。
下一章恰好也是一个相当大的 OpenStack 云作为客户需求而提出的。没有任何云运营商不想知道或拥有他们的云的完整清单。跟踪资源、审计用户和总结网络利用率只是我们日常/每周例行工作的一部分。想象一下,您可以通过一个命令创建完整的报告。这可能吗?好吧,我不告诉你。您将不得不继续阅读第九章,“清点您的云”,以找出答案。
第九章:清单您的云
我非常兴奋地进入这一章,因为我们将专注于在管理 OpenStack 云时被认为具有挑战性的一个主题。收集有关正在使用的系统的指标是日常优先事项清单上的一个非常重要的项目。坏消息是,OpenStack 并不一定使这成为一项容易的任务。为了辩护 OpenStack,我会说最近的版本已经做了很多工作来改进这一点。新的 OpenStackClient(OSC)做得更好,允许云操作员汇总有关云的各种不同指标。
与此同时,有办法以临时方式收集这些指标,然后制作一个非常简单的报告。与 OpenStack 相关的大多数事情一样,有几种方法可以解决。在尝试使用多种方法进行此操作后,我发现通过对 OpenStack 数据库执行查询很容易实现。我知道,我知道……没有人想要触碰数据库。在过去的生活中,我曾经是一名数据库管理员,从那段经历中我学到的一件事是,简单明了的查询对任何数据库都是无害的。结合这个理论,并使用诸如 Ansible 之类的工具将收集到的所有信息汇总在一起是一个成功的组合。在本章中,我们将回顾如何动态地对 OpenStack 云资源的各个部分进行清单。我们将学习哪些指标具有价值,以及如何将这些信息存储以供以后参考。作为云操作员,拥有这样一个极其强大的工具是非常有用的。
-
收集云指标
-
用户报告
-
项目报告
-
网络报告
-
卷度报告
-
一目了然的云报告
-
编写操作手册和角色
-
审查操作手册和角色
收集云指标
这个过程的第一步是确定对您来说哪些指标是重要的。请记住,这里概述的方法只是我个人处理这个问题的方式。作为云操作员,您可能有不同的处理方式。将其作为一个起点来帮助您开始。
根据我的经验,最好汇总用户、项目、网络和卷度指标。然后,将所有数据合并在一起,输出总的云利用率指标。这与 Horizon 仪表板的功能非常相似。虽然登录 Horizon 并进行快速审查很容易,但如果您想向领导提供全面的报告呢?或者您可能想要拍摄一个时间点的快照,以比较一段时间内的云利用情况。将来可能需要对您的云进行审计。在不使用第三方工具的情况下,没有真正简单的方法以报告格式来做到这一点。下面的方法可以满足所有这些情况。
让我们从收集用户指标开始。
用户报告
捕获有关云中定义的用户的信息可能是记录的最简单的指标。当有一天您必须因合规性和安全原因对您的云进行审计时,您会注意到您列出了用户,甚至列出了分配给用户的角色,但没有将两者结合在一起。同样,您可以列出项目中的用户,但没有将分配给该用户的项目的角色一起列出。您可以看出我要说的是什么。将用户的完整列表与他们的 ID、分配给他们的角色以及他们可以访问的项目一起列在一份报告中是很有意义的。使用以下简单的数据库查询,您可以非常容易地获得这些信息:
USE keystone;
SELECT local_user.user_id, local_user.name as username, role.name as role, project.name as tenant from local_user
INNER JOIN assignment ON
local_user.user_id=assignment.actor_id INNER JOIN
role ON assignment.role_id=role.id INNER JOIN
project ON assignment.target_id=project.id
ORDER BY tenant;
这个查询将结合数据库中名为 keystone 的四个不同表的数据。keystone 数据库是所有与用户相关的数据的所有者。数据库中的每个表至少有一个可以用来将数据联系在一起的主键。以下是这里使用的表及其功能的快速概述:
User # contains the raw user information such as ID, name,
password and etc.
Assignment # contains the role assignment for all users
Role # is the list of roles created/available
Project # contains the list of projects/tenants created/available
在这个例子中,我们将专注于从四个表中只拉回必要的列。为了让事情变得更容易阅读,我们还重新命名了一些列标签。最后,我们将按项目名称按升序对数据进行排序,以便得到清晰和简单的输出。我保证不会在这个 SQL 查询中深入探讨太多。这是一本关于 OpenStack 和 Ansible 的书,不是 SQL 命令,对吧?
提示
始终尝试使用表的 ID 列来在可能的情况下链接其他表中的数据。ID 列将始终是一个唯一值,每次都会提供可靠的数据关联。如果使用包含项目名称值的列,最终可能会导致冲突,如果表中存在具有重复值的行。即使整个 OpenStack 也使用这种方法,您会注意到在 OpenStack 中创建的任何内容都有一个与之关联的 ID。
执行此查询后,输出将类似于以下内容:

项目报告
在整个云生命周期中,清晰地了解云中存在的项目和正在使用的资源可以非常有价值。最近,部门或部门费用分摊似乎是一种非常流行的方法。将这些指标作为时间点资源审查可以清楚地了解每个项目正在使用多少资源。为了成功完成这一点,必须为每个项目收集 vCPU、内存和磁盘指标。使用以下简单的数据库查询,您可以非常容易地获得这些信息:
USE nova;
SELECT SUM(instances.vcpus) as vCPU, SUM(instances.memory_mb) as memory_MB, SUM(instances.root_gb) as disk_GB, keystone.project.name as tenant from instances
INNER JOIN keystone.project ON
instances.project_id=keystone.project.id
WHERE instances.vm_state='active' GROUP BY tenant;
此查询将合并来自两个不同数据库(nova和keystone)中的数据。nova数据库拥有所有与实例相关的数据。keystone数据库在前面的部分中已经审查过。就像之前的例子一样,每个表至少有一个主键。以下是这里使用的表及其功能的快速概述:
nova
Instances # contains the raw information about instances created
keystone
Project # contains the list of projects/tenants created/available
为了获得这些数据,我们必须有点巧妙,并直接从包含原始实例信息的表中提取资源指标。如果我们安装了 Ceilometer,将会有一个特定的数据库,记录这些指标在更微观的水平上。由于我们目前没有这个功能,这种方法是目前可用的最好方法。在此查询中,我们将再次只返回必要的列并重新命名列标签。最后,我们将缩小输出范围,只包括活动实例,并按项目名称按升序对数据进行排序。因此,通过获取每个实例的资源信息并将其与实例所属的每个项目相关联,我们能够创建类似于这样的简单输出:

网络报告
在您的云上创建的 Neutron 网络的快照可能看起来对管理整个 OpenStack 云并不重要。相信我,在大局中是重要的。不必要或配置不正确的网络可能会给整个云功能增加延迟。直接导致这种情况的并不是网络本身,而是与每个项目相关的安全组的存在。这些信息主要可以帮助解决项目报告的问题。它提供了一个快速参考,了解每个项目中存在哪些网络以及与之关联的网络无类域间路由选择(CIDR),即网络地址空间。在本地,网络服务(Neutron)在一个命令中并不提供这样的报告。就像之前一样,我们将直接从数据库中提取这些信息。使用以下简单的数据库查询,我们将收集网络 ID、名称、子网、分配的 CIDR、状态和关联的项目:
USE neutron;
SELECT networks.id, networks.name, subnets.name as subnet, subnets.cidr, networks.status, keystone.project.name as tenant from networks
INNER JOIN keystone.project ON networks.project_id COLLATE utf8_unicode_ci = keystone.project.id
INNER JOIN subnets ON networks.id=subnets.network_id
ORDER BY tenant;
对于这个查询,我们将合并来自两个不同数据库neutron和keystone中的三个不同表的数据。neutron数据库拥有所有与网络相关的数据。以下是这里使用的表及其功能的快速概述:
neutron
Networks # contains the raw information about networks created
Subnets # contains the subnet details associated with the networks
keystone
Project # contains the list of projects/tenants created/available
收集这些指标相当简单,因为大部分数据都存在于网络表中。我们所要做的就是从子网表中提取匹配的 CIDR,然后引入与该网络相关联的项目名称。在组合这个查询的过程中,我注意到keystone和neutron数据库表之间存在连接问题。显然,neutron数据库对 ID 列的模式定义不同,因此必须在内部连接语句中添加以下值:COLLATE utf8_unicode_ci。最终,输出将按项目名称按升序排序。输出的示例将类似于:

卷报告
在云中对整体卷消耗进行详细报告的能力似乎是 OpenStack 目前的一个较大的空白。块存储服务(Cinder)负责在云中维护和跟踪卷。为了获得准确的指标,我们需要直接查询 Cinder。能够有一份报告来分解每个项目创建的卷的数量将是很好的。然后,能够有一个快速的汇总报告来显示每个项目使用了多少卷存储。现在由于 Cinder 支持多个存储后端,最好跟踪卷类型的消耗情况。随着 Cinder 的成熟,我相信这将成为一个更容易的任务,但目前,我们可以再次直接查询数据库以提取我们正在寻找的指标。以下是用于收集这些指标的数据库查询的示例:
USE cinder;
SELECT volumes.id, volumes.display_name as volume_name, volumes.size as size_GB, volume_types.name as volume_type, keystone.project.name as tenant from volumes
INNER JOIN keystone.project ON volumes.project_id=keystone.project.id
INNER JOIN volume_types ON volumes.volume_type_id=volume_types.id
WHERE volumes.status='available'
ORDER BY tenant;
SELECT SUM(volumes.size) as volume_usage_GB, keystone.project.name as tenant from volumes
INNER JOIN keystone.project ON volumes.project_id=keystone.project.id
WHERE volumes.status='available'
GROUP BY tenant;
SELECT volume_types.name as volume_type, SUM(volumes.size) as volume_usage_GB from volumes
INNER JOIN volume_types ON volumes.volume_type_id=volume_types.id
WHERE volumes.status='available'
GROUP BY volume_type;
对于这个查询,至少涉及两个数据库cinder和keystone中的三个不同表。如您所见,收集这些信息相当复杂。我们需要发出三个单独的SELECT语句。第一个SELECT语句将从卷表中关联原始卷信息和来自 keystone 表的项目数据。此外,在同一个语句中,我们将包括卷类型的名称。由于卷表包含活动和非活动卷,必须应用额外的过滤器来仅返回活动卷。完整的输出将按项目名称按升序排序。第一个查询的输出将类似于这样:

下一个SELECT语句将查询数据库,收集每个项目的总卷消耗指标。它与前一个语句非常相似,但这里的主要区别是我们将为每个项目添加volume_usage_GB列,以计算总消耗量。第二个查询的输出将类似于这样:

最终的SELECT语句专注于报告卷类型的消耗情况。由于卷表仅记录卷类型 ID,我们必须内部连接volume_types表,以引入创建时定义的实际卷名称。这也是之前提到的其他语句所做的事情。第三个查询的输出将类似于:

一览云报告
这份报告旨在快速概述云的整体消耗情况。它返回云中存在的用户、项目、卷和网络的总数。以及当前使用的 vCPU、内存和临时磁盘的总数。以下是用于收集这些数据的数据库查询:
USE keystone;
SELECT count(*) as total_users from user WHERE user.enabled=1;
SELECT count(*) as total_projects from project WHERE project.enabled=1;
USE cinder;
SELECT count(*) as total_volumes, SUM(volumes.size) as total_volume_usage_GB from volumes
WHERE volumes.status='available';
USE neutron;
SELECT count(*) as total_networks from networks WHERE networks.status='ACTIVE';
USE nova;
SELECT SUM(instances.vcpus) as total_vCPU, SUM(instances.memory_mb) as total_memory_MB, SUM(instances.root_gb) as total_disk_GB from instances
WHERE instances.vm_state='active';
基本上使用的SELECT语句是将被调用的表中的列相加。然后将列名重命名为更具描述性的标签,最后进行过滤,忽略任何不处于活动状态的行。一旦执行,前面查询的输出将类似于这样:

现在我们知道如何收集报告的指标,让我们去学习如何完全自动化这个任务。
编写 playbooks 和 roles
在本节中,我们将创建 playbook 和 roles 来生成全面的云报告。一旦执行 playbook,输出和最终结果将是两份报告,包括我们在上一节中学习如何收集的信息。这两份报告将保存在您确定的目录中以便检索。在那时,您可以直接将其发送给领导和/或同行进行审查。在下一章中,我们将学习如何进一步进行,并直接通过电子邮件发送报告作为额外的奖励。
与上一章非常相似,我们将将多个任务分解为单独的角色,以保持组织。接下来,我们将审查用于自动创建我们的云报告的六个角色。
云清单
我们将创建的第一个角色将包括设置云报告基础所需的任务。文件名将是main.yml,位于名为cloud-inventory/tasks的角色目录中。该文件的内容将如下所示:
---
name: Create working directory
file: path="{{ REPORT_DIR }}" state=directory
ignore_errors: yes
name: Copy the cloud_report script
copy: src=cloud_report.sql dest=/usr/share mode=0755
name: Add report header
shell: ( echo "+------------------------------------+"; echo "| {{ COMPANY }} Cloud Report |"; echo "| Created at {{ lookup('pipe', 'date +%Y-%m-%d%t%X') }} |"; echo "+------------------------------------+"; ) >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
name: Execute cloud report
shell: chdir=/usr/bin mysql -u root --password={{ MYSQLPASS }} --table < /usr/share/cloud_report.sql >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
前三个任务只是处理创建报告所需的先决步骤。这将包括创建报告保存的目录,处理要执行的 SQL 脚本,并向报告添加标题。总体思路是创建一个视觉上吸引人、准确且灵活的报告。这是通过动态添加报告运行时间/日期并相应命名报告来实现的。最后一个任务将直接针对云中 Galera 容器中找到的 MySQL 数据库执行cloud_report.sql文件。
cloud_report.sql文件包含前面一览云报告部分中描述的 SQL 查询。该文件可以在此角色的cloud-inventory/files目录中找到。
云使用
接下来的角色将创建第二份报告,概述当前云利用率按项目分解。该文件将命名为main.yml,位于名为cloud-usage/tasks的角色目录中。该文件的内容将如下所示:
---
name: Create working directory
file: path="{{ REPORT_DIR }}" state=directory
ignore_errors: yes
name: Retrieve projectIDs
shell: openstack --os-cloud="{{ CLOUD_NAME }}"
project list | awk 'NR > 3 { print $2 }'
register: tenantid
name: Add report header
shell: ( echo "+------------------------------------+"; echo "| Project Usage Report |"; echo "| Created at {{ lookup('pipe', 'date +%Y-%m-%d%t%X') }} |"; echo "+------------------------------------+"; echo " "; ) >> {{ REPORT_DIR }}/os_usage_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
name: Record project usage
shell: ( echo "Project - {{ item }}" && openstack --os-cloud="{{ CLOUD_NAME }}"
usage show --start {{ RPTSTART }} --end {{ RPTEND }} --project {{ item }} && echo " " ) >> {{ REPORT_DIR }}/os_usage_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
with_items: "{{ tenantid.stdout_lines }}"
name: Retrieve project usage report file
fetch: src={{ REPORT_DIR }}/os_usage_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log dest={{ REPORT_DEST }} flat=yes
所有报告的预设置工作都在前面显示的第一和第三个任务中处理(创建报告目录和标题)。为了收集我们需要的报告指标,我们可以使用本机 OpenStack CLI 命令。使用的两个命令是:openstack project list和usage show。这些命令作为上面显示的第二和第四个任务的一部分执行。此角色中的最后一个任务将从远程位置检索报告并将其移动到 playbook/roles 执行的本地位置。
用户清单
该角色将负责执行前面部分描述的用户报告。文件将命名为main.yml,位于名为user-inventory/tasks的角色目录中。在这里,您将找到该文件的内容:
---
name: Create working directory
file: path={{ REPORT_DIR }} state=directory
ignore_errors: yes
name: Copy the user_report script
copy: src=user_report.sql dest=/usr/share mode=0755
name: Add report header
shell: ( echo "+------------------------+"; echo "| Cloud User Report |"; echo "+------------------------+"; ) >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
name: Execute user report
shell: chdir=/usr/bin mysql -u root --password={{ MYSQLPASS }} --table < /usr/share/user_report.sql >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
为了使报告模块化且不相互依赖,我让每个角色创建一个报告工作目录并插入特定于报告的标题。这样,您可以包含或排除您希望的任何角色/报告。
用于创建此角色的基本原则将重复用于其余角色。它包括以下步骤:
-
创建报告工作目录;如果目录已经存在,它将继续报告无错误
-
将 SQL 脚本复制到远程位置
-
向报告添加自定义标题信息
-
执行 SQL 脚本以生成特定子报告
user_report.sql文件包含了前面部分描述的用户报告中的 SQL 查询。现在我们已经定义了框架,我们可以快速地完成剩下的角色。
project-inventory
这个角色的目的是执行我们在前面部分审查过的项目报告。文件将被命名为main.yml,存放在名为project-inventory/tasks的角色目录中。在这里,你会找到这个文件的内容:
---
name: Create working directory
file: path={{ REPORT_DIR }} state=directory
ignore_errors: yes
name: Copy the tenant_report script
copy: src=project_report.sql dest=/usr/share mode=0755
name: Add report header
shell: ( echo "+-------------------------+"; echo "| Cloud Project Report |"; echo "+-------------------------+"; ) >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
name: Execute tenant report
shell: chdir=/usr/bin mysql -u root --password={{ MYSQLPASS }} --table < /usr/share/project_report.sql >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
由于这个角色将遵循为用户清单角色概述的相同步骤,我们将注意力集中在执行的独特功能上。对于这个角色,project_report.sql文件将包含前面项目报告部分中描述的 SQL 查询。
network-inventory
这个角色的目的是执行我们在前面部分审查过的网络报告。文件将被命名为main.yml,存放在名为network-inventory/tasks的角色目录中。在这里,你会找到这个文件的内容:
---
name: Create working directory
file: path={{ REPORT_DIR }} state=directory
ignore_errors: yes
name: Copy the network_report script
copy: src=network_report.sql dest=/usr/share mode=0755
name: Add report header
shell: ( echo "+-------------------------+"; echo "| Cloud Network Report |"; echo "+-------------------------+"; ) >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
name: Execute network report
shell: chdir=/usr/bin mysql -u root --password={{ MYSQLPASS }} --table < /usr/share/network_report.sql >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
volume-inventory
这个最后的角色将执行我们之前涵盖的卷报告的最终子报告。文件将被命名为main.yml,存放在名为volume-inventory/tasks的角色目录中。在这里,你会找到这个文件的内容:
---
name: Create working directory
file: path={{ REPORT_DIR }} state=directory
ignore_errors: yes
name: Copy the volume_report script
copy: src=volume_report.sql dest=/usr/share mode=0755
name: Add report header
shell: ( echo "+--------------------------+"; echo "| Cloud Volume Report |"; echo "+--------------------------+"; ) >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
name: Execute volume report
shell: chdir=/usr/bin mysql -u root --password={{ MYSQLPASS }} --table < /usr/share/volume_report.sql >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
name: Retrieve completed cloud report file
fetch: src={{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log dest={{ REPORT_DEST }} flat=yes
值得注意的一件特别的事情是,这个角色的最后一个任务使用fetch Ansible 模块从远程位置检索创建的报告。这与云使用角色中使用的行为相同。就个人而言,我觉得这个模块非常方便,让我们不必处理一系列的安全复制命令。这对任何人来说都不是一个好时机。
为了支持这些角色,我们现在需要创建与之配套的变量文件。由于我们将使用两个单独的主机来执行一系列角色,所以需要两个全局变量文件。文件名分别是util_container和galera_container,它们将被保存到 playbook 的group_vars/目录中。
提示
请记住,变量文件中定义的值是为了在正常的日常使用中在每次执行前进行更改的。
你应该注意到为新角色定义的一些新变量。除了用于认证进入你的 OpenStack 云的标准变量之外,我们还添加了一些与报告创建和位置相关的新变量:
util_container
# Here are variables related globally to the util_container host group
CLOUD_NAME: default
REPORT_DIR: /usr/share/os-report
REPORT_DEST: /usr/share/
RPTSTART: 2016-10-01
RPTEND: 2016-11-01
galera_container
# Here are variables related globally to the galera_container host group
MYSQLPASS: passwd
COMPANY: Rackspace RPC
REPORT_DIR: /usr/share/os-report
REPORT_DEST: /usr/share/
注意
注意:
由于这个文件的内容,它应该被存储为一个安全文件,无论你使用什么代码库来存储你的 Ansible playbooks/roles。获取这些信息可能会危及你的 OpenStack 云安全。
让我们花点时间来分解新的变量。总结如下:
REPORT_DIR # the directory where the report is
stored temporarily remotely
REPORT_DEST # the directory where the report is saved locally
RPTSTART # the start date when collecting cloud usage
RPTEND # the end date when collecting cloud usage
MYSQLPASS # the password for the root database user
COMPANY # the company name to show up in the report header
注意
由于有两个共享相同变量名的全局变量文件,请确保如果你希望两个报告存在于同一个目录中,保持变量值同步。这不是一个要求,因为每个报告(云报告和云使用)都可以独立存在。只是觉得值得一提,以免引起混淆。
变量文件完成后,我们可以继续创建主要的 playbook 文件。由于我们的目标是创建一个关于云资源的报告(记住我们将云使用报告作为奖励添加了进来),我们将从一个 playbook 中调用所有的角色。playbook 文件的完整内容最终看起来会类似于这样:
---
# This playbook used to run a cloud resource inventory report.
hosts: galera_container
remote_user: root
become: true
roles:
- cloud-inventory
hosts: util_container
remote_user: root
become: true
roles:
- cloud-usage
hosts: galera_container
remote_user: root
become: true
roles:
- user-inventory
- project-inventory
- network-inventory
- volume-inventory
正如提到的,我们创建的所有用于清点云的角色将按照 playbook 中显示的顺序执行。所有的角色都使用相同的主机,除了云使用角色。背后的原因是我们在那个角色中使用了 OpenStack CLI 命令,这就需要使用util_container。
注意
playbook 和 role 的名称可以是您选择的任何内容。这里提供了具体的名称,以便您可以轻松地跟踪并引用 GitHub 存储库中找到的已完成代码。唯一的警告是,无论您决定如何命名角色,当在 playbook 中引用时,它必须保持统一。
因此,由于我们现在在此 playbook 中涉及了一个额外的主机,我们必须将此主机添加到名为hosts的清单文件中。通过添加新主机占位符,主机文件现在将如下所示:
[localhost]
localhost ansible_connection=local
[util_container]
172.29.236.85
[galera_container]
172.29.236.72
我非常兴奋地确认我们现在已经准备好开始运行一些云报告了。按照我们的传统,我们将以快速回顾刚刚创建的 playbook 和 role 来结束本章。
审查 playbooks 和 roles
让我们立即开始审查我们创建的 roles。
位于cloud-inventory/tasks目录中的已完成的 role 和名为main.yml的文件如下所示:
---
name: Create working directory
file: path="{{ REPORT_DIR }}" state=directory
ignore_errors: yes
name: Copy the cloud_report script
copy: src=cloud_report.sql dest=/usr/share mode=0755
name: Add report header
shell: ( echo "+------------------------------------+"; echo "| {{ COMPANY }} Cloud Report |"; echo "| Created at {{ lookup('pipe', 'date +%Y-%m-%d%t%X') }} |"; echo "+------------------------------------+"; ) >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
name: Execute cloud report
shell: chdir=/usr/bin mysql -u root --password={{ MYSQLPASS }} --table < /usr/share/cloud_report.sql >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
位于cloud-usage/tasks目录中的已完成的 role 和名为main.yml的文件如下所示:
---
name: Create working directory
file: path="{{ REPORT_DIR }}" state=directory
ignore_errors: yes
name: Retrieve projectIDs
shell: openstack --os-cloud="{{ CLOUD_NAME }}"
project list | awk 'NR > 3 { print $2 }'
register: tenantid
name: Add report header
shell: ( echo "+------------------------------------+"; echo "| Project Usage Report |"; echo "| Created at {{ lookup('pipe', 'date +%Y-%m-%d%t%X') }} |"; echo "+------------------------------------+"; echo " "; ) >> {{ REPORT_DIR }}/os_usage_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
name: Record project usage
shell: ( echo "Project - {{ item }}" && openstack --os-cloud="{{ CLOUD_NAME }}"
usage show --start {{ RPTSTART }} --end {{ RPTEND }} --project {{ item }} && echo " " ) >> {{ REPORT_DIR }}/os_usage_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
with_items: "{{ tenantid.stdout_lines }}"
name: Retrieve project usage report file
fetch: src={{ REPORT_DIR }}/os_usage_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log dest={{ REPORT_DEST }} flat=yes
位于user-inventory/tasks目录中的已完成的 role 和名为main.yml的文件如下所示:
---
name: Create working directory
file: path={{ REPORT_DIR }} state=directory
ignore_errors: yes
name: Copy the user_report script
copy: src=user_report.sql dest=/usr/share mode=0755
name: Add report header
shell: ( echo "+------------------------+"; echo "| Cloud User Report |"; echo "+------------------------+"; ) >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
name: Execute user report
shell: chdir=/usr/bin mysql -u root --password={{ MYSQLPASS }} --table < /usr/share/user_report.sql >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
位于project-inventory/tasks目录中的已完成的 role 和名为main.yml的文件如下所示:
---
name: Create working directory
file: path={{ REPORT_DIR }} state=directory
ignore_errors: yes
name: Copy the tenant_report script
copy: src=project_report.sql dest=/usr/share mode=0755
name: Add report header
shell: ( echo "+-------------------------+"; echo "| Cloud Project Report |"; echo "+-------------------------+"; ) >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
name: Execute tenant report
shell: chdir=/usr/bin mysql -u root --password={{ MYSQLPASS }} --table < /usr/share/project_report.sql >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
位于network-inventory/tasks目录中的已完成的 role 和名为main.yml的文件如下所示:
---
name: Create working directory
file: path={{ REPORT_DIR }} state=directory
ignore_errors: yes
name: Copy the network_report script
copy: src=network_report.sql dest=/usr/share mode=0755
name: Add report header
shell: ( echo "+-------------------------+"; echo "| Cloud Network Report |"; echo "+-------------------------+"; ) >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
name: Execute network report
shell: chdir=/usr/bin mysql -u root --password={{ MYSQLPASS }} --table < /usr/share/network_report.sql >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
位于volume-inventory/tasks目录中的已完成的 role 和名为main.yml的文件如下所示:
---
name: Create working directory
file: path={{ REPORT_DIR }} state=directory
ignore_errors: yes
name: Copy the volume_report script
copy: src=volume_report.sql dest=/usr/share mode=0755
name: Add report header
shell: ( echo "+--------------------------+"; echo "| Cloud Volume Report |"; echo "+--------------------------+"; ) >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
name: Execute volume report
shell: chdir=/usr/bin mysql -u root --password={{ MYSQLPASS }} --table < /usr/share/volume_report.sql >> {{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log
name: Retrieve completed cloud report file
fetch: src={{ REPORT_DIR }}/os_report_{{ lookup('pipe', 'date +%Y%m%d') }}.log dest={{ REPORT_DEST }} flat=yes
相应的全局变量文件名为util_container,保存在完整 playbook 的group_vars/目录中:
# Here are variables related globally to the util_container host group
CLOUD_NAME: default
REPORT_DIR: /usr/share/os-report
REPORT_DEST: /usr/share/
RPTSTART: 2016-10-01
RPTEND: 2016-11-01
相应的全局变量文件名为galera_container,保存在完整 playbook 的group_vars/目录中:
# Here are variables related globally to the galera_container host group
MYSQLPASS: passwd
COMPANY: Rackspace RPC
REPORT_DIR: /usr/share/os-report
REPORT_DEST: /usr/share/
现在主 playbook 文件已经创建,并将位于playbook目录的root目录中:
inventory.yml
---
# This playbook used to run a cloud resource inventory report.
hosts: galera_container
remote_user: root
become: true
roles:
- cloud-inventory
hosts: util_container
remote_user: root
become: true
roles:
- cloud-usage
hosts: galera_container
remote_user: root
become: true
roles:
- user-inventory
- project-inventory
- network-inventory
- volume-inventory
最后,我们创建了hosts文件,也位于playbook目录的root目录中:
[localhost]
localhost ansible_connection=local
[util_container]
172.29.236.85
[galera_container]
172.29.236.72
注
完整的代码集可以在以下 GitHub 存储库中找到:github.com/os-admin-with-ansible/os-admin-with-ansible-v2/tree/master/cloud-inventory。
在我们结束这个话题之前,当然需要测试我们的工作。在运行此 playbook 和 roles 结束时,您将有两份报告需要审查。假设您之前已经克隆了 GitHub 存储库,从部署节点测试 playbook 的命令如下:
**$ cd os-admin-with-ansible-v2/cloud-inventory**
**$ ansible-playbook -i hosts inventory.yml**
假设 playbook 成功运行并且没有错误,您将在全局变量文件中指定的目录中找到创建的两个报告。报告应该类似于这样:

...

再次干得好!希望这些云报告能够真正帮助简化您日常的 OpenStack 管理任务!
总结
我们的 OpenStack 管理工具箱在本书中已经开始看起来相当丰富。强调拥有云状态的快照有多么重要是无法言喻的。这些报告可能是拥有快照的一个很好的起点。在结束本章之前,让我们花一点时间回顾本章。我们一起审查了 OpenStack 关于云库存报告的一些空白以及您如何克服它们。然后提供了如何通过查询数据库来获取我们需要的指标和统计信息的详细信息。接下来,我们详细研究了用于从数据库中提取数据的自定义 SQL 查询。最后,我们开发了 Ansible playbook 和 role 来自动生成云报告。
很遗憾地说,下一章是我们的最后一章。话虽如此,它肯定是最重要的章节之一。了解您的云的健康状况对于拥有一个正常运行的 OpenStack 生态系统至关重要。由于 OpenStack 的模块化性质,您将有许多服务需要跟踪。让它们都正常工作是在 OpenStack 内部创造了良好的和谐。虽然您当然可以手动完成,但我相信您会同意自动化这样的任务更理想。请继续阅读下一章,了解如何可以自动监控您的云的健康状况,甚至将健康报告直接发送到您的收件箱。
第十章:使用 Nagios 检查云的健康状况
监控恰好是我非常关心的一个话题。我花了很多年时间观察许多组织的网站和应用,以确保它们的可用性尽可能接近 99.99%的正常运行时间。这项任务在任何领域都不是软弱的人可以做的。支撑我度过这一切的是有一种坚实的监控方法,不需要我每天都亲自盯着它。在本章中,我们将介绍一些常见的方法来手动检查您的 OpenStack 云的健康状况,然后利用 Ansible 来设置我最喜欢的开源监控工具 Nagios。
由于我们在整本书中一直在使用openstack-ansible(OSA)部署方法进行实验,让我们继续利用 OSA 的内置 Ansible 功能来执行各种系统检查。请记住,我们在这里所做的事情不应该取代任何第三方监控工具,这些工具很可能会更好地保持被监控任务的可靠轮换。我们将使用 OpenStack 和 Linux 的本机功能来快速查看您的云的健康状况。同时,我们还将介绍其他监控技巧和窍门:
-
监控云
-
基础设施服务
-
核心 OpenStack 服务
-
设置 Nagios 并导入检查
-
通过 SNMP 收集指标
-
编写操作手册和角色
-
审查操作手册和角色
监控云
如果允许的话,我想在开始之前先介绍一些监控基础知识。希望我将在这里分享的三个原则对你来说并不新鲜。在评估监控某些内容时,有三个基本原则需要牢记:
-
保持简单
-
将您的监控保持在基础设施附近
-
创建良好的监控器
第一个要点很容易理解,因为它相当不言自明。你能做的最糟糕的事情就是过于复杂化如监控这样重要的事情。这个原则不仅适用于你的整体方法,也适用于你选择用来进行监控的工具。如果你不得不创建一个你的监控平台的Visio图,那就太复杂了。
将监控保持在基础设施附近的下一个要点是,用于监控的工具应该物理上靠近基础设施/应用程序。监控平台不应该需要穿越 VPN 或多个防火墙才能轮询设备/应用程序。将监控平台集中放置,这样您就可以以最小的路径轮询尽可能多的系统。您应该能够在防火墙中打开一两个端口以启用监控,并且这应该成为启动新设备/应用程序的标准化流程的一部分。
最后一点是另一个相当不言自明的概念。创建良好的监控器至关重要,它将避免错误的监控警报。随着时间的推移,如果大部分警报都被证明是虚假警报,个人将开始忽略监控警报。确保每个监控检查都按预期工作,并调整以尽量避免虚假警报。在各种工作负载和一天中的不同时间测试之后,再启动新的警报监控。此外,毋庸置疑,确保监控器增加价值并且不是多余的。
现在我们已经了解了基础知识,我们现在可以专注于监控 OpenStack。这通常涵盖以下四个领域:
-
监控物理硬件(基础资源消耗)
-
监控 OpenStack API 端点
-
监控 OpenStack 服务进程
-
通过基础设施节点监控计算节点
由于前两个领域实际上更适合由监控工具处理,我们在本书中不会重点关注这些工具。因此,我们的重点将主要放在检查基础设施服务(即 Galera、RabbitMQ 等)、核心 OpenStack 服务进程和计算节点的健康状况上。
提示
专业提示
在监控 OpenStack API 端点时,确保包括端点响应时间作为记录的指标。这样可以帮助您识别和跟踪任何与服务相关的减速,最终可能导致服务故障。捕获这些信息可以让您随着时间查看性能趋势,这可以让您在故障发生之前主动解决服务相关问题。修复可能是简单的事情,比如增加运行特定服务的容器,调整服务参数和/或数据库调优。
OpenStack 服务进程
在转到下一部分之前,我觉得包括一些关于 OpenStack 服务进程的细节会很有帮助。这里有一张表格,列出了 OpenStack 服务和相关的进程名称。与 OpenStack 相关的任何内容,进程名称都可能因每个版本的发布而发生变化。我希望这至少是一个很好的起点。
| 服务名称 | 代码名称 | 进程名称 |
|---|---|---|
| 计算 | Nova | nova-api-metadata, nova-api-os-compute, nova-cert, nova-compute, nova-consoleauth, nova-spicehtml5proxy, nova-api-ec2, nova-api, nova-conductor, nova-scheduler |
| 对象存储 | Swift | swift-proxy-server, swift-account-server, swift-account-auditor, swift-account-replicator, swift-account-reaper, swift-container-server, swift-container-auditor, swift-container-replicator, swift-container-updater, swift-object-server, swift-object-auditor, swift-object-replicator, swift-object-updater |
| 镜像 | Glance | glance-api, glance-registry |
| 身份 | Keystone | keystone-all, apache2 |
| 仪表板 | Horizon | apache2 |
| 网络 | Neutron | neutron-dhcp-agent, neutron-l3-agent, neutron-linuxbridge-agent, neutron-metadata-agent, neutron-metering-agent, neutron-server |
| 块存储 | Cinder | cinder-api, cinder-volume, cinder-scheduler |
| 编排 | Heat | heat-api, heat-api-cfn, heat-api-cloudwatch, heat-engine |
| 遥测 | Ceilometer | ceilometer-agent-compute, ceilometer-agent-central, ceilometer-agent-notification, ceilometer-collector, ceilometer-alarm-evaluator, ceilometer-alarm-notifier, ceilometer-api |
| 数据库 | Trove | trove-api, trove-taskmanager, trove-conductor |
基础设施服务
所有 OpenStack 服务的基础称为基础设施服务。这些是 OpenStack 在任何级别上工作所需的服务/组件。这些组件是 SQL 数据库服务器软件、数据库集群软件和消息服务器软件。在我们的特定情况下,这些组件按照相同的顺序将是 MariaDB、Galera 和 RabbitMQ。确保所有这些组件都健康并按预期工作是首要任务。每个软件包都有本机命令来报告它们的健康状况,所以我们在这方面已经有所准备。因此,挑战将是针对各种规模的云查询此信息的最佳方式是什么。想象一下,您有一个 20 节点的控制平面。您可以执行 20 次健康检查命令,或者只需使用 Ansible 执行一个命令即可获取状态。
MariaDB 和 Galera
从前两个组件开始,有一种方法可以执行一个命令来同时进行 MySQL 检查以及检查数据库集群的健康状况。如果您还记得第二章中的内容,Ansible 简介,我们讨论了动态清单的主题,以及 OSA 提供了预构建的动态清单脚本,可以用来简化云管理。我们将利用这种能力来简化检查这些基础设施服务的流程。
有一个快速提醒,介绍如何使用 OSA 动态清单脚本与 Ansible。从根 OSA 部署(/opt/openstack-ansible/playbooks)目录中,您可以使用定义的组名来调用任何 OpenStack 服务所在的容器。每个 OpenStack 服务和基础设施组件在动态清单中都有一个定义的组。与我们目前正在处理的特定任务相关,有一个名为galera_container的组。
该组包含安装了云端 MySQL 和 Galera 的所有容器。然后,您可以将此组名替换为您通常在 playbook 的hosts文件中提供的任何主机名。尝试针对您的 OSA 云执行以下命令,以显示 Galera 容器的详细信息:
**$./inventory/dynamic_inventory.py | grep galera**
输出将类似于这样:

注意
请记住,前面的示例是针对 OpenStack 的 AIO(All-In-One)部署收集的。通常情况下,您应该在galera_container组下找到三个或更多不同的容器。
与 Ansible 相关的我们尚未涉及的领域是使用仅使用 Ansible 运行时包来发出更基本的临时命令的能力。在安装了 Ansible 的命令提示符中执行以下命令,以查看如何使用基本的 Ansible 程序的详细信息:
**$ ansible -h**
您会注意到参数与ansible-playbook程序非常相似,主要区别在于ansible程序不打算执行 playbooks。相反,它旨在能够直接在命令行上执行临时任务,使用与 playbook 通常使用的相同模块。我们将使用基本的 Ansible 程序来演示如何检索这些基础设施服务的状态。
现在,如果我们把这些都放在一起,报告 MySQL 和 Galera 的健康状况的工作示例将如下所示:
**$ ansible galera_container -m shell -a "mysql -h localhost\-e
'show status like "%wsrep_cluster_%";'"**
前面的命令告诉 Ansible 使用galera_container组作为要运行shell命令的主机。shell命令将调用 MySQL 并执行show status查询。命令的输出将类似于这样:

再次说明,由于使用 AIO 部署,您会注意到示例显示的集群大小只有一个。通常,集群大小应该显示为三个或更多,并且状态将显示在每个容器上(输出将对每个容器重复)。需要注意的关键领域是:每个容器报告成功,集群大小正确,并且集群 ID 在所有集群中都相同。
RabbitMQ
我们将使用与之前对 MariaDB/Galera 进行状态和健康检查时相同的原则来检查 RabbitMQ 集群的状态和健康状况。RabbitMQ 容器的组名是rabbit_mq_container,我们可以在 OSA 根部署目录中执行以下命令来查看组的详细信息:
**$./inventory/dynamic_inventory.py | greprabbit_mq**
现在我们可以继续测试一些命令,报告 RabbitMQ 集群的健康状况。这里的第一个命令将直接报告集群状态,第二个命令将列出包含消息的所有队列(换句话说,非空队列):
**$ ansible rabbit_mq_container -m shell -a "rabbitmqctlcluster_status"**
**$ ansible rabbit_mq_container -m shell -a
"rabbitmqctllist_queues | awk '\$2>0'"**
命令的输出将类似于这样:

让每个容器回报“成功”,确保运行节点列表完全匹配,并且每个节点显示相同的集群名称是最重要的。如果发现队列中仍然存在消息,请不要太担心。关键是这些消息应该在合理的时间内清除。使用这个指标来寻找消息在队列中被卡住的趋势。
核心 OpenStack 服务
在涵盖了所有基础设施服务之后,我们可以转向核心 OpenStack 服务。在本节中,我们将介绍一些可以用于任何 OpenStack 服务的原则。这种方法允许您根据个人需求交换任何基本方法中的任何服务。
我通常会检查的前三个服务是 Keystone、Nova 和 Neutron。这些服务可能会对云中的许多其他服务产生不利影响,需要正常运行才能在技术上拥有一个正常运行的云。虽然没有明确的 OpenStack 命令可以用来检查 Keystone 服务,但如果 Keystone 服务不可用,任何/所有 OpenStack CLI 命令都将失败,这将变得非常明显。我个人发现测试 Keystone 的最简单方法是要么登录到 Horizon 仪表板,要么发出以下 OpenStack CLI 命令:
**$ openstack service list**
如果您使用 Keystone 返回服务列表,您刚刚测试了向 Keystone 传递用户凭据进行身份验证,并且 Keystone 返回了适当的授权令牌。我们负责测试 Keystone,下一步可以是发出两个 OpenStack CLI 命令,快速报告 Nova 和 Neutron 的状态:
**$ nova service-list**
**$ neutron agent-list**
nova service-list命令将轮询所有与 Nova 相关的容器和计算节点,以确定它们的区域、状态、状态和状态更改的时间。此命令的输出将类似于此:

接下来,neutron agent-list命令将执行与上述相同的操作,只是针对与 Neutron 相关的组件。您将注意到在即将介绍的示例中,使用了笑脸图形来确定 neutron 代理的状态。代理的状态也将与此命令一起报告:

在这一点上,您将不得不依靠直接检查实际运行在容器中的 OpenStack 服务进程的状态来进行更详细的监控。这将类似于 OpenStack 网站上发布的一些方法,http://docs.openstack.org/ops-guide/operations.html。主要区别在于我们将使用 Ansible 根据需要在所有容器或节点上执行命令。使用基本的 Ansible 程序和之前提到的 OpenStack 服务进程表,您将能够检查在您的云中运行的 OpenStack 进程的状态。以下是一些可以实现这一目标的示例。建议获取云的动态清单的完整输出,这样您将了解所有定义的组。使用以下命令获取完整的清单输出(此命令假定您在 root OSA 部署目录中):
**$ cd playbooks/inventory**
**$ ./dynamic_inventory.py**
将 JSON 输出保存在将来可以引用的地方。
服务和进程检查示例
以下示例显示了如何使用 Ansible 执行服务和进程监视检查:
**# check if a process is running**
**$ ansible neutron_server_container -m shell -a
"ps -ef | grep neutron-server"**
**# check status of a service**
**$ ansible compute_hosts -m shell -a "service nova-compute status"**
**# stop/start/restart a service (process)**
**$ ansible glance_container -m shell -a "service glance-registry stop"**
**$ ansible glance_container -m shell -a "service glance-registry start"**
**$ ansible glance_container -m shell -a "service glance-registry restart"**
**# parseservice logs**
**$ ansible nova_scheduler_container -m shell -a
"grep 35f83ac8 /var/log/nova/nova-scheduler.log"**
您可以使用这些示例中的任何一个来确定任何大小的云上 OpenStack 服务的健康状况,无论是大还是小。想象一下,能够在一个命令中查询跨 200 个节点的云中的nova-compute服务状态的能力。很棒,对吧?当然,我们必须尝试将其提升到下一个级别,通过将更多先进的监控工具整合到一起,以创建更强大的解决方案。
设置 Nagios 并导入检查
回顾之前提到的关于监控的第一个原则,保持简单。感觉我们无法比选择一种领先的开源监控平台 Nagios Core 更简单了。如果您对 Nagios 不熟悉,请花点时间阅读一下www.nagios.com/products。
是的,虽然它在视觉上可能不是最引人注目的仪表板,但它是我使用过的最强大和轻量级的监控应用之一。使用 Nagios,您可以对监控生态系统的许多方面进行最终控制。它可以从能够创建自定义插件一直到明确定义执行窗口为主机。管理可以直接从平面文件中处理,或者您可以使用许多第三方工具,例如NConf在exchange.nagios.org/directory/Addons/Configuration/NConf/details。随着新版本 XI 的推出,越来越多的仅在第三方工具中找到的功能已经内置。一些突出的新功能包括高级图表、集成到事件管理工具以及更清晰的 SLA 报告。
通过 SNMP 收集您的指标
当然,伴随着强大的功能有时会带来巨大的开销。通常,我发现将监控保持在基础设施附近可以避免由于防火墙限制等原因而限制监控的内容。强烈建议使用 SNMP(UDP 端口161),而不是 NRPE 代理,无需安装代理。此外,我通常坚持使用 Perl 编写的插件来简化故障排除。创建良好的监视器对于最小化错误警报至关重要,而这些错误警报最终会变成被忽略的警报。如果发现某个服务检查持续发送错误警报,请修复它!不要让它持续几天。
由于 OpenStack 通过 API 公开了所有功能,因此监控变得很容易。可以创建自定义插件脚本来监视整个 OpenStack 堆栈,并交叉参考任何瓶颈到物理基础设施问题。这种积极的监控可以导致防止停机导致的故障。
由于我对 OSA 有着深厚的热爱,因此将一系列 Ansible playbooks 组合起来处理大部分 Nagios 和 NConf 的设置似乎是再合适不过的了。此外,因为我喜欢回馈,还包括了 OSA 定制的 Nagios 配置(checkcommands、服务和一堆全局 Nagios 配置),可以在几分钟内用来监视您的 OpenStack 云。
编写 playbooks 和 roles
在本节中,我们将利用所有这些 Ansible 魔法来创建一系列 playbooks 和 roles,以设置 Nagios 来监视您的 OpenStack 云。完成后,您将拥有一个完全运行的 Nagios 安装,该安装将被定制为使用我们在上一节中审查的一些监控检查来监视 OpenStack。这一次,我们将任务分解为八个角色,以保持简单。让我们在后面审查每个角色。
snmp-config
我们将创建的第一个角色将包括设置收集监控数据的基础所需的任务。该文件的名称将是main.yml,位于名为snmp-config/tasks的role目录中。该文件的内容将如下所示:
---
name: Install additional packages
apt: name="{{ item }}" state=present
with_items:
- snmpd
name: Move standard config
command: mv /etc/snmp/snmpd.conf /etc/snmp/snmpd.conf.org
name: Place new config file
template: src=snmpd.conf dest=/etc/snmp/snmpd.conf
name: Update SNMP options
shell: chdir=/bin sed -i 's+^SNMPDOPTS.*+SNMPDOPTS="-Lsd -Lf /dev/null -u snmp -I -smux -p /var/run/snmpd.pid -c /etc/snmp/snmpd.conf"+' /etc/default/snmpd
name: Stop SNMP service
command: service snmpd stop
name: Start SNMP service
command: service snmpd start
name: Set SNMP service to start automatically
command: update-rc.d snmpd defaults
前四个任务只是处理安装和配置每个要监视的主机上所需的步骤。这将包括安装snmp软件包,复制自定义配置文件到指定位置,并编辑 SNMP 选项。
处理自定义snmp.conf的任务将使用存储在此角色的snmp-config/templates目录中的template文件。这样做的原因是为了利用已在您的 playbook 中定义的变量,而不是硬编码参数。文件的内容将如下所示:
rocommunity {{ SNMP_COMMUNITY }}
syslocation {{ SYS_LOCATION }}
syscontact {{ SYS_CONTACT }}
install-nagios
下一个角色将专注于安装 Nagios 及其先决条件。该文件将被命名为main.yml,位于名为install-nagios/tasks的role目录中。该文件的内容将如下所示:
---
name: Install additional packages
apt: name={{item}} state=present
with_items:
- vim
- nagios3
- unzip
name: Backup Nagios config files
command: cp -r /etc/nagios3 /etc/nagios3.backup
name: Check Nagios service
shell: ps -ef |grep nagios3
name: Create user .ssh directory
file: path=/var/lib/nagios/.ssh state=directory
ignore_errors: yes
name: Copy SSH private keys
copy: src=id_dsa dest=/var/lib/nagios/.ssh mode=0600
name: Copy SSH public keys
copy: src=id_dsa.pub dest=/var/lib/nagios/.ssh mode=0644
name: Copy nagios Ubuntu logo archive
copy: src=nagios-ubuntu-logo.tar dest=/usr/share
name: Decompress nagios Ubuntu logo archive
command: chdir=/usr/share tar xvf nagios-ubuntu-logo.tar -C
/usr/share/nagios/htdocs/images/logos/base
这个角色在执行 Nagios 的清洁安装所需的任务时非常直接。由于我们将监控 Ubuntu 系统,因此在这个角色中的最后两个任务是为了将 Ubuntu 标志安装到 Nagios 中。
nagios-plugins
这个角色将负责安装我们自定义的 Nagios 插件,我们将使用它来监视我们的云。文件将被命名为main.yml,位于名为nagios-plugins/tasks的role目录中。在这里,你会找到这些内容:
---
name: Copy nagios plugins tar file
copy: src=nagios-plugins.tar dest=/usr/share
name: Decompress nagios plugins archive
command: chdir=/usr/share tar xvf nagios-plugins.tar -C /usr/lib/nagios/plugins
name: Confirm plugin file permissions
file: path=/usr/lib/nagios/plugins/* mode=0774
name: Confirm plugin file ownership
file: path=/usr/lib/nagios/plugins owner=nagios owner=nagios recurse=yes
name: Copy nagios configs zip file
copy: src=NagiosConfig.zip dest=/usr/share
- name: Create rpc-nagios-configs directory
file: path=/etc/nagios3/rpc-nagios-configs state=directory
name: Decompress nagios configs archive
command: chdir=/usr/share unzip NagiosConfig.zip -d /etc/nagios3/rpc-nagios-configs
前面的角色在 Nagios 服务器上复制并设置了两个非常重要的文件(nagios-plugins.tar和NagiosConfig.zip)。如果没有这些插件和配置,你将只有一个普通的 Nagios 安装。通过运行这个角色,你基本上得到了一个预配置的 Nagios 设置,可以监控使用 OSA 部署的 OpenStack 云。在这个模型中,你也可以自定义附加到 Nagios 服务器的插件或特定配置。如果你感到好奇,可以打开这些存档文件查看一下。
install-nconf
这个角色在技术上可以被认为是可选的,因为你不需要 NConf 来运行 Nagios 服务器。我个人发现 NConf 在配置服务检查和主机方面是 Nagios 的一个很好的补充。文件将被命名为main.yml,位于名为install-nconf/tasks的role目录中。以下是该文件的内容:
---
name: Install additional packages
apt: name={{item}} state=present
with_items:
- mysql-server
- mysql-client
- php5
- libapache2-mod-php5
- php5-mysql
- php5-cli
- php5-common
- libtext-csv-xs-perl
name: Download NConf binaries
command: wget http://sourceforge.net/projects/nconf/files/nconf/1.3.0-0/nconf-1.3.0-0.tgz/download -O /usr/share/nconf-1.3.0-0.tgz
name: Unpack NConf binaries
command: chdir=/usr/share tar xzvf nconf-1.3.0-0.tgz -C /var/www/html
name: Set proper NConf directory permissions
command: chdir=/var/www/html/nconf chmod 644 config output static_cfg temp
name: Copy NConf DB script
template: src=create-nconf-db.sql dest=/usr/share
name: Create NConf DB
shell: chdir=/usr/bin mysql -u "{{ DB_USER }}" --password= < /usr/share/create-nconf-db.sql
- name: Set NConf directory ownership
file: path=/var/www/html/nconf owner=www-data group=www-data recurse=yes
- name: Set NConf directory permissions
file: path=/var/www/html/nconf mode=0777
- name: Stop apache
command: service apache2 stop
- name: Start apache
command: service apache2 start
注意
与处理安装 Nagios 的角色非常相似,它涵盖了安装和配置 NConf 所需的步骤。有关如何安装 NConf 的更多详细信息,请访问www.nconf.org/dokuwiki/doku.php?id=nconf:help:documentation:start:installation。
nconf-post-install
将这个角色视为前一个角色的后续,因为它处理了 NConf 安装的后续步骤。它将在安装完成后处理特定文件的清理工作。文件将被命名为main.yml,位于名为nconf-post-install/tasks的role目录中。以下是该文件的内容:
---
name: Remove installation directories and files
command: rm -r /var/www/html/nconf/INSTALL
name: Remove installation directories and files
command: rm /var/www/html/nconf/INSTALL.php
name: Remove installation directories and files
command: rm -r /var/www/html/nconf/UPDATE
name: Remove installation directories and files
command: rm /var/www/html/nconf/UPDATE.php
接下来的两个角色旨在准备控制节点监视本地容器上运行的 OpenStack 进程和 API。你必须能够通过 SSH 远程运行服务检查。好消息是,已经存在 Nagios 插件来做到这一点(check_by_ssh)。
create-nagios-user
这个角色的名称基本上解释了它将处理的任务。它将创建一个名为 Nagios 的用户,这个用户将作为 Nagios 插件的服务账户。文件将被命名为main.yml,位于名为create-nagios-user/tasks的role目录中。以下是该文件的内容:
---
name: Create Nagios user
user: name="{{ USER }}" comment="{{ USER }} User"
ignore_errors: yes
name: Create user .ssh directory
file: path=/home/"{{ USER }}"/.ssh state=directory
name: Copy authorized keys
copy: src=nagios-key dest=/home/"{{ USER }}"/.ssh/authorized_keys mode=0644
name: Set home directory permissions
file: path=/home/"{{ USER }}" owner="{{ USER }}" group="{{ USER }}" recurse=yes
infra-plugin-config
这个角色将安装额外的 SNMP 软件包前提条件,并直接在控制节点上安装本地 Nagios 插件。通过 SSH,Nagios 将执行这些本地插件,并将状态报告回 Nagios 进行记录。这就是你必须说你只是喜欢技术的地方。文件将被命名为main.yml,位于名为infra-plugin-config/tasks的role目录中。以下是该文件的内容:
---
name: Install additional packages
apt: name={{item}} state=present
with_items:
- libnet-snmp-perl
name: Create bin directory
file: path=/home/"{{ USER }}"/bin state=directory
ignore_errors: yes
name: Fix libcrypto link
command: chdir=/lib/x86_64-linux-gnu ln -s libcrypto.so.1.0.0 libcrypto.so.6
ignore_errors: yes
name: Fix libssl link
command: chdir=/lib/x86_64-linux-gnu ln -s libssl.so.1.0.0 libssl.so.6
ignore_errors: yes
name: Copy nagios plugins
copy: src=check_http dest=/home/"{{ USER }}"/bin
name: Copy nagios plugins
copy: src=check_port.pl dest=/home/"{{ USER }}"/bin
name: Copy nagios plugins
copy: src=check_snmp_process.pl dest=/home/"{{ USER }}"/bin
name: Copy nagios plugins
copy: src=show_users dest=/home/"{{ USER }}"/bin
name: Copy perl utils
copy: src=utils.pm dest=/etc/perl
name: Copy perl utils
copy: src=utils.sh dest=/etc/perl
name: Confirm plugin file permissions
file: path=/home/nagios/bin/check* mode=0777
name: Confirm plug file ownership
file: path=/home/nagios/bin owner=nagios group=nagios recurse=yes
nagios-post-install
最后但肯定不是最不重要的是组成这个系列的最后一个角色。最后一个角色将完成 Nagios 配置,并设置 NConf 与你的 Nagios 安装一起工作。文件将被命名为main.yml,位于名为nagios-post-install/tasks的role目录中。以下是该文件的内容:
---
name: Copy NConf config file
copy: src=nconf.php dest=/var/www/html/nconf/config mode=0644
name: Change default Nagios config to use NConf
shell: chdir=/bin sed -i 's/^cfg_dir.*/#/g' /etc/nagios3/nagios.cfg
name: Change default Nagios config to use NConf
shell: chdir=/bin sed -i 's/^cfg_file.*/#/g' /etc/nagios3/nagios.cfg
name: Make import directory
file: path=/etc/nagios3/import state=directory
name: Copy Nagios config snippet
copy: src=nagios.txt dest=/usr/share
name: Change default Nagios config to use NConf
shell: chdir=/usr/share cat /usr/share/nagios.txt >> /etc/nagios3/nagios.cfg
name: Copy updated NConf deployment script
copy: src=deploy_local.sh dest=/var/www/html/nconf/ADD-ONS mode=0777
为了支持这些角色,我们现在需要创建与之配套的变量文件。由于我们将使用三个单独的主机来执行一系列角色,因此需要三个全局变量文件。文件名分别为hosts、all_containers和nagios-server;它们将保存在 playbook 的group_vars/目录中。
提示
请记住,变量文件中定义的值是为了在正常的日常使用中在每次执行前进行更改的。
在本章中添加了许多新变量。让我们花点时间来审查每个变量文件的内容:
all_containers
# Here are variables related to the install
USER: nagios
SNMP_COMMUNITY: osad
SYS_LOCATION: SAT
SYS_CONTACT: support@rackspace.com
hosts
# Here are variables related to the install
USER: nagios
SNMP_COMMUNITY: osad
SYS_LOCATION: SAT
SYS_CONTACT: support@rackspace.com
nagios-server
# Here are variables related to the install
DB_NAME: NCONF_DB
DB_USER: root
DB_PASS: passwd
让我们花点时间来分解新的变量。总结如下:
USER # user to be created on the OSA nodes to match up
against the default Nagios user created,
the default user is 'nagios'
SNMP_COMMUNITY # the SNMP community string used for
the OSA nodes and containers
SYS_LOCATION # additional SNMP information (optional)
SYS_CONTACT # additional SNMP information (optional)
DB_NAME # name of the NConf database to be created
DB_USER # root user for the local mysql server
DB_PASS # root user password for the local mysql server
注意
由于有两个共享相同变量名称的全局变量文件,请确保如果您希望两个报告在同一个目录中,则保持变量值同步。
完成变量文件后,我们可以继续创建主 playbook 文件。由于在运行 playbook 之间需要进行手动配置,主 playbook 被分成了多个 playbook。第一个名为base.yml的 playbook 的内容如下:
---
# This playbook deploys components needed for Infrastructure hosts and containers.
hosts: all_containers
remote_user: root
become: true
roles:
- snmp-config
hosts: hosts
remote_user: root
become: true
roles:
- snmp-config
下一个 playbook 的名称为base-nagios.yml,内容如下:
---
# This playbook deploys components needed for Nagios.
hosts: nagios-server
remote_user: root
become: true
roles:
- install-nagios
- nagios-plugins
以下 playbook 的名称为base-nconf.yml,内容如下:
---
# This playbook deploys components needed for NConf.
hosts: nagios-server
remote_user: root
become: true
roles:
- install-nconf
下一个 playbook 的名称为post-nconf-install.yml,内容如下:
---
# This playbook deploys components needed for NConf.
hosts: nagios-server
remote_user: root
become: true
roles:
- nconf-post-install
下一个 playbook 的名称为base-infra.yml,内容如下:
---
# This playbook deploys components needed for the Infrastructure hosts.
hosts: hosts
remote_user: root
become: true
roles:
- create-nagios-user
- infra-plugin-config
下一个 playbook 的名称为post-nagios-install.yml,内容如下:
---
# This playbook deploys components needed for NConf.
hosts: nagios-server
remote_user: root
become: true
roles:
- nagios-post-install
注意
playbook 和 role 的名称可以是您选择的任何内容。这里提供了具体的名称,以便您可以轻松地跟踪并引用 GitHub 存储库中找到的完成代码。唯一的警告是,无论您决定如何命名角色,都必须在 playbook 中引用时保持统一。
我们的 playbooks 和 roles 的清单文件非常简单。在清单文件中,我们只需要定义 Nagios 服务器的地址。示例如下:
[nagios-server]
021579-nagios01
希望您对所有的工作都感到满意。按照我们的传统,我们将以快速审查刚刚创建的 playbooks 和 roles 结束本章,并包含一些额外的指示。
审查 playbook 和 role
让我们立即开始检查我们创建的角色。
位于snmp-config/tasks目录中的完成角色和文件名为main.yml的文件如下:
---
name: Install additional packages
apt: name="{{ item }}" state=present
with_items:
- snmpd
name: Move standard config
command: mv /etc/snmp/snmpd.conf /etc/snmp/snmpd.conf.org
name: Place new config file
template: src=snmpd.conf dest=/etc/snmp/snmpd.conf
name: Update SNMP options
shell: chdir=/bin sed -i 's+^SNMPDOPTS.*+SNMPDOPTS="-Lsd -Lf /dev/null -u snmp -I -smux -p /var/run/snmpd.pid -c /etc/snmp/snmpd.conf"+' /etc/default/snmpd
name: Stop SNMP service
command: service snmpd stop
name: Start SNMP service
command: service snmpd start
name: Set SNMP service to start automatically
command: update-rc.d snmpd defaults
位于install-nagios/tasks目录中的完成角色和文件名为main.yml的文件如下:
---
name: Install additional packages
apt: name={{item}} state=present
with_items:
- vim
- nagios3
- unzip
name: Backup Nagios config files
command: cp -r /etc/nagios3 /etc/nagios3.backup
name: Check Nagios service
shell: ps -ef |grep nagios3
name: Create user .ssh directory
file: path=/var/lib/nagios/.ssh state=directory
ignore_errors: yes
name: Copy SSH private keys
copy: src=id_dsa dest=/var/lib/nagios/.ssh mode=0600
name: Copy SSH public keys
copy: src=id_dsa.pub dest=/var/lib/nagios/.ssh mode=0644
name: Copy nagios Ubuntu logo archive
copy: src=nagios-ubuntu-logo.tar dest=/usr/share
name: Decompress nagios Ubuntu logo archive
command: chdir=/usr/share tar xvf nagios-ubuntu-logo.tar -C /usr/share/nagios/htdocs/images/logos/base
位于nagios-plugins/tasks目录中的完成角色和文件名为main.yml的文件如下:
---
name: Copy nagios plugins tar file
copy: src=nagios-plugins.tar dest=/usr/share
name: Decompress nagios plugins archive
command: chdir=/usr/share tar xvf nagios-plugins.tar -C /usr/lib/nagios/plugins
name: Confirm plugin file permissions
file: path=/usr/lib/nagios/plugins/* mode=0774
name: Confirm plugin file ownership
file: path=/usr/lib/nagios/plugins owner=nagios owner=nagios recurse=yes
name: Copy nagios configs zip file
copy: src=NagiosConfig.zip dest=/usr/share
name: Create rpc-nagios-configs directory
file: path=/etc/nagios3/rpc-nagios-configs state=directory
name: Decompress nagios configs archive
command: chdir=/usr/share unzip NagiosConfig.zip -d /etc/nagios3/rpc-nagios-configs
位于install-nconf/tasks目录中的完成角色和文件名为main.yml的文件如下:
---
name: Install additional packages
apt: name={{item}} state=present
with_items:
- mysql-server
- mysql-client
- php5
- libapache2-mod-php5
- php5-mysql
- php5-cli
- php5-common
- libtext-csv-xs-perl
- name: Download NConf binaries
command: wget http://sourceforge.net/projects/nconf/files/nconf/1.3.0-0/nconf-1.3.0-0.tgz/download -O /usr/share/nconf-1.3.0-0.tgz
name: Unpack NConf binaries
command: chdir=/usr/share tar xzvf nconf-1.3.0-0.tgz -C /var/www/html
name: Set proper NConf directory permissions
command: chdir=/var/www/html/nconf chmod 644 config output static_cfg temp
- name: Copy NConf DB script
template: src=create-nconf-db.sql dest=/usr/share
name: Create NConf DB
shell: chdir=/usr/bin mysql -u "{{ DB_USER }}" --password= < /usr/share/create-nconf-db.sql
- name: Set NConf directory ownership
file: path=/var/www/html/nconf owner=www-data group=www-data recurse=yes
name: Set NConf directory permissions
file: path=/var/www/html/nconf mode=0777
name: Stop apache
command: service apache2 stop
name: Start apache
command: service apache2 start
位于nconf-post-install/tasks目录中的完成角色和文件名为main.yml的文件如下:
---
name: Remove installation directories and files
command: rm -r /var/www/html/nconf/INSTALL
name: Remove installation directories and files
command: rm /var/www/html/nconf/INSTALL.php
name: Remove installation directories and files
command: rm -r /var/www/html/nconf/UPDATE
name: Remove installation directories and files
command: rm /var/www/html/nconf/UPDATE.php
位于create-nagios-user/tasks目录中的完成角色和文件名为main.yml的文件如下:
---
name: Create Nagios user
user: name="{{ USER }}" comment="{{ USER }} User"
ignore_errors: yes
name: Create user .ssh directory
file: path=/home/"{{ USER }}"/.ssh state=directory
name: Copy authorized keys
copy: src=nagios-key dest=/home/"{{ USER }}"/.ssh/authorized_keys mode=0644
name: Set home directory permissions
file: path=/home/"{{ USER }}" owner="{{ USER }}" group="{{ USER }}" recurse=yes
位于infra-plugin-config/tasks目录中的完成角色和文件名为main.yml的文件如下:
---
name: Install additional packages
apt: name={{item}} state=present
with_items:
- libnet-snmp-perl
name: Create bin directory
file: path=/home/"{{ USER }}"/bin state=directory
ignore_errors: yes
name: Fix libcrypto link
command: chdir=/lib/x86_64-linux-gnu ln -s libcrypto.so.1.0.0 libcrypto.so.6
ignore_errors: yes
name: Fix libcrypto link
command: chdir=/lib/x86_64-linux-gnu ln -s libssl.so.1.0.0 libssl.so.6
ignore_errors: yes
name: Copy nagios plugins
copy: src=check_http dest=/home/"{{ USER }}"/bin
name: Copy nagios plugins
copy: src=check_port.pl dest=/home/"{{ USER }}"/bin
name: Copy nagios plugins
copy: src=check_snmp_process.pl dest=/home/"{{ USER }}"/bin
name: Copy nagios plugins
copy: src=show_users dest=/home/"{{ USER }}"/bin
name: Copy perl utils
copy: src=utils.pm dest=/etc/perl
name: Copy perl utils
copy: src=utils.sh dest=/etc/perl
name: Confirm plugin file permissions
file: path=/home/nagios/bin/check* mode=0777
name: Confirm plug file ownership
file: path=/home/nagios/bin owner=nagios group=nagios recurse=yes
位于nagios-post-install/tasks目录中的完成角色和文件名为main.yml的文件如下:
---
name: Copy NConf config file
copy: src=nconf.php dest=/var/www/html/nconf/config mode=0644
name: Change default Nagios config to use NConf
shell: chdir=/bin sed -i 's/^cfg_dir.*/#/g' /etc/nagios3/nagios.cfg
name: Change default Nagios config to use NConf
shell: chdir=/bin sed -i 's/^cfg_file.*/#/g' /etc/nagios3/nagios.cfg
name: Make import directory
file: path=/etc/nagios3/import state=directory
name: Copy Nagios config snippet
copy: src=nagios.txt dest=/usr/share
name: Change default Nagios config to use NConf
shell: chdir=/usr/share cat /usr/share/nagios.txt >> /etc/nagios3/nagios.cfg
name: Copy updated NConf deployment script
copy: src=deploy_local.sh dest=/var/www/html/nconf/ADD-ONS mode=0777
对应的全局变量文件名为all_containers,保存在完整 playbook 的group_vars/目录中:
# Here are variables related to the install
USER: nagios
SNMP_COMMUNITY: osad
SYS_LOCATION: SAT
SYS_CONTACT: support@rackspace.com
对应的全局变量文件名为hosts,保存在完整 playbook 的group_vars/目录中:
# Here are variables related to the install
USER: nagios
SNMP_COMMUNITY: osad
SYS_LOCATION: SAT
SYS_CONTACT: support@rackspace.com
对应的全局变量文件名为nagios-server,保存在完整 playbook 的group_vars/目录中:
# Here are variables related to the install
DB_NAME: NCONF_DB
DB_USER: root
DB_PASS: passwd
现在主 playbook 文件已经创建,并位于playbook目录的root目录中:
base.yml
---
# This playbook deploys components needed for Infrastructure hosts and
containers.
hosts: all_containers
remote_user: root
become: true
roles:
- snmp-config
hosts: hosts
remote_user: root
become: true
roles:
- snmp-config
base-nagios.yml
---
# This playbook deploys components needed for Nagios.
hosts: nagios-server
remote_user: root
become: true
roles:
- install-nagios
- nagios-plugins
base-nconf.yml
---
# This playbook deploys components needed for NConf.
hosts: nagios-server
remote_user: root
become: true
roles:
- install-nconf
post-nconf-install.yml
---
# This playbook deploys components needed for NConf.
hosts: nagios-server
remote_user: root
become: true
roles:
- nconf-post-install
base-infra.yml
---
# This playbook deploys components needed for the Infrastructure hosts.
hosts: hosts
remote_user: root
become: true
roles:
- create-nagios-user
- infra-plugin-config
post-nagios-install.yml
---
# This playbook deploys components needed for NConf.
hosts: nagios-server
remote_user: root
become: true
roles:
- nagios-post-install
最后,我们创建了hosts文件,也位于playbook目录的root目录中:
[nagios-server]
021579-nagios01
注意
完整的代码集可以在以下 GitHub 存储库中再次找到:https://github.com/os-admin-with-ansible/os-admin-with-ansible-v2/tree/master/nagios-openstack。
在完成本主题之前,我们当然需要测试我们的工作,并添加一些额外的说明来完成 Nagios 设置。在运行这些 playbooks 和 roles 之后,您将拥有一个功能强大的监视机器,用于监视您的 OpenStack 云和其他应用程序。假设您之前已经克隆了 GitHub 存储库,从部署节点测试 playbook 的命令如下:
- 将 playbooks 和 roles 移动到 OSA 部署目录中。
为了利用 OSA 提供的动态清单功能,playbooks 和 roles 需要在部署目录中。相信我,你会喜欢这个!
**$ cd os-admin-with-ansible-v2/nagios-openstack**
**$ mkdir /opt/openstack-ansible/playbooks/groups_vars**
**$ cp ~/nagios-openstack/group_vars/* /opt/openstack-
ansible/playbooks/group_vars**
**$ cp -r ~/nagios-openstack/roles/* /opt/openstack-ansible/roles**
**$ cp ~/nagios-openstack/base* /opt/openstack-ansible/playbooks**
**$ cp ~/nagios-openstack/hosts /opt/openstack-ansible/playbooks**
- 执行以下 playbook 来安装和配置 OSA 云上的 SNMP:
**$ cd /opt/openstack-ansible/**
**$ openstack-ansible -i inventory/dynamic_inventory.py
playbooks/base.yml**
提示
如果 SNMP 服务第一次启动失败,请执行以下命令:
**$ ansible all_containers -m shell -a "service snmpd start"**
**$ ansible hosts -m shell -a "service snmpd start"**
- 执行以下 playbook 来在监视服务器上安装和配置 Nagios:
**$ cd playbooks**
**$ openstack-ansible -i hosts base-nagios.yml**
然后通过 SSH 连接到监视服务器,执行以下命令来设置nagiosadmin用户密码(用于登录 Nagios Web 仪表板)并重新启动 Nagios:
**$ cd /etc/nagios3**
**$ sudo htpasswd -c /etc/nagios3/htpasswd.users nagiosadmin**
**$ service nagios3 restart**
- 执行以下 playbook 来在监视服务器上安装和配置 NConf:
**$ openstack-ansible -i hosts base-nconf.yml**
- NConf 初始配置:我尝试自动化这部分工作并不成功,所以您必须使用 NConf Web 控制台完成 NConf 配置。浏览
http://<monitoring server IP>/nconf并按照提示完成初始配置。以下是建议的输入,并保留其他默认设置:
DBNAME: same as what you inputed in the variables file above
DBUSER: same as what you inputed in the variables file above
DBPASS: same as what you inputed in the variables file above
NCONFDIR: /var/www/html/nconf
NAGIOS_BIN: /usr/sbin/nagios3
- 执行后 NConf playbook:
**$ openstack-ansible -i hosts post-nconf-install.yml**
- 执行以下 playbook 来配置 OSA 节点以允许通过 SSH 进行监视:
为了监视运行在本地容器上的 OpenStack 进程和 API,您必须通过 SSH 远程运行服务检查。好消息是,Nagios 插件已经存在(check_by_ssh):
**$ cd ..**
**$ openstack-ansible -i
inventory/dynamic_inventory.py
playbooks/base-infra.yml**
-
确认 Nagios 和 NConf 的安装:在浏览器中,转到以下 URL:
-
http://<monitoring server IP>/nagios3 -
http://<monitoring server IP>/nconf
- 是时候为监视 OSA 配置 Nagios 了。
不幸的是,这部分需要手动配置,因为每个安装的差异太大,无法自动化。从宏观上看,这只会帮助您提高 Nagios 技能。不用担心;Nagios 目录的副本已经被保存。这一步需要一些时间,不应该被匆忙完成。
第一步是自定义位于监视服务器上/etc/nagios3/rpc-nagios-configs目录中的 Nagios 配置文件。所有配置文件都很重要,但最关键的是advanced_services.cfg和hosts.cfg文件。
在advanced_services.cfg文件中,您需要更新每个服务检查的 IP 地址,以便与 OSA 安装中的容器的 IP 地址匹配。获取信息的最快方法是执行以下命令,并在每个基础设施节点上捕获输出:lxc-ls --fancy。这是一个例子:
define service {
service_description infra1_check_ssh_process_glance-api
check_command check_by_ssh_process!<glance container
IP>!glance-api
check_period 24x7
notification_period 24x7
host_name <OSAD node name>
contact_groups +admins,rpc-openstack-support
use rpc-service
}
hosts.cfg文件也是一样;请更新 OSA 节点的名称和 IP 地址:
define host {
host_name <OSAD node name>
address <OSAD node IP>
icon_image_alt Ubuntu 14.04
icon_image base/ubuntu.gif
statusmap_image base/ubuntu.gd2
check_command check-host-alive
check_period 24x7
notification_period 24x7
contact_groups +admins,rpc-openstack-support
use rpc-node
}
还需要将以下内容添加到 Nagios 目录根目录(/etc/nagios3)中的resources.cfg文件的底部:
**$USER10$=<random SNMP community string of your choice, keep it
simple>**
如果您在使用编辑器进行配置更新时遇到问题,请不要感到紧张,因为下一步将使这个过程变得更容易。
- 将 Nagios 配置导入 NConf
然后将/etc/nagios3/rpc-nagios-configs目录中配置文件的内容附加到当前 Nagios 配置文件的底部。每个主机、主机组、检查、服务和联系组都有唯一的名称,以避免与当前 Nagios 设置冲突。然后,我们将按照 NConf 网站上的说明进行操作,www.nconf.org/dokuwiki/doku.php?id=nconf:help:how_tos:import:import_nagios。
正如 NConf 教程建议的那样,首先使用-s参数运行命令来模拟导入过程。在能够无错误运行后,删除-s参数进行最终导入。通过 SSH 连接到监控服务器后,运行以下命令:
**$ cd /var/www/html/nconf**
**$ bin/add_items_from_nagios.pl -c
timeperiod -f /path/to/timeperiods.cfg -s**
**$ bin/add_items_from_nagios.pl -c
misccommand -f /path/to/misccommands.cfg -s**
**$ bin/add_items_from_nagios.pl -c
checkcommand -f
/path/to/checkcommands.cfg -s**
**$ bin/add_items_from_nagios.pl -c contact -f
/path/to/contacts.cfg -s**
**$ bin/add_items_from_nagios.pl -c
contactgroup -f
/path/to/contactgroups.cfg -s**
**$ bin/add_items_from_nagios.pl -c
host-template -f
/path/to/host_templates.cfg -s**
**$ bin/add_items_from_nagios.pl -c
service-template -f
/path/to/service_templates.cfg -s**
**$ bin/add_items_from_nagios.pl -c hostgroup -f
/path/to/hostgroups.cfg -s**
**$ bin/add_items_from_nagios.pl -c host -f
/path/to/hosts.cfg -s**
**$ bin/add_items_from_nagios.pl -c advanced-
service -f /path/to/advanced-services.cfg -s**
现在您可以在 NConf Web 控制台中编辑所有 Nagios 配置。
- 执行后续的 Nagios playbook:
**$ cd playbooks**
**$ openstack-ansible -i hosts post-nagios-
install.yml**
- 生成你的第一个 Nagios 配置
一旦您对所有自定义的 Nagios 配置感到满意(相信我,您会做几次这样的事情),请点击 NConf Web 控制台侧边栏上的生成 Nagios 配置链接。它会提示是否遇到任何错误。有时,您会看到警告,它们只是警告,没有什么紧急的。
最后但同样重要的是,从监控服务器执行以下命令将 Nagios 配置部署到 Nagios(可能需要使用sudo):
**$ cd /var/www/html/nconf/ADD-ONS**
**$ ./deploy_local.sh**
总结
拥有一个坚如磐石的监控平台是云成功的关键(实际上是任何生产系统的成功)。请记住,这只是一个起点。我期待您根据自己的特定需求进行改进/定制。我期待在未来看到您的出色工作。请确保分享您所做的任何更改,记住开源就是分享。在结束这最后一章之前,让我们花点时间回顾一下我们讨论过的内容。首先,我们介绍了一些监控技巧和诀窍。然后,我们研究了值得监控的 OpenStack 组件。接下来,我们学习了如何使用 Ansible 的临时命令。然后,我们转入如何设置 Nagios 并导入用于服务检查的自定义插件。最后,我们开发了 Ansible playbook 和 role 来自动化 Nagios 和 NConf 的基本安装,并对其进行定制,以完全监控 OpenStack 云。
女士们先生们,这真的很有趣,也很荣幸能够第二次与您分享这些自动化示例。请继续保持良好的工作,并密切关注未来的修订,因为 OpenStack 和 Ansible 都在不断成熟。我真的很期待听到您的反馈,并看到您如何将这些示例提升到下一个水平。


浙公网安备 33010602011771号