DO447学习笔记

DO447----高级自动化:可靠的最佳实践

 

课程内容摘要
       • 了解高效实用地使用 Ansible 实现自动化的推荐做法。
       • 借助 Ansible 自动化操作执行滚动更新。
       • 使用 Ansible 的高级功能来处理数据,包括过滤器和插件。
       • 借助 Ansible Playbook,通过 REST API 控制应用。
       • 实施红帽 Ansible Tower,以集中协调和扩展红帽 Ansible 自动化。
       • 利用红帽 Ansible Tower 的功能来管理复杂的自动化工作流。
       • 借助 Git 和红帽 Ansible Tower,实现 CI/CD 业务流程自动化。

******借助 Ansible Playbook,通过 REST API 控制应用******

在Firefox浏览器中,访问https://tower.lab.example.com/api/。你应该看到你的Ansible Tower服务器的可浏览API。

【 job_templates": "/api/v2/job_templates/" 】

JSON-PP必须安装:

[student@workstation ~]$ sudo yum install perl-JSON-PP

[student@workstation ~]$ curl -X GET --user admin:redhat https://tower.lab.example.com/api/v2/job_templates/?name="Demo Job Template" -k -s |json_pp

用python3:

 [student@workstation ~]$ curl -X GET --user admin:redhat https://tower.lab.example.com/api/v2/job_templates/?name="Demo Job Template" -k -s |python3 -m json.tool  

 对于这个命令,-k(--insecure)选项指示curl操作,即使它不能验证SSL证书。使用 -s(--silent)选项,curl只显示返回的数据,而不显示进度状态或错误消息。

切换分支:

[student@workstation my_webservers_DEV]$ git branch
* master
[student@workstation my_webservers_DEV]$ git branch development
[student@workstation my_webservers_DEV]$ git branch
  development
* master
[student@workstation my_webservers_DEV]$ git checkout development
Switched to branch 'development'
[student@workstation my_webservers_DEV]$ git branch
* development
  master
大多数清单插件在默认情况下是禁用的。你可以在你的ansible.cfg配置文件中启用特定的插件,在inventory部分的enable_plugins指令中:

[inventory]
enable_plugins = host_list, script, auto, yaml, ini, toml
[lb_servers]
    servera.lab.example.com
[web_servers]
   serverb.lab.example.com
   serverc.lab.example.com
[backend_server_pool]
   server[b:f].lab.example.com

转换成yml文件格式

lb_servers:
   hosts:
     servera.lab.example.com:
web_servers:
  hosts:
    serverb.lab.example.com:
    serverc.lab.example.com:
backend_server_pool:
  hosts:
    server[b:f].lab.example.com:

清单变量:

 基于yaml的清单文件中直接设置清单变量,就像在基于inil的清单文件中一样。

变量及其值存储在清单的host_vars或group_vars文件中。

在一个基于ini的静态清单文件中,可以将主机localhost的ansible_connection变量设置为local,如下所示:

 

 基于yaml的静态清单文件:

 

 -------------------设置变量-----------------

* 在角色的defaults和vars目录中。
* 在清单文件中,可以作为主机变量或组变量。
* 在playbook或inventory的group_vars或host_vars子目录下的变量文件中。
* 在play, role, task中

 

--------------变量的精确优先级列表从低到高:---------

1 直接设置在清单文件或动态清单脚本中的组变量。
2 在清单的group_vars/all文件或子目录中设置所有变量组。
3 对剧本的group_vars/all文件或子目录中的所有设置的变量进行分组。
4 为清单的group_vars子目录中设置的其他组的变量分组。
5 为剧本的group_vars子目录中设置的其他组的变量分组。
6 主机变量直接设置在清单文件或由一个动态清单脚本。
7 主机变量设置在清单的host_vars子目录中。
8 在playbook的host_vars子目录中设置主机变量。
9 宿主事实和缓存事实。

-----------------------剧本变量的优先级列表从低到高:-------

1 剧本的vars部分设定的。
2 通过在剧本中使用vars_prompt部分提示用户来设置。
3 通过剧本的vars_files部分从外部文件列表中设置。
4 通过角色的rolename/vars/子目录中的文件设置。
5 为当前块设置一个vars部分。
6 设置当前任务的vars部分。
7 用include_vars模块动态加载。
8 通过使用set_fact模块或使用register来记录任务在主机上执行的结果来为特定的主机设置。
9 在剧本中的角色部分或使用include_role模块加载时为剧本中的角色设置的参数。
10 由include_tasks模块中包含的任务的vars部分设置。

* include_vars加载的变量具有较高的优先级,可以覆盖为角色和特定块和任务设置的变量。在许多情况下,如果不希望用外部变量文件覆盖这些值,则可能希望使用vars_files代替。

* set_fact模块和register指令都设置了特定于主机的信息,要么是一个事实,要么是任务在该主机上执行的结果。

* Extra变量使用ansible-playbook命令的-e选项设置的额外变量总是具有最高的优先级

##### ansibl变量的优先级 #####

1. extra vars变量(在命令行中使用 -e);优先级最高

2. 在inventory中定义的连接变量(比如ansible_ssh_user);优先级第二

3. 大多数的其他变量(命令行转换,play中的变量,include的变量,role的变量等);优先级第三

4. 在inventory定义的其他变量;优先级第四

5. 有系统发现的facts;优先级第五

6. "role默认变量",这个是最默认的值,很容易丧失优先权。优先级最小。

##### inventory清单列表里定义变量:单个主机定义的变量优先级高于主机组定义的变量 #####

ansible使用inventory定义变量的优先级顺序从高到低为:

1. host_vars下定义变量

2. inventory中单个主机定义变量

3. group_vars下定义变量

4. inventory中组定义变量

--------------------官网中的定义:----------(从低到高)

1. command line values (for example, -u my_user, these are not variables)
2. role defaults (defined in role/defaults/main.yml)
3. inventory file or script group vars
4. inventory group_vars/all
5. playbook group_vars/all
6. inventory group_vars/*
7. playbook group_vars/*
8. inventory file or script host vars
9. inventory host_vars/*
10. playbook host_vars/*
11. host facts / cached set_facts
12. play vars
13. play vars_prompt
14. play vars_files
15. role vars (defined in role/vars/main.yml)
16. block vars (only for tasks in block)
17. task vars (only for the task)
18. include_vars
19. set_facts / registered vars
20. role (and include_role) params
21. include params
22. extra vars (for example, -e "user=my_user")(always win precedence)

---> 更好的方法是将变量定义从清单文件移动到单独的变量文件中,每个主机组一个变量文件。每个变量文件以主机组命名,包含主机组的变量定义:

 ---> 一种更好的方法是在group_vars目录下为每个主机组创建子目录。Ansible解析这些子目录中的任何YAML,并根据父目录将变量与主机组关联:

2.2.4 特殊清单变量

 ansible_connection: 用于访问托管主机的连接插件。缺省情况下,除了使用local的localhost,所有主机都使用ssh。

 ansible_host: 连接到托管主机时要使用的实际IP地址或完全限定域名,而不是使用清单文件中的名称(inventory_hostname)。默认情况下,这个变量的值与清单主机名相同。

ansible_port: Ansible用于连接被管理主机。对于(默认的)SSH连接插件,值默认为22。

ansible_user: Ansible以这个用户连接到被管理的主机。Ansible的默认行为是使用与在控制节点上运行Ansible Playbook的用户相同的用户名连接到托管主机。

ansible_become_user: Ansible连接到托管主机,它将使用ansible_become_method(默认为sudo)切换到该用户。您可能需要以某种方式提供身份验证凭据。

ansible_python_interpreter: Ansible应该在托管主机上使用的Python可执行文件的路径。在Ansible2.8及以后版本,默认为auto,

2.2.5 使用变量识别当前主机

inventory_hostname:  当前正在处理的托管主机的名称,从清单中获取。

ansible_host:   用于连接受管理主机的实际IP地址或主机名。

ansible_facts['hostname'] :   作为事实从托管主机收集的短主机名。

ansible_facts['fqdn'] :   作为事实从托管主机收集的完全合格域名(FQDN)。

-----------------------管理任务执行---------------------------

提权升级指令::

become、become_user、become_method、become_flags。

配置特权升级::
如果将Ansible配置文件的[privilege_escalation]部分中的become布尔值设置为yes(或true),那么剧本中的所有剧本将默认使用privileqe_escalation。当运行在被管理的主机上时,这些play将使用当前的become_method方法切换到当前的become_user。

CONFIGURATION DIRECTIVE COMMAND-LINE OPTION
become --become or -b
become_method --become-method=BECOME_METHOD
become_user --become-user=BECOME_USER
become_password --ask-become-pass or -k

剧本的特权升级:

第一个play中使用:become:true

连接变量的特权升级:

连接变量来配置特权升级。这些连接变量可以作为组或单个主机上的清单变量应用。

configuration or playbook directive CONNECTION VARIABLE
become ansible_become
become_method ansible_become_method
become_user ansible_become_user
become_password ansible_become_pass

 

--------------特权升级方法----------

. 让你的剧本保持简单(Ansible最佳实践的关键原则)是首要的考虑。
. 其次要考虑的是,在可能的情况下以最小权限运行任务(以避免由于playbook错误而导致托管主机的意外损害)。

-------------控制任务执行-----------

---
- name: Ensure Apache is deployed    
  hosts: localhost
  gather_facts: no
  tasks:
    - name: Open the firewall     *******Ansible 会在打开防火墙之前执行角色的任务,
即使任务部分是先定义
firewalld: service: http permanent: yes state: enabled roles:
- role: apache

 pre_tasks :是在roles部分之前运行的tasks部分

 post_tasks: 是在一个tasks部分之后运行的

 

------------------------执行顺序----------------------
Ansible按以下顺序运行play部分:
1    pre_tasks
2    pre_tasks部分中通知的处理程序
3    角色
4    任务
5    角色和任务部分中通知的处理程序
6    post_tasks
7    post_tasks 部分中通知的处理程序

 

---
- name: Testing host order
  hosts: localhost
  order: sorted                       ********order指令接受以下值****
  tasks:
    - name: Createing a test file in /tmp
      copy:
        content: "This is a sample file"
        dest: /tmp/test.out

order指令接受的值:

1、inventory :清单order 这是默认值

2、reverse_inventory: 清单order的倒序

3、 sorted: 主机按字母顺序倒序排列

4、 revers_sorted: 主机按字母顺序

5、shuffle: 每次运行play时都是随机顺序

 

-----------标签ANSIBLE资源------------------

当使用ansible-playbook运行一个playbook时,

---->使用 --tags选项来过滤这个playbook,并只执行特定的带标签的play或任务。

---->使用 --skip-tags选项跳过带有特定标记的任务。

 

---
- name: Example play using tagging
  hosts:
    - servera.lab.example.com
    - serverb.lab.example.com
  tasks:
    - name: httpd is installed
      yum:
        name: httpd
        state: latest
      tags: webserver     <----tags标签

    - name: postfix is installed
      yum:
        name: postfix
        state: latest

 

[student@workstation yuan]$ ansible-playbook t1.yml --tags webserver   <----运行标签处

 

[student@workstation yuan]$ ansible-playbook t1.yml --list-tags  <----列出剧本中所有的标签

特殊标签:

(1) Ansible有一个特殊的方法,可以在剧本中分配:always.。标记了always的资源将始终运行,即使它与传递给 --tags的标签列表不匹配。唯一的例外是使用 --skip-tags always选项显式跳过它。

(2)  never特殊标记标记的任务不会运行,除非您将 --tags选项设置为never或与该任务关联的另一个tag运行playbook。

三个额外的特殊tag
(1) tagged的标记将使用显式标记运行任何资源。
(2) 未标记的tag将运行任何没有显式tag的资源,并排除所有标记的资源。
(3) all tag将包含play中的所有任务,无论他们是否有tag。这是Ansible的默认行为。

 

------------------变量类型----------------

 字符串(字符序列)
 数字(数值)
 布尔值(真/假)
 日期(ISO-8601日历日期)
 Null(设置变量为未定义的变量)
 列表或数组(值的排序集合)
 字典(键值对的集合)

YAML格式允许定义多行字符串,使用pipe操作符(|)来保留换行符,

使用大于操作符(>)来抑制换行符。

  string_with_breaks: |
             This string
             has several
             line breaks
  string_without_breaks: >
            This string will not

            contain any line breaks.
            Separated lines are joined
            by a space character.

字典:也称为映射或散列,是一种将字符串键与值链接起来以便直接访问的结构

my_dict: { Douglas: Human, Marvin: Robot, Arthur: Human }

my_dict:
    Douglas: Human
    Marvin: Robot
    Arthur: Human

通过键来访问字典中的项,在字典名后面立即提供键,并用方括号括起来:

assert:
    that:
        - my_dict['Marvin'] == 'Robot'

-------------用过滤器处理数据-------------

下面的表达式对变量myname的值进行过滤,通过使用标准的Jinja2过滤器确保值的第一个字母大写:
{{ myname | capitalize }}

 

- name: Test to see if the assertion        
  assert:
    that:
      - "{{ [1,4,2,2] | unique | sort }} is eq([1,2,4])"

 

Jinja2中正式可用的过滤器:

  http://jinja.pocoo.org/docs/2.10/templates/#builtinfilters上记录的Jinja2过滤器提供了大量有用的实用函数。(https://jinja.palletsprojects.com/en/2.10.x/templates/#builtinfilters)(http://docs.jinkan.org/docs/jinja2/templates.html#id2)

mandatory:
如果变量没有定义值,则会失败并中止Ansible剧本。
{{ my_value | mandatory }}

default
如果变量没有定义值,那么这个过滤器将把它设置为括号中指定的值。如果括号中的第二个参数是True,那么如果变量的初始值是空字符串或布尔值False,过滤器也会将该变量设置为默认值。
{{ my_value | default(my_default, True) }}

提起列表元素:

 

- name: All three of these assertions are true
assert:
that:
- "{{ [ 2, 4, 6, 8, 10, 12 ] | length }} is eq( 6 )"  ---length 列表长度
- "{{ [ 2, 4, 6, 8, 10, 12 ] | first }} is eq( 2 )"   ---first 列表第一个
- "{{ [ 2, 4, 6, 8, 10, 12 ] | last }} is eq( 12 )"   ---last 列表的最后一个

 

random(随机)过滤器从列表中返回一个随机元素:
{{ ['Douglas', 'Marvin', 'Arthur'] | random }}

 

- name: reversion and sorting lists
  assert:
    that: 
      - "{{[2,4,6,8,10] | reverse |list }}is eq([10,8,6,4,2])"  ---把列表颠倒过来
      - "{{[4,8,10,6,2] | sort|list}} is eq([2,4,6,8,10])"      ---把列表顺序排列

 

合并列表:flatten过滤器递归地接受输入列表值中的任何内部列表

- name: Flatten turns nested lists on the left to list on the right
  assert:
    that:
      - "{{ [2,[4,[6,8]],10] | flatten }} is eq([2,4,6,8,10])"   ---flatten合并列表

将列表作为集合进行操作:unique 唯一过滤器,

- name: unique leaves unique elements
  assert:
    that:     --unique保证唯一,---list列表转成集合
      - "{{ [1,1,2,2,2,3,4,4]|unique|list }}is eq([1,2,3,4])"

如果两个列表没有重复的元素,那么你可以对它们使用集合理论操作。
  union联合过滤器返回一个集合,其中包含来自两个输入集合的元素。
  intersect过滤器返回一个集合,该集合具有两个集合共有的元素。
  difference过滤器返回一个集合,其中包含第一个集合中不存在于第二个集合中的元素。

- name: difference
  assert:
    that:   -----difference 第一个集合中有的,第二个集合中没有的
      - "{{[2,4,6,8,10] | difference([2,4,6,16])}} is eq([8,10])"

 操纵字典:

字典是没有任何顺序的。它们只是键值对的集合。但是您可以使用过滤器来构造字典,并且您可以将它们转换为列表,或者将列表转换为字典。

- name: combine
  vars:
    expected:
      a: 234
      b: 456
      c: 890
  assert:
    that:      ----combine 字典联合起来
      - "{{ {'a':234,'b':456} | combine({'b':456,'c':109 }) }} is eq(expected)"

重塑字典:
使用dict2items过滤器可以将一个字典重新塑造成一个条目列表,使用items2dict过滤器也可以将一个条目列表重新塑造成一个字典

- name: converting
  vars:
    c1:   
      do1: Human
      do2: Robot
      do3: Human
    c2:  
      - key: d1
        value: Human      
      - key: d3
        value: Human          ---dict2items 把列表转换成字典
  assert:                     ---items2dict 把字典转换成列表
    that:      
      - "{{ c1 | dict2items }} is eq(c2)"
      - "{{ c2 | items2dict }} is eq(c1)"

对字符串进行哈希、编码和操作:hash哈希过滤器使用提供的哈希算法返回输入字符串的哈希值:

- name: SHA-1 hash
  vars:
    e1: '9ka0ka0akdfj912klkjasd334cal'
  assert:  
    that:
      - "'{{ 'Arthur' | hash('sha1')}}' is eq(e1)"

使用password_hash过滤器来生成密码散列:

{{ 'secret_password'| password_hash('sha512')}}

编码的字符串
二进制数据可以通过b64encode过滤器转换成base64,再通过b64decode过滤器转换回二进制格式。

- name: Base64
  assert:
    that: 
      - "'{{ 'asdfkl'| b64encode }}'is eq('w8LDkkjj==')"
      - "'{{ 'w8kjsjd==' | b64encode }}'is eq('asdfkl')"

格式化文本
使用lower、upper或capitalize过滤器来强制输入字符串的大小写:

- name: cc
  assert:
    that: 
      - "'{{ 'Marvin' | lower }}'is eq('marvin')"         --全部转换为小写
      - "'{{ 'Marvin' | upper }}'is eq('MARVIN')"         --全部转换为大写
      - "'{{ 'marvin' | capitalize }}'is eq('Marvin')"    --首字母转换为大写

替换文本
当你需要替换输入字符串中出现的所有子字符串时,replace过滤器很有用:

- name: Replace
  assert:
    that:        ---replace 替换
      - "'{{ 'marvin, arthur' | replace('ar','**')}}'is eq('m**vin,**thur')"

更复杂的搜索和替换可以通过使用正则表达式和regex_search和regex_replace过滤器来实现。

解析和编码数据结构

数据结构通过to_json和to_yaml过滤器序列化为JSON或YAML格式。使用to_nice_json和to_nice_yaml过滤器来获得格式化的人类可读输出。

- name: Convert JSON and YAML format
  vars:
    hosts:
      - name: bastion
        ip:
          - 172.25.250.254
          - 172.25.252.1
    hosts_json: '[{"name":"bastion","ip":["172.25.250.254","172.25.252.1"]}]'
  assert:
    that:
      - "'{{ hosts | to_json}}'is eq(hosts_json)"

 4.3 实现高级循环

   使用循环迭代任务可以帮助简化您的Ansible Playbooks。loop关键字在项目的平面列表上循环。当与查找插件一起使用时,可以在循环列表中构造更复杂的数据。

loop关键字代替with_*样式循环有以下好处:
 不需要记忆或找到一个with * style关键字来适应您的迭代场景。相反,使用插件和过滤器来使循环关键字任务适应您的用例。
 重点学习Ansible中可用的插件和过滤器,它们的适用性比迭代更广泛。
 可以通过ansible-doc -t lookup命令对查找插件文档进行命令行访问。这将帮助发现查找插件并使用它们设计自定义迭代场景。

 

 

 

 

 

 

posted @ 2021-09-22 11:49  yyuuee  阅读(530)  评论(0)    收藏  举报