天天看点

Ansible 在知乎大数据的实践

作者:闪念基因

本文主要讲解运维架构的设计、选型、以及 Ansible 在知乎大数据实践。

1 背景

目前的大数据运维大致分为了三个方向:

  • 购买商业产品,例如 CDH。
  • 继续维护历史遗留的 Apache Ambari (社区停止维护,继续维护成本较高)。
  • 使用运维框架 Ansible、Chef、Puppet 来开发运维平台,或自研。

知乎大数据运维的发展分为以下三个阶段,单点、半自动、工程化。目前正处于工程化阶段。

  1. 单点阶段:由半自动化大量 Shell 脚本 + 零散 Salt 组成。此阶段主要问题是:
    1. 扩缩容、配置变更等大多数变更都成为了一次性的工作,无法实现幂等。
    2. 没有工程化,个人风格代码很多,他人很难参与共同维护,容易出现单点问题。
  2. 半自动阶段:开始使用 Ansible 按照原来历史遗留的 Shell 进行重构。这样解决了幂等问题,但又出现了部署效率慢、架构设计不合理等衍生问题;
  3. 工程化阶段:实现了工程与效率兼并。因为大数据 Hadoop 生态维护都是侧重配置与服务,急需一个工程化,多人协作的架构,既能解决运维效率问题、又能解决单点问题、还能满足知乎的专属大数据需求。

2 目标

知乎在工程化阶段的目标如下:

  • 快速兼容:短时间兼容大数据 Apache Hadoop 生态运维;
  • 历史接管:接管遗留脚本;
  • 工程化:方便配置管理和多人协作;
  • 降低运维心智:开发和使用的复杂度不能高于遗留 Shell 脚本。

3 大数据运维架构

3.1 架构概述

运维自动化是我们的长期目标,但是一味地追求自动化,容易忽略真正的业务诉求,比如业务后端架构或大数据架构自动化部署、配置变更、服务变更、跨机房迁移等场景的易用与稳定。

业务正是考验运维质量的试金石。什么样的架构设计是对开发和运维友好的、对业务负责的呢?我们总结了以下几个关键点:

  • 架构独立:优秀的运维系统需要具备独立部署、独立测试、依赖解耦的能力;
  • 部署友好:实现部署友好需要深入了解应用和业务的关系,评估潜在风险,对环境和依赖都需要慎重选择;
  • 降低运维心智成本:运维开发时需要额外考虑运维时的心智负担,尽量多做幂等性、保护性、可协作性开发,投入大量的稳定性开发工作才能换来更安全地变更;
  • 数据可观测:所有系统的设计之初,一定需要做到数据可观测,才能有效的迭代下去。数据可观测包括但不限于监控性、稳定性、扩展性、协作性。

3.2 框架选型

基于以上,我们需要在短时间内兼容以往的大数据运维系统,解决历史遗留问题,并保证工程化、多人协作和效率,同时降低运维时的心智成本。

我们进行了以下运维工具的对比:

- Puppet Chef Ansible CM&CDH Ambari
开发语言 Ruby Ruby Python Python&混合 Java
二次开发 支持 支持 支持 商业支持 支持(社区维护不稳定)
运维DSL Puppet DSL Chef DSL YAML WebUI WebUI
安装复杂度
架构 主从+代理 主从+代理 主从+无代理 主从+代理 主从+代理
通信与加密 HTTP、SSL HTTP、SSL SSH、OpenSSH HTTP、SSL HTTP、SSL
配置方式 PULL PULL PUSH & PULL PUSH & PULL PUSH & PULL
社区&文档 丰富 丰富 丰富 商业支持 社区维护不稳定
学习成本 中(商业支持) 高(社区维护不稳定)

(注:2023 年初社区启动了 Ambari 复活计划)

对比发现 Ansible 学习成本较低,满足低成本快速兼容的需求,且它的 Agentless (SSH) 架构天生具备快速运维的优势。所以,最终我们选择 Ansible 进行大数据自动化运维管理。

3.3 Ansible 介绍

Ansible 是一种用 Python 编写的开源自动化工具,具有非常强大的扩展性和可定制性,设计理念如下:

  • 提供了 Inventory 主机清单的功能,可以将需要维护的主机以及关键变量存放在此,方便管理;
  • 提供了模块 Modules 方便开发具体运维能力,例如复制、解压、配置更新等;
  • 提供了角色 Roles 功能,可以将多个 Modules 串联起来,方便依赖或重用;
  • 提供了剧本 Playbook 以实现复杂的工作流编排;
  • 提供了快速调试 Adhoc 与 Playbook 两种执行方式,可以实现多流程的一键自动化,也可以快速调试;
  • 所有功能自带幂等性,简单的调用即可实现幂等操作;
  • 学习需求极低,无需编程语言基础,只需要管理语言 YAML 即可;
  • 不需要在被管理的节点上安装任何代理程序;
  • 提供了丰富的内置优化策略,虽然比不过 Agent based 的工具,但是能够轻松胜任数千台规模的集群;
  • 支持几乎所有的操作系统和云平台。

3.4 Ansible 架构设计

架构分为四个部分:

  • Inventory 设计;
  • 合理使用模块 Modules 与插件 Plugins,进行功能扩展;
  • 遵循一键原则的剧本 Playbook,同时搭配标签 Tags 能够灵活组织流程;
  • 原子化、依赖化设计角色 Roles。
Ansible 在知乎大数据的实践

3.4.1 Inventory 设计

Inventory 一般是用于管理主机清单的功能。但它不仅仅是一个简单的清单,它可以上升到一个集群的 “文字拓扑描述”,使用该思路配置后,能清晰地了解集群中的程序、版本、关键变量配置等信息。

社区一般推荐使用动静结合模式(与 CMDB 对接),并可以配置多个 Inventory。但是我们目前的规模,使用静态模式足以支撑。社区还贴心地提供了灵活的 Inventory 对接模式,方便以后切换。

Inventory 的内部设计分为两部分,全局关键配置部分和主机组部分。

  1. 全局关键配置部分:存放一些核心全局关键变量,比如 Hadoop、Hive、Spark 等的版本信息、关键配置信息、关键目录信息,以及一些工程设计上的重要模式变量。
  2. 主机组部分:按照逐级放量的方式分配数量,比如 Hadoop DataNode 和 NodeManager 会定义多个组,组内的机器数量由1、10、500、数千组成,这样的好处是在变更的时候可以分组进行放量 (这会造成配置冗余,对接动态模式能缓解此问题),并且主机组还会搭配专属的主机组变量。

通过全局关键配置部分和主机组部分,以及全局关键变量和主机组变量的配合使用,能够快速了解集群拓扑现状,降低了拓扑环境学习的成本,通过对 Inventory 修改即可实现大多数变更,例如扩缩容、关键配置调整和版本升级。

3.4.2 功能扩展

Ansible 提供了 Modules 和 Plugins 来完成功能补充与扩展。

Modules 是用来补充 Ansible 内置模块的功能,使得用户可以通过自定义的 Modules 实现一些特定功能,比如管理文件、用户、安装软件、创建云资源等等。而且 Modules 的实现不限于特定语言,可以使用 Python、Bash、Perl、PowerShell 脚本等等。

Plugins 则是用来扩展其功能的组件,包括模块、连接器、变量插件、回调插件等等。Plugins 是可插入的代码单元,可以更改 Ansible 的行为和扩展其功能。Plugins 常用类型分为三种:Inventory Plugins、Connection Plugins、和 Callback Plugins。Ansible 插件通常用于实现一些高级自动化任务,比如使用插件来在部署过程中自定义通知、从不同的数据源中提取信息等。(注:Ansible 提供了10种以上的插件,此处只介绍了常用的3个)。

这里可以补充一下,该如何区分什么时候用 Modules、什么时候用 Plugins 呢?

Ansible 内置了 copy、template、unarchive 等高效模块,比如在使用这些模块功能后,需要调用外部接口获得相关数据,或者实现临时通知发送,此时可以使用 Modules 去补充。

整体剧本执行完想进行一些数据收集、数据统计、或者回调其他操作时,可以使用 Plugins 去扩展。

所以在此部分我们企业微信通知、特定服务接口数据获取等其他功能全用 Modules 来实现,而 Plugins 只使用了 Callback 部分。Var、Lookup、Action 等 Plugins 这些插件的使用门槛较高,使用不当容易适得其反。

3.4.3 一键操作

Playbook 的编排是运维内核设计的第一道考验。它是一种将系统配置视为目标状态的方法,而不是一系列必须执行的操作。换句话说,我们只需要描述系统的最终状态,而不是告诉 Ansible 需要执行哪些命令。

Playbook 提倡模块化和可重用性。每个 Playbook 可以包含一个或多个角色或任务,每个角色又包含多个模块,这种层级关系极大地提高了灵活性。模块可以使用标签进行区分,不但可以一键 Playbook 完成所有操作,还可以实现流程细节控制。

但是,在这么多关系和嵌套的前提下,很容易出现过度设计或冗余设计。例如,我们在部署 Hadoop 的 DataNode 和 NodeManager 时,经常会把同类型的操作放在一个 YAML 文件中,导致在实际执行时会出现很多干扰信息,出现大量的跳过,增加心智负担。所以我们重新思考了最开始的那句话,“我们只需要描述系统的最终状态,而不是具体要执行哪些命令”。

所以我们早期在 Playbook 里开发的创建目录、配置变量、服务启停等功能,发现不应该放在这里,而是应该使用 Roles 封装起来。 例如我们需要 “jdk、jmx_agent、execute_account、hadoop_pkg、datanode_conf、serv_state_check” 等相关 Roles,可以将对应的 Roles 组织起来,也可以把它们想象成软件开发中的方法来使用。

最后,Playbook 的设计将会如同编程语言开发一样,使用各种函数去编排逻辑。

3.4.4 角色设计

Roles 的设计核心思想是将系统配置分解为可重用的组件,以提高代码的可维护性和可复用性。通过将复杂的系统配置任务分解为多个独立的可重用角色,每个角色专注于完成特定的功能。

同时也因为 Roles 的开发是动态脚本,不像 Java 开发中 Spring Boot 这类重工业框架有着大量的约定和限制,而且每个 Roles 包含一组相关的任务和变量,在这种多重环境下,容易出现循环重用的问题,如何避免这样的问题呢?

  1. 确定 Role 的作用范围:在设计 Role 时,要明确其作用范围,并尽可能将其限制在特定的任务或功能领域内。这样可以确保 Role 只包含必要的任务和变量,避免不必要的可重用。
  2. 避免过于抽象的命名:为了使 Role 更加通用和可重用,有时会采用过于抽象的命名方式,例如 common 或 base。然而,这种抽象命名方式可能会导致 Roles 变得复杂和难以理解。因此,最好使用更具体和明确的命名方式,以便更好地表达 Roles 的作用范围和目的。
  3. 擅用依赖:有时候,会有一些通用的任务和变量,它们可以被多个 Role 共享。为了避免重复编写这些代码,可以将这些通用的任务和变量单独放在一个 Role 中,并将它们作为其他 Role 的依赖。
  4. 灵活使用 Role 的继承和包含特性:Ansible 提供了 Role 的继承和包含特性,这些特性可以帮助规避循环重用。通过继承和包含,可以将一些常用的任务和变量抽象为一个基础 Role,然后在其他 Role 中包含或继承这个基础 Role,避免重复编写代码。但是,在使用这些特性时,需要确保基础 Role 的作用范围清晰明确,以免引入不必要的复杂性。

在知乎,我们前后迭代了三类 Roles 的设计思路,从 “直白命令思路”、到 “类型划分思路”、最后到 “原子方法思路”,介绍如下:

3.4.4.1 直白命令思路

早期采用的是 “直白命令思路”,比如以下代码是 HDFS Router 进行的直接操作,这种角色的复用性、依赖性和兼容性都存在很多问题。因为这种简单直白的逻辑,都会使用许多外部变量来实现主要的逻辑,如果外部依赖发生了变化,Roles 将会失效。这种直白的场景通常是为了满足当时的需求而完成的,没有做更多的设计考量。

# 直白命令思路
├── hdfs
│   └── router
│       ├── files
│       │   ├── ***
│       ├── tasks
│       │   └── main.yml
│       └── templates
│           ├── ***
│           └── ***           

3.4.4.2 类型划分思路

中期的 “类型划分思路” 为 Roles 的模块化提供了一种框架思路,通过变量区分安装包、配置、服务的部署,从而提高了一定的复用性。但实际上并没有真正解决问题,反而增加了一些流程变量,加大了功能管理的复杂度。

如果按照此类思路设计,将无法兼容更为复杂的逻辑。而且会错误地认为使用 A 逻辑变量对应安装包,B 逻辑变量对应配置,在 Playbook 中随意使用逻辑变量,容易遇到 Ansible 大型工程中的另一个尴尬问题:“变量重复替换”。

# 类型划分思路
grafana
├── tasks
│   ├── cfg_file_deploy.yml
│   ├── main.yml (when cfg, when pkg, when serv)
│   ├── pkg_deploy.yml
│   └── serv_make.yml
└── templates
    ├── ***
    └── ***           

这个 “变量重复替换” 问题在知乎大数据运维内核的迭代历程中困扰了我们很久。因为在常规的开发中比如 Java 或 Go,在某段逻辑给某个变量进行了重新赋值后,新值可以在当前作用域生效。可是在 Ansible 中,如果一个变量有多次赋值,它不会区分作用域,而会取最后一次赋值的内容导致出现错误部署。

因此,我们总结了以下几点

  1. 规范 Roles :可以按照编程中的方法来理解,所有的方法都需要入参和出参,那么 Roles 中是所需要的变量都按照入参来定义即可。比如默认值、入参检测、参数类型、以及传递后可以用不同的变量名来接收;
  2. 规范作用域:虽然可以使用变量作用域来控制变量的生命周期,避免变量被重复替换,比如 hostvars 或 group_vars 来定义变量的作用域,这样每个主机或组都有自己独立的变量空间;
  3. 依赖表示:如果一个变量依赖于另一个变量的值,应该在 Role 中明确指出这种依赖关系。可以使用 Ansible 的变量依赖关系来实现这一点,而不是直接依赖赋值,否则容易出现一些未知变量不存在的问题。

3.4.4.3 原子方法思路

现在的 “原子方法思路”,牺牲了一点工程便捷性,提升了一些工程目录复杂度,换来的大量的原子 Roles,简单的理解就是我们把需要做的细节进行原子拆分,然后它们彼此之前可以使用参数传递,通过 Roles 级别的依赖进行约束,在 Playbook 中则只需要组合 Roles 即可,不需要去关心 Roles 的实现细节。所以此时我们再来理解 “我们只需要描述系统的最终状态,而不是具体要执行哪些命令” 会有焕然一新的感觉。

# 原子方法思路
alluxio
├── alluxio_conf
│   └── ***
├── alluxio_master_serv
│   └── ***
├── alluxio_pkg
│   └── ***
├── alluxio_worker_cache
│   └── ***
└── alluxio_worker_serv
    └── ***           

3.5 Hadoop 生态运维实例

3.5.1 Hadoop 运维概述

知乎大数据生态使用的是 Apache Hadoop。在前期单点 Shell 阶段,虽然可以快速应对简单的场景,但是遇到大规模复杂场景时相对吃力,难以保证自身稳定性。

Apache Hadoop 生态的运维工作相对复杂,主要是因为大数据生态系统由多个组件构成,每个组件之间有着复杂的依赖关系和配置参数,不同的企业和组织在使用大数据生态时,往往会面临不同的应用场景和业务需求,需要根据具体的情况进行定制化配置和管理。在知乎,大数据相关组件已经全部上云 (裸金属一类),在部署层面有大量定制性需求,因此我们面临着以下难题:

  1. 部署复杂:Apache Hadoop 集群基础安装就已经涉及到多个组件的配置管理,而且知乎存储和计算都实现了联邦的架构,部署难度大幅度增加;
  2. 配置复杂多样:知乎的大数据集群有多套环境和跨机房的场景,在面对测试、灰度以及多集群配置时复杂度上升到了一个难以维护的层面;
  3. 数据存储和管理难度大:Apache Hadoop 生态系统支持海量数据的存储和管理,但也带来了数据存储和管理的复杂度。因为需要对接多个不同形态的业务,多个存储主节点导致压力已经超过社区推荐值,为运维变更以及稳定性带来了极大的难题;
  4. 变更周期长:因为知乎大数据使用的 Apache Hadoop 生态,HDFS、YARN、Spark、Flink、Presto、Hive、调度、同步等等组件都有定制开发,在版本更新、功能扩展、Bug 修复等场景带来了极大的难度,比较严重是变更周期长;
  5. 业务连续性和可靠性问题:Apache Hadoop 生态系统中的数据处理和分析任务往往涉及到关键业务,需要保证任务的连续性和可靠性,早期的脚本很难达到业务无感知的情况下变更,极端情况需要短暂停业务才能变更。

3.5.2 Hadoop 运维实践对比

接下来将在 Hadoop 运维中列举几个部署、配置、协作场景,来对比知乎早期脚本和现在的 Ansible 工程化,以及商业 CDH 在开发和运维层面的区别。

3.5.2.1 历史遗留 Shell 运维方式

通过向历史脚本传参进行部署操作,问题如下:

  • 部署流程无法控制范围和服务,一开始操作则是全重来一遍;
  • 无法滚动部署;
  • 部署后的服务观测难度大。
Ansible 在知乎大数据的实践

3.5.2.2 工程化 Ansible 运维方式

通过向工程提交 Merge Request 进行运维变更操作,流程如下:

  • 选择已经设计好的角色 Roles 和 剧本 Playbook;
  • 变更 Inventory 配置新增的节点数量;
  • MR review 以及等待代码合并后,执行 Playbook 即完成扩容。

如下列图片,我们的变更是在 Inventory 中进行,而此次变更会搭配 Git 进行版本控制和 Review 管理。方便他人审查或者对接其他 GitOps,能够清晰的知道我们的变更内容是什么。以及变更的风险性。

Ansible 在知乎大数据的实践

运维开发者的角度,我们可以看到有大量相关的 Hadoop Roles,和指定的 Playbook,像前面说的“我们只需要描述系统的最终状态,而不是具体要执行哪些命令”。

最后的执行,就是一条符合逻辑的命令,比如要做什么,在什么集群,转换为 Ansible 命令就是 “ansible-playbook hadoop_worker_deploy.yml -i inventory_abc.ini”。

Ansible 在知乎大数据的实践
Ansible 在知乎大数据的实践

需要注意的是,变更内容都是以代码的形式存在,我们是通过 Code Review 的方式进行运维协作开发。

同样我们也具备一些 WebUI,如果暂时不想关心细节,只需选中需要做的内容点击 RUN 即可,由 Ansible 和目前的框架设计来为你保证稳定。

Ansible 在知乎大数据的实践
Ansible 在知乎大数据的实践

注意,你没看错,这个扩容流程就是 Hadoop Worker (DataNode,NodeManager,***) 的部署流程,它会根据我们的变更内容和机器分组,对指定的机器进行相关操作。因为预先幂等性的设计,只会对状态不同的内容变更,而已存在的内容则会进行一些预检测,然后根据需要执行。很多的维护工作都是同样执行逻辑,虽然在执行命令上会有细微区别,但是可以在 WebUI 上都封装成一个按钮。

3.5.2.3 商业 CDH 运维方式

CDH 管理工作全由 CM 来完成,它可以方便地进行 Hadoop 集群管理、配置更新、监控、扩容等工作。使用 CDH 进行 Hadoop HDFS 扩容,需要进行以下步骤:

  • 在 CDH 管理界面上选择 Add New Hosts,将新节点添加到集群中;
  • 在 NameNode 上运行 hdfs dfsadmin -refreshNodes;
  • 在新增节点上启动 DataNode 服务。可以通过在 CM 管理界面上选择新增节点并启动服务来完成。

下面例举了常见的扩容流程。可以看出 CM 的设计,以及管理功能已经很丰富了,只需简单的 Web 操作,就可以完成所有工作。

Ansible 在知乎大数据的实践
Ansible 在知乎大数据的实践
Ansible 在知乎大数据的实践

3.5.3 Hadoop 运维对比总结

根据以上基础的运维功能对比,可以明显的看到:

  • 历史的 Shell 脚本方式功能单一且无法胜任复杂的场景,并且变更心智成本要求较高;
  • Ansible 的变更流程,因为架构设计使它的基础运维模块开发、生产服务变更都是以 Code Review 的方式出现。虽然与脚本对比,同样也有需要了解,才敢变更的相似性,但是这是一个工程化的架构,是多人协作开发与测试产出的结果,在变更稳定性上是得过验证的,所以它是具有自动一键完成的能力;
  • 商业产品 CDH 可以看到截图中,把所有流程都以可观测性的方式展现出来,这对变更非常有帮助。但这是优点也是缺点,如果其中出现问题,可能需要商业支持,或者商业培训以及专人驻场。

最后,相对于历史的 Shell 脚本方式,工程化的实践是质的飞跃。再比较 CDH,在产品设计理念层面比如功能稳定性、WebUI,可能相较甚远,但是面向公司内的高度定制化,Ansible 能够以低成本的方式高效完成工作,这也是我们为什么会选择 Ansible 的原因。

4 Ansible 调优

4.1 高效幂等

Ansible 另一个最大的优点是它自带的幂等性。因为只需要简单调用内置方法,即能实现需求又能达到幂等操作,且可以重复运行,避免多次运行不一致。

幂等性的原理是基于每个模块内部的状态检查机制来完成的。举例几个源码中的实现:

file 模块的幂等:

1. 首先使用 os.stat 函数获取指定文件的状态信息,包括文件的大小、权限模式、所有者、组等属性;
2. 然后根据参数中指定的属性信息,检查文件的状态和属性是否满足要求。如果不满足,则需要修改文件的状态和属性;
3. 根据参数中指定的属性信息,使用 os.chmod、os.chown 和 os.utime 等函数,修改文件的权限模式、所有者、组和时间戳等属性,使其满足要求;
4. 最后,该方法会返回一个布尔值,表示文件的状态和属性是否已经满足要求。           

user 模块的幂等:

1. 如果用户或组已经存在,则不执行任何操作,否则会创建用户或组;
2. 对于已存在的用户或组,Ansible 会检查其属性(例如用户名、UID、GID 等),并根据需要修改这些属性以保持一致性;
3. 对于需要创建的用户或组,Ansible 会使用系统命令来创建用户或组,并设置其属性。           

它们的实现都有一个执行前后多重检查的共性。

可是这里有个问题,假设需要重复创建 100 个目录、或者 100 个用户时,继续使用默认的幂等方式是低效的。首先检测代码是多重的调用栈 “ansible yaml -> python -> c”,因为模块的不同,检查的数量也不同,模块要执行的任务较多时,检查的耗时是较高的。虽然社区的建议可以使用批处理模块 batch、异步操作 async+poll、循环 loop 去提速,但是都没有从根源解决 “合理避免冗余的幂等性检测” 的问题。所以当工程规模达到一定程度时,可以根据需求做一些合理的预检测来避免冗余的幂等性检测。

比如前面举例需要创建 100 个用户场景,如果使用社区的一些优化建议会复杂化问题。其实可以根据需求做一些重型逻辑前的预检测,首先获得当前主机的用户信息,将其与需要创建的用户列表进行 diff,根据 diff 的增量内容传入后续的逻辑,这样的方式可以大幅度提升重复跑时的效率。其他的模块也同理,甚至还可以实现特殊需求的幂等。

4.2 减少运维心智成本

相对于其他运维工具,当项目体积庞大了之后,很容易陷入一个复杂地狱,导致变更会具有极高的心智成本。

什么样的设计理念,可以减少运维心智成本呢?

应该是达到一键完成的运维变更操作,才能有效的减少运维心智成本。在 IaC(基础设施即代码)概念中有一个框架 Terraform,它的设计理念优雅地达到了这个状态。

简单介绍一下 Terraform 的设计理念,运维部署的所有结果 (集群、服务、变量、配置等等) 都保存了一个状态,每次的变更都会进行状态 diff (集群节点数量增加,增加了两个配置文件此类), 而且这些 diff 都会体现在状态文件中,最后只用变更状态不同的服务或主机即可,其他的容错、幂等都有框架去保证,这也是 Ansible 命令式与 Terraform 声明式最大的区别,Terraform 只需要声明我的最终架构状态,其他的全由工具去完成。

Terraform 在实际运维操作时所有的变更 (资源开通、销毁、变更) 都是一个命令,因为使用了状态 diff 检测,即使多次执行也不用担心出错,真正解放了运维时的心智成本。

反观目前的 Ansible 运维架构设计,确实无法像 Terraform 靠状态 diff 去做增量变更 (工具之间命令式与声明式的区别)。但是 Ansible 搭配好了架构设计,规划了变量布局,统一了 Roles 的开发规范,换一个角度想,实际运维变更时也是执行某个 Playbook,是否可以把这个 Playbook 当做一个一键完成呢?或者说朝着那个方向努力呢?

这确实这是一个好的方向,我们要走的路还很长,需要迭代的东西还很多,比如在剧本执行时中断了,如何使用 retry 机制合理的自动重试。Inventory 的动静模式结合如何合理规划集群。幂等如何避免大量冗余检查。以上都应该是朝着一键完成的角度去进步。

4.3 效率突破

常见的优化比如并发执行、减少 SSH 连接 (ssh_args、pipelining、SSH ControlMaster)、搭配缓存插件 (cache_plugin)、异步执行 (async、poll)。如何使用在这就不做过多介绍,因为这类资料很常见,所以我们分享一些能起到大幅度效率突破的内容。

  • 工程独立核心配置:可以在工程中放置一个自定义的 ansible.cfg 文件,来自定义一些优化参数,执行的默认 host、SSH 调优、插件路径、关键变量等都可以通过工程自定义 cfg 完成,而且也建议使用自定义,这样可以更好的了解工程;
  • 统一 Python 环境:pipelining 在使用的时候最好能统一 Python 环境和运维账号的 SSH 权限,经过测试 SSH 的连接量级至少能减少 30% 以上;
  • SSH 会话保持:建议使用 SSH 的 control_path 来保存 SSH 的 socket 状态文件,原理也是减少 SSH 的连接成本,需要注意的是存放 socket 的目录应提前创建好;
  • 慎用 vars_plugins:分享一个踩到的大坑,使用 vars_plugins 目的是为了快速获取变量,但是这里有个问题是自定义实现的 vars_plugins 会与 playbook 的主线流程有对接,源码中的主线流程将异常隐藏起来,当 vars_plugins 自定义实现处理不当时,会影响每次的 playbook 执行,严重的话就会出现丢逻辑;
Ansible 在知乎大数据的实践
  • 回调插件:建议在 callback_plugins 中实现任务执行时间统计功能,这样可以在每次执行完时查看效率情况,方便定位慢逻辑问题,达到数据可观测性;
Ansible 在知乎大数据的实践
  • 信息收集:gather_subset 收集系统信息时,最好是可选性收集,比如只收集网络或者硬件,默认的收集特别耗时间。下面给出一个参考:
- name: Gather hardware info
  setup:
    gather_subset:
      - '!all'
      - '!any'
      - hardware           

5 成果与展望

经过工程化阶段的迭代与实践,成果如下:

  • 支持跨机房运维;
  • 1 小时内完成 PoC 规模 (10 ~ 100台) 的大数据集群部署;
  • 不影响 (或极低影响) 业务的情况下,4~7 天滚动变更完所有集群;
  • 纯配置更新 5 分钟数千台;
  • 全流程部署 1 台/70~100s;
  • 半天文档阅读即可上手开发;
  • 70+ 功能角色模块 Roles、30+ 固定、10+ 动态流程剧本 Playbook。

未来展望:

本文主要是讲了运维架构内核的设计和效率提升,后续将继续提升易用性和稳定性,简化 Web UI 操作,朝着一键完成持续努力。

作者:陈大炮

出处:https://zhuanlan.zhihu.com/p/617731670

继续阅读