Tips:与前文 《进击的 Ansible(一):Ansible 快速入门》 一样,本文使用的 Ansible 版本 2.5.4,项目演示环境 MacOS。由于 Ansible 项目开发活跃版本更新快,很多 API 接口不向后兼容,所以对照本文实践时请确保所用版本一致。

学完前文《进击的 Ansible(一):Ansible 快速入门》后,用来发布单体项目绰绰有余。但是实际生产环境中一个服务往往有多个组件,比如部署大数据服务时,常常需要部署一个“大数据全家桶”:Hadoop、 Zookeeper、 Hive、 Mysql、 Flink 等。这时仅靠前文中的知识就有点捉襟见肘了,繁多的 yaml 文件和其他配置文件依赖关系复杂,如果不能正确地划分目录组织项目结构,对于后期维护非常不利。所以今天的文章着重解决一下这个问题:如何科学正确地划分 Ansible 应用的目录结构?

把 Ansible 视为一种编程语言

首先要树立这样一个观念:“把 Ansible 视为一种编程语言”。我们可以将 Ansible 理解为专门用来管理自动化发布的 DSL,它的基本语法规则约等于 yaml 语言规则,诸如 synchronize、pip、template 等。同时 Ansible 模块可等价为语言的内置函数或内置包,也就是编写 playbook 就是在写 Ansible 这门语言的代码块。如此,只要我们沿着编程语言的思路去理解 Ansible,很多疑惑会迎刃而解:比如 Ansible 支持变量,模块 when 就是编程语言的流程控制语句 if, 模块 loop 或 with_* 就是编程语言中的循环迭代语句 for 或 while。

使用编程语言进行项目开发的过程中,我们是如何降低项目复杂度的?当然是进行“模块化”。不同的功能封装到不同的包或文件中,这样构成一个业务功能的最小单位我们称之为“模块”。在项目的入口文件中,我们通过 import (Python、Golang 等语言中使用此关键字) 或 require (Node.js 等语言使用此关键字)等关键字把需要的模块载入进来,然后就可以进行业务逻辑上的编排。

这样做的优点显而易见,一个模块是一个业务功能的具体实现,当后期有修改的需求时只需要修改相关的模块即可,这正是 SOLID 原则中的 “SRP” (单一职责原则) 所提倡的。此外,模块化还支持像“搭积木”一样根据业务需求灵活地组织业务流程,这样就能最大限度地复用当前模块,这也符合编程原则中的 “DRY” (Don't Repeat Yourself)。

因此在我们将 Ansible 视为一门编程语言(DSL)的现在,我们可以发现在 Ansible 文档中用 Roles 表示“模块”这个概念,用 import_tasks、include_tasks 等表示 import 这个概念。这里说一个题外话,不知道有没有人觉得 Ansible 中发明的“playbook”、“roles” 等直译成“剧本”、“角色”的概念让人摸不着头脑?

至关重要的概念:Roles

现在我们树立了“Roles = 模块”的概念。我们在看一下文档中对 Roles 的定义,丰富一下细节:

Roles let you automatically load related vars, files, tasks, handlers, and other Ansible artifacts based on a known file structure. After you group your content in roles, you can easily reuse them and share them with other users.

“角色允许您基于已知的文件结构自动加载相关的变量、文件、任务、处理程序和其他Ansible工件。在将内容按角色分组后,可以轻松地重用它们并与其他用户共享它们。”

直白来讲就是 Roles 是对变量、文件、任务等的封装,目的是为了模块重用。

如何使用 Roles?

在软件设计范式的最佳实践中有一条叫做 “约定大于配置”,简单来讲就是“软件中做了一些前提性假设,这些假设就是软件开发者与软件用户的约定。作为软件用户你遵守这些约定即可,不再需要(或不支持)对这些约定进行配置。”—— 一定程度上可以理解为软件中的默认配置。

Ansible 在使用 Roles 时同样存在 “约定大于配置” 的情况,并且这些约定是硬性的(即不支持在配置文件中自定义)。

首先,Role 的目录结构是固定的。

An Ansible role has a defined directory structure with eight main standard directories. You must include at least one of these directories in each role. You can omit any directories the role does not use.

Ansible 角色有一个定义好的目录结构,其中有八个主要的标准目录。该已规定好的目录结构,示例如下:

├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml

By default Ansible will look in each directory within a role for a main.yml file for relevant content.

这八个目录的作用是:

  • tasks/main.yml:放置 role 执行任务时用到的文件。

  • handlers/main.yml:处理程序,可以在 role 内部或外部使用

  • library/my_module.py:模块,可以在 role 中使用(有关更多信息,请参见在rroles 中嵌入模块和插件)。

  • defaults/main.yml:role 的默认变量(有关更多信息,请参阅使用变量)。这些变量具有所有可用变量中最低的优先级,并且可以被任何其他变量(包括库存变量)轻松覆盖。

  • vars/main.yml:role 中的其他变量。(与 Ansible 模块中的 vars 作用一致,只不过这里的 vars 表示目录。)

  • files/main.yml:role 部署时用到的文件。

  • templates/main.yml:role 部署时用到的模板。与 Ansible 模块中的 templates 作用一致,只不过这里的 templates 表示目录。)

  • meta/main.yml:role 使用到的元数据。

在每个角色中必须包含至少一个这样的目录,当然我们可以省略角色不使用的任何目录,但是每个目录下的 main.yml 文件是该目录的入口文件,Ansible 读取时会默认查找该文件。所以这个 main.yml 文件不能省略。

其次,存储和查找 roles。

上文我们已经了解到了一个 role 的内部目录结构,但是这远远不能满足实际生产的需求。在文章的开始部分我们也以一个大数据项目为例,往往需要部署 Flink、Hadoop 等多个组件。这每一个组件都可以看做是一个 role。那么 Ansible 是如何查找 roles 的呢?

默认情况下,有 2 种方式:

  • 在 Ansible 的发布项目中,创建一个叫做 roles 的目录。

  • 默认情况下,Ansible 会自动查找 /etc/ansible/roles 目录下的 role。

举个例子,我们要使用 Ansible 创建一个发布大数据“全家桶”的项目 bigdata,该项目下要包含 Flink、Mysql、Hive 这 3 个 role。那么 bigdata 这个项目的目录结构大致如下:

➜  bigdata tree -L 3
.
└── roles
├── flink
│ ├── defaults
│ ├── files
│ ├── handlers
│ ├── meta
│ ├── tasks
│ ├── templates
│ ├── tests
│ └── vars
├── hive
│ ├── defaults
│ ├── files
│ ├── handlers
│ ├── meta
│ ├── tasks
│ ├── templates
│ ├── tests
│ └── vars
└── mysql
├── defaults
├── files
├── handlers
├── meta
├── tasks
├── templates
├── tests
└── vars

很明显可以看到 roles 下的 Flink、hive、Mysql 的子目录结构就是上文中提到的八大目录。

ansible-galaxy

快速创建 Role

从上文可知,每创建一个 role 都必须至少含有八大目录之一。所以 Ansible 中已内置了一个命令行工具 ansible-galaxy 快速创建 role 的八大目录,减轻我们的工作量。

假设该 role 名称是 flink,用如下命令生成相关目录:

ansible-galaxy init flink

使用 tree 命令看到 ansible-galaxy 生成的目录正是 role 所要求的标准的八个目录。

➜  tree flink
flink
├── README.md
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml

ansible-galaxy init 其他几个实用的参数:

  • ansible-galaxy init -force role_name,默认情况下创建的 role 与当前工作目录下存在的文件重名的话,会抛出异常。使用 -force 选项会强制创建 role 目录,并对 role 目录下重名的目录或文件进行替换。

  • ansible-galaxy init --role-skeleton=/path/to/skeleton role_name,使用过 Maven 的同学应该知道, Maven 支持以其他项目做骨架创建新项目。ansible-galaxy 同样支持该功能,以 /path/to/skeleton 路径下的 role 为骨架,把所有的文件都进行拷贝来创建新的 role。

Galaxy:role 在线分享社区

此外与 Docker Hub、Grafana Dashboards 类似,Ansible Galaxy 也有一个在线社区 Galaxy[https://galaxy.ansible.com/home],上面有开发者分享的各种已经开发好的 roles。方便我们搜索现成的 role 下载,也可以上传自己开发的 role 到 Galaxy。

下载或上传 role 到 Galaxy 网站,同样需要使用命令行工具 ansible-galaxy。默认情况下 ansible-galaxy 调用的 Galaxy 服务端的地址是 https://galaxy.ansible.com, 可以通过 -server 选项或在 ansible.cfg 文件中重新配置 Galaxy 的地址。

下载 roles

下载 roles 的语法模板是:

$ ansible-galaxy install username.role_name

默认情况下 ansible-galaxy 会把 role 下载到环境变量 ANSIBLE_ROLES_PATH 中,ansible-galaxy 提供了参数 --role_path 指定 role 下载的地址。

ansible-galaxy 其他命令速览

  • ansible-galaxy search elasticsearch,查找 Galaxy 网站中的 role elasticsearch

  • ansible-galaxy info username.role_name,查看 username.role_name 的详细信息

  • ansible-galaxy list,查看已安装的 roles

  • ansible-galaxy remove username.role_name,卸载安装的 username.role_name

  • ansible-galaxy login,登录 Galaxy 网站

整体布局:Suit is best

如果你坚持读完上述部分,那么你肯定对于如何使用 role 了然于心,简单来讲就是当前 Ansible 应用下需要存在一个叫做 roles 的目录。接下来我们聊聊 Ansible 应用下除了 roles 目录外,其他目录该如何布局呢?Ansible 最佳实践官方文档[https://docs.ansible.com/ansible/2.5/user_guide/playbooks_best_practices.html#content-organization]中是这样建议的:

Your usage of Ansible should fit your needs, however, not ours, so feel free to modify this approach and organize as you see fit.

One crucial way to organize your playbook content is Ansible’s “roles” organization feature, which is documented as part of the main playbooks page. You should take the time to read and understand the roles documentation which is available here: Roles.

Ansible 整体的目录结构没有一定之规,适合你的当前需求就好。但是 Roles 这个概念至关重要。

良心的 Ansible 官方在 Github 上开了一个项目 ansible-examples[https://github.com/ansible/ansible-examples]专门用来收集优秀的最佳实践。大家可以根据实际需求吸收借鉴,下面我分享一下我常用的项目布局:


.
├── Makefile
├── README.md
├── deploy.retry
├── deploy.yml
├── files
│ ├── apache-maven-3.8.3-bin.tar.gz
│ ├── apache-zookeeper-3.7.0-bin.tar.gz
│ ├── flink-1.14.0-bin-scala_2.11.tgz
│ ├── hadoop-2.7.5.tar
│ ├── hadoop-3.3.1.tar.gz
│ ├── mysql-connector-java-8.0.26-1.el7.noarch.rpm
│ ├── mysql-connector-java-8.0.26.jar
│ └── openjdk-11.0.2_linux-x64_bin.tar.gz
├── inventories
│ └── hosts
└── roles
├── flink
├── hadoop
├── hadoop3
├── hive
├── java
├── linux
├── mvn
├── mysql
└── zookeeper
  • Makefile,用来封装 Ansible 的发布命令

  • deploy.ym,是执行 Ansible 命令时的入口文件

  • files,用来存放 role 相关的部署包,一般体积较大,不会使用 git 进行版本管理。

  • inventories,用来管理部署机器。

  • roles,用来部署的组件。从上面的目录可知,当前主要是用来部署大数据相关的组件。

给自己打个小广告:在下一个实战章节将使用这个项目布局发布大数据项目,在这个过程中又需要补充哪些 Ansible 的知识呢?为什么我不直接使用 Galaxy 网站上的 role 而是要自己从头开发呢?在部署 Hadoop3、Flink 项目中,使用 Ansible 又踩了哪些坑呢?

敬请期待 《进击的 Ansible(三):Ansible 大数据实践》

参考资料

  • 使用 Ansible 传输文件的几种方式:

https://zdyxry.github.io/2019/11/22/使用-Ansible-传输文件的几种方式/

  • Ansible:

https://gist.github.com/MrNice/89a3bbe44e218c9d2309

  • Roles:

https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html#id2

  • Galaxy User Guide:

https://docs.ansible.com/ansible/2.5/reference_appendices/galaxy.html

  • 约定优于配置:

https://zh.wikipedia.org/wiki/约定优于配置

推荐阅读

一文读懂浏览器存储与缓存机制

Python Type Hints 从入门到实践

进击的 Ansible(二):如何快速搞定生产环境 Ansible 项目布局?的更多相关文章

  1. Spring Boot 之 Profile --快速搞定多环境使用与切换

    Spring Profile是Spring3引入的概念,主要用在项目多环境运行的情况下,通过激活方式实现多环境切换,省去多环境切换时配置参数和文件的修改,并且Spring profile提供了多种激活 ...

  2. 手把手教你制作微信小程序,开源、免费、快速搞定

    最近做了个"罗孚传车"的小程序 一时兴起,做了一个小程序,将个人收集的同汽车相关的行业资讯和学习资料,分享到小程序中,既作为历史资料保存,又提供给更多的人学习和了解,还能装一下:) ...

  3. 10分钟快速搞定pandas

    本文是对pandas官方网站上<10 Minutes to pandas>的一个简单的翻译,原文在这里.这篇文章是对pandas的一个简单的介绍,详细的介绍请参考:Cookbook .习惯 ...

  4. 面试大总结之二:Java搞定面试中的二叉树题目

    package BinaryTreeSummary; import java.util.ArrayList; import java.util.Iterator; import java.util.L ...

  5. Spring Boot之Profile--快速搞定多环境使用与切换

    Spring Profile是Spring3引入的概念,主要用在项目多环境运行的情况下,通过激活方式实现多环境切换,省去多环境切换时配置参数和文件的修改,并且Spring profile提供了多种激活 ...

  6. 一文搞定Spring Boot + Vue 项目在Linux Mysql环境的部署(强烈建议收藏)

    本文介绍Spring Boot.Vue .Vue Element编写的项目,在Linux下的部署,系统采用Mysql数据库.按照本文进行项目部署,不迷路. 1. 前言 典型的软件开发,经过" ...

  7. Spring Boot (二)集成Jsp与生产环境部署

    一.简介 提起Java不得不说的一个开发场景就是Web开发,也是Java最热门的开发场景之一,说到Web开发绕不开的一个技术就是JSP,因为目前市面上仍有很多的公司在使用JSP,所以本文就来介绍一下S ...

  8. ruby on rails 生产环境调试项目日志查看

    1.项目目录:log/production.log 2.nginx日志:/opt/nginx/logs 生产环境下做的任何更改都要重启服务器 重启 sudo kill $(cat /opt/nginx ...

  9. 吐血整理:二叉树、红黑树、B&B+树超齐全,快速搞定数据结构

    前言 没有必要过度关注本文中二叉树的增删改导致的结构改变,规则操作什么的了解一下就好,看不下去就跳过,本文过多的XX树操作图片纯粹是为了作为规则记录,该文章主要目的是增强下个人对各种常用XX树的设计及 ...

随机推荐

  1. SharkCTF2021 pwn“初见”1

    (无内鬼 今日不想学了 水一篇) nc nc nc easyoverflow Intoverflow

  2. RabbitMQ设计原理解析

    背景 RabbitMQ现在用的也比较多,但是没有过去那么多啦.现在很多的流行或者常用技术或者思路都是从过去的思路中演变而来的.了解一些过去的技术,对有些人来说可能会产生众里寻他千百度的顿悟,加深对技术 ...

  3. 洛谷 P5658 [CSP-S2019] 括号树

    链接: P5658 分析: 显然我们应该在dfs树的同时维护每个点的答案. 注意到第 \(u\) 个点的答案可以分成两部分,不包含 \(u\) 点时的答案,和加入 \(u\) 点后新增的答案,前者可以 ...

  4. vcs(UST)Undefined System Task Call

    转载:VCS求助啊 - 微波EDA网 (mweda.com) Error-[UST] Undefined System Task Call../../path/bench/path.v, 51Unde ...

  5. lollipop_softap启动wifi ap失败

    最近一直在调试lollipop,翻译成中文好像是棒棒糖的意思,就是个wifi控制管理工具,比如设置DLNA或者WFD模式等,其原理是通过本地通信工具sockets控制其他接口来启动wpa_suplic ...

  6. MVC中单选按钮的实现

    -------------控制器-------------- ViewBag.Kinds = SYS_Category.List(xxxxxxxxxxxxxxxxxxxxxxx); --------- ...

  7. linux 内核源代码情景分析——linux 内核源码中的汇编语言代码

    1. 用汇编语言编写部分核心代码的原因: ① 操作系统内核中的底层程序直接与硬件打交道,需要用到一些专用的指令,而这些指令在C语言中并无对应的语言成分: ② CPU中的一些特殊指令也没有对应的C语言成 ...

  8. (转)刚来的大神彻底干掉了代码中的if else...

    一旦代码中 if-else 过多,就会大大的影响其可读性和可维护性. 首先可读性,不言而喻,过多的 if-else 代码和嵌套,会使阅读代码的人很难理解到底是什么意思.尤其是那些没有注释的代码. 其次 ...

  9. LINUX系统新增及自动挂载硬盘-九五小庞

    Linux系统下,添加新硬盘后,自动挂载的方法   1,列出所有硬盘,找到需要挂载的硬盘,例如/dev/vdb.输入: fdisk -l   2,查看硬盘是不是已经被挂载.一个硬盘不能重复挂载,已经挂 ...

  10. 说Redis

    一:简单介绍 Redis(Remote Dictionary Server 远程字典服务器) key-value 内存数据库 key是一个string value可以是string,list,hash ...