【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.rhkb.cn/news/234250.html

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

相关文章

ros gazebo机械臂仿真,手动控制与MoveIt自动控制

本文总结归纳古月居胡春旭ros机械臂教程&#xff0c;给出了一些error的解决方法&#xff0c;补充了通过python运行moveit。十分建议去看github huchunxu源代码的repository。 创建机械臂的xacro模型 首先创建一个工作空间&#xff0c;在工作空间中创建arm_description功能包。…

GitHub 一周热点汇总 第4期 (2024/01/01-01/06)

GitHub一周热点汇总第四期 (2023/12/24-12/30)&#xff0c;梳理每周热门的GitHub项目&#xff0c;了解热点技术趋势&#xff0c;掌握前沿科技方向&#xff0c;发掘更多商机。2024年到了&#xff0c;希望所有的朋友们都能万事顺遂。 说明一下&#xff0c;有时候本周的热点项目会…

【HarmonyOS4.0】第三篇-类web开发模式

【HarmonyOS4.0】第三篇-类web开发模式 一、鸿蒙介绍 课程核心 为什么我们需要学习鸿蒙&#xff1f; 哪些人适合直接转鸿蒙&#xff1f; 鸿蒙系统优势是什么&#xff1f; 课程内容 (1)为什么要学习鸿蒙 从行情出发&#xff1a; 美国商务部长访问中国&#xff0c;2023年…

【Java并发】深入浅出 synchronized关键词原理-下

上一篇文章&#xff0c;简要介绍了syn的基本用法和monter对象的结构&#xff0c;本篇主要深入理解&#xff0c;偏向锁、轻量级锁、重量级锁的本质。 对象内存布局 Hotspot虚拟机中&#xff0c;对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据 (Instance Da…

【Sublime Text】| 02——常用插件安装及配置

系列文章目录 【Sublime Text】| 01——下载软件安装并注册 【Sublime Text】| 02——常用插件安装及配置 失败了也挺可爱&#xff0c;成功了就超帅。 文章目录 1. 汉化2. 更换颜色主题3. 更改编码插件—ConvertToUTF84. 对齐插件—Alignment5. 括号高亮插件—BracketHighligh…

win11修改本地hosts,自定义域名

目录 &#x1f9c8;1.打开指定目录 &#x1f95a;2.粘贴至桌面 &#x1f373;3.添加自己的域名和对应的ip地址 &#x1f37f;4.替换原来的hosts文件 1.打开指定目录&#x1f9c2;&#x1f9c2; 在C盘下打开 --------C:\Windows\System32\drivers\etc&#xff0c;找到hos…

众和策略:沪指跌0.91%险守2900点,半导体、金融等板块走低

8日早盘&#xff0c;两市股指低开低走&#xff0c;沪指一度失守2900点&#xff0c;深成指、创业板指跌约1%&#xff0c;科创50指数创前史新低。 到午间收盘&#xff0c;沪指跌0.91%报2902.4点&#xff0c;深成指跌1.17%&#xff0c;创业板指跌0.99%&#xff0c;科创50指数跌超…

vue3中使用elementplus中的el-tree-select,自定义显示名称label

<el-tree-select v-model"addPval" node-key"id" :data"menulists" :render-after-expand"false" :props"menuProps" /> <el-divider />let menuProps {//自定义labellabel: (data: { name: any; }) > {ret…

程序媛的mac修炼手册-- 终端(terminal)常用命令

「终端&#xff08;terminal&#xff09;」相当于macOS的一个 App &#xff0c;它的特殊之处是&#xff0c;它是管理其它App的App&#xff0c;操作主要通过命令行界面 (CLI) 。 相比于我们日常熟悉的用户界面&#xff08;User Interface&#xff0c;UI&#xff09;&#xff0c…

vue3 封裝一个常用固定按钮组件(添加、上传、下载、删除)

效果图 这个组件只有四个按钮&#xff0c;添加&#xff0c;上传、下载、删除&#xff0c;其中删除按钮的颜色默认是灰色&#xff0c;当表格有数据选中时再变成红色 实现 组件代码 <script lang"ts" setup> import { Icon } from /components/Icon/index im…

Qt应用-实现图像截取功能类似QQ上传头像截取功能

本文演示利用Qt实现图像截取功能类似QQ上传头像截取功能。 效果如下,通过移动中间的裁剪区域可以获得一张裁剪后的图片。 目录

OpenAI ChatGPT-4开发笔记2024-02:Chat之text generation之completions

API而已 大模型封装在库里&#xff0c;库放在服务器上&#xff0c;服务器放在微软的云上。我们能做的&#xff0c;仅仅是通过API这个小小的缝隙&#xff0c;窥探ai的奥妙。从程序员的角度而言&#xff0c;水平的高低&#xff0c;就体现在对openai的这几个api的理解程度上。 申…

【unity】基于Obi的绳长动态修改(ObiRopeCursor)

文章目录 一、在运行时改变绳子长度:ObiRopeCursor1.1 Cursor Mu&#xff08;光标μ&#xff09;1.2 Source Mu&#xff08;源μ&#xff09;1.3 Direction&#xff08;方向&#xff09; 一、在运行时改变绳子长度:ObiRopeCursor Obi提供了一个非常通用的组件来在运行时修改绳…

Vue3-39-路由-导航异常的检测 afterEatch 与 编程式导航之后的订阅动作

说明 本文主要是介绍一下 路由的后置守卫 afterEatch 的一个重要的作用 &#xff1a; 就是检测路由异常信息。 它的实现方式是 通过第三个参数来返回的。 而且&#xff0c;它的异常检测是全局的。导航的异常有以下三种类型&#xff1a; aborted : 在导航守卫中 被拦截并返回了…

苹果Find My查找芯片-伦茨科技ST17H6x支持苹果Find My认证

Apple「查找」Find My可通过庞大的“Apple Find My Network” 实现全球查找功能。无数iOS、iPadOS、macOS、watchOS激活设备与Find My 设备结合在一起&#xff0c;无需连接到Wi-Fi或者蜂窝网络&#xff0c;用户也可以给遗失的设备定位。对于任何iOS、iPadOS、macOS、watchOS设备…

Plantuml之nwdiag网络图语法介绍(二十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

算法第十一天-组合总和Ⅳ

组合总和Ⅳ 题目要求 解题思路 来自[负雪明烛] 题目有个明显的提示&#xff1a;求组合的个数&#xff0c;而不是每个组合。如果是要求出每个组合&#xff0c;那么必须使用回溯法&#xff0c;保存所有路径。但是如果是组合个数&#xff0c;一般都应该想到[动态规划]的解法。 直…

*4.3 CUDA MEMORY TYPES

CUDA设备包含几种类型的内存&#xff0c;可以帮助程序员提高计算到全局内存的访问率&#xff0c;从而实现高执行速度。图4.6显示了这些CUDA设备内存。全局内存和恒定内存出现在图片的底部。主机可以通过调用API函数来写入&#xff08;W&#xff09;和读取&#xff08;R&#xf…

Python私有变量的定义与访问

class Student():def __init__(self, name, age):self.name nameself.age ageself.__score 0def marking(self, score):if score < 0:return 分数不能为0self.__score scoreprint(self.name 同学本次得分是: str(self.__score)) def __talk(self): # 私有的类可通过在…

Python爬虫-爬取豆瓣Top250电影信息

&#x1f388; 博主&#xff1a;一只程序猿子 &#x1f388; 博客主页&#xff1a;一只程序猿子 博客主页 &#x1f388; 个人介绍&#xff1a;爱好(bushi)编程&#xff01; &#x1f388; 创作不易&#xff1a;喜欢的话麻烦您点个&#x1f44d;和⭐&#xff01; &#x1f388;…