【Python】不一样的Ansible(一)

不一样的Ansible——进阶学习

  • 前言
  • 正文
    • 概念
      • Ansible Core
      • Plugins和Modules
    • 插件
      • 插件类型
      • 编写自定义插件
        • 基本要求
        • 插件选项
        • 文档标准
        • 编写插件
      • 添加一个本地插件
        • 注册为内置插件
        • 指定插件目录
    • 其他一些技巧
      • 更改Strategy
  • 结语

前言

Ansible 是一个极其简单的 IT 自动化引擎,可自动执行云配置、配置管理、应用程序部署、服务内编排和许多其他 IT 需求;基本上每一个运维工程师都会听过或者使用过Ansible(这是必然的,只要你需要管理超过2位数的服务器,Ansible基本是一个必备的工具),如果你还不知道Ansible是怎么用的,一定要找一个环境试试,这是一个真正让运维工作自动化并且事半功倍的工具。

Ansible不使用任何代理程序,也就是不需要部署agent,也不使用额外的自定义安全基础设施,因此很容易部署 - 最重要的是,使用yaml配置就能编写整个自动化流程,不需要学习太多额外的编程知识。

这一部分的内容,不是对Ansible做基本介绍的,而是对其中一些进阶的或者平时不会知道的东西做一个学习和记录的。比如各种插件的特殊用法、如何在我们自己的Python程序中去使用Ansible、以及一些其他的技巧或者知识点。

本系列文章的所有内容基于Ansible 9编写,Ansible 9使用Ansible-core 2.16

正文

概念

Ansible Core

Ansible Coreansible-coreAnsible的主要构件和架构,包含了许多的核心逻辑,比如:

  • CLI 工具,如 ansible-playbook、ansible-doc 等,用于驱动自动化并与之交互。
  • Ansible 语言使用 YAML 创建一套用于开发 Ansible Playbook 的规则,包括conditionals, blocks, includes, loops以及其他的一些内容。
  • 允许通过Ansible collections进行扩展的架构框架

总结来说,Ansible Core提供你目前所熟知的所有Ansible的基础能力,并且允许开发者在现有基础上拓展Ansible的功能。

Plugins和Modules

Ansible中非常重要的两个概念,插件和模块,那么他们有什么区别,以及怎么在自己实现拓展功能的时候选择使用哪个特性:

  • Plugins:扩展了Ansible Core的功能。大多数插件都在/usr/bin/ansible进程的控制节点上执行。插件为Ansible的核心功能提供选项和扩展:转换数据、记录输出、连接Inventory
  • Modules:本质也是一种Plugins,但是其特点在于能够在远程服务器上执行自动化任务。模块以独立脚本的形式运行,由Ansible在控制节点之外的进程中执行。模块与Ansible的接口主要是 json,接受参数并在退出前通过向stdout打印json字符串返回信息。与其他插件(必须用 Python 编写)不同,模块可以用任何语言编写;不过Ansible只提供PythonPowershell版本的模块。

这其实很好理解,Plugins是用来添加Ansible的核心代码逻辑的,因此需要Ansible能够把它加载进来,Ansible既然是Python编写的,自然也要求Plugins是要用Python编写,而Modules是通过ssh分发到受管节点执行的,那么他可以是任意程序,只要远程服务器能够运行它就可以了,默认Ansible的那些模块其实在受管节点也是调用了Python解释器来运行的

插件

插件类型

上面已经介绍了插件是什么,在Ansible中有多种插件类型,下面将官网的:

  • Action pluginsActionmodule一起执行playbook任务所需的动作,通常在后台自动运行,在模块实际运行前执行一些前期工作;
  • Become plugins:用于执行命令时进行提权或者用户切换,我们最常用的就是借助become完成sudo操作;
  • Cache pluginsCache插件允许Ansible存储收集到的受管节点信息或者inventory信息,避免因为从源数据检索而影响性能,这个最直观的体现就是我们执行playbook时如果没有关闭gather_fact,可能会等待很长时间,借助cache可以有效减少重复在受管节点执行操作时的这个步骤耗时;
  • Callback plugins:回调插件可以在Ansible响应事件时添加新的行为。默认情况下,回调插件可控制运行命令行程序时看到的大部分输出,但也可用于添加额外输出、与其他工具集成以及将事件汇聚到存储后端;对于想要对接Ansible做自动化管理的同学来说,这个插件很重要;
  • Cliconf pluginsCliconf插件是网络设备CLI接口的抽象。它们为Ansible在这些网络设备上执行任务提供了标准接口,主机维护不常用,我也没用过;
  • Connection pluginsConnection插件允许Ansible使用既定方式连接到目标主机,以便在上面执行任务,Ansible自身附带了许多连接插件,比如opensshparamiko,但每台主机一次只能使用一个;
  • Docs fragmentsDocs fragments可让使用者在一个地方记录多个插件或模块的通用参数;
  • Filter pluginsFilter插件也是一个功能常用的插件,他允许对数据进行特定的序列化或者其他自定义的处理,比如对字符串进行拆分和连接,默认Ansible使用的是jinja2提供的标准Filter功能,同时增加了一些特性;
  • Httpapi pluginsHttpapi 插件告诉Ansible如何与远程设备基于HTTPAPI交互,并在设备上执行任务;
  • Inventory pluginsInventory插件是一个允许用户指向仓库数据源的插件,也就是我们执行ad-hoc命令式的-i参数执行的逻辑;
  • Lookup pluginsLookup插件是Jinja2模板语言的Ansible特定扩展。用户可以使用该插件从外部来源(文件、数据库、key/value存储、API 和其他服务)访问playbook中的数据。与所有模板一样,LookupAnsible 控制节点上运行。Ansible使用标准模板系统提供查找插件返回的数据。你可以使用查找插件从外部资源加载变量或模板信息。
  • Modules:上文说过,Modules也是一种插件,所以这里也有它;
  • Module utilities:包含多个插件使用的共享代码,最好把它和Modules一起理解;
  • Netconf plugins:网络设备Netconf接口抽象,用于在网络设备上执行ansible任务;
  • Shell pluginsShell插件的作用是确保Ansible运行的基本命令格式正确,能够在目标计算机上运行,并允许用户配置与Ansible执行任务方式相关的某些行为,具体的情况可以跳转到
  • Strategy pluginsStrategy通过handles和受管节点的调度来控制playbook的执行流程,具体的策略可以查看:更改Strategy
  • Terminal pluginsTerminal插件包含有关如何正确初始化特定网络设备的SSH shell以配合Ansible使用的信息。这通常包括禁用自动分页、检测输出中的错误,以及在设备支持和需要的情况下启用特权模式。
  • Test pluginsTest插件可评估模板表达式并返回TrueFalse。有了测试插件,用户就可以创建条件(比如when关键字)来实现tasks、blocks、play、playbooks和roles的逻辑。Ansible使用作为Jinja一部分提供的标准测试,并添加了一些专门的测试插件
  • Vars pluginsVars插件拓展了一些变量的使用方式

上面所说的插件,其中一部分(ActionCacheCallbackConnectionFilterInventoryLookupTestVars)支持开发者进行自定义的编写,实现新的功能。

编写自定义插件

基本要求

让我们先来编写一个自定义的插件来为Ansible提供额外的核心能力,编写之前我们要知道一个Ansible插件必须要满足的条件:

  • 使用Python编写,这个在上一节已经说明原因
  • 对异常进行合适的处理,使用raise抛出异常
  • 返回值要是unicode编码的字符串
  • 符合Ansible的配置和文档标准

接下来针对上面的要求做一下分别的解释

使用Python编写

使用Python编写插件,这样它才能被PluginLoader加载,并作为任何模块都能使用的Python对象返回。由于插件将在控制节点上执行,因此必须使用兼容的 Python 版本编写插件

使用raise处理异常

在插件执行过程中遇到错误时,应通过引发AnsibleError()或类似类来返回错误信息。在将其他异常包装成错误信息时,应始终使用to_nativeAnsible函数,以确保不同Python版本的字符串兼容,下面是一个简单的样例:

from ansible.module_utils.common.text.converters import to_nativetry:cause_an_exception()
except Exception as e:raise AnsibleError('Something happened, this was original exception: %s' % to_native(e))

由于Ansible仅在需要时才会对变量进行解析,因此filter插件和test插件应使用jinja2.exceptions.UndefinedErrorAnsibleUndefinedVariable异常,以确保未定义变量仅在必要时才引起程序的Fatal

返回值符合unicode编码

这个要求是为了保证过程中的字符串str能够在Jinja2中正常运行和解析,Ansible提供了响应的方法进行转换:

from ansible.module_utils.common.text.converters import to_text
result_string = to_text(result_string)
插件选项

为了给我们自己的插件定义可配置选项,我们需要在Python文件的DOCUMENTATION部分进行描述和声明,这种规范可以确保我们插件响应选项的文档时钟保持正确和最新,以下是格式定义:

options:option_name:description: describe this config optiondefault: default value for this config optionenv:- name: NAME_OF_ENV_VARini:- section: section_of_ansible.cfg_where_this_config_option_is_definedkey: key_used_in_ansible.cfgvars:- name: name_of_ansible_var- name: name_of_second_varversion_added: X.xrequired: True/Falsetype: boolean/float/integer/list/none/path/pathlist/pathspec/string/tmppathversion_added: X.x

我们来看一个样例,这里以自带的Callback插件中的json.py演示:

# (c) 2016, Matt Martz <matt@sivel.net>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = typeDOCUMENTATION = '''callback: jsonshort_description: Ansible screen output as JSONversion_added: "2.2"description:- This callback converts all events into JSON output to stdouttype: stdoutrequirements:- Set as stdout in configoptions:show_custom_stats:version_added: "2.6"name: Show custom statsdescription: 'This adds the custom stats set via the set_stats plugin to the play recap'default: Falseenv:- name: ANSIBLE_SHOW_CUSTOM_STATSini:- key: show_custom_statssection: defaultstype: boolnotes:- When using a strategy such as free, host_pinned, or a custom strategy, host results willbe added to new task results in ``.plays[].tasks[]``. As such, there will exist duplicatetask objects indicated by duplicate task IDs at ``.plays[].tasks[].task.id``, each with anindividual host result for the task.
'''

ok,现在假设我们设定好了自己的配置项,如果要在插件内部使用对应的配置项,一般使用self.get_option(<option_name>)进行获取,不过有些插件的处理方式会有些不同;

配置部分遵循了Ansible中值的优先原则,当在同一个类别下定义多个值时,最后一个值优先,比如上述样例的这部分:

    vars:- name: name_of_ansible_var- name: name_of_second_var

此时option_name选项的值使用name_of_second_var,如果要对option值进行设置,则要使用self.set_option()方法;

文档标准

支持嵌入式文档(参见 ansible-doc 列表)的插件应包含格式良好的文档字符串,如果我们继承一个插件,则必须在文档中记录被继承插件的选项;

编写插件

Action plugin为例,编写一个插件,首先,编写的插件应该继承自对应的Base类,比如:

from ansible.plugins.action import ActionBaseclass ActionModule(ActionBase):pass

允许使用_execute_module方法来调用内置的Module并获取返回结果:

module_return = self._execute_module(module_name='<NAME_OF_MODULE>',module_args=module_args,task_vars=task_vars, tmp=tmp)

我们以官方文档中的示例为准,进行讲解,这里添加了一个通过setup模块获取的内容进行服务器时间差异的比对动作的action

from __future__ import (absolute_import, division, print_function)
__metaclass__ = typefrom datetime import datetime
from ansible.plugins.action import ActionBaseclass ActionModule(ActionBase):"""Action"""def run(self, tmp=None, task_vars=None):super().run(tmp, task_vars)module_args = self._task.args.copy()module_return = self._execute_module(module_name='setup',module_args=module_args,task_vars=task_vars, tmp=tmp)ret = {}remote_date = Noneif not module_return.get('failed'):for key, value in module_return['ansible_facts'].items():if key == 'ansible_date_time':remote_date = value['iso8601']if remote_date:remote_date_obj = datetime.strptime(remote_date, '%Y-%m-%dT%H:%M:%SZ')time_delta = datetime.utcnow() - remote_date_objret['delta_seconds'] = time_delta.secondsret['delta_days'] = time_delta.daysret['delta_microseconds'] = time_delta.microsecondsreturn {"ansible_facts": dict(ret)}

在上述代码中,调用了内部的setup模块,拉取受管节点的信息,在其返回值中,有这样的数据结构:

        "ansible_date_time": {"date": "2024-01-08","day": "08","epoch": "1704704528","epoch_int": "1704704528","hour": "17","iso8601": "2024-01-08T09:02:08Z","iso8601_basic": "20240108T170208306063","iso8601_basic_short": "20240108T170208","iso8601_micro": "2024-01-08T09:02:08.306063Z","minute": "02","month": "01","second": "08","time": "17:02:08","tz": "CST","tz_dst": "CST","tz_offset": "+0800","weekday": "Monday","weekday_number": "1","weeknumber": "02","year": "2024"},

随后在自定义插件逻辑中我们比对了iso8061的值,计算出时间的差异值,然后返回,这个插件的返回值类似以下的格式:

172.18.0.25 | SUCCESS => {"ansible_facts": {"delta_days": 0,"delta_microseconds": 152565,"delta_seconds": 0},"changed": false
}

如果想要进一步修改插件,满足自己的需求,只需要在刚才编写的ActionModule中进行变更即可。

添加一个本地插件

上面我们已经编写了一个自定义的插件,但是只是把代码写好是没法按照预期的方式通过ad-hoc的方式使用的,大概率会出现以下的报错:

172.18.0.25 | FAILED! => {"msg": "The module myaction was not found in configured module paths"
}

而想要直接便利的使用自定义的插件,有以下几种方式进行配置:

注册为内置插件

这种方法不建议,只做介绍,直接将编写好的python文件拷贝到ansible的安装目录下,比如我们使用虚拟环境,通过pip安装的ansible,那么我们的ansible安装目录ANSIBLE_INSTALL_DIR大概是这样的:$ENV_HOME/lib/python3.9/site-packages/ansible,那么只需要把我们的插件放在$ANSIBLE_INSTALL_DIR/plugins/目录下对应的插件类型下:

image-20240108173808183

不建议使用这种方法的原因主要是因为我们个人编写的代码在规范性上和官方的多少有些差异,最好还是做一些区分,这样在出问题的时候排查也会变得容易一些

指定插件目录

这个方式是比较规范的,当我们编写的插件是一个提供给全局使用的插件时,我们就这样做就可以了,编辑ansible的配置文件/etc/ansible/ansible.cfg,添加action_plugins配置项:

[defaults]
# ...
# 可以自定义为自己需要的目录
action_plugins     =  /usr/share/ansible/plugins/action 

然后把我们的插件放在上面配置的目录下,就能正常使用了

这种配置便于我们对插件进行管理,且配置一次后,所有人都能使用

除此之外,插件也可以放在Collection中,这个在讲到Collection时再单独写

其他一些技巧

更改Strategy

默认情况下,Ansible使用linear strategy进行任务的调度,除此之外,Ansible还提供了以下集中Strategy plugins

  • debug strategy – 在debug模式下运行Task.
  • free strategy – 不等待其他受管节点完成当前状态,直接运行Task
  • host_pinned strategy – 尽可能快的在每个受管节点执行play(按照serial定义的批次执行,默认为全部),除非一个play可以在不被其他主机Task中断的情况下完成,否则Ansible不会给一个新的主机启动play,即:拥有执行中的play的受管节点数量不超过forks规定的数量
  • linear strategy – 默认的策略,线性执行,这个线性是指Task,也是我们熟知的,一个 Task会在所有服务器执行完后再执行下一个Task

假设我们希望每个节点都能速度拉满,我们可以把策略改成free,通过在playbook中指定或者更改配置文件都可以实现,先看更改playbook:

- hosts: allstrategy: freetasks:# ...

或者更改ansible.cfg

[defaults]
strategy = free

结语

本篇文章主要抛砖引玉,说明了Ansible中模块和插件的区别,以及如何自己编写一个简单的Action Ansible插件,接下来会再多讲解一些其他类型的插件编写方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/607091.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows下Redis5+可视化软件下载、安装和配置教程-2024年1月8日

Windows下Redis5下载、安装和配置教程-2024年1月8日 一、下载二、安装三、配置环境四、配置可视化客户端 一、下载 redis是现在是没有对win系统版进行维护的&#xff0c;这个是大神完成的&#xff0c;目前是到5版本&#xff0c;选择Redis-x64-5.0.14.1.zip点击下载 下载地址&…

pgAdmin和asdf postgres的安装

安装pgAdmin&#xff1a; curl https://www.pgadmin.org/static/packages_pgadmin_org.pub | sudo apt-key addsudo sh -c echo "deb https://ftp.postgresql.org/pub/pgadmin/pgadmin4/apt/$(lsb_release -cs) pgadmin4 main" > /etc/apt/sources.list.d/pgadmi…

.NET 6中如何使用Redis

1、安装redis Redis在windows平台上不受官方支持&#xff0c;所以想要在window安装Redis就必须去下载windows提供的安装包。安装地址&#xff1a;https://github.com/tporadowski/redis/releases 2、在NueGet安装包 3、在appsettings.json文件里面添加Redis相关配置信息 &quo…

MySQL之子查询、连接查询(内外)以及分页查询

一、案例&#xff08;接上一篇文章&#xff09; 09&#xff09;查询学过「张三」老师授课的同学的信息 -- 一共有两种方式 -- 第一种方式&#xff1a; SELECT s.*,c.cname,t.tname,sc.score FROMt_mysql_teacher t,t_mysql_course c,t_mysql_student s,t_mysql_score sc WHERE…

鸿蒙设备-开发板基础学习(BearPi-HM Micro)

theme: minimalism 每当学习一门新的编程语言或者上手一款新的开发板&#xff0c;在学习鸿蒙设备开发过程中&#xff0c;带大家写的第一个程序&#xff0c;通过这个程序&#xff0c;我们可以对鸿蒙设备开发的整个流程有一个初步的体验。BearPi-HM Micro开发板为例&#xff1a;…

「MCU」SD NAND芯片之国产新选择优秀

文章目录 前言 传统SD卡和可贴片SD卡 传统SD卡 可贴片SD卡 实际使用 总结 前言 随着目前时代的快速发展&#xff0c;即使是使用MCU的项目上也经常有大数据存储的需求。可以看到经常有小伙伴这样提问&#xff1a; 大家好&#xff0c;请问有没有SD卡芯片&#xff0c;可以…

MongoDB高级集群架构设计

两地三中心集群架构设计 容灾级别 RPO & RTO RPO&#xff08;Recovery Point Objective&#xff09;&#xff1a;即数据恢复点目标&#xff0c;主要指的是业务系统所能容忍的数据丢失量。RTO&#xff08;Recovery Time Objective&#xff09;&#xff1a;即恢复时间目标&…

C++学习笔记——string类和new函数

目录 string类 1.功能增强 1.1 子字符串提取 1.2 字符串拼接 1.3 大小写转换 1.4 字符串比较 2.性能优化 3.使用示例 下面是一个简单的使用示例&#xff0c;展示了如何使用改进后的String类&#xff1a; NEW函数 2.1NEW函数的基本用法 2.2NEW函数的注意事项 2.3避…

密码学:一文读懂非对称加密算法 DH、RSA

文章目录 前言非对称加密算法的由来非对称加密算法的家谱1.基于因子分解难题2.基于离散对数难题 密钥交换算法-DH密钥交换算法-DH的通信模型初始化DH算法密钥对甲方构建DH算法本地密钥乙方构建DH算法本地密钥DH算法加密消息传递 典型非对称加密算法-RSARSA的通信模型RSA特有的的…

建模软件Rhinoceros mac介绍说明

Rhinoceros mac是一款3D设计软件“犀牛”&#xff0c;在当今众多三维建模软件中&#xff0c;Rhinoceros 版因为其体积小、功能强大、对硬件要求低而广受欢迎&#xff0c;对于专业的3D设计人员来说它是一款不错的3D建模软件&#xff0c;Rhinoceros Mac中文版能轻易整合3DS MAX与…

Git命令+github仓库克隆

Git github Git常用命令 开始 git init #创建仓库 git status #查看仓库的状态 git status -s #简单的查看仓库的状态 git ls-files #查看暂存区的内容 git reflog #查看操作的历史记录 暂存区 git add git add <file&g…

网安入门11-文件上传(前后端绕过,变形马图片马)

Upload-Labs Upload-Labs是一个使用PHP语言编写、专注于文件上传漏洞的闯关式网络安全靶场。练习该靶场可以有效地了解并掌握文件上传漏洞的原理、利用方法和修复方案。 思考&#xff1a;他只让我传一个.jpg的图片&#xff0c;我想传一个.php的木马&#xff0c;两者什么区别 …

如何计算指标波动贡献率?(附Pandas实现)

大家好&#xff0c;我是阿粥 “为什么这个月销售额提升了30%&#xff1f;” “为什么转化率又降了&#xff0c;同比竟然降低了42%&#xff0c;什么原因导致的呢&#xff1f;” 这些都是数据分析师在工作中经常会遇到的问题&#xff0c;甚至有些基础岗的数据分析师要花80%以上的…

静态S5在项目管理中的应用与案例分享

静态S5作为一种强大的数据分析工具&#xff0c;不仅在数据处理和可视化方面表现出色&#xff0c;还在项目管理中发挥着重要作用。本篇将通过实际案例分享&#xff0c;探讨静态S5在项目管理中的应用与优势。 一、静态S5在项目管理中的应用 项目进度管理&#xff1a;静态S5通过…

软件测试|深入理解SQL CROSS JOIN:交叉连接

简介 在SQL查询中&#xff0c;CROSS JOIN是一种用于从两个或多个表中获取所有可能组合的连接方式。它不依赖于任何关联条件&#xff0c;而是返回两个表中的每一行与另一个表中的每一行的所有组合。CROSS JOIN可以用于生成笛卡尔积&#xff0c;它在某些情况下非常有用&#xff…

SpringBoot-自定义监听器

Spring Boot提供了强大的事件模型&#xff0c;其中包括多种内置监听器&#xff0c;同时也支持开发者自定义监听器。通过实现ApplicationListener接口&#xff0c;开发者可以创建自己的监听器&#xff0c;并在Spring Boot应用程序中进行配置。这样一来&#xff0c;在特定的应用程…

python 函数中字典的修改会影响函数外字典的值

def modify_dict(d):d[key] new valueprint(函数中字典d的位置,id(d))# 创建一个字典 original_dict {key: old value} print(函数外字典的位置,id(original_dict))# 调用函数来修改字典 modify_dict(original_dict)# 输出原始字典的值&#xff0c;可以看到它已经被修改了 pr…

一种DevOpts的实现方式:基于gitlab的CICD(二)

写在之前 前文已经搭建了基于gitlab的cicd环境&#xff0c;现在我们来更近一步&#xff0c;结合官网给出的案例来详细介绍如何一步一步实现CI的过程。 基于gitlab搭建一个前端静态页面 环境依赖&#xff1a; gitlabgitlab runner&#xff08;docker版本&#xff09; 环境达吉…

04、Kafka ------ 各个功能的作用解释(Cluster、集群、Broker、位移主题、复制因子、领导者副本、主题)

目录 启动命令&#xff1a;CMAK的用法★ 在CMAK中添加 Cluster★ 在CMAK中查看指定集群★ 在CMAK中查看 Broker★ 位移主题★ 复制因子★ 领导者副本和追随者副本★ 查看主题 启动命令&#xff1a; 1、启动 zookeeper 服务器端 小黑窗输入命令&#xff1a; zkServer 2、启动 …

【REST2SQL】06 GO 跨包接口重构代码

【REST2SQL】01RDB关系型数据库REST初设计 【REST2SQL】02 GO连接Oracle数据库 【REST2SQL】03 GO读取JSON文件 【REST2SQL】04 REST2SQL第一版Oracle版实现 【REST2SQL】05 GO 操作 达梦 数据库 对所有关系数据的操作都只有CRUD&#xff0c;采用Go 的接口interface{}重构代码…