1、Ansible高级自动化best实践 1.1高效使用Ansible的方法 1.1.1 Keep Things Simple a、让你的剧本具有可读性 保持你的剧本注释良好,易于阅读。使用留白和注释。总是给游戏和任务起有意义的名字,让他们清楚地知道游戏或任务在做什么。这些实践有助于记录剧本,并使对失败的剧本运行进行故障排除变得更容易。
YAML不是一种编程语言。它擅长表达任务或项的列表,或一组键值对。如果您难以在您的Ansible Playbook中表达一些复杂的控制结构或条件,那么考虑用不同的方式来处理这个问题。您可以使用模板和Jinja2过滤器来处理变量中的数据,这可能是解决您的问题的更好方法。
b、使用现有的模块 尽可能使用Ansible中包含的特殊用途模块,而不是命令、shell、raw或其他类似模块。虽然在某些情况下您需要使用这些通用模块,但如果您针对特定任务使用模块desiąned,那么将更容易使您的剧本幂等性且易于维护。
许多模块都有一个默认状态或其他变量来控制它们的操作。例如,yum模块目前假设在大多数情况下,您所命名的包应该出现。但是,您应该显式地指定您想要的状态。这样做可以更容易地阅读剧本,并防止在Ansible的后续版本中更改模块的默认行为。
c、坚持标准风格 您应该考虑有一个标准的“风格”,让您和您的同事在编写Ansible项目时遵循。你要缩进多少空格?您希望如何使用垂直留白?任务、角色、角色和变量应该如何命名?应该评论什么,如何评论?有不止一种合理的方法可以做到这一点,但是拥有一个一致的标准可以帮助提高可维护性和可读性。
1.1.2 Stay Organized a、遵循命名变量的约定 变量命名可能特别重要,因为Ansible有一个相当平坦的名称空间。使用描述性变量,比如apache_tls_port,而不是描述性较低的变量,比如asp。在角色中,最好在角色变量前面加上角色名。例如,如果你的角色被命名为myapp,那么你的变量名可以以myapp_开头,以帮助命名其他角色和剧本中的变量。
b、项目结构标准化 在构建文件系统上的Ansible项目的文件时,使用一致的模式。
c、使用动态库存 尽可能使用动态清单。动态库存允许从一个集中的真相来源集中管理您的主机和组,并确保库存自动更新。动态清单与云提供商、容器和虚拟机管理系统结合使用时特别强大。这些系统可能已经有了Ansible可以使用的库存信息。
如果您不能使用动态清单,那么其他工具可以帮助您动态地构造组或其他信息。例如,可以使用group_by模块根据一个事实动态地生成组成员关系。这个组的成员资格对其余的剧本是有效的。
d、主机组分类 主机可以是多个组的成员。考虑将你的主机根据不同的特点划分为不同的类别:
地理位置:区分来自不同地区、国家、大洲或数据中心的主机。
环境:区分专用于软件生命周期的不同阶段的主机,包括开发、准备、测试或生产。
站点或服务:提供或链接到一个功能子集的主机组,如一个特定的网站、一个应用程序或一个功能子集。
请记住,主机将从它们所属的所有组继承变量。如果两个组对同一个变量有不同的设置,并且主机是这两个组的成员,那么使用的值是最后加载的值。如果可能同时使用的两个不同group之间的设置存在差异,那么要特别注意确定应该如何设置这些变量。
e、为可重用内容使用roles 角色帮助您保持剧本简单,并允许您通过跨项目重用公共代码来节省工作。如果你正在编写自己的角色,那么让他们专注于一个特定的目的或功能,就像剧本一样。通过变量使角色具有通用性和可配置性,以便在使用不同的剧本集时不需要编辑它们。
使用ansible-galaxy命令初始化你的角色的目录层次结构并提供初始模板文件。如果你选择这样做,这也将使你更容易在Ansible Galaxy网站上分享你的角色。
f、在控制节点上运行剧本 控制对系统的访问并审计Ansible活动。考虑使用一个专用的控制节点来运行所有的Ansible playbook。
系统管理员应该在系统上仍然拥有自己的帐户,以及连接到托管主机的凭据,并在需要时升级权限。当系统管理员退出时,可以从被管理主机的authorized_keys文件中删除他们的SSH密钥,并撤销他们的sudo命令特权,而不影响其他管理员。
1.1.3Test Often a、测试任务的结果 如果您需要确认任务成功,请验证任务的结果,而不是信任模块的返回代码。有不止一种方法来验证一个任务,这取决于所涉及的模块。
b、使用块/救援恢复或回滚 block指令对分组任务很有用;当与救援指令一起使用时,它有助于从错误或失败中恢复。
c、使用最新的Ansible版本开发剧本 即使您没有在生产环境中使用最新版本的Ansible,您也应该经常使用最新版本的Ansible来测试您的剧本。这个测试将帮助您避免Ansible模块和特性发展过程中出现的问题。
如果您的剧本在运行时打印了警告或弃用消息,那么您应该注意它们并做出调整。通常,如果Ansible中的某个特性正在被弃用或发生变化,项目会在该特性被移除或改变之前提供四个次要版本的弃用通知。
2、管理inventory 2.1 yaml格式的inventory使用
Ansible自动将这些主机中的任何一台放在一个特殊的分组中。通过显式地将主机分配给未分组的主机,你可以在基于yaml的静态目录中完成同样的事情。
在许多情况下,在静态目录文件本身中避免使用storinq变量是一个最佳实践。许多有经验的Ansible开发人员喜欢使用静态目录文件来简单地存储关于管理哪些主机以及它们属于哪些组的信息。
变量及其值存储在目录的host_vars或group_vars文件中。在某些情况下,您可能希望将ansible_port或ansible_connection等变量保存在与目录本身相同的文件中,从而将这些信息保存在一个地方。如果在太多不同的地方设置变量,则很难记住在哪里设置了特定的变量
2.2 ini格式inventory转换成yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 [workstation development-practices] $ cat test.inventory [lb_servers] servera.lab.example.com [web_servers] serverb.lab.example.com serverc.lab.example.com [web_servers:vars] alternate_server=serverd.lab.example.com [backend_server_pool] server[b:f].lab.example.com [workstation development-practices] $ ansible-inventory --yaml -i test.inventory --list --output test.yaml [workstation development-practices] $ cat test.yaml all: children: backend_server_pool: hosts: serverb.lab.example.com: alternate_server: serverd.lab.example.com serverc.lab.example.com: alternate_server: serverd.lab.example.com serverd.lab.example.com: {} servere.lab.example.com: {} serverf.lab.example.com: {} lb_servers: hosts: servera.lab.example.com: haproxy_appservers: - ip: 172.25.250.11 name: serverb.lab.example.com - ip: 172.25.250.12 name: serverc.lab.example.com ungrouped: {} web_servers: hosts: serverb.lab.example.com: {} serverc.lab.example.com: {}
{} 代表后面不写任何的东西,写不写都可以,生产环境中一般主机组变量和主机变量直接写在了inventory yaml文件中。
2.3 常见的YAML格式文件的语法错误
2.4 编写yaml格式的inventory文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [workstation inventory-yaml] $ cat inventory.yaml active_web_servers: hosts: server[b:c].lab.example.com: inactive_web_servers: hosts: server[d:f].lab.example.com: region_eu: hosts: serverc.lab.example.com: serverf.lab.example.com: web_servers: children: active_web_servers: inactive_web_servers: all_servers: hosts: servera.lab.example.com: children: web_servers:
2.5 inventory变量-经验之谈 当你在一个项目中定义和管理变量时,请遵循以下原则:
2.5.1 保持简单 尽管Ansible变量可以用很多不同的方式定义,但是尽量只使用几种不同的方法和在少数地方定义变量。
2.5.2 不要重复设置变量 如果一组系统具有公共配置,则将它们组织成一个组,并在group_vars目录中的一个文件中为它们设置库存变量。这样,您就不必在每个主机上定义相同的设置,而且当您必须为该组系统修改一个变量时,您可以通过更新变量文件来实现。
2.5.3 在可读的小文件中组织变量 如果您有一个带有许多主机组和变量的大型项目,那么将变量定义拆分为多个文件。为了更容易找到特定的变量,将相关变量分组到一个文件中,并给该文件一个有意义的名称。
2.6 变量的优先级 当以多种方式定义同一个变量时,Ansible使用优先级规则为该变量选择一个值。在处理所有变量定义之后,Ansible在每个任务开始时为每个主机生成一组合并变量。当Ansible合并变量时,如果同一个变量在不同的位置有两个定义,它将使用优先级最高的位置的值。
优先顺序的列表可以在https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html的“变量优先:我应该把变量放在哪里?”小节中找到。
2.6.1 命令行 在命令行上传递给ansible-playbook的选项(除了用于额外变量的-e)优先级最低。例如,可以使用-u选项设置远程用户以覆盖配置文件,但是可以通过将ansible_user设置为更高的优先级来覆盖它。
2.6.2 role 的缺省 role在rolename/defaults/中的文件中设置的默认值具有非常低的优先级,因此它们很容易被覆盖。使用这些默认值是为了使用一些合理的值定义角色的变量,但可以将其配置为其他值,因此role默认值通常用于用户配置role。
2.6.3 主机和主机组变量 您可以在许多地方设置特定于主机和特定于组的变量。你可以根据你的库存位置来设置它们。您可以相对于您的剧本的位置设置它们。最后,可以通过收集主机facts(或从缓存中读取facts)来设置它们。
这些变量的确切优先级列表是,从最低到最高:
在库存文件或动态库存脚本中直接设置的组变量。
在inventory的group_vars/all文件或子目录中为所有设置分组。
在剧本的group_vars/all文件或子目录中对所有设置的变量进行分组。
在inventory的group_vars子目录中设置的其他组的组变量。
在剧本的group_vars子目录中设置的其他组的组变量。
主机变量直接在库存文件或动态库存脚本中设置。
在inventory的host_vars子目录中设置主机变量。
在剧本的host_vars子目录中设置的主机变量。
主机facts和缓存facts
2.6.4 play中的变量 变量是在剧本中作为剧本、任务、角色参数或包含或导入的部分设置的变量。它们的优先级高于主机或组变量、角色默认值和除-e之外的命令行选项。
这些变量的优先级列表是,从最低到最高:
由剧本的vars部分设置。
通过在戏剧中提示用户使用vars_prompt部分来设置。
通过剧本的vars_files部分从外部文件列表中设置。
由角色的rolename/vars/子目录中的文件设置。
设置当前block的vars部分。
设置当前task的vars部分。
通过include_vars模块动态加载。
通过使用set_fact模块或使用寄存器记录主机上任务执行的结果,为特定主机设置。
由剧本的角色部分或使用include_role模块加载时为剧本中的角色设置的参数。
由vars部分设置包含在include_tasks模块中的任务。
2.7 特殊的inventory变量 2.7.1 ansible_connection 用于访问托管主机的连接插件。默认情况下,ssh用于除localhost外的所有主机,localhost使用local。
2.7.2 ansible_host 连接到托管主机时要使用的实际IP地址或完全限定的域名,而不是使用清单文件中的名称(inventory_hostname)。默认情况下,该变量的值与inventory主机名相同。
2.7.3 ansible_port Ansible用来连接到被管理主机的端口。对于(默认的)SSH连接插件,该值默认为22。
2.7.4 ansible_user Ansible作为这个用户连接到托管主机。Ansible的默认行为是使用与在控制节点上运行Ansible Playbook的用户相同的用户名连接到托管主机。
2.7.5 ansible_become_user 一旦Ansible连接到托管主机,它将使用absible_become_method(默认为sudo)切换到这个用户。您可能需要以某种方式提供身份验证凭据。
2.7.6 ansible_python_interpreter Ansible应该在托管主机上使用的Python可执行文件的路径。在Ansible2.8及更高版本上,默认为auto,这将自动选择一个Python解释器
2.7.7 inventory_hostname 当前正在处理的托管主机的名称,取自inventory
2.7.8 ansible_host ssh连接的名字
2.7.9 ansible_facts[‘hostname’] 短的(非限定的)主机名作为一个facts从被管理的主机收集。
2.7.10 ansible_facts[‘fqdn’] 从托管主机收集的完全限定域名(FQDN)作为facts
最后一个有用的变量是ansible_play_hosts,它是当前播放期间尚未失败的所有主机的列表(因此将用于播放中剩余的任务)
3、管理task执行 3.1 提权策略 Ansible Playbooks可以实现许多不同级别的特权升级。根据您想要控制特权升级的级别,Ansible使用指令或连接变量。对于play、role、block和task,你需要使用权限升级指令:become、become_user、become_method和become_flags。
如果Ansible配置文件指定变成:false,但命令行包含-b选项,那么Ansible将忽略配置文件,并在默认情况下使用特权升级。
默认情况下配置文件中的提权都是关闭的,一般来说把需要提权的task放在一起,新建block对整个task进行提权。
3.2 控制playbook的task执行顺序 在play中,Ansible总是先运行role部分定义的task,例如下图的play就是先运行role然后再运行task
Ansible的最新版本允许你将role作为task包括或导入,而不是使用play的role部分。这种方法的优点是,您可以轻松地运行一组任务、导入或包含role,然后运行更多task。一个潜在的缺点是,在没有仔细检查的情况下,你可能不太清楚你的剧本使用的是哪些role。
include_role模块是动态导入 import_role是静态导入
import_role意味着静态导入,在开始执行之前首先解析并在开始执行之前将角色加入到playbook中,如果此时role有语法错误,playbook并不会成功执行。
include_role是动态导入,是在执行到这个task的时候才解析这个role,如果再执行这个role的时候报错就会退出playbook。
而且使用include_role的时候,如果when条件判断为false,那么将不会执行这个role。
定义pre_tasks,指定先被执行;post_tasks,指定最后执行的task
3.3 handlers的监听通知用法
如果在play里面都定义了handlers,它们都会按照顺序执行多次
若要立即运行已被playbook中的task中的notify任何处理程序,添加一个使用带有flush handlers的模块
会运行在这个模块前所有的handlers task
3.4 handlers的监听通知用法 除了被task通知之外,一个handlers也可以订阅一个特定的通知,并且当通知被触发之后自动的运行handlers。这样就运行了多个handlers可以被一个触发引用。
缺省情况下,一个通知的名字必须匹配handlers的名字才可以触发handlers。然而每个handler必须有一个唯一的名字,唯一触发多个handlers的方式就是订阅同样消息的handlers。
可以针对tasks层面进行标记,也可以在play层面进行标记,也可以在block层面进行标记
3.6 使用参数来控制带tag的task执行
1 2 ansible-playbook playbook.yml --list-tags
3.7 ansible特殊的tag标记
3.8 优化playbook的执行 有很多方法可以优化你的Ansible剧本。随着管理主机数量的增加,编写有效运行的playbook变得越来越重要。
红帽Ansible引擎的每个版本都增加了增强和改进。运行最新版本的Ansible可能有助于提高你的playbook的速度,因为Ansible的核心组件,特别是与它一起提供的模块是经过时间优化的
从网络的角度来看,您可以进行的一种架构优化是让您的控制节点“接近”被管理节点。Ansible在很大程度上依赖于网络通信和数据传输。控制节点和被管理主机之间的高延迟连接或低带宽会降低plavbooks的执行时间。
有些facts变量的值你可以用Inventory变量或者magic变量,这样你就不用搜集facts变量了。
你也可以选择通过运行setup模块手工收集facts变量,这样就可以在指定的主机上搜集,而不是所有的主机都搜集。
当Ansible正在运行一个play时,它将在当前批处理中的每个主机上运行第一个task,然后在当前批处理中的每个主机上运行第二个task,如此循环,直到play完成。
forks参数控制Ansible同时可以激活多少个连接。默认情况下,这个值被设置为5。这意味着即使在当task中有100个主机需要处理。Ansible会以五人一组的形式与他们交流。一旦它与所有的100个通信,Ansible就会进入下一个task。
通过增加fork值,Ansible可以同时在更多的主机上运行每个任务。剧本通常会在更短的时间内完成。例如,如果将“forks”设置为100。Ansible可以尝试同时打开前面例子中所有100个主机的连接。这将增加控制节点的负载,它仍然需要足够的时间与每个主机通信。
3.8.1 高效复制文件到托管主机 copy模块递归地将目录复制到托管主机。当目录较大时。有很多文件,拷贝要花很长时间。如果多次运行剧本,后续的复制将花费更少的时间,因为模块只复制不同的文件。
然而,使用同步模块将大量文件复制到托管主机通常会更高效。这个模块在后台使用rsync,在大多数情况下比复制模块更快。
下面的playbook使用同步模块递归地复制web_content到web服务器。
3.8.2 使用template模板 lineinfile模块在文件中插入或删除行,比如配置文件中的配置指令。下面的剧本示例通过替换几行来更新Apache HTTP Server配置文件。
当与循环一起使用时,lineinfile不是很有效(而且很容易出错)。在这种情况下,要么使用模板,要么使用复制模块。
3.8.3 优化SSH连接 SSH连接的建立过程可能比较缓慢。当一个剧本有很多任务,目标是一个大的节点集,Ansible花在建立这些连接上的总时间可以显著增加剧本的全局执行时间。
ControlMaster SSH指令允许远程主机同时进行多个SSH会话,使用单个网络连接。第一个SSH会话建立连接,到同一主机的其他会话重用该连接,绕过了缓慢的初始过程。一旦最后一个会话关闭,SSH就会销毁共享连接。(避免了多次ssh协商)
ControlPersist SSH指令在后台保持连接打开,而不是在最后一次会话后销毁连接。这个指令允许以后的SSH会话重用连接。ControlPersist表示SSH应该保持空闲连接打开多长时间;每个新的会话都会重置这个空闲计时器。
Ansible在配置文件的[ssh_connection]部分下的ssh_args指令中启用了ControlMaster和ControlPersist特性。ssh_args的默认值如下。
3.8.4 开启流水线模式 开启之后会直接在ssh会话中执行,而不用把脚本传到被执行主机上再执行。
重要 :使用这个参数要在/etc/sudoers中关闭requiretty 参数(在linux 8中该参数默认是关闭的)
3.9 playbook的执行分析 Ansible提供了一组回调插件,你可以通过callback_whitelist指令在Ansible .cfg文件中启用这些插件。
3.9.1 分析控制节点CPU和内存 cgroup_perf_recap回调插件在剧本运行期间对控制节点进行概要分析。在剧本执行结束时,它显示一个全局摘要和每个任务的摘要。这些总结包括CPU和内存消耗,以及剧本和任务执行期间启动的最大进程数。
3.9.2 Timing Tasks and Roles
4、使用fileter和插件转换数据 在完成这个章节后,你可以使用filter格式化,解析和定义变量。
ansible应用变量到playbook并且使用jinja2表达式来使用变量。例如下面的J2表达式中的变量使用两个大括号括起来。
J2表达式也支持filter。Filter在playbook或者模板中被用来修改或者处理要替代的变量值。一些filter使用J2语言来使用,其他的一些filter被包含在ansible的插件中。它也可以创建自定义的filter,尽管这超出了本课程的范围。filter在playbook中或者template用于准备使用的数据是非常有用的。
为了理解filter,你必须首先知道ansible是如何处理变量的。
Ansible将运行时数据存储在变量中。YAML结构或值的内容定义了确切的数据类型。一些值类型包括: 字符串 数字 布尔值(对或者错误) 日期(遵循ios8601) Null(未被定义的 ) 列表或阵列(变量矩阵每个值都是等价的) 字典
字符串不需要使用单引号 或者双引号 。ansible会在不加引号的时候去掉结尾多域的空格。
| 表示都在一行 > 表示分行
4.1 体验filter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 [workstation development-practices] $ cat debug.yml --- - name: test filter hosts: servera.lab.example.com vars: var1: test var2: 123 tasks: - name: test capitalize filter debug: msg: "{{ var1 | capitalize }}" - name: test string filter debug: msg: "{{ var2 }}" - name: test string filter debug: msg: "{{ var2 | string }}" - name: debug: msg: "{{ [1,4,2,2] | unique | sort }}" [workstation development-practices] $ ansible-playbook debug.yml PLAY [test filter] ************************************************************* TASK [Gathering Facts] ********************************************************* ok: [servera.lab.example.com] TASK [test capitalize filter] ************************************************** ok: [servera.lab.example.com] => { "msg" : "Test" } TASK [test string filter] ****************************************************** ok: [servera.lab.example.com] => { "msg" : 123 } TASK [test string filter] ****************************************************** ok: [servera.lab.example.com] => { "msg" : "123" } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : [ 1, 2, 4 ] } PLAY RECAP ********************************************************************* servera.lab.example.com : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
4.2 mandatory和default filter用法 判断变量是否存在,和when的区别是,when是对task层面做的定义,假如这个变量没有被定义,这个play还会继续运行下去。
mandatory
default
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 [workstation development-practices] $ cat default.yml --- - name: test filter hosts: servera.lab.example.com vars: var1: test tasks: - name: use defalt filter debug: msg: "{{ var1 | mandatory }}" - name: use defalt filter debug: msg: "{{ var2 | mandatory }}" tags: - mandatory - name: use defalt filter debug: msg: "{{ var1 | default('var1 is not exist') }}" - name: use defalt filter debug: msg: "{{ var1 | default('var1 is not exist',true) }}" - name: use defalt filter debug: msg: "{{ var2 | default('var1 is not exist') }}" - name: use defalt filter debug: msg: "{{ var2 | default('var1 is not exist',true) }}" - name: use defalt filter debug: msg: "{{ var1 | default(omit) }}" - name: use defalt filter debug: msg: "{{ var2 | default(omit) }}" [workstation development-practices] $ ansible-playbook default.yml --skip-tags mandatory PLAY [test filter] ************************************************************* TASK [Gathering Facts] ********************************************************* ok: [servera.lab.example.com] TASK [use defalt filter] ******************************************************* ok: [servera.lab.example.com] => { "msg" : "test" } TASK [use defalt filter] ******************************************************* ok: [servera.lab.example.com] => { "msg" : "test" } TASK [use defalt filter] ******************************************************* ok: [servera.lab.example.com] => { "msg" : "test" } TASK [use defalt filter] ******************************************************* ok: [servera.lab.example.com] => { "msg" : "var1 is not exist" } TASK [use defalt filter] ******************************************************* ok: [servera.lab.example.com] => { "msg" : "var1 is not exist" } TASK [use defalt filter] ******************************************************* ok: [servera.lab.example.com] => { "msg" : "test" } TASK [use defalt filter] ******************************************************* ok: [servera.lab.example.com] => { "msg" : "Hello world!" } PLAY RECAP ********************************************************************* servera.lab.example.com : ok=8 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
4.3 算数运算filter介绍和使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 [workstation development-practices] $ cat number.yml --- - name: number hosts: servera.lab.example.com tasks: - debug: msg: "{{ 1 + 1 }}" - debug: msg: "{{ ( ansible_facts['date_time']['hour'] | int ) + 1 }}" - debug: msg: "{{ 10 - 1 }}" - debug: msg: "{{ 2 * 4 }}" - debug: msg: "{{ 10 / 3 }}" - debug: msg: "{{ 9 / 3 }}" - debug: msg: "{{ 10 // 3 }}" - debug: msg: "{{ 15 % 4 }}" - debug: msg: "{{ 2 ** 4 }}" - debug: msg: "{{ 1764 | root }}" [workstation development-practices] $ ansible-playbook number.yml PLAY [number] ****************************************************************** TASK [Gathering Facts] ********************************************************* ok: [servera.lab.example.com] TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : "2" } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : "7" } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : "9" } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : "8" } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : "3.3333333333333335" } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : "3.0" } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : "3" } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : "3" } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : "16" } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : "42.0" } PLAY RECAP ********************************************************************* servera.lab.example.com : ok=11 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
4.4 使用filter操作变量字典 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 [workstation development-practices] $ cat dict.yml --- - name: dict filter test hosts: servera.lab.example.com vars: test1: name1: gzy name2: mwc name3: ywj test2: - key: name1 value: gzy - key: name2 value: mwc - key: name3 value: ywj tasks: - name: dict to list filter debug: msg: "{{ test1 | dict2items }}" - name: list to dict filter debug: msg: "{{ test2 | items2dict }}" [workstation development-practices] $ ansible-playbook dict.yml PLAY [dict filter test ] ******************************************************** TASK [Gathering Facts] ********************************************************* ok: [servera.lab.example.com] TASK [dict to list filter] ***************************************************** ok: [servera.lab.example.com] => { "msg" : [ { "key" : "name1" , "value" : "gzy" }, { "key" : "name2" , "value" : "mwc" }, { "key" : "name3" , "value" : "ywj" } ] } TASK [list to dict filter] ***************************************************** ok: [servera.lab.example.com] => { "msg" : { "name1" : "gzy" , "name2" : "mwc" , "name3" : "ywj" } } PLAY RECAP ********************************************************************* servera.lab.example.com : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
4.5 使用filter操作变量矩阵 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 [workstation development-practices] $ cat list.yml --- - name: list filter hosts: servera.lab.example.com tasks: - debug: msg: "{{ [2,4,6,8,10,12] | sum }}" - debug: msg: "{{ [2,4,6,8,10,12] | max }}" - debug: msg: "{{ [2,4,6,8,10,12] | min }}" - debug: msg: "{{ [2,4,6,8,10,12] | length }}" - debug: msg: "{{ [2,4,6,8,10,12] | first }}" - debug: msg: "{{ [2,4,6,8,10,12] | last }}" - debug: msg: "{{ [2,4,6,8,10,12] | random }}" - debug: msg: "{{ [2,4,6,8,10,12] | reverse | list }}" - debug: msg: "{{ [4,2,6,10,8,12] | sort }}" - debug: msg: "{{ [2,[4,[6,8]],10,12] | flatten }}" - debug: msg: "{{ [4,2,2,2,8,12] | unique | list }}" - debug: msg: "{{ [4,2,6,10,8,12] | union([2,10,16,18]) }}" - debug: msg: "{{ [4,2,6,10,8,12] | intersect([2,4,100,102])}}" - debug: msg: "{{ [4,2,6,10,8,12] | difference([2,4,16,19]) }}" - debug: msg: "{{ [4,2,6,10,8,12] | symmetric_difference([2,4,16,19]) }}" [workstation development-practices] $ ansible-playbook list.yml PLAY [list filter] ************************************************************* TASK [Gathering Facts] ********************************************************* ok: [servera.lab.example.com] TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : "42" } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : "12" } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : "2" } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : "6" } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : "2" } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : "12" } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : "12" } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : [ 12, 10, 8, 6, 4, 2 ] } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : [ 2, 4, 6, 8, 10, 12 ] } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : [ 2, 4, 6, 8, 10, 12 ] } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : [ 4, 2, 8, 12 ] } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : [ 4, 2, 6, 10, 8, 12, 16, 18 ] } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : [ 4, 2 ] } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : [ 6, 10, 8, 12 ] } TASK [debug] ******************************************************************* ok: [servera.lab.example.com] => { "msg" : [ 6, 10, 8, 12, 16, 19 ] } PLAY RECAP ********************************************************************* servera.lab.example.com : ok=16 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
4.6 使用filter实现哈希,编码解码,操作字符串 可以使用许多过滤器来操作值的文本。您可以采用各种校验和、创建密码散列,并将文本转换为Base64编码,许多应用程序都使用这种方法
4.7 使用filter实现yml和json格式的数码转换 Ansible使用的许多数据结构都是JSON格式的。JSON和YAML表示法密切相关,Ansible数据结构可以处理为JSON。同样,Ansible Playbooks可能与许多api交互,使用或提供JSON格式的信息。因为这种格式被广泛使用,所以JSON过滤器特别有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 [workstation development-practices] $ cat json.yml --- - name: Get the 'name' elements from the list of dictionaries in 'hosts' hosts: servera.lab.example.com vars: hosts: - name: bastion ip: - 172.25.250.254 - 172.25.252.1 - name: classroom ip: - 172.25.252.254 tasks: - assert: that: - "{{ hosts | json_query('[*].name') }} is eq ( ['bastion','classroom'] )" [workstation development-practices] $ ansible-playbook json.yml [WARNING]: Found variable using reserved name: hosts PLAY [Get the 'name' elements from the list of dictionaries in 'hosts' ] ******** TASK [Gathering Facts] ********************************************************* ok: [servera.lab.example.com] TASK [assert] ****************************************************************** ok: [servera.lab.example.com] => { "changed" : false , "msg" : "All assertions passed" } PLAY RECAP ********************************************************************* servera.lab.example.com : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
4.8 使用filter处理变量练习 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 [workstation data-filters(master *%)] $ cat roles/webapp/tasks/main.yml --- - name: Ensure stub web content is deployed copy: content: "{{ webapp_message }} (version {{ webapp_version }})\n" dest: "{{ webapp_content_root_dir }}/index.html" - name: Find deployed webapp files find: paths: "{{ webapp_content_root_dir }}" recurse: yes register: webapp_find_files - debug: var: webapp_find_files - name: Compute the webapp file list set_fact: webapp_deployed_files: "{{ webapp_find_files['files'] | map(attribute='path') | list }}" - debug: var: webapp_deployed_files - name: Compute the relative webapp file list set_fact: webapp_rel_deployed_files: "{{ webapp_deployed_files | map('relpath',webapp_content_root_dir) | list }}" - debug: var: webapp_rel_deployed_files - name: Remove Extraneous Files file: path: "{{ webapp_content_root_dir }}/{{ item }}" state: absent loop: "{{ webapp_rel_deployed_files | difference(webapp_file_list) }}" [workstation data-filters(master *%)] $ [workstation data-filters(master *%)] $ cat roles/webapp/tasks/main.yml --- - name: Ensure stub web content is deployed copy: content: "{{ webapp_message }} (version {{ webapp_version }})\n" dest: "{{ webapp_content_root_dir }}/index.html" - name: Find deployed webapp files find: paths: "{{ webapp_content_root_dir }}" recurse: yes register: webapp_find_files - debug: var: webapp_find_files - name: Compute the webapp file list set_fact: webapp_deployed_files: "{{ webapp_find_files['files'] | map(attribute='path') | list }}" - debug: var: webapp_deployed_files - name: Compute the relative webapp file list set_fact: webapp_rel_deployed_files: "{{ webapp_deployed_files | map('relpath',webapp_content_root_dir) | list }}" - debug: var: webapp_rel_deployed_files - name: Remove Extraneous Files file: path: "{{ webapp_content_root_dir }}/{{ item }}" state: absent loop: "{{ webapp_rel_deployed_files | difference(webapp_file_list) }}" [workstation data-filters(master *%)] $ ansible-playbook deploy_webapp.yml PLAY [Ensure Web App is deployed] *********************************************************************************** TASK [webapp : Ensure stub web content is deployed] ***************************************************************** ok: [webserver_01] ok: [webserver_02] TASK [webapp : Find deployed webapp files] ************************************************************************** ok: [webserver_02] ok: [webserver_01] TASK [webapp : debug] *********************************************************************************************** ok: [webserver_01] => { "webapp_find_files" : { "changed" : false , "examined" : 1, "failed" : false , "files" : [ { "atime" : 1644670341.8931136, "ctime" : 1644670212.4471157, "dev" : 64513, "gid" : 0, "gr_name" : "root" , "inode" : 20975720, "isblk" : false , "ischr" : false , "isdir" : false , "isfifo" : false , "isgid" : false , "islnk" : false , "isreg" : true , "issock" : false , "isuid" : false , "mode" : "0644" , "mtime" : 1644670212.1701157, "nlink" : 1, "path" : "/var/www/html/index.html" , "pw_name" : "root" , "rgrp" : true , "roth" : true , "rusr" : true , "size" : 40, "uid" : 0, "wgrp" : false , "woth" : false , "wusr" : true , "xgrp" : false , "xoth" : false , "xusr" : false } ], "matched" : 1, "msg" : "" } } ok: [webserver_02] => { "webapp_find_files" : { "changed" : false , "examined" : 1, "failed" : false , "files" : [ { "atime" : 1644670553.3174498, "ctime" : 1644670212.449421, "dev" : 64513, "gid" : 0, "gr_name" : "root" , "inode" : 20975718, "isblk" : false , "ischr" : false , "isdir" : false , "isfifo" : false , "isgid" : false , "islnk" : false , "isreg" : true , "issock" : false , "isuid" : false , "mode" : "0644" , "mtime" : 1644670212.169421, "nlink" : 1, "path" : "/var/www/html/index.html" , "pw_name" : "root" , "rgrp" : true , "roth" : true , "rusr" : true , "size" : 40, "uid" : 0, "wgrp" : false , "woth" : false , "wusr" : true , "xgrp" : false , "xoth" : false , "xusr" : false } ], "matched" : 1, "msg" : "" } } TASK [webapp : Compute the webapp file list] ************************************************************************ ok: [webserver_01] ok: [webserver_02] TASK [webapp : debug] *********************************************************************************************** ok: [webserver_01] => { "webapp_deployed_files" : [ "/var/www/html/index.html" ] } ok: [webserver_02] => { "webapp_deployed_files" : [ "/var/www/html/index.html" ] } TASK [webapp : Compute the relative webapp file list] *************************************************************** ok: [webserver_01] ok: [webserver_02] TASK [webapp : debug] *********************************************************************************************** ok: [webserver_01] => { "webapp_rel_deployed_files" : [ "index.html" ] } ok: [webserver_02] => { "webapp_rel_deployed_files" : [ "index.html" ] } TASK [webapp : Remove Extraneous Files] ***************************************************************************** PLAY RECAP ********************************************************************************************************** webserver_01 : ok=7 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 webserver_02 : ok=7 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
4.9 lookup插件介绍和使用 Jinja2模板语言的Ansible扩展。这些插件使Ansible能够使用来自外部源的数据,比如文件和shell环境。
在ansible2.5之后,query也可以实现相同的功能,换行符\n都是被保留下来的。query输出的是变量矩阵形式的。
4.10 lookup插件常用功能介绍和使用 4.10.1 查看lookup插件功能 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 [workstation ~] $ ansible-doc -t lookup -l aws_account_attribute Look up AWS account attributes aws_secret Look up secrets stored in AWS Secrets Manager aws_service_ip_ranges Look up the IP ranges for services provided in AWS such as EC2 and S3 aws_ssm Get the value for a SSM parameter or all parameters under a path cartesian returns the cartesian product of lists chef_databag fetches data from a Chef Databag config Lookup current Ansible configuration values conjur_variable Fetch credentials from CyberArk Conjur consul_kv Fetch metadata from a Consul key value store cpm_metering Get Power and Current data from WTI OOB/Combo and PDU devices cpm_status Get status and parameters from WTI OOB and PDU devices credstash retrieve secrets from Credstash on AWS csvfile read data from a TSV or CSV file cyberarkpassword get secrets from CyberArk AIM dict returns key/value pair items from dictionaries dig query DNS using the dnspython library dnstxt query a domain(s)'s DNS txt fields env read the value of environment variables etcd get info from an etcd server file read file contents fileglob list files matching a pattern filetree recursively match all files in a directory tree first_found return first file found from list flattened return single list completely flattened grafana_dashboard list or search grafana dashboards hashi_vault retrieve secrets from HashiCorp' s vault hiera get info from hiera data indexed_items rewrites lists to return 'indexed items' ini read data from a ini file inventory_hostnames list of inventory hosts matching a host pattern items list of items k8s Query the K8s API keyring grab secrets from the OS keyring laps_password Retrieves the LAPS password for a server lastpass fetch data from lastpass lines read lines from command list simply returns what it is given manifold get credentials from Manifold.co mongodb lookup info from MongoDB nested composes a list with nested elements of other lists nios Query Infoblox NIOS objects nios_next_ip Return the next available IP address for a network nios_next_network Return the next available network range for a network-container onepassword fetch field values from 1Password onepassword_raw fetch an entire item from 1Password password retrieve or generate a random password, stored in a file passwordstore manage passwords with passwordstore.org's pass utility pipe read output from a command rabbitmq Retrieve messages from an AMQP/AMQPS RabbitMQ queue random_choice return random element from list redis fetch data from Redis redis_kv fetch data from Redis sequence generate a list based on a number sequence shelvefile read keys from Python shelve file skydive Query Skydive objects subelements traverse nested key from a list of dictionaries template retrieve contents of file after templating with Jinja2 together merges lists into synchronized list url return contents from URL varnames Lookup matching variable names vars Lookup templated value of variables
4.10.2 查看功能详细用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 [workstation ~] $ ansible-doc -t lookup file > FILE (/usr/lib/python3.6/site-packages/ansible/plugins/lookup/file.py) This lookup returns the contents from a file on the Ansible controller's file system. * This module is maintained by The Ansible Community OPTIONS (= is mandatory): = _terms path(s) of files to read - lstrip whether or not to remove whitespace from the beginning of the looked-up file [Default: False] type: bool - rstrip whether or not to remove whitespace from the ending of the looked-up file [Default: True] type: bool NOTES: * if read in variable context, the file can be interpreted as YAML if the content is valid to the parser. * this lookup does not understand ' globing', use the fileglob lookup instead. AUTHOR: Daniel Hokka Zakrisson <[email protected] > METADATA: status: - preview supported_by: community EXAMPLES: - debug: msg="the value of foo.txt is {{lookup(' file', ' /etc/foo.txt') }}" - name: display multiple file contents debug: var=item with_file: - "/path/to/foo.txt" - "bar.txt" # will be looked in files/ dir relative to play or in role - "/path/to/biz.txt" RETURN VALUES: _raw: description: - content of file(s)
4.11 使用lookup插件匹配外部数据练习 4.11.1 passwd插件的使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [workstation users(master *%)] $ cat site.yml - name: Populate users from file hosts: all gather_facts: no tasks: - name: Create remote user debug: msg: "To be done" vars: password: "{{ lookup('password','credentials/' + item + ' length=6') }}" loop: "{{ query('lines','cat users.txt')}}" [workstation users(master *%)] $ ll credentials/ total 12 -rw-------. 1 student student 7 Feb 13 05:49 janebar -rw-------. 1 student student 7 Feb 13 05:49 jonfoo -rw-------. 1 student student 7 Feb 13 05:49 philbaz
4-12 loops和lookup插件结合使用 4-13 with_dict和dict2item处理字典,使用file 4-14 with和loop高级循环用法 4-14-1 场景1 scenario_1.yml包含一个单独的play和一个task,在身份管理服务器上创建用户。该task使用with_dict关键字迭代users变量中的条目,users变量定义在group_vars/all/users.yml中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 [workstation data-loops] $ cat scenario_1.yml --- - name: Add Users To IDM hosts: localhost gather_facts: no tasks: - name: Create Users ipa_user: name: "{{ item.key }}" givenname: "{{ item.value.firstname }}" sn: "{{ item.value.surname }}" displayname: "{{ item.value.firstname + ' ' + item.value.surname }}" sshpubkey: "{{ lookup('file', item.value.pub_key_file) }}" state: present ipa_host: "{{ ipa_server }}" ipa_user: "{{ ipa_admin_user }}" ipa_pass: "{{ ipa_admin_pass }}" validate_certs: "{{ ipa_validate_certs }}" with_dict: "{{ users }}" [workstation data-loops] $ cat scenario_1.yml --- - name: Add Users To IDM hosts: localhost gather_facts: no tasks: - name: Create Users ipa_user: name: "{{ item.key }}" givenname: "{{ item.value.firstname }}" sn: "{{ item.value.surname }}" displayname: "{{ item.value.firstname + ' ' + item.value.surname }}" sshpubkey: "{{ lookup('file', item.value.pub_key_file) }}" state: present ipa_host: "{{ ipa_server }}" ipa_user: "{{ ipa_admin_user }}" ipa_pass: "{{ ipa_admin_pass }}" validate_certs: "{{ ipa_validate_certs }}" loop: "{{ users | dict2items }}" [workstation data-loops] $ ansible-playbook --vault-id @prompt scenario_1.yml Vault password (default): PLAY [Add Users To IDM] *************************************************************** TASK [Create Users] ******************************************************************* changed: [localhost] => (item={'key' : 'johnd' , 'value' : {'firstname' : 'John' , 'surname' : 'Doe' , 'pub_key_file' : 'pubkeys/johnd/id_rsa.pub' }}) changed: [localhost] => (item={'key' : 'janed' , 'value' : {'firstname' : 'Jane' , 'surname' : 'Doe' , 'pub_key_file' : 'pubkeys/janed/id_rsa.pub' }}) PLAY RECAP **************************************************************************** localhost : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
4-14-2场景2 scenario_2.yml配置一个数据库服务器,并允许适当的用户访问托管在服务器上的数据库。playbook task 使用with_subbelements关键字为db_use_access变量中的每个用户遍历一个数据库列表。db_user_access变量定义在group_vars/all/db_vars.yml文件中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 [workstation data-loops] $ cat scenario_2.yml --- - name: Enable Database Access hosts: db_server gather_facts: no tasks: - import_tasks: database_setup.yml - name: Enable user access to database mysql_user: name: "{{ item.0.username }}" priv: "{{ item.1 }}.*:ALL" host: workstation.lab.example.com append_privs: yes password: "{{ user_passwords[item.0.username] }}" state: present loop: "{{ db_user_access | subelements('db_list') }}" - name: Smoke Test - database connectivity shell: "{{ lookup('template', 'db_test_command.j2') }}" loop: "{{ db_user_access | subelements('db_list') }}" delegate_to: workstation changed_when: no [workstation data-loops] $ cat templates/db_test_command.j2 echo "use {{ item.1 }}" | \ mysql -u {{ item.0.username }} \ -p{{ user_passwords[item.0.username] }} \ -h {{ inventory_hostname }} [workstation data-loops] $ ansible-playbook --ask-vault-pass scenario_2.yml Vault password: PLAY [Enable Database Access] ************************************************** TASK [Enable Firewalld] ******************************************************** ok: [servere] TASK [Install MySQL] *********************************************************** ok: [servere] TASK [Start and enable the MySQL service] ************************************** ok: [servere] TASK [Ensure databases exist] ************************************************** ok: [servere] => (item=employees) ok: [servere] => (item=inventory) ok: [servere] => (item=invoices) TASK [Enable user access to database] ****************************************** ok: [servere] => (item=[{'username' : 'johnd' , 'dept' : 'sales' , 'role' : 'manager' , 'db_list' : ['employees' , 'invoices' ]}, 'employees' ]) ok: [servere] => (item=[{'username' : 'johnd' , 'dept' : 'sales' , 'role' : 'manager' , 'db_list' : ['employees' , 'invoices' ]}, 'invoices' ]) ok: [servere] => (item=[{'username' : 'janed' , 'dept' : 'sales' , 'role' : 'associate' , 'db_list' : ['invoices' , 'inventory' ]}, 'invoices' ]) ok: [servere] => (item=[{'username' : 'janed' , 'dept' : 'sales' , 'role' : 'associate' , 'db_list' : ['invoices' , 'inventory' ]}, 'inventory' ]) TASK [Smoke Test - database connectivity] ************************************** ok: [servere] => (item=[{'username' : 'johnd' , 'dept' : 'sales' , 'role' : 'manager' , 'db_list' : ['employees' , 'invoices' ]}, 'employees' ]) ok: [servere] => (item=[{'username' : 'johnd' , 'dept' : 'sales' , 'role' : 'manager' , 'db_list' : ['employees' , 'invoices' ]}, 'invoices' ]) ok: [servere] => (item=[{'username' : 'janed' , 'dept' : 'sales' , 'role' : 'associate' , 'db_list' : ['invoices' , 'inventory' ]}, 'invoices' ]) ok: [servere] => (item=[{'username' : 'janed' , 'dept' : 'sales' , 'role' : 'associate' , 'db_list' : ['invoices' , 'inventory' ]}, 'inventory' ]) PLAY RECAP ********************************************************************* servere : ok=6 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
4-15 搜集和处理特定的facts变量 这里有一些特别有用的事实。事实ansible_facts[‘interfaces’]是系统上所有网络接口名称的列表。您可以使用这个列表来检查系统上每个网络接口的事实。例如,如果enp11s@是系统上的一个接口,那么它有一个名为ansible_facts[‘ enp11s9’]的事实,它是一个包含其MAC地址、IPv4和IPv6地址、内核模块等值的字典。还有一些其他有用的事实需要记住:
4-16 使用ipaddr和dig插件处理网络信息 ipaddr过滤器提供了操作和验证与网络有关的事实的功能。您可以使用它检查IP地址的语法,将VLSN子网掩码转换为CIDR子网前缀表示法,执行子网数学,并在网络范围内找到下一个可用的地址,等等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 [workstation development-practices] $ cat network.yml --- - name: ipaddr test hosts: servera.lab.example.com vars: addr: "1.1.1.1/24" addr1: "1.1.1.0/24" addr2: "1.1.1.128/25" tasks: - debug: msg: "{{ addr | ipaddr }}" - debug: msg: "{{ addr | ipaddr('netmask') }}" - debug: msg: "{{ addr | ipaddr('address') }}" - debug: msg: "{{ addr | ipaddr('net') }}" - debug: msg: "{{ addr1 | ipaddr('net') }}" - debug: msg: "{{ addr | ipaddr('host') }}" - debug: msg: "{{ addr | ipaddr('prefix') }}" - debug: msg: "{{ addr | ipaddr('host/prefix') }}" - debug: msg: "{{ addr | ipaddr('network/prefix') }}" - debug: msg: "{{ addr | ipaddr('public') }}" - debug: msg: "{{ addr | ipaddr('private') }}" - debug: msg: "{{ addr | ipaddr('size') }}" - debug: msg: "{{ addr2 | ipaddr('10') }} [workstation development-practices] $ ansible-playbook network.yml PLAY [ipaddr test] ********************************************************************************* TASK [Gathering Facts] ***************************************************************************** ok: [servera.lab.example.com] TASK [debug] *************************************************************************************** ok: [servera.lab.example.com] => { " msg": " 1.1.1.1/24" } TASK [debug] *************************************************************************************** ok: [servera.lab.example.com] => { " msg": " 255.255.255.0" } TASK [debug] *************************************************************************************** ok: [servera.lab.example.com] => { " msg": " 1.1.1.1" } TASK [debug] *************************************************************************************** ok: [servera.lab.example.com] => { " msg": " " } TASK [debug] *************************************************************************************** ok: [servera.lab.example.com] => { " msg": " 1.1.1.0/24" } TASK [debug] *************************************************************************************** ok: [servera.lab.example.com] => { " msg": " 1.1.1.1/24" } TASK [debug] *************************************************************************************** ok: [servera.lab.example.com] => { " msg": " 24" } TASK [debug] *************************************************************************************** ok: [servera.lab.example.com] => { " msg": " 1.1.1.1/24" } TASK [debug] *************************************************************************************** ok: [servera.lab.example.com] => { " msg": " 1.1.1.0/24" } TASK [debug] *************************************************************************************** ok: [servera.lab.example.com] => { " msg": " 1.1.1.1/24" } TASK [debug] *************************************************************************************** ok: [servera.lab.example.com] => { " msg": " " } TASK [debug] *************************************************************************************** ok: [servera.lab.example.com] => { " msg": " 256" } TASK [debug] *************************************************************************************** ok: [servera.lab.example.com] => { " msg": " " } PLAY RECAP ***************************************************************************************** servera.lab.example.com : ok=14 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
您可以使用许多lookup插件来收集关于网络环境的信息。其中包括dig以查找DNS信息,dns txt以获取DNS TXT记录,以及更深奥的插件,如aws service_ip_ranges以获取Amazon aws EC2服务的IP范围,以及nios next IP以从Infoblox获得网络的下一个可用IP。
5 、使用ansible实现滚动更新 5-1 task委派和facts委派练习 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 [workstation update-delegation(master *)] $ cat query_times.yml --- - name: Query server times and store them locally hosts: web_servers gather_facts: false tasks: - name: Take time on server shell: 'date' register: date changed_when: false - debug: var: date - name: Save times to localhost vars: record: "{{ inventory_hostname }} time: {{ date.stdout_lines[0] }}" shell: "echo '{{ record }}' >> /tmp/times.txt" delegate_to: localhost [workstation update-delegation(master *)] $ cat /tmp/times.txt servera.lab.example.com time: Wed Feb 23 14:28:15 CST 2022 serverb.lab.example.com time: mié feb 23 14:28:15 CST 2022 serverd.lab.example.com time: Wed Feb 23 14:28:15 CST 2022 serverc.lab.example.com time: Wed Feb 23 14:28:15 CST 2022 servere.lab.example.com time: Wed Feb 23 14:28:15 CST 2022
5-2 滚动更新serial参数的高级用法
5-3 滚动更新练习 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 [workstation update-management(master *%)] $ ansible-playbook -e "webapp_version=v1.1.1" update_webapp.yml PLAY [web_servers] ********************************************************************************* TASK [Gathering Facts] ***************************************************************************** ok: [serverb.lab.example.com] TASK [Remove web server from service during the update] ******************************************** ok: [serverb.lab.example.com] => (item=servera.lab.example.com) TASK [webapp : Create a temporary repository dir on the controller] ******************************** changed: [serverb.lab.example.com] TASK [webapp : Set webapp facts] ******************************************************************* ok: [serverb.lab.example.com] TASK [webapp : Clone webapp to the controller] ***************************************************** changed: [serverb.lab.example.com] TASK [webapp : Set webapp file whitelist fact] ***************************************************** ok: [serverb.lab.example.com] TASK [webapp : Get list of remote files in the content directory] ********************************** ok: [serverb.lab.example.com] TASK [webapp : Remove extraneous remote files] ***************************************************** changed: [serverb.lab.example.com] => (item=index.html) TASK [webapp : Copy webapp content] **************************************************************** changed: [serverb.lab.example.com] => (item=index.php) TASK [Smoke Test - Ensure HTTP 200 OK] ************************************************************* ok: [serverb.lab.example.com] TASK [Enable healthy server in load balancers] ***************************************************** changed: [serverb.lab.example.com] => (item=servera.lab.example.com) PLAY [web_servers] ********************************************************************************* TASK [Gathering Facts] ***************************************************************************** ok: [serverc.lab.example.com] TASK [Remove web server from service during the update] ******************************************** changed: [serverc.lab.example.com] => (item=servera.lab.example.com) TASK [webapp : Create a temporary repository dir on the controller] ******************************** changed: [serverc.lab.example.com] TASK [webapp : Set webapp facts] ******************************************************************* ok: [serverc.lab.example.com] TASK [webapp : Clone webapp to the controller] ***************************************************** changed: [serverc.lab.example.com] TASK [webapp : Set webapp file whitelist fact] ***************************************************** ok: [serverc.lab.example.com] TASK [webapp : Get list of remote files in the content directory] ********************************** ok: [serverc.lab.example.com] TASK [webapp : Remove extraneous remote files] ***************************************************** changed: [serverc.lab.example.com] => (item=index.html) TASK [webapp : Copy webapp content] **************************************************************** changed: [serverc.lab.example.com] => (item=index.php) TASK [Smoke Test - Ensure HTTP 200 OK] ************************************************************* ok: [serverc.lab.example.com] TASK [Enable healthy server in load balancers] ***************************************************** changed: [serverc.lab.example.com] => (item=servera.lab.example.com) PLAY [web_servers] ********************************************************************************* TASK [Gathering Facts] ***************************************************************************** ok: [serverd.lab.example.com] ok: [serverf.lab.example.com] ok: [servere.lab.example.com] TASK [Remove web server from service during the update] ******************************************** changed: [serverd.lab.example.com] => (item=servera.lab.example.com) changed: [servere.lab.example.com] => (item=servera.lab.example.com) changed: [serverf.lab.example.com] => (item=servera.lab.example.com) TASK [webapp : Create a temporary repository dir on the controller] ******************************** changed: [serverd.lab.example.com] TASK [webapp : Set webapp facts] ******************************************************************* ok: [serverd.lab.example.com] ok: [servere.lab.example.com] ok: [serverf.lab.example.com] TASK [webapp : Clone webapp to the controller] ***************************************************** changed: [serverd.lab.example.com] TASK [webapp : Set webapp file whitelist fact] ***************************************************** ok: [serverd.lab.example.com] ok: [servere.lab.example.com] ok: [serverf.lab.example.com] TASK [webapp : Get list of remote files in the content directory] ********************************** ok: [serverd.lab.example.com] ok: [serverf.lab.example.com] ok: [servere.lab.example.com] TASK [webapp : Remove extraneous remote files] ***************************************************** changed: [servere.lab.example.com] => (item=index.html) changed: [serverd.lab.example.com] => (item=index.html) changed: [serverf.lab.example.com] => (item=index.html) TASK [webapp : Copy webapp content] **************************************************************** changed: [serverd.lab.example.com] => (item=index.php) changed: [servere.lab.example.com] => (item=index.php) changed: [serverf.lab.example.com] => (item=index.php) TASK [Smoke Test - Ensure HTTP 200 OK] ************************************************************* ok: [serverd.lab.example.com] ok: [servere.lab.example.com] ok: [serverf.lab.example.com] TASK [Enable healthy server in load balancers] ***************************************************** changed: [serverd.lab.example.com] => (item=servera.lab.example.com) changed: [servere.lab.example.com] => (item=servera.lab.example.com) changed: [serverf.lab.example.com] => (item=servera.lab.example.com) PLAY RECAP ***************************************************************************************** serverb.lab.example.com : ok=11 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 serverc.lab.example.com : ok=11 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 serverd.lab.example.com : ok=11 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 servere.lab.example.com : ok=9 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 serverf.lab.example.com : ok=9 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 [workstation update-management(master *%)] $ cat update_webapp.yml --- - hosts: web_servers serial: - 1 - 25% - 100% pre_tasks: - name: Remove web server from service during the update haproxy: state: disabled backend: app host: "{{ inventory_hostname }}" delegate_to: "{{ item }}" with_items: "{{ groups['lb_servers'] }}" roles: - role: webapp post_tasks: - name: Smoke Test - Ensure HTTP 200 OK uri: url: "http://{{ inventory_hostname }}:{{ apache_port }}" status_code: 200 delegate_to: "{{ groups['lb_servers'][0] }}" become: no - name: Enable healthy server in load balancers haproxy: state: enabled backend: app host: "{{ inventory_hostname }}" delegate_to: "{{ item }}" with_items: "{{ groups['lb_servers'] }}"
6、Ansible Tower介绍安装和使用 6.1 ansible tower介绍 随着企业使用Ansible的经验的成熟,通常会有更多的机会利用Ansible来简化和改进IT操作。运营团队用于部署生产系统的Ansible playbook也可以用于在软件开发生命周期的早期阶段部署相同的系统。当使用Ansible实现自动化时,复杂的生产支持任务通常由熟练的工程师处理,可以很容易地委托给入门级技术人员处理。
然而,共享现有的Ansible基础设施来扩展整个企业的IT自动化可能会带来一些挑战。虽然编写良好的Ansible playbook可以跨团队使用,但Ansible并没有提供任何管理共享访问的工具。此外,尽管剧本可能允许委派复杂的任务,但执行这些任务可能需要高度特权和保护的管理员凭据。IT团队通常在他们喜欢的工具集上有所不同。虽然有些团队可能更喜欢剧本的直接执行,但其他团队可能希望从现有的持续集成和交付工具套件中触发剧本的执行。此外,那些传统上使用基于gui的工具的人可能会发现Ansible纯粹的命令行界面令人生畏和尴尬。
Red Hat Ansible Tower通过提供一个框架,在企业规模上有效地运行和管理Ansible,克服了许多这些问题。Ansible Tower通过引入一些特性,例如用于剧本管理的集中式web Ul、基于角色的访问控制(RBAC)以及集中式日志记录和审计,在维护组织安全的同时,简化了共享Ansible基础设施的管理。REST API确保Ansible Tower可以轻松地与企业现有的工作流和工具集集成。Ansible Tower API和通知功能使得将Ansible playbook与其他工具(如Jenkins、Red Hat Cloud Forms或Red Hat Satellite)关联起来特别容易,从而实现持续集成和部署。它提供了一种机制,可以集中使用和控制机器凭证和其他秘密,而不会将它们暴露给Ansible Tower的最终用户。
6.1 ansible tower 架构和部署模式介绍