Ansible剧本-playbook

news/2025/2/25 7:50:38

Ansible剧本-playbook

  • 1 playbook基础
    • 1.1 简介
    • 1.2 playbook的组成结构
        • Task 任务列表
        • 任务报错,如何继续执行
        • 响应事件Handler
    • 1.3 常用选项
        • 执行playbook
        • playbook查询帮助信息
        • 校验playbook语法
        • 测试playbook能否正常运行
  • 2 变量 的定义方式
    • 2.1 定义规则
    • 2.2 vars 变量
    • 2.3 在 主机清单 定义变量
    • 2.4 在 主机、主机组目录 定义变量
    • 2.5 从 变量文件 引入变量
    • 2.6 注册变量 (引用命令返回值)
    • 2.7 命令行 临时定义变量
    • 2.8 facts 事实变量
        • 简介
        • 使用setup 收集
        • 引用、使用 facts变量
        • 开启、禁用facts变量
        • 自定义facts
        • 使用 set_fact 定义
    • 2.9 lookup 生成变量(只用于主控机)
      • 2.9.1 说明
      • 2.9.2 方法
        • file 从文件中获取值
        • pipe 获取命令的输出值
        • env 获取环境变量的值
        • 更多方法
    • 2.10 魔法变量 (内置变量)
        • hostvars 获取指定主机的变量
        • inentory_hostname 显示运行当前任务的主机名
        • groups 列出所有主机组
        • 更多魔法变量
    • 2.11 其他
        • 调试变量 debug
        • 如何获取 字典 的值
  • 3 运算符
    • 3.1 比较运算符( >、<、=)
    • 3.2 逻辑运算符(and、or)
  • 4 判断语句
    • 4.1 单次 条件判断 when
        • 判断变量
        • 判断执行结果
        • 根据rc返回值来进行判断task任务是否执行成功
        • 判断 一个路径是什么东西
        • 判断字符串 大小写
        • 判断奇、偶数、整除
        • 其他
    • 4.2 block 判断多个任务
  • 5 异常处理
    • 5.1 resuce 报错时执行
    • 5.2 always 无论都会执行
    • 5.3 fail 满足条件就退出
    • 5.4 faild_when 满足条件就退出
  • 6 循环语句
    • 6.1 with_items 基于列表的循环
    • 6.2 with_dict 基于字典的循环
    • 6.3 loop循环
        • 常用过滤器
  • 7 文件管理模块
    • 7.1 lineinfile 修改单行
      • 7.1.1 选项
      • 7.1.2 举例
        • 替换行
        • 在匹配行 前 添加内容
        • 修改 匹配行、文件权限
        • 删除 匹配行
        • 文件存在则添加一行内容
        • 验证 配置文件 修改后是否有效
    • 7.2 blockinfile 修改多行
      • 7.2.1 选项
      • 7.2.2 举例
        • 增加多行内容
        • 删除多行内容
          • 说明
          • 删除的内容 有块标记
          • 删除的内容 没有块标记
  • 8 jinja2
    • 8.1 jinja2 模板文件
      • 8.1.1 说明
      • 8.1.2 jinja2 语法
        • if判断
        • for循环
    • 8.2 template 模块
      • 8.2.1 选项
    • 8.3 过滤器
      • 8.3.1 说明、语法格式
      • 8.3.2 字符串操作
      • 8.3.3 数字操作
      • 8.3.4 列表操作
    • 8.4 举例
        • jinjia2简单例子
        • if、elif 多重判断
        • for 循环
  • 9 ansible 角色、集合管理
    • 9.1 理论
        • 角色的背景说明
        • 角色的引用语法
        • 角色(roles)的目录结构
        • 集合(collection)
        • role和collection
        • galaxy网站、文档
    • 9.2 ansible-galaxy 角色、集合管理命令
      • 9.2.1 常用命令
      • 9.2.2 举例-角色管理
        • 初始化角色目录结构
        • 安装角色roles
        • 查询已安装的角色
        • include_tasks 引入task任务文件
        • 编写角色 并执行
        • 在执行任务前后,先执行指定任务
      • 9.2.3 举例-集合管理
        • 安装集合
  • 10 ansible-vault 加解密文件
    • 10.1 选项
    • 10.2 例子
        • 执行一个 加密的剧本
        • 创建 加密文件
        • 加密 已存在的文件
        • 解密 已经加密的文件
        • 修改 加密文件密码
        • 查看 加密文件 内容
  • 11 ansible-navigator 导航器
    • 11.1 说明
    • 11.2 选项
    • 11.3 举例
        • 安装ansible-navigator
        • 执行剧本
        • 指定默认使用的 EE 镜像
        • 查看ee镜像信息
        • 查看主机清单的主机

学习RHCE的笔记,仅供参考

1 playbook基础

1.1 简介

ansible的playbook也叫做剧本,是一系列ansible命令的集合,利用yaml语言进行编写;

  • 一个 playbook 中可以由 一个play或者多个play 进行组成,
  • 每一个play 下面可以有 多个task任务,
  • 一个task任务只能使用一个模块,
  • 剧本的执行,是通过从上往下的顺序依次执行
  • 支持变量、循环、判断、错误纠正等高级操作

1.2 playbook的组成结构

playbook主要有以下 四部分 构成:

  1. Target section:目标
    1. 可以定义play的名字
    2. 可以定义远程操作的被控节点主机
    3. 可以定义远程操作的用户
    4. 可以定义提权的相关配置等
  2. Variable section:变量
    1. 可以定义playbook运行时需要使用的变量
  3. Task section:任务
    1. 可以定义将要在远程主机上执行的任务列表
  4. Handler section:特殊task任务
    1. 可以定义task执行完成以后需要调用的任务
    2. 有条件 有目的的任务,是一个特殊的handler任务,和tasks处于同一个层级;一般情况下handler任务不会执行,只会触发再执行。handler任务是在所有任务执行完成之后才会执行

默认情况下,在一个play中,只要有task执行失败,则play终止,即使是与handler关联的task在失败的task之前运行成功了,handler也不会被执行。如果希望在这种情况下handler仍然能够执行,则需要使用如下配置:force_handlers: yes
说明:如果与handler关联的task还未执行,在其前的task已经失败,整个play终止,则handler未被触发,也不会执行。

# cat user.yml
- name: create user
  hosts: all
  remote_user: root
  gather_facts: false
  vars:
    user: "test"
  tasks:
   - name: create user
     user: name="{{ user }}"

name:剧本 的 名字【可选】
hosts:指定对 哪些被管理机 进行操作;
remote_user:使用什么用户远程被控机
gather_facts:指定在执行任务之前,是否先执行setup模块获取主机相关信息,如未用到,可不指定
vars:定义后续任务中会使用到的变量,如未用到,可不指定
tasks:定义具体需要执行的任务
name:任务 的 名字【可选】
user:调用的模块名
name:user模块里的一个参数,用于指定创建的用户名称

Task 任务列表

task是playbook的任务列表,所有的任务都定义在tasks下面;

  1. 任务的执行 ,顺序是从上往下
  2. playbook 执行时,遇到报错的情况:
    1. 如果有多个play,task本身 报错
      2. 第一个play执行失败,不会影响第二个play的执行
    2. 如果有多个task任务,执行的主机 报错
      1. 任务执行失败的主机, 不会再执行后面的task任务
      2. 其他执行成功的主机,不受影响 会继续执行
      3. 总结:只会停止错误主机的任务,其他主机的任务不会影响
  3. ansible具有幂等性,
    1. 幂等性:就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用
    2. 如果任务列表中的任务反复执行,一次执行和多次执行的结果都是一样的如果期望值和执行的任务结果一致,则任务就是执行成功的(ok)
      1. 例如:原本playbook当中有一个task任务是创建devops用户,再次执行的话不会报错,因为期望值的结果是一致的,如果任务本来就有,多次执行,结果就是OK成功的;
    3. 如果任务执行的前后,内容修改了,那么后面再次执行这个任务的时候就会发生覆盖;
任务报错,如何继续执行

如果playbook执行失败,导致task任务没有执行,如何解决?
在playbook当中,rc作为命令的返回值,通过判断rc返回值即可知道命令是否执行成功
如果rc返回值为0,则执行成功;如果非0.则执行失败
任务报错后,只会影响运行任务的主机,其他的主机不受影响

  1. 如果是命令执行模块执行失败
    1. 使用 || 逻辑或,配置/usr/bin/true 来让rc返回值为0
  2. 如果是其他ansible模块执行失败
    1. 使用ignore_errors: true 如果当前任务执行失败,就跳过当前任务,继续去执行下一个任务
  3. 使用hanler来规避错误(也是一种特殊的tasks,和tasks是并齐的关系)
    1. 默认情况下:在一个play中,只要有task执行失败,则play终止
    2. 如果与handler关联的task还 已经执行,但是后面的 task失败了,handler也不会被执行(因为handler是最后才执行的)。
      1. 这种情况下,如果希望 handler仍然能够执行,则需要使用如下配置:force_handlers: yes
    3. 如果与handler关联的task还 未执行,在其前的task已经失败,整个play终止,则handler未被触发,也不会执行。【配了 force_handlers: yes 也不行】
# 过程:使用`ignore_errors: true` 如果当前任务执行失败,就跳过当前任务,继续去执行下一个任务
# 如果没添加`ignore_errors: true`,任务1报错就结束了,不会再执行任务2
[root@rhel ~]# cat ceshi_ignore.yml 
- name: 剧本1
  hosts: 10.0.0.23
  tasks:
    - name: 任务1
      # 进去一个不存在的目录,让他报错
      shell: cd /root/sksjdfklsj
      # 使用`ignore_errors: true` 如果当前任务执行失败,就跳过当前任务,继续去执行下一个任务
      ignore_errors: true
    - name: 任务2
      shell: echo "执行任务2"
[root@rhel ~]# 
[root@rhel ~]# 
[root@rhel ~]# ansible-playbook ceshi_ignore.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [剧本1] **********************************************************

TASK [Gathering Facts] **********************************************************************
ok: [10.0.0.23]

TASK [任务1] *********************************************************
fatal: [10.0.0.23]: FAILED! => {"changed": true, "cmd": "cd /root/sksjdfklsj", "delta": "0:00:00.007359", "end": "2024-08-26 16:32:51.693584", "msg": "non-zero return code", "rc": 1, "start": "2024-08-26 16:32:51.686225", "stderr": "/bin/sh: 第 1 行:cd: /root/sksjdfklsj: 没有那个文件或目录", "stderr_lines": ["/bin/sh: 第 1 行:cd: /root/sksjdfklsj: 没有那个文件或目录"], "stdout": "", "stdout_lines": []}
...ignoring

TASK [任务2] *********************************************************
changed: [10.0.0.23] => {"changed": true, "cmd": "echo \"执行任务2\"", "delta": "0:00:00.004179", "end": "2024-08-26 16:32:52.339460", "msg": "", "rc": 0, "start": "2024-08-26 16:32:52.335281", "stderr": "", "stderr_lines": [], "stdout": "执行任务2", "stdout_lines": ["执行任务2"]}

PLAY RECAP ***********************************************************
10.0.0.23                  : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=1   

[root@rhel ~]# 
# 默认情况:
[root@rhel ~]# cat ceshi.yml 
- name: 剧本1
  hosts: 10.0.0.23
  # force_handlers: yes
  tasks:
     - name: 任务1
       shell: echo "执行任务1"
       notify: handler任务_1
     - name: 任务2
       # 进去一个不存在的目录,让他报错
       shell: cd /root/sksjdfklsij
     - name: 任务3
       shell: echo "执行任务3"
    
  handlers:
    - name: handler任务_1
      shell: echo "执行 handler任务_1"
[root@rhel ~]# 
[root@rhel ~]# ansible-playbook ceshi.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [剧本1] ******************************************************************

TASK [Gathering Facts] ******************************************************************
ok: [10.0.0.23]

TASK [任务1] ******************************************************************
changed: [10.0.0.23] => {"changed": true, "cmd": "echo \"执行任务1\"", "delta": "0:00:00.004199", "end": "2024-08-26 17:33:58.128032", "msg": "", "rc": 0, "start": "2024-08-26 17:33:58.123833", "stderr": "", "stderr_lines": [], "stdout": "执行任务1", "stdout_lines": ["执行任务1"]}

TASK [任务2] ******************************************************************
fatal: [10.0.0.23]: FAILED! => {"changed": true, "cmd": "cd /root/sksjdfklsij", "delta": "0:00:00.004440", "end": "2024-08-26 17:33:58.743674", "msg": "non-zero return code", "rc": 1, "start": "2024-08-26 17:33:58.739234", "stderr": "/bin/sh: 第 1 行:cd: /root/sksjdfklsij: 没有那个文件或目录", "stderr_lines": ["/bin/sh: 第 1 行:cd: /root/sksjdfklsij: 没有那个文件或目录"], "stdout": "", "stdout_lines": []}

PLAY RECAP ******************************************************************
10.0.0.23                  : ok=2    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 
# 配置 force_handlers: yes
# 如果与handler关联的task还 已经执行,但是后面的 task失败了,handler也不会被执行(因为handler是最后才执行的)。
# 如果希望在这种情况下handler仍然能够执行,则需要使用如下配置:`force_handlers: yes`
[root@rhel ~]# cat ceshi.yml 
- name: 剧本1
  hosts: 10.0.0.23
  force_handlers: yes
  tasks:
     - name: 任务1
       shell: echo "执行任务1"
       notify: handler任务_1
     - name: 任务2
       # 进去一个不存在的目录,让他报错
       shell: cd /root/sksjdfklsij
     - name: 任务3
       shell: echo "执行任务3"
    
  handlers:
    - name: handler任务_1
      shell: echo "执行 handler任务_1"
[root@rhel ~]# 
[root@rhel ~]# ansible-playbook ceshi.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [剧本1] ******************************************************************

TASK [Gathering Facts] ******************************************************************
ok: [10.0.0.23]

TASK [任务1] ******************************************************************
changed: [10.0.0.23] => {"changed": true, "cmd": "echo \"执行任务1\"", "delta": "0:00:00.006246", "end": "2024-08-26 17:37:18.555561", "msg": "", "rc": 0, "start": "2024-08-26 17:37:18.549315", "stderr": "", "stderr_lines": [], "stdout": "执行任务1", "stdout_lines": ["执行任务1"]}

TASK [任务2] ******************************************************************
fatal: [10.0.0.23]: FAILED! => {"changed": true, "cmd": "cd /root/sksjdfklsij", "delta": "0:00:00.004290", "end": "2024-08-26 17:37:19.199767", "msg": "non-zero return code", "rc": 1, "start": "2024-08-26 17:37:19.195477", "stderr": "/bin/sh: 第 1 行:cd: /root/sksjdfklsij: 没有那个文件或目录", "stderr_lines": ["/bin/sh: 第 1 行:cd: /root/sksjdfklsij: 没有那个文件或目录"], "stdout": "", "stdout_lines": []}

RUNNING HANDLER [handler任务_1] ***********************************************************************************
changed: [10.0.0.23] => {"changed": true, "cmd": "echo \"执行 handler任务_1\"", "delta": "0:00:00.004095", "end": "2024-08-26 17:37:19.863765", "msg": "", "rc": 0, "start": "2024-08-26 17:37:19.859670", "stderr": "", "stderr_lines": [], "stdout": "执行 handler任务_1", "stdout_lines": ["执行 handler任务_1"]}

PLAY RECAP ******************************************************************
10.0.0.23                  : ok=3    changed=2    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 
# 如果与handler关联的task还 未执行,在其前的task已经失败,整个play终止,则handler未被触发,也不会执行【配了 force_handlers: yes 也不行】
[root@rhel ~]# cat ceshi.yml 
- name: 剧本1
  hosts: 10.0.0.23
  force_handlers: yes
  tasks:
     - name: 任务1
       shell: echo "执行任务1"
       # notify: handler任务_1
     - name: 任务2
       # 进去一个不存在的目录,让他报错
       shell: cd /root/sksjdfklsij
     - name: 任务3
       shell: echo "执行任务3"
       notify: handler任务_1

  handlers:
    - name: handler任务_1
      shell: echo "执行 handler任务_1"
[root@rhel ~]# 
[root@rhel ~]# ansible-playbook ceshi.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [剧本1] ******************************************************************

TASK [Gathering Facts] ******************************************************************
ok: [10.0.0.23]

TASK [任务1] ******************************************************************
changed: [10.0.0.23] => {"changed": true, "cmd": "echo \"执行任务1\"", "delta": "0:00:00.003827", "end": "2024-08-26 17:43:55.833754", "msg": "", "rc": 0, "start": "2024-08-26 17:43:55.829927", "stderr": "", "stderr_lines": [], "stdout": "执行任务1", "stdout_lines": ["执行任务1"]}

TASK [任务2] ******************************************************************
fatal: [10.0.0.23]: FAILED! => {"changed": true, "cmd": "cd /root/sksjdfklsij", "delta": "0:00:00.004614", "end": "2024-08-26 17:43:56.506364", "msg": "non-zero return code", "rc": 1, "start": "2024-08-26 17:43:56.501750", "stderr": "/bin/sh: 第 1 行:cd: /root/sksjdfklsij: 没有那个文件或目录", "stderr_lines": ["/bin/sh: 第 1 行:cd: /root/sksjdfklsij: 没有那个文件或目录"], "stdout": "", "stdout_lines": []}

PLAY RECAP ******************************************************************
10.0.0.23                  : ok=2    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 
响应事件Handler

Playbook基本语法 - 响应事件(Handler) - 《Ansible入门》 - 书栈网 · BookStack

handler任务,和tasks处于同一个层级;handlers需要在tasks中被调用,才有可能被执行;handler任务 是在 所有任务执行完成之后 才会执行(所以无论调用多少次,一个handler任务,最终只在 最后 执行一次)【类似函数,你调用了,才可能执行】

在Ansible中,只有在 task的执行状态changed 的时候,才会执行 该task调用 的 handler,这也是handler与普通的event机制不同的地方

应用场景:什么情况下使用handlers呢?
如果你在tasks中修改了apache的配置文件。需要重起apache。此外还安装了apache的插件。那么还需要重起apache。像这样的应该场景中,重起apache就可以设计成一个handler.

1.3 常用选项

执行playbook

执行playbook的话,使用 ansible-playbook 执行

playbook查询帮助信息
ansible-playbook查询帮助信息

查看使用的ansible配置文件
ansible-playbook user.yml -v
-vv 查看python和ansible的版本
-vvv 查看ssh的连接信息
校验playbook语法

检查yaml文件语法

ansible-playbook test.yml --syntax-check
测试playbook能否正常运行

测试playbook能否正常运行,但不会真的运行【大写的C】

ansible-playbook test.yml -C
[root@rhel ~]# cat test.yml 
- name: 剧本1
  hosts: 10.0.0.23
  tasks:
    - name: 任务1:查看文件
      file:
        path: /root/a.txt
        state: file
    - name: 任务2:创建用户
      user: 
        name: ansible
        state: present
        comment: "创建一个名为 ansible 的用户"
[root@rhel ~]# 
[root@rhel ~]# ansible-playbook test.yml --syntax-check 

playbook: test.yml
[root@rhel ~]# ansible-playbook test.yml -C -v
Using /etc/ansible/ansible.cfg as config file

PLAY [剧本1] ******************************************************************

TASK [Gathering Facts] ******************************************************************
ok: [10.0.0.23]

TASK [任务1:查看文件] ******************************************************************
ok: [10.0.0.23] => {"changed": false, "gid": 0, "group": "root", "mode": "0644", "owner": "root", "path": "/root/a.txt", "secontext": "unconfined_u:object_r:admin_home_t:s0", "size": 0, "state": "file", "uid": 0}

TASK [任务2:创建用户] ******************************************************************
changed: [10.0.0.23] => {"append": false, "changed": true, "comment": "", "group": 1003, "home": "/home/ansible", "move_home": false, "name": "ansible", "shell": "/bin/bash", "state": "present", "uid": 1003}

PLAY RECAP ******************************************************************
10.0.0.23                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 


2 变量 的定义方式

什么情况下会使用变量??例如如果所有的被控节点的用户以及ssh端口不是顺序统一的,例如一个被控节点ssh端口是2222,另外一个ssh端口是9090
所以我们需要使用变量来代替,通过定义变量的方式

2.1 定义规则

变量的约束:

  1. ansible的变量由 数字、字母、下划线_ 组成
  2. ansible的变量名不能以数字开头,可以是字母,也可以是下划线_
  3. ansible的变量 区别大小写
  4. ansible自定义变量的时候 尽量不要以 ansible 开头,因为系统中有ansible开头的内置变量等

2.2 vars 变量

定义变量:使用 vars 定义变量,写在 yml文件里
引用变量:用2个大括号引起来 "{{ 变量名 }} {{ 变量名 }}" ,如果有多个变量、需要添加其他字符串,要用双引号 把外面 引起来。双引号一定要加上

- name: 剧本1
  gather_facts: no
  hosts: 10.0.0.23
  vars:
    var1: "变量1"
    var2: "变量2"
  tasks:
     - name: 任务1
       # 引用变量
       debug:
         msg: "这个是 {{ var1 }} {{ var2 }}"
     - name: 任务2
       debug:
        msg: 这个是 "{{ var1 }}"
     - name: 任务3
       shell: "touch {{ var1 }}.txt"
     - name: 任务4
       shell: "ls -l {{ var1 }}.txt"
# 过程
[root@rhel ~]# ansible-playbook ceshi.yml -v
Using /etc/ansible/ansible.cfg as config file

PLAY [剧本1] ****************************************************************************

TASK [任务1] ****************************************************************************
ok: [10.0.0.23] => {
    "msg": "这个是 变量1 变量2"
}

TASK [任务2] ****************************************************************************
ok: [10.0.0.23] => {
    "msg": "这个是 \"变量1\""
}

TASK [任务3] ****************************************************************************
changed: [10.0.0.23] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"}, "changed": true, "cmd": "touch 变量1.txt", "delta": "0:00:00.004909", "end": "2024-08-27 17:36:11.905528", "msg": "", "rc": 0, "start": "2024-08-27 17:36:11.900619", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}

TASK [任务4] ****************************************************************************
changed: [10.0.0.23] => {"changed": true, "cmd": "ls -l 变量1.txt", "delta": "0:00:00.006743", "end": "2024-08-27 17:36:12.532251", "msg": "", "rc": 0, "start": "2024-08-27 17:36:12.525508", "stderr": "", "stderr_lines": [], "stdout": "-rw-r--r--. 1 root root 0  8月 27 17:36 变量1.txt", "stdout_lines": ["-rw-r--r--. 1 root root 0  8月 27 17:36 变量1.txt"]}

PLAY RECAP ******************************************************************************
10.0.0.23                  : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 

2.3 在 主机清单 定义变量

在 Inventory主机清单 定义变量。包括 内置主机变量、自定义变量 2种:

  1. 内置主机变量:
    1. 所谓内置变量 其实就是 ansible.cfg配置文件 中的选项,在其前加上 ansible_ 即成为内置变量。【就是已经 提前预定好的 变量】
    2. 内置变量 拥有 比ansible.cfg中 选项更高的优先级
    3. 而且针对不同的主机,可以定义不同的值。
    4. 包含 主机变量 和 主机组变量
  2. 自定义变量:
    1. 我们自己定义的变量
    2. 包含 主机变量 和 主机组变量
  3. 主机变量 和 主机组变量 的优先级
    1. 主机变量 的 优先级 高
    2. 主机组变量 的 优先级 低

常用内置主机变量

# 一般连接
ansible_host 		# 用于指定被管理的主机的真实IP
ansible_port 		# 用于指定连接到被管理主机的ssh端口号,默认是22
ansible_user 		# ssh连接时默认使用的用户名

# 特权升级
ansible_become				# 相当于ansible_sudo或者ansible_su,允许强制特权升级
ansible_become_user			# 通过特权升级到的用户,相当于ansible_sudo_user或者ansible_su_user
ansible_become_pass			# 提升特权时,如果需要密码的话,可以通过该变量指定,相当于ansible_sudo_pass或者ansible_su_pass
ansible_sudo_exec			# 如果sudo命令不在默认路径,需要指定sudo命令路径

# 特定ssh连接
ansible_connection				# SSH连接的类型:local, ssh, paramiko,默认是ssh
ansible_ssh_pass				# ssh连接时的密码
ansible_ssh_private_key_file	# 秘钥文件路径,如果不想使用ssh-agent管理秘钥文件时可以使用此选项
ansible_ssh_executable			# 如果ssh指令不在默认路径当中,可以使用该变量来定义其路径

主机和主机组 内置变量 示例:

192.168.1.1 ansible_user=admin ansible_port=22 # 定义主机变量

[test]
192.168.1.2
192.168.1.3

[test:vars] # 定义 test主机组 的 内置变量
ansible_user=root
ansible_port=22

主机和主机组 自定义变量 示例:

192.168.1.1 webserver=httpd # 定义主机变量

[test]
192.168.1.2
192.168.1.3

[test:vars] # 定义 test主机组 的 自定义变量
webserver=nginx

2.4 在 主机、主机组目录 定义变量

通过主机(host_vars) 和 主机组(group_vars) 的 目录文件 定义变量

  1. 前提:需要在 inventory主机清单 所在的目录 创建 主机目录、主机组目录
    1. 主机目录
      1. 目录名字为 host_vars
      2. 目录下的 变量文件的名称 以主机名来命名,其他主机无法使用此变量,只有指定主机可以
    2. 主机组目录
      1. 目录名字为 group_vars
      2. 目录下的 变量文件的名称 以主机组来命名
    3. 说明:创建好对应的目录名字就可以用
# 示例:

# 主机目录 需要和 主机清单的路径 处于同一个目录下
mkdir /etc/ansible/host_vars
# 只有主机10.0.0.23 才能引用此变量
echo "rhel_name: rhel9" > /etc/ansible/host_vars/10.0.0.23


# 主机组目录 需要和 主机清单的路径 处于同一个目录下
mkdir /etc/ansible/group_vars
# 只有主机组host_group 下面的主机才能引用变量
echo "group_name: itgroup" > /etc/ansible/group_vars/host_group
# 过程
[root@rhel ~]# mkdir /etc/ansible/host_vars
[root@rhel ~]# mkdir /etc/ansible/group_vars

 # 只有主机10.0.0.23 才能引用此变量
[root@rhel ~]# echo "rhel_name: rhel9" > /etc/ansible/host_vars/10.0.0.23[root@rhel ansible]# 

# 只有主机组host_group 下面的主机才能引用变量
[root@rhel ~]# echo "group_name: itgroup" > /etc/ansible/group_vars/host_group
[root@rhel ~]# 

[root@rhel ~]# tree /etc/ansible/
/etc/ansible/
├── ansible.cfg
├── group_vars
│   └── host_group
├── hosts
├── host_vars
│   └── 10.0.0.23
└── roles

3 directories, 4 files
[root@rhel ~]# 


# 测试 主机组的变量【这里的 host1 属于 host_group】
[root@rhel ~]# ansible host1 -m debug -a "var=group_name"
host1 | SUCCESS => {
    "group_name": "itgroup"
}
[root@rhel ~]# 

# 测试 主机的变量
[root@rhel ~]# ansible 10.0.0.23 -m debug -a "var=rhel_name"
10.0.0.23 | SUCCESS => {
    "rhel_name": "rhel9"
}
[root@rhel ~]# 

# 创个文件测试下
[root@rhel ~]# ansible 10.0.0.23 -m shell -a "touch {{rhel_name}}.txt"
10.0.0.23 | CHANGED | rc=0 >>

[root@rhel ~]# ansible 10.0.0.23 -m shell -a "ls -l  {{rhel_name}}.txt"
10.0.0.23 | CHANGED | rc=0 >>
-rw-r--r--. 1 root root 0 827 21:00 rhel9.txt
[root@rhel ~]# 



# 没有定义的,就用不了
[root@rhel ~]# ansible host1 -m debug -a "var=rhel_name"
host1 | SUCCESS => {
    "rhel_name": "VARIABLE IS NOT DEFINED!"
}
[root@rhel ~]# 

2.5 从 变量文件 引入变量

使用关键字 vars_files 引入 外部的变量文件

vars_files 的格式:

  • 用 列表 表示
  • 值 就写 变量文件 的路径。这个变量文件路径,可以是 绝对路径、相对路径

变量文件 的格式

  • 只支持 字典 的格式,不可以是 列表
  • 变量的定义格式是成键值对出现的,键值对之间可以嵌套,最终形成一个大字典
# 示例:
- name: 从 变量文件 引入变量
  gather_facts: no
  hosts: 10.0.0.23
  # 使用 `vars_files`引入 外部的变量文件
  vars_files:
    - /root/vars.yml
  tasks:
    - name: 任务1
      debug:
        msg: "user01是: {{ users.user01.name }}"
    - name: 任务2
      debug:
        msg: "user02是: {{ users.user02.name }}"
# 外部变量文件 
users:
  user01:
    name: ZhangSan
    age: 11
    home_dirs: /home/zhangsan
  user02:
    name: LiSi
    age: 18
    home_dirs: /home/lisi

如何获取 字典 的值

# 过程
[root@rhel ~]# ansible-playbook host1.yml 

PLAY [从 变量文件 引入变量] ****************************************************

TASK [任务1] *******************************************************************
ok: [10.0.0.23] => {
    "msg": "user01是: ZhangSan"
}

TASK [任务2] *******************************************************************
ok: [10.0.0.23] => {
    "msg": "user02是: LiSi"
}

PLAY RECAP *********************************************************************
10.0.0.23                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 

2.6 注册变量 (引用命令返回值)

关键字 register 可以将 某一任务结果 保存为 一个变量【这个变量是个大字典,包括 状态、时间、结果。。】

注册变量的应用场景
在一台远端的服务器获取一个目录下的一列表的文件,然后下载这些文件
在handler执行之前,发现前面一个task发生了changed,然后执行一个指定的task
获取远端服务器的ssh key的内容,构建出known_hosts文件

# 关于 使用 register 的输出部分 重点说明如下:
login: 变量名,其值为一个字典
changed: ansible基于此来判断是否发生了状态改变
cmd: 被调用的命令
failed: 是否运行失败
delta: 任务执行的时间
rc: 返回值,0代表正常,非0代表异常
stderr: 错误结果输出,输出值是一个字符串
stderr_lines: 错误结果输出,输出值是一个列表
stdout_lines: 正确结果输出,输出值是一个列表
stdout: 正确结果输出,输出值是一个字符串,可以对列表循环取值需要说明的是,通过register注册的变量的结果并不是一成不变的,在不确定返回值的情况下,尽量调试看看输出结果。
# 举例
[root@rhel ~]# cat zhuce.yml 
- name: 将whoami命令执行的结果注册到变量login
  hosts: host1
  tasks:
    - name: 任务1
      command: whoami
      # 通过`register` 将 某一任务结果 保存为 一个变量
      register: login
    - name: 任务2
      debug:
        var: login
    - name: 任务3
      debug:
        msg: "输出结果: {{ login.stdout }}"
[root@rhel ~]# 
[root@rhel ~]# ansible-playbook zhuce.yml 

PLAY [将whoami命令执行的结果注册到变量login] ***********************************

TASK [Gathering Facts] *********************************************************
ok: [host1]

TASK [任务1] *******************************************************************
changed: [host1]

TASK [任务2] *******************************************************************
ok: [host1] => {
    "login": {
        "changed": true,
        "cmd": [
            "whoami"
        ],
        "delta": "0:00:00.002698",
        "end": "2024-08-27 21:22:16.379132",
        "failed": false,
        "msg": "",
        "rc": 0,
        "start": "2024-08-27 21:22:16.376434",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "root",
        "stdout_lines": [
            "root"
        ]
    }
}

TASK [任务3] *******************************************************************
ok: [host1] => {
    "msg": "输出结果: root"
}

PLAY RECAP *********************************************************************
host1                      : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 

2.7 命令行 临时定义变量

如果要定义多个变量,添加参数 -e 或者 --extra-vars

  • 临时定义 单个变量,可以直接赋值。比如
    • 变量名1=变量值
  • 临时定义 多个变量。比如
    • "变量名1=变量值 变量名2=变量值"
    • '{"hosts":"vipers","user":"starbuck"}'
# 使用ad-hoc临时定义变量
ansible node1 -m debug -a 'var=rhel_name' -e rhel_name=rhel9.0

# 使用playboog临时定义变量
ansible-playbook playbook -e rhel_name=rhel9.0 
# 示例yml文件 如下:
- hosts: '{{ hosts }}'
    remote_user: '{{ user }}'


# 使用命令行的方式定义变量
ansible-playbook release.yml --extra-vars "hosts=vipers user=starbuck"

2.8 facts 事实变量

简介

ansible的 setup模块 会收集被控节点的主机信息,这些 收集到的系统信息叫做 facts。它会把这些收集到的信息 保存到 ansible_facts 这个变量(事实变量,字典的格式,底下包含各种 系统信息的字典)中。

变量包括:主机名、网卡设备、IP地址、磁盘名称和磁盘的存储空间、文件系统bios版本 架构…

使用setup 收集

通过setup模块收集facts数据,常用获取信息如下

关键字说明
ansible_python_versionpython版本
ansible_distribution显示是什么系统
ansible_distribution_major_version显示是系统主版本号
ansible_devices显示磁盘设备信息
ansible_lvm显示lvm相关信息
ansible_memtotal_mb显示系统总内存
ansible_kernel显示内核版本
ansible_ens160显示网卡版本
ansible_fqdn显示主机fqdn
ansible_hostname显示主机名
# 查看 所有的fact信息
ansible 10.0.0.23 -m setup

# 查看网卡的信息
ansible 10.0.0.23 -m setup -a 'filter=ansible_ens160'

# 查看主机内存信息
ansible 10.0.0.23 -m setup -a 'filter=ansible_*_mb'


# 导出facts变量到文件
ansible 10.0.0.23 -m setup --tree /opt/setup.txt
ansible 10.0.0.23 -m setup  > /opt/setup-node1
# 举例:显示lvm相关信息
[root@rhel ~]# ansible 10.0.0.23 -m setup -a 'filter=ansible_lvm'
10.0.0.23 | SUCCESS => {
    "ansible_facts": {
        "ansible_lvm": {
            "lvs": {
                "root": {
                    "size_g": "17.00",
                    "vg": "cs"
                },
                "swap": {
                    "size_g": "2.00",
                    "vg": "
引用、使用 facts变量

setup的 filter 参数,也可以用过来过滤 facts变量

  • facts比较特殊,它以ansible_facts 作为 键,ansible每次收集后会自动将其注册为变量,所以facts中的数据都可以直接通过变量引用。
    • 例如:引用被控端ipv4的地址address项
      • 直接引用 值,写成如下
      • 而不能写成 ansible_facts.值
  • filter 只能过滤 ansible_facts 的下一层级的变量,如果有多个层级,无法进行收集
# 通过指定方式收集信息

# 直接写 ansible_facts下的值
ansible 10.0.0.23 -m setup -a 'filter=ansible_all_ipv4_addresses'
# 如果写 ansible_facts.下的值,就过滤不到
ansible 10.0.0.23 -m setup -a 'filter=ansible_facts.ansible_all_ipv4_addresses'

# 通过通配符收集信息
ansible 10.0.0.23 -m setup -a 'filter=ansible_*addresses'
# 直接写 ansible_facts下的值
[root@rhel ~]# ansible 10.0.0.23 -m setup -a 'filter=ansible_all_ipv4_addresses'
10.0.0.23 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.0.106",
            "192.168.122.1",
            "10.0.0.23"
        ],
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false
}
# 如果写 ansible_facts.下的值,就过滤不到
[root@rhel ~]# ansible 10.0.0.23 -m setup -a 'filter=ansible_facts.ansible_all_ipv4_addresses'
10.0.0.23 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false
}
[root@rhel ~]# 

开启、禁用facts变量
# 开启facts收集
gather_facts: true
gather_facts: yes


# 关闭facts收集
gather_facts: false
gather_facts: no
# 举例: 在yml文件中 关闭facts收集 `gather_facts: no`
- hosts: node1
  gather_facts: no
  tasks:
  - debug:
      # 由于事实变量没有收集,则此地方无法找到变量值
      var: ansible_all_ipv4_addresses

如果在playbook中的target section位置,禁用了gather_facts: false ,也可以在tasks中添加模块 setup 进行收集。一般不会这么干

- hosts: node1
  gather_facts: no
  tasks:
  - setup:
  - debug:
     var: ansible_all_ipv4_addresses

自定义facts

ansible除了能获取到预定义的fact的内容,还支持手动为某个主机定制fact。称之为本地fact。
本地fact默认存放于被控端的 /etc/ansible/facts.d 目录下,如果文件为ini格式或者json格式,ansible会自动识别。以这种形式加载的fact是key为ansible_local的特殊变量。

ansibler主控端定义一个 ini 格式的 custom.fact 文件内容如下:

[general]
package = httpd
service = httpd
state = started

案例:自定义facts事实变量

  1. 创建facts变量文件
vim userinfo.fact  
[user]
name = zhangsan
age = 18
sex = boy
  1. 编写playbook,将facts变量文件拷贝到被控节点 /etc/ansible/facts.d 目录下
vim userinfo.yml
- name: use fact
  hosts: node1
  tasks:
  - name: create dir
    file:
      path: /etc/ansible/facts.d
      state: directory
  - name: copy
    copy:
      src: /opt/userinfo.fact
      dest: /etc/ansible/facts.d
  1. 引用facts变量
ansible node1 -m setup -a 'filter=ansible_local'
使用 set_fact 定义

set_fact模块 可以把 facts 里的变量拿出来用,重新组合,自定义成我们要的变量,再拿来用

- hosts: 10.0.0.23
  tasks:
  - name: set_fact举例
    set_fact:
      # 把 ansible_distribution 和 ansible_distribution_version 这两个变量的值,拼接起来,并赋值给 version 这个变量
      version: "{{ ansible_distribution }}-{{ansible_distribution_version}}"
  - name: 输出 version 变量值
    debug:
      msg: "{{ version }} "
# 过程
[root@rhel ~]# ansible 10.0.0.23 -m setup -a 'filter=ansible_distribution'
10.0.0.23 | SUCCESS => {
    "ansible_facts": {
        "ansible_distribution": "CentOS",
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false
}
[root@rhel ~]# 
[root@rhel ~]# ansible 10.0.0.23 -m setup -a 'filter=ansible_distribution_version'
10.0.0.23 | SUCCESS => {
    "ansible_facts": {
        "ansible_distribution_version": "9",
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false
}
[root@rhel ~]# 

# 执行脚本
[root@rhel ~]# ansible-playbook set_fact 

PLAY [10.0.0.23] ***************************************************************

TASK [Gathering Facts] *********************************************************
ok: [10.0.0.23]

TASK [set_fact举例] ************************************************************
ok: [10.0.0.23]

TASK [输出 version 变量值] *****************************************************
ok: [10.0.0.23] => {
    "msg": "CentOS-9 "
}

PLAY RECAP *********************************************************************
10.0.0.23                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 

自定义的facts事实变量,会保存到ansible_local变量中

整合facts变量,让变量拼凑在一块,set_fact模块

- name: usr set_fact
  hosts: node1
  tasks:
    - set_fact:
       get_version: "{{ ansible_distribution }}-{{ ansible_distribution_version }}"
    - debug:
       var: get_version

2.9 lookup 生成变量(只用于主控机)

2.9.1 说明

  • 有些时候,我们希望 从诸如 文本文件 或者 .csv文件 中收集数据作为ansible的变量,或者 直接获取某些命令的输出 作为 ansible的变量,这个时候,我们就需要通过 ansible 的 lookup插件 来从这些数据源中读取配置数据,传递给ansbile变量,并在playbook或者模板中使用这些数据。
  • 但是 注意 lookup获取的变量来自于 主控端 主控端 主控端
  • ansible支持一套从不同数据源获取数据的 lookup,包括file, password, pipe, env, template,csvfile, dnstxt, redis_kv, etcd等

2.9.2 方法

写在yml文件里

# 语法
{{ lookup('使用什么方法','参数') }}
# 把文件的内容作为变量的值(file查看的是主控节点的文件)
get_passwd: "{{ lookup('file','/etc/passwd') }}"

# 把命令的结果作为变量的值
key_content: "{{ lookup('file','/root/.ssh/id_rsa.pub')}}"

# 把shell变量的结果作为变量的值
get_env: "{{ lookup('env', 'JAVA_HOME')}}"
file 从文件中获取值

注意 lookup获取的变量来自于 主控端 主控端 主控端

使用file lookup可以从文本文件中获取数据,并在这些数据传递给ansible变量,在task
或者jinja2模板中进行引用。下面是获取ssh公钥并生成变量的示例:

- hosts: node1
  tasks:
  - name: set facts
    set_fact:
      key_content: "{{ lookup('file','/root/.ssh/id_rsa.pub')}}"
  - name: debug set facts
    debug:
      msg: "{{ keycontent }}"

pipe 获取命令的输出值

注意 lookup获取的变量来自于 主控端 主控端 主控端

使用pipe lookup可以直接调用外部命令,并将命令执行的结果打印到标准输出,作为ansible变量。下面的例子通过pipe调用date指令拿到一个以时间数字组成的字串。

- hosts: 127.0.0.1
  vars:
    # 这里'+%Y-%m-%d--%H:%M:%S' 中间要是有空格就会报错,所以用--代替了
    aaa: "{{ lookup('pipe', 'date +%Y-%m-%d--%H:%M:%S') }}"
  tasks:
    - name: "pipe 获取命令的输出值-1"
      debug:
        var: aaa
    - name: "pipe 获取命令的输出值-2"
      debug:
        msg: "命令的输出:{{ lookup('pipe', 'expr $(date +%s) + 2592000') }}"
[root@hn ~]# ansible-playbook pipe.yml 

PLAY [127.0.0.1] ***************************************************************

TASK [Gathering Facts] *********************************************************
ok: [127.0.0.1]

TASK [pipe 获取命令的输出值-1] *************************************************
ok: [127.0.0.1] => {
    "aaa": "2024-09-29--16:25:42"
}

TASK [pipe 获取命令的输出值-2] *************************************************
ok: [127.0.0.1] => {
    "msg": "命令的输出:1730190342"
}

PLAY RECAP *********************************************************************
127.0.0.1                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@hn ~]# 

env 获取环境变量的值

注意 lookup获取的变量来自于 主控端 主控端 主控端

env lookup实际就是获取在控制主机上的某个环境变量的值。

- name: 获取 环境变量 $JAVA_HOME 的值
  debug:
    msg: "{{ lookup('env', 'JAVA_HOME')}}"
更多方法

查看更多的lookup可以使用的值:

ansible-doc -t lookup -l

2.10 魔法变量 (内置变量)

Ansible 默认会提供一些 内置的变量 以实现一些特定的功能,我们称之为魔法变量 (内置变量)。

hostvars 获取指定主机的变量

获取 指定主机 的相关变量

# 语法
{{ hostvars['主机名/IP'].该主机的变量 }}

举例:有一台web服务器的配置文件中需要指定db服务器的ip地址,我们假定这台db服务器的hostname为db.exmaple.com,ip地址绑定在eth0网卡上,我们可以通过 hostvars方法 在web服务器上调用db服务器的ip地址

{{ hostvars['db.example.com'].ansible_eth0.ipv4.address }}

需要注意
db.example.com 不能使用ip地址来取代,只能使用主机名,要用引号引起来。
如果一定要写 IP地址,在 主机清单、yml文件的主机 里要有写这个ip地址,才可以引用

# 举例
- hosts: node1,node2
  gather_facts: yes
  tasks:
    - debug:
        msg: "{{ hostvars['node1'].ansible_default_ipv4.address }}"
[root@rhel ~]# cat hostvars 
- hosts: 10.0.0.23
  tasks:
    - debug:
        msg: "{{ hostvars['10.0.0.23'] }}"
[root@rhel ~]# 
[root@rhel ~]# ansible-playbook hostvars 

PLAY [10.0.0.23] ******************************

TASK [Gathering Facts] ********************************
ok: [10.0.0.23]

TASK [debug] *****************************
ok: [10.0.0.23] => {
    "msg": {
        "ansible_all_ipv4_addresses": [
            "10.0.0.23",
            "192.168.224.140"
        ],
        "ansible_all_ipv6_addresses": [
            "fe80::bd94:3643:89aa:4908",
            "fe80::20c:29ff:fe25:7f90"
        ],
。。。。。
inentory_hostname 显示运行当前任务的主机名

inventory_hostname是Ansible所 识别 的 当前正在运行task 的 主机 的主机名

# 举例
- hosts: node1,node2
  gather_facts: yes
  tasks:
    - debug:
       var: ansible_hostname
      when: inventory_hostname == 'node1'
[root@rhel ~]# cat inentory_hostname 
- hosts: 10.0.0.23
  tasks:
    - debug:
        msg: "执行当前任务的主机 的主机名是:{{ ansible_hostname }} "
[root@rhel ~]# 
[root@rhel ~]# ansible-playbook inentory_hostname 

PLAY [10.0.0.23] ***************************************************************

TASK [Gathering Facts] *********************************************************
ok: [10.0.0.23]

TASK [debug] *******************************************************************
ok: [10.0.0.23] => {
    "msg": "执行当前任务的主机 的主机名是:centos "
}

PLAY RECAP *********************************************************************
10.0.0.23                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 

groups 列出所有主机组

groups是inventory中所有主机组的列表,可用于枚举主机组中的所有主机。

{{ groups }} 		# 列出 所有的 主机组
{{ groups.test }} 	# 列出 test组 内的所有主机,可用于循环主机组内的主机(常用于循环)
{{ group_names }} 	# 列出 当前正在执行task 的 目标主机 位于的 主机组。
# 过程
[root@rhel ~]# cat groups 
- hosts: 10.0.0.23
  tasks:
    - debug:
        msg: "{{ groups.group01 }}"
[root@rhel ~]# 
[root@rhel ~]# ansible-playbook groups 

PLAY [10.0.0.23] ***************************************************************

TASK [Gathering Facts] *********************************************************
ok: [10.0.0.23]

TASK [debug] *******************************************************************
ok: [10.0.0.23] => {
    "msg": [
        "10.1.0.11",
        "10.1.0.25",
        "10.1.0.26",
        "10.1.0.27",
        "10.1.0.28",
        "10.1.0.29",
        "10.1.0.30",
        "xiaoshou1",
        "10.0.0.23"
    ]
}

PLAY RECAP *********************************************************************
10.0.0.23                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 

更多魔法变量

查看更多的ansible魔法变量:
Special Variables — Ansible Community Documentation

2.11 其他

调试变量 debug

调试变量的方式:
使用debug模块,有2个参数,分别是msg和var(二者无法连用)
msg
可以输出 自定义文本 + 变量值
格式 还是引用变量的格式
var
只能打印变量
格式:只要写个变量名就行

[root@rhel ~]# cat ceshi.yml 
- name: 剧本1
  gather_facts: no
  hosts: 10.0.0.23
  vars:
    var1: "变量1"
    var2: "变量2"
  tasks:
     - name: 任务1
       debug:
         msg: "这个是: {{ var1 }}"
     - name: 任务2
       debug: 
         var: var2
[root@rhel ~]# 
[root@rhel ~]# ansible-playbook ceshi.yml 

PLAY [剧本1] ****************************************************************************

TASK [任务1] ****************************************************************************
ok: [10.0.0.23] => {
    "msg": "这个是: 变量1"
}

TASK [任务2] ****************************************************************************
ok: [10.0.0.23] => {
    "var2": "变量2"
}

PLAY RECAP ******************************************************************************
10.0.0.23                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 

如何获取 字典 的值

当我们 定义了一个稍微复杂的字典变量,如果想要获取 字典 里的 字典,可以使用 中括号[]、点号.

# 获取users 中 bjones用户 的 first_name
{{ users.bjones.first_name }}
# 或者如下写法:
{{ users['bjones']['first_name'] }}

# 说明:中括号引用时,中括号内 可以是 变量,不需要加引号,定义flag变量的值为bjones
可以写成{{ users[flag]['first_name'] }}

3 运算符

3.1 比较运算符( >、<、=)

ansible中,还支持如下比较运算符:

==	 # 比较两个对象是否相等,相等 则返回 真。可用于比较 字符串、数字
!=	 # 比较两个对象是否不等,不等 则为真。
>    # 比较两个对象的大小,左边的值大于右边的值,则为真
<	 # 比较两个对象的大小,左边的值小于右边的值,则为真
>=	 # 比较两个对象的大小,左边的值大于等于右边的值,则为真
<=	 # 比较两个对象的大小,左边的值小于等于右边的值,则为真

示例:

when: ansible_machine == "x86_64"
when: max_memory <= 512

3.2 逻辑运算符(and、or)

在Ansible中,除了比较运算符,还支持逻辑运算符

and		# 逻辑与,当左边和右边两个表达式同时为真,则返回真
or		# 逻辑或,当左右和右边两个表达式任意一个为真,则返回真
not		# 逻辑否,对表达式取反
()		# 当一组表达式组合在一起,形成一个更大的表达式,组合内的所有表达式都是逻辑与的关系
# 逻辑或
when: ansible_distribution == "RedHat" or ansible_distribution == "Fedora"

# 逻辑与
when: ansible_distribution_version == "8.0" and ansible_kernel == "4.18.0-80.el8.x86_64"

# 组合
when: ( ansible_distribution == "RedHat" and ansible_distribution_major_version == "8" ) or ( ansible_distribution == "Fedora" and ansible_distribution_major_version == "28")
# 逻辑与:使用 and 符号进行连接,所有的条件满足才为真 
- hosts: node1,node2
  gather_facts: yes
  tasks:
    - debug:
       msg: hello rhce
      when: ansible_hostname == 'node1' and ansible_ens160.ipv4.address == '192.168.112.128'
# 逻辑或:使用 or 符号进行连接,只要满足其中一个条件则为真
- hosts: node1,node2
  gather_facts: yes
  tasks:
    - debug:
       msg: hello rhce
      when: ansible_hostname == 'node2' or ansible_ens160.ipv4.address == '192.168.112.128'
# 逻辑非: 使用 not 进行判断,对表达式进行取反(not应该放到整个的表达式的最前面)
- hosts: node1,node2
  gather_facts: yes
  tasks:
    - debug:
       msg: hello rhce
      when: not inventory_hostname == 'node1'
# 多个表达式组成一组:使用 () 来表示一组表达式,也就是多个表达式的集合

# 案例:主机名是node1,并且IP地址是192.168.112.128 或者 系统是RedHat,并且大版本是9 则执行任务
- hosts: node1,node2
  gather_facts: yes
  tasks:
    - debug:
       msg: hello rhce
      when: ( ansible_hostname == 'node1' and ansible_ens160.ipv4.address == '192.168.112.128' ) or ( ansible_os_family == 'RedHat'  and ansible_distribution_major_version == '9' )

4 判断语句

4.1 单次 条件判断 when

ansible中,使用 条件判断 的关键字就是 when。

当 表达式的结果 返回 true ,便会 执行 本次的任务
当 表达式的结果 返回 false,便会 跳过 本次的任务

when判断 默认就识别变量名,所以不需要使用 "{{}}" 括起来
when 是对 单个task任务进行判断,多个任务就要写多个when

判断变量
defined		# 判断 变量 是否 已定义,已定义 则返回 真
undefined	# 判断 变量 是否 未定义,未定义 则返回 真
none		# 判断 变量的值 是否为 空,如果 变量已定义 且 值为空,则返回真
# 举例
- hosts: 10.0.0.23
  gather_facts: no
  vars:
    testvar: "test"
    testvar1:
  tasks:
  - debug:
    msg: "testvar 已定义"
    when: testvar is defined
  - debug:
    msg: "testvar2 未定义"
    when: testvar2 is undefined
  - debug:
    msg: "testvar1 已定义 且 值为空"
    when: testvar1 is none
判断执行结果
ok						# 目标状态与期望值一致,没有发生变更
change 或 changed		# 目标发生变更,与期望值一样
sucess 或 succeeded		# 目标状态与期望值一致,或者任务执行成功
failure 或 failed		# 任务执行失败
skip 或 skipped			# 任务被跳过
# 语法
when: 要判断的东西 is 执行结果
# 示例:
- hosts: test
  tasks:
    - shell: 'cat /testdir/aaa'
      register: result
      # `ignore_errors: true` 如果当前任务执行失败,就跳过当前任务,继续去执行下一个任务
      ignore_errors: true
    - debug:
      msg: "success 目标状态与期望值一致,或者任务执行成功"
      when: result is success
    - debug:
      msg: "failed 任务执行失败"
      when: result is failure
    - debug:
      msg: "changed 目标发生变更,与期望值一样"
      when: result is change
    - debug:
      msg: "skip 任务被跳过"
      when: result is skip
根据rc返回值来进行判断task任务是否执行成功

register 注册变量—>保存task任务的执行信息

- hosts: all
  tasks:
    - shell: ls /etc/passwd1
      register: get_status
      ignore_errors: yes
    - debug:
       var: get_status
    - debug:
       msg: rc is ok
      when: get_status.rc == 0
    - debug:
       msg: rc is error
      when: get_status.rc != 0
判断 一个路径是什么东西
file		# 判断 指定路径 是否为 一个文件,是则为真
directory	# 判断 指定路径 是否为 一个目录,是则为真
link		# 判断 指定路径 是否为 一个软链接,是则为真
mount		# 判断 指定路径 是否为 一个挂载点,是则为真
exists		# 判断 指定路径 是否 存在,存在则为真

特别注意:关于 路径 的所有判断均 是 判断 主控端 上的路径,而非 被控端 上的路径

# 示例:
- hosts: test
  gather_facts: no
  vars:
    testpath1: "/testdir/test"
    testpath2: "/testdir"
  tasks:
    - debug:
      msg: "是 一个文件"
      when: testpath1 is file
    - debug:
      msg: "是 一个目录"
      when: testpath2 is directory
判断字符串 大小写
lower	# 判断 字符串 中的 所有字母 是否都是 小写,是则为真
upper	# 判断 字符串 中的 所有字母 是否都是 大写,是则为真
# 示例:
- hosts: test
  gather_facts: no
  vars:
    str1: "abc"
    str2: "ABC"
  tasks:
    - debug:
      msg: "str1 是 小写"
      when: str1 is lower
    - debug:
      msg: "str2 是 大写"
      when: str2 is upper
判断奇、偶数、整除
even				# 判断 数值 是否为 偶数,是 则为 真
odd					# 判断 数值 是否为 奇数,是 则为 真
divisibleby(数字)	# 判断 是否 可以整除 指定的数值,是 则为真
# 示例:
- hosts: test
  gather_facts: no
  vars:
    num1: 6
    num2: 8
    num3: 15
  tasks:
    - debug:
      msg: "判断 数值 是否为 偶数"
      when: num1 is even
    - debug:
      msg: "判断 数值 是否为 奇数"
      when: num2 is odd
    - debug:
      msg: "判断 是否 可以整除 指定的数值"
      when: num3 is divisibleby(3)
其他

其他条件测试:


subset 		# 判断一个list是不是另一个list的 子集:when: a is subset(b)
superset 	# 判断一个list是不是另一个list的 父集:when: b is superset(a)

in 			# 判断一个字符串是否存在于另一个字符串中,也可用于判断某个特定的值是否存在于列表中

string 		# 判断对象是否为一个字符串,是则为真 when: var1 is string
number 		# 判断对象是否为一个数字,是则为真 when: var3 is number
# in 判断一个字符串是否存在于另一个字符串中,也可用于判断某个特定的值是否存在于列表中
supported_distros:
- RedHat
- CentOS
when: ansible_distribution in supported_distros
# subset 判断一个list是不是另一个list的子集:when: a is subset(b)
# superset 判断一个list是不是另一个list的父集:when: b is superset(a)
- hosts: all
  gather_facts: no
  vars:
    OS:
      - rhel
      - openeuler
      - centos
    os:
      - rhel
      - centos
  tasks:
    - debug:
       msg: ansible ok
      when: OS is superset(os)
# in 判断 关键字是否处理列表中
- hosts: all
       tasks:
        - debug:
          msg: ok
          when: "'ens224' in ansible_interfaces"
# string 判断是否是字符串
# number 判断是否是数字
- hosts: all
  vars:
    RHEL_NAME: rhel9
    RHEL_NUM: 9
  tasks:
    - debug:
       msg: string ok
      when: RHEL_NAME is string
    - debug:
       msg: num ok
      when: RHEL_NUM is number

4.2 block 判断多个任务

when 判断,一次 只能判断 一个task任务,
block 判断,一次 可以判断 多个task任务。

  • ansible通过block来包裹多个task任务,直接对block进行when判断,
  • 如果 判断成功 则执行block 中所有的task任务。
- hosts: node1
  gather_facts: no
  tasks:
    - block:
       - shell: ls /etc/shadow1
       - file:
          path: /etc/passwd
          state: file
      when: inventory_hostname == 'node1'

5 异常处理

5.1 resuce 报错时执行

resuce关键字,一般搭配 block块 使用:

  • 如果 block中的任务 执行 失败,则 会 执行 resuce 下面的任务;
  • 如果 block中的任务 执行 成功,则 不会 执行 resuce 下面的任务
- hosts: node1
  gather_facts: no
  tasks:
    - block:
        - shell: ls /etc/shadow
        - file:
            path: /etc/passwd
            state: file
      rescue:
        - debug:
            msg: resuce
      when: inventory_hostname == 'node1'

5.2 always 无论都会执行

always关键字,无论block的任务执行是成功还是失败,都要执行always关键字中的task任务

- hosts: node1
  gather_facts: no
  tasks:
    - block:
        - shell: ls /etc/shadow1
        - file:
            path: /etc/passwd
            state: file
      rescue:
        - debug:
            msg: resuce
      always:
        - debug:
            msg: always
      when: inventory_hostname == 'node1'

5.3 fail 满足条件就退出

fail模块用于终止当前playbook的执行;
通常与条件语句组合使用,当满足条件时,终止当前playbook的运行。

备注:直接用 faild_when 比较方便

# 选项只有一个:
msg:终止前打印出信息
- hosts: all
  tasks:
    - shell: ls /etc/paaa
      ignore_errors: yes
      register: get_status
    - fail:
        msg: playbook exit
      when: get_status.rc != 0
    - debug:
        msg: a
    - debug:
        msg: b

5.4 faild_when 满足条件就退出

当 fail 和 when组合使用 的时候,还有一个更简单的写法,即 failed_when,

faild_when 当满足条件时,终止当前playbook的运行。

- hosts: all
  tasks:
    - shell: ls /etc/paaa
      ignore_errors: yes
      register: get_status
    - debug:
        msg: playbook exit
      failed_when: get_status.rc != 0
    - debug:
        msg: a
    - debug:
        msg: b
# 如果在 command_result 存在错误输出,且错误输出中,包含了`FAILED`字串,即返回失败状态:
- hosts: test
  tasks:
    - name: 此命令失败时打印FAILED
      command: /usr/bin/example-command -x -y -z
      register: command_result
      failed_when: "'FAILED' in command_result.stderr"

6 循环语句

基于列表的循环
基于字典的循环
loop循环

在Ansible 2.5以前,playbook通过不同的循环语句以实现不同的循环,这些语句使用with_作为前缀。这些语法目前仍然兼容,但在未来的某个时间点,会逐步废弃。

6.1 with_items 基于列表的循环

不能使用对象的方式进行循环

第一种方式:利用变量的形式

- hosts: 10.0.0.23
  vars:
    users:
      - zhangsan
      - lisi
      - wangwu
  tasks:
    - debug:
        msg: "{{ item }}"
      with_items: "{{ users }}"
[root@rhel ~]# ansible-playbook with 

PLAY [10.0.0.23] ***************************************************************

TASK [debug] *******************************************************************
ok: [10.0.0.23] => (item=zhangsan) => {
    "msg": "zhangsan"
}
ok: [10.0.0.23] => (item=lisi) => {
    "msg": "lisi"
}
ok: [10.0.0.23] => (item=wangwu) => {
    "msg": "wangwu"
}

PLAY RECAP *********************************************************************
10.0.0.23                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 

第二种方式:直接写列表

- hosts: node1
  gather_facts: no
  vars:
    users:
      - zhangsan
      - lisi
      - wangwu
  tasks:
    - debug:
        msg: "{{ item }}"
      with_items:
        - zhangsan
        - lisi
        - wangwu

第三种方式:类似于python的写法,一个行内对象

- hosts: node1
  gather_facts: no
  vars:
    users:
      - zhangsan
      - lisi
      - wangwu
  tasks:
    - debug:
        msg: "{{ item }}"
      with_items: ["zhangsan", "lisi", "wangwu"]

6.2 with_dict 基于字典的循环

在字典中是以 key value的格式

- hosts: node1
  gather_facts: no
  vars:
    users:
      memeda01:
        name: zhangsan
        uid: 3030
      memeda02:
        name: lisi
        uid: 4040
  tasks:
    - debug:
        # key 是 memeda01 和memeda02;value是 name:zhangsan  和 uid:3030
        msg: "{{ item.value.name}}"
      with_dict: "{{ users }}"
    - debug:
        msg: "{{ item.value.uid}}"
      with_dict: "{{ users }}"

6.3 loop循环

ansible 2.5及以前的版本当中,所有的循环都是使用with_X风格。
但是从2.6版本开始,官方开始推荐使用loop关键字来代替with_X风格的关键字

loop天生适合列表循环,如果要对其循环字典,必须使用jinja2过滤器 dict2items 进行将其转化类型。

- name: 循环列表
  gather_facts: no
  hosts: 10.0.0.23
  vars:
    users:
      - zhangsan
      - lisi
  tasks:
    - debug:
        msg: "{{ item }}"
      loop: "{{ users }}"
[root@rhel ~]# ansible-playbook loop_list 

PLAY [循环列表] ****************************************************************

TASK [debug] *******************************************************************
ok: [10.0.0.23] => (item=zhangsan) => {
    "msg": "zhangsan"
}
ok: [10.0.0.23] => (item=lisi) => {
    "msg": "lisi"
}

PLAY RECAP *********************************************************************
10.0.0.23                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 
# 使用loop配合dict2items过滤器实现 循环字典 功能:
- name: 循环字典
  hosts: 10.0.0.23
  gather_facts: no
  vars:
    users:
      alice: female
      bob: male
  tasks:
    - debug:
        msg: "键是:{{ item.key }} 值是: {{ item.value}}"
      loop: "{{users | dict2items }}"
[root@rhel ~]# ansible-playbook loop_dict

PLAY [循环字典] ****************************************************************

TASK [debug] *******************************************************************
ok: [10.0.0.23] => (item={'key': 'alice', 'value': 'female'}) => {
    "msg": "键是:alice 值是: female"
}
ok: [10.0.0.23] => (item={'key': 'bob', 'value': 'male'}) => {
    "msg": "键是:bob 值是: male"
}

PLAY RECAP *********************************************************************
10.0.0.23                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 
常用过滤器
dict2items 					# 将 字典 转化为 列表
password_hash('sha512') 	# 将 字符串通过sha512进行加密,常常用于用户的密码
defalt('值') 				# 当变量不存在的适合,使用默认的值

7 文件管理模块

通过此类模块 主要用来修改配置文件,因为有个选项validate 可以 校验配置文件是否正确(copy模块的content 是覆盖文件 而不是修改原有的内容)

lineinfile 修改单行配置内容
blockinfile 修改多行配置内容

7.1 lineinfile 修改单行

修改的是 行内容 而不是 关键字内容

7.1.1 选项


常用参数
path			# 要修改的文件 路径
line			# 替换/插入 匹配到的 行(如果匹配到多行一样的内容,则修改最后一行内容)

regexp			# 查找 行【支持通配符、正则表达式】
backrefs yes|no # 配合regexp、line使用
# backrefs默认是no,如果regexp没有没有匹配到行,line会在最后一行添加上去!!!而不是取消修改(正常是regexp匹配到行,line才执行修改)。所以,backrefs:yes,就是regex如果没有匹配到行,line不会执行替换

insertbefore	# 在匹配的行 前面 插入
insertafter		# 在匹配的行 后面 插入

create yes|no	# 默认是no;如果是yes,当文件不存在则生成文件
state absent|present	# 如果是absent删除的话,会将所有匹配的内容都给删除掉

backup			# 是否备份文件
validate		# 验证配置文件 在 修改之前 和 之后 是否仍然有效【使用的是应用程序本身的验证机制,而不是lineinfile模块】例如 httpd服务,使用httpd -t 验证语法;nginx 服务,使用nginx -t 验证语法

7.1.2 举例

替换行
# 将/etc/selinux/config 中匹配到 以 'SELINUX=' 开头的 行,将其替换为 'SELINUX=disabled'
- hosts: 10.0.0.23
  tasks:
    - name: 修改匹配行
      lineinfile:
        path: /etc/selinux/config
        regexp: "^SELINUX="
        line: "SELINUX=disabled"
        # backrefs:yes,就是regex如果没有匹配到行,line不会执行替换
        backrefs: yes
在匹配行 前 添加内容
# 在http.conf文件的 Listen 80 前面后面添加一行Listen 8080,task示例如下:
- hosts: 10.0.0.23
  tasks:
    - name: Ensure the default Apache port is 8080
      lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: '^Listen '
        insertafter: '^#Listen '
        line: Listen 8080
        # backrefs:yes,就是regex如果没有匹配到行,line不会执行替换
        backrefs: yes
修改 匹配行、文件权限
# 修改/etc/hosts,将以127.0.0.1开头的行 替换为 "127.0.0.1 localhost",并将/etc/hosts的属主和属组都修改为root,权限改为644,如下:
- hosts: 10.0.0.23
  tasks:
    - name: modify hosts
      lineinfile:
        dest: /etc/hosts
        regexp: '^127\.0\.0\.1'
        line: "127.0.0.1 localhost"
        owner: root
        group: root
        mode: 0644
删除 匹配行

删除文件内容

# 修改/etc/sudoers,将以%wheel开头的行删除,如下:
- hosts: 10.0.0.23
  tasks:
    - name: Make sure group wheel is not in the sudoers configuration
      lineinfile:
        path: /etc/sudoers
        state: absent
        regexp: "^%wheel"
文件存在则添加一行内容
# 往文件里添加一行(多次执行,不会重复添加),如果文件不存在,则创建文件
- hosts: 10.0.0.23
  tasks:
    - name: Add a line to a file if the file does not exist, without passing regexp
      lineinfile:
        path: /tmp/testfile
        line: 192.168.1.99 foo.lab.net foo
        create: yes
验证 配置文件 修改后是否有效

validate:验证文件修改的有效性,需要文件自带验证机制

- hosts: 10.0.0.23
  gather_facts: no
  tasks:
    - lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: "^Listen 80"
        line: "Listen 9999"
        # 验证文件修改的有效性,需要文件自带验证机制
        validate: httpd -t -f %s
        # backrefs:yes,就是regex如果没有匹配到行,line不会执行替换
        backrefs: yes

validate关键字后面的命令是httpd -t -f %s,其作用解释如下:

  • httpd:这是Apache HTTP服务器的二进制执行文件。
  • -t:这是测试配置文件的语法是否正确的选项。
  • -f %s:这是指定配置文件路径的选项,%s是一个占位符,会被替换为被修改的文件的实际路径(即/etc/httpd/conf/httpd.conf)。
    lineinfile模块尝试替换httpd.conf文件中的Listen 80行时,Ansible会在 执行替换 之前、之后httpd -t -f /etc/httpd/conf/httpd.conf命令来验证配置文件的语法。

Ansible会在 执行替换 之前、之后 检查配置文件是否正常(2次检查),如果配置文件验证不通过,则报错,不会修改配置文件

graph TB
	%% s=start  e=end  f=fork  n=normal
	
	s([validate检查 配置文件 语法 流程图])-->f1{{检查 配置文件 是否 包含语法错误}};

	%% 分支点1
	f1--语法正常-->f2{{验证 执行替换后的配置文件 能否通过语法}};
	f1-->f1_1([语法异常])-->f1_2[报错,取消修改配置文件]-->e([结束]);

	%% 分支点2
	f2--语法正常-->f2_1[完成替换]-->e;
	f2-->f1_1;
# 在被控机上,把httpd的配置文件加上“aaa”,让它配置文件出错
[root@centos conf]# vim httpd.conf 

#Listen 12.34.56.78:80
Listen 80
aaa
[root@centos conf]# 
# 使用校验命令,可以看到报错的原因
[root@centos conf]# httpd -t -f /etc/httpd/conf/httpd.conf 
AH00526: Syntax error on line 48 of /etc/httpd/conf/httpd.conf:
Invalid command 'aaa', perhaps misspelled or defined by a module not included in the server configuration
[root@centos conf]# 

# 主控机:Ansible会在 执行替换之前 用`httpd -t -f /etc/httpd/conf/httpd.conf`命令来验证配置文件的语法
[root@rhel ~]# ansible-playbook validate 

PLAY [10.0.0.23] ***************************************************************

TASK [lineinfile] **************************************************************
fatal: [10.0.0.23]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"}, "changed": false, "msg": "failed to validate: rc:1 error:AH00526: Syntax error on line 48 of /root/.ansible/tmp/ansible-tmp-1725348932.5288007-26204-126713900772622/tmpo5gj8agj:\nInvalid command 'aaa', perhaps misspelled or defined by a module not included in the server configuration\n"}

PLAY RECAP *********************************************************************
10.0.0.23                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 
# 被控机,配置文件正常
[root@centos conf]# vim httpd.conf 

#Listen 12.34.56.78:80
Listen 80

[root@centos conf]# 


# 主控机:我们尝试 改个错误的配置,看下是否会修改、报错【结果在"msg"中提示报错】
[root@rhel ~]# cat validate 
- hosts: 10.0.0.23
  gather_facts: no
  tasks:
    - lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: "^Listen 80"
        line: "Listen-- 80"
        # 验证文件修改的有效性,需要文件自带验证机制
        validate: httpd -t -f %s
[root@rhel ~]# 
[root@rhel ~]# ansible-playbook validate 

PLAY [10.0.0.23] ***************************************************************

TASK [lineinfile] **************************************************************
fatal: [10.0.0.23]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"}, "changed": false, "msg": "failed to validate: rc:1 error:AH00526: Syntax error on line 47 of /root/.ansible/tmp/ansible-tmp-1725353490.8070686-26615-244536184511062/tmpowl_7iii:\nInvalid command 'Listen--', perhaps misspelled or defined by a module not included in the server configuration\n"}

PLAY RECAP *********************************************************************
10.0.0.23                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 


# 回到被控机查看 配置文件是否发生变化,结果不变
[root@centos conf]# vim httpd.conf 

#Listen 12.34.56.78:80
Listen 80

[root@centos conf]# 

7.2 blockinfile 修改多行

在Ansible中,blockinfile 模块用于管理文件中的多行文本块。
它允许你确保一个指定的文本块存在于文件中,或者如果该文本块不存在,则将其插入到文件中的特定位置

blockinfile 模块可以用来确保指定的文本块存在于文件中,或者在文件中不存在的情况下,将其插入到指定的位置

7.2.1 选项

# 常用参数 与 lineinfile 类似:
参数说明:
block:要插入的文本内容
marker:指定块标记,"# {mark} ANSIBLE块标记"  mark这里是变量,因为块标记 上下各一行,到时候会输出为BEGIN、END


path			# 【必须】要操作的文件路径。
block			# 【必须】要插入或替换的文本块。

marker			# 【可选】指定在文本块前后添加的标记字符串,用于识别文本块。默认为 # BEGIN ANSIBLE MANAGED BLOCK 和 # END ANSIBLE MANAGED BLOCK。
insertafter		# 【可选】指定在文件中的哪个行之后插入文本块。可以是一个字符串,也可以是一个正则表达式。
insertbefore	# 【可选】指定在文件中的哪个行之前插入文本块。可以是一个字符串,也可以是一个正则表达式。
state			# 【可选】指定文本块应该存在 (present) 或不存在 (absent)。默认为 present。
create			# 【可选】如果文件不存在,是否 创建文件。默认为 no。
[root@rhel ~]# cat validate 
- hosts: 10.0.0.23
  gather_facts: no
  tasks:
    - blockinfile:
        path: /etc/httpd/conf/aaa
        # 默认插入块,会在 插入块 的上方、下方 各有一行注释
        block: |
          1-1 : 1
          2-1231
          33033 3121- aa啊
[root@rhel ~]# 
[root@rhel ~]# ansible-playbook validate 

PLAY [10.0.0.23] ***************************************************************

TASK [blockinfile] *************************************************************
changed: [10.0.0.23]

PLAY RECAP *********************************************************************
10.0.0.23                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# ansible 10.0.0.23 -m shell -a 'cat /etc/httpd/conf/aaa'
10.0.0.23 | CHANGED | rc=0 >>
1
2
3

Listen 80
# BEGIN ANSIBLE MANAGED BLOCK
1-1 : 1
2-1231
33033 3121- aa啊
# END ANSIBLE MANAGED BLOCK
[root@rhel ~]# 

7.2.2 举例

增加多行内容
- hosts: 10.0.0.23
  gather_facts: no
  tasks:
    - blockinfile:
       path: /root/a.txt
       create: yes
       # 指定块标记。{mark} 这里是变量,因为块标记 上下各一行,到时候 mark会输出为BEGIN、END
       marker: "# RHCE {mark} ansible blockinfile"
       # 写个 | 表示下面的内容是插入
       block: |
         name=zhangsan
         age=20
         sex=boy

block 插入多行内容,marker做标记

# 过程
[root@rhel ~]# cat validate 
- hosts: 10.0.0.23
  gather_facts: no
  tasks:
    - name: 添加多行内容,并自义定标记块
      blockinfile:
        path: /root/a.txt
        # 指定块标记
        marker: "# RHCE {mark} ansible blockinfile"
        block: |
          1-1 : 1
          2-1231
          33033 3121- aa啊
[root@rhel ~]# 
[root@rhel ~]# ansible 10.0.0.23 -m shell -a 'cat /root/a.txt'
10.0.0.23 | CHANGED | rc=0 >>
1
2
3
# BEGIN ANSIBLE MANAGED BLOCK
1-1 : 1
2-1231
33033 3121- aa啊
# END ANSIBLE MANAGED BLOCK
[root@rhel ~]# 
[root@rhel ~]# ansible-playbook validate 

PLAY [10.0.0.23] ***************************************************************

TASK [添加多行内容,并自义定标记块] ********************************************
changed: [10.0.0.23]

PLAY RECAP *********************************************************************
10.0.0.23                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# ansible 10.0.0.23 -m shell -a 'cat /root/a.txt'
10.0.0.23 | CHANGED | rc=0 >>
1
2
3
# BEGIN ANSIBLE MANAGED BLOCK
1-1 : 1
2-1231
33033 3121- aa啊
# END ANSIBLE MANAGED BLOCK
手动备注:上面的是 是默认的块标记,下面是我指定的 块标记
# RHCE BEGIN ansible blockinfile
1-1 : 1
2-1231
33033 3121- aa啊
# RHCE END ansible blockinfile
[root@rhel ~]# 

删除多行内容
说明
  1. 如果 不指定 marker标记,删除的则是默认创建出来的多行内容,也就是标记为 # BEGIN ANSIBLE MANAGED BLOCK# END ANSIBLE MANAGED BLOCK 中间的内容
  2. 如果 指定 marker标记,则删除的是 mark标记之间 的多行内容
    1. 删除标记的时候,因为我们用 {mark} 生成了BEGIN、END,所以写删除标记的语句,也要用 {mark}来代表 BEGIN、END
    2. 比如 marker: "# RHCE {mark} ansible blockinfile" 就会匹配以下2个:
      1. # BEGIN ANSIBLE MANAGED BLOCK
      2. # END ANSIBLE MANAGED BLOCK
  3. 如果有 多个marker标记是一样 的,则删除的是 末尾的marker标记中的内容;
删除的内容 有块标记
- hosts: 10.0.0.23
  gather_facts: no
  tasks:
    - name: 删除多行,内容有 块标记
      blockinfile:
       path: /root/a.txt
       create: yes
       # 动作为删除
       state: absent
       # 指定块标记
       marker: "# RHCE {mark} ansible blockinfile"
# 过程
[root@rhel ~]# cat rm_marker 
- hosts: 10.0.0.23
  gather_facts: no
  tasks:
    - name: 删除多行,内容有 块标记
      blockinfile:
       path: /root/a.txt
       create: yes
       # 动作为删除
       state: absent
       # 指定块标记
       marker: "# RHCE {mark} ansible blockinfile"
[root@rhel ~]#
[root@rhel ~]# ansible 10.0.0.23 -m shell -a 'cat /root/a.txt'
10.0.0.23 | CHANGED | rc=0 >>
1
2
3
# BEGIN ANSIBLE MANAGED BLOCK
1-1 : 1
2-1231
33033 3121- aa啊
# END ANSIBLE MANAGED BLOCK
# RHCE BEGIN ansible blockinfile
1-1 : 1
2-1231
33033 3121- aa啊
# RHCE END ansible blockinfile
[root@rhel ~]# 
[root@rhel ~]# ansible-playbook rm_marker 

PLAY [10.0.0.23] ***************************************************************

TASK [删除多行,内容有 块标记] *************************************************
changed: [10.0.0.23]

PLAY RECAP *********************************************************************
10.0.0.23                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]#
[root@rhel ~]# ansible 10.0.0.23 -m shell -a 'cat /root/a.txt'
10.0.0.23 | CHANGED | rc=0 >>
1
2
3
# BEGIN ANSIBLE MANAGED BLOCK
1-1 : 1
2-1231
33033 3121- aa啊
# END ANSIBLE MANAGED BLOCK
[root@rhel ~]# 
删除的内容 没有块标记

删除的内容 没有块标记,就需要配合lineinfile来进行使用。手动在 要删除的多行内容 前面、后面 添加上 块标记,然后把整个块标记删掉

- hosts: 10.0.0.23
  gather_facts: no
  tasks:
    - name: 手动在要删除的多行内容 前面 添加上 块标记
      lineinfile:
        path: /root/a.txt
        insertbefore: "2abc"
        line: "# 块标记 BEGIN 块标记"
    - name: 手动在要删除的多行内容 后面 添加上 块标记
      lineinfile:
        path: /root/a.txt
        insertafter: "5abc"
        line: "# 块标记 END 块标记"
    - name: 删除多行(块标记内的内容)
      blockinfile:
        path: /root/a.txt
        create: yes
        # 动作为删除
        state: absent
        marker: "# 块标记 {mark} 块标记"
# 过程:删除a.txt文件中 从 2abc到5abc的内容
[root@rhel ~]# ansible 10.0.0.23 -m shell -a 'cat /root/a.txt'
10.0.0.23 | CHANGED | rc=0 >>
1
2abc
3
# BEGIN ANSIBLE MANAGED BLOCK
1-1 : 1
2-1231
33033 3121- aa啊
# END ANSIBLE MANAGED BLOCK

5abc

2
3
[root@rhel ~]# 
[root@rhel ~]# ansible-playbook duohang 

PLAY [10.0.0.23] ***************************************************************

TASK [手动在要删除的多行内容 前面 添加上 块标记] *******************************
changed: [10.0.0.23]

TASK [手动在要删除的多行内容 后面 添加上 块标记] *******************************
changed: [10.0.0.23]

TASK [删除多行(块标记内的内容)] **********************************************
changed: [10.0.0.23]

PLAY RECAP *********************************************************************
10.0.0.23                  : ok=3    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 
[root@rhel ~]# ansible 10.0.0.23 -m shell -a 'cat /root/a.txt'
10.0.0.23 | CHANGED | rc=0 >>
1

2
3
[root@rhel ~]# 

8 jinja2

8.1 jinja2 模板文件

8.1.1 说明

Jinja2是基于python的模板引擎

技术背景
如果要写配置文件,不需要大改,只要小改,比如 只要修改 100台被控机 各自的 ip、主机名。
因为 ip、主机名 每台机子都不一样,且绝大部分的配置其实都是相同的,只要修改 部分内容

这个时候最好的方式:
用一个通用的配置文件(jinja2 模板文件)来解决所有的问题。
将 所有需要修改的地方 使用 变量替换

如下示例中 redis.conf.j2 文件,写完后我们使用 template 模块 来发送到被控机上,jinja2模版文件传到被控机后,会根据主机对应的 facts 事实变量,来替换变量的内容。过程参考 jinjia2简单例子

# 这是一个jinja2配置文件【需要修改成 被控机上的信息,使用变量来代替】

主机名是:{{ansible_hostname}}
IP地址是:{{ansible_ens160.ipv4.address}}

文件拓展名*.j2

8.1.2 jinja2 语法

if判断

if判断的格式:

# if 判断语法:(表达式就是利用when判断的条件来判断)
{% if 表达式 %}
执行语句
{% elif 表达式 %}
执行语句
{% else %}
执行语句
{% endif %}
for循环

for循环的格式:

{% for 变量 in 循环体 %}
循环结构(执行语句)
{% endfor %}

8.2 template 模块

playbook使用 template 模块来实现 *.j2模板文件的分发

template 用法与copy模块基本相同,唯一的区别是:

  • copy模块会将原文件原封不动的复制到被控端,
  • template 会将原文件复制到被控端,并且 使用变量的值 将文件中的变量 替换 以生成完整的配置文件

编写jinja2模板文件的注意事项

  1. 不要关闭facts变量,因为模板中的变量依赖于facts变量
  2. 变量不需要使用 ""引号 引起来,template会自动识别到变量;

8.2.1 选项

src			# 源模板文件路径
dest		# 目标文件路径

group		# 目标文件属组
mode		# 目标文件的权限
owner		# 目标文件属主

force		# 是否强制覆盖,默认为yes
backup		# 如果原目标文件存在,则先备份目标文件

8.3 过滤器

8.3.1 说明、语法格式

说白了就是 内置了不同方法的 命令

语法格式

"{{ 变量名 | jinja2过滤器 }}"

使用defaul的jinja2过滤器
当变量没有定义值的时候,就会使用默认的值来进行代替

# defaul的jinja2过滤器 举例
[root@rhel ~]# cat defult 
- hosts: 10.0.0.23
  vars:
    users:
      - name: zhangsan
        home: /opt/zhangsan
      - name: lisi
  tasks:
    - debug:
        msg: "name是:{{ item.name }} ,home是:{{ item.home|default('/tmp/defualt') }}"
      loop: "{{ users }}"
[root@rhel ~]# 
[root@rhel ~]# ansible-playbook defult 

PLAY [10.0.0.23] **********************

TASK [debug] ************************
ok: [10.0.0.23] => (item={'name': 'zhangsan', 'home': '/opt/zhangsan'}) => {
    "msg": "name是:zhangsan ,home是:/opt/zhangsan"
}
ok: [10.0.0.23] => (item={'name': 'lisi'}) => {
    "msg": "name是:lisi ,home是:/tmp/defualt"
}

PLAY RECAP ***************************
10.0.0.23                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 

常用的过滤器:
default
password_hash
dict2items

8.3.2 字符串操作

关键字说明
upper将所有字符串 转换为大写
lower将所有字符串 转换为小写
capitalize将字符串的首字母大写,其他字母小写
reverse将字符串倒序排列
first返回字符串的 第一个字符
last返回字符串的 最后一个字符
trim将字符串开头和结尾的空格 去掉
center(30)将字符串放在中间,并且字符串两边用空格补齐 30位
length返回字符串的长度,与count等价
list字符串 转换为 列表
shufflelist 将字符串转换为列表,但是顺序排列,shuffle同样将字符串转换为列表,但是会随机打乱字符串顺序

8.3.3 数字操作

关键字说明
int将对应的值转换为整数
float将对应的值转换为浮点数
abs获取绝对值
round小数点四舍五入
random从一个给定的范围中获取随机值

8.3.4 列表操作

关键字说明
length返回 列表长度
first返回 列表的第一个值
last返回 列表的最后一个值
min返回 列表中最小的值
max返回 列表中最大的值
sort重新排列列表,默认为升序排列,sort(reverse=true)为降序
sum返回 纯数字非嵌套列表中所有数字的和
flatten如果列表中包含列表,则flatten可拉平嵌套的列表,levels参数可用于指定被拉平的层级
join将列表中的元素合并为一个字符串
random从列表中随机返回 一个元素
union将两个列表合并,如果元素有重复,则只留下一个
intersect获取两个列表的交集
difference获取存在于第一个列表中,但不存在于第二个列表中的元素
symmetric_difference取出两个列表中各自独立的元素,如果重复则只留一个

8.4 举例

jinjia2简单例子
  1. 写 jinja2 模板文件
  2. 写剧本,把模板文件发到被控机
  3. 执行剧本
 1. 写 jinja2 模板文件
[root@rhel ~]# cat jiaja2_test.j2 
# 这是一个jinja2配置文件

主机名是:{{ansible_hostname}}
IP地址是:{{ansible_ens160.ipv4.address}}
[root@rhel ~]# 


 2. 写剧本,把模板文件发到被控机
[root@rhel ~]# cat j2_juben 
- name: 剧本1
  gather_facts: on
  hosts: 10.0.0.23
  tasks:
    - name: "任务1: 将 模板文件 发送到被控端"
      template:
        src: /root/jiaja2_test.j2
        dest: /root/jiaja2.conf
    - name: "任务2: 查看文件内容"
      shell: "cat /root/jiaja2.conf"
      # 通过`register` 将 某一任务结果 保存为 一个变量
      register: file_j2
    - name: "任务3: 打印文件内容"
      debug:
        var: file_j2.stdout
[root@rhel ~]# 

 3. 执行剧本
[root@rhel ~]# ansible-playbook j2_juben

PLAY [剧本1] *************************************

TASK [Gathering Facts] ****************************
ok: [10.0.0.23]

TASK [任务1: 将 模板文件 发送到被控端] ***********
changed: [10.0.0.23]

TASK [任务2: 查看文件内容] *****************************
changed: [10.0.0.23]

TASK [任务3: 打印文件内容] **************************
ok: [10.0.0.23] => {
    "file_j2.stdout": "# 这是一个jinja2配置文件\n\n主机名是:node01\nIP地址是:192.168.224.140"
}

PLAY RECAP ********************
10.0.0.23                  : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 


if、elif 多重判断

例如:现在有多台主机,都需要跑httpd服务,每一个机器的80端口都是监听到本地的IP地址上,对于这种情况,就案例1:判断,如果被控节点有ens160网卡,那么就监听ens160网卡的IP地址;如果被控节点有ens224网卡,那么就监听ens224网卡的IP地址;如果都有没,则监听所有的IP地址。

{% if ansible_ens160 is defined %}
Listen {{ ansible_ens160.ipv4.address }}:80
{% elif ansible_ens224 is defined %}
Listen {{ ansible_ens224.ipv4.address }}:80
{% else %}
Listen 0.0.0.0:80
{% endif %}

案例2:如果是在node1主机上,则内容为 node1 is ok;如果是在node2主机上,则内容为 node2 is ok

{% if ansible_hostname == 'node1' %}
  node1 is ok
{% elif ansible_hostname == 'node2' %}
  node2 is ok
{% else %}
  error
{% endif %}
for 循环

案例1:在node1主机上,在/etc/hosts中,包含所有主机的IP地址和FQDN(完整域名)和主机名的映射关系

[root@controller tmp]# cat for.j2
{% for i in groups.all %}
{{ hostvars[i].ansible_ens160.ipv4.address }} {{ hostvars[i].ansible_fqdn }} {{ hostvars[i].ansible_hostname }}
{% endfor %}


[root@controller tmp]# 
[root@controller tmp]# cat for.yml
# 单独写个剧本,获取所有主机的 facts变量信息,供后面的剧本使用
- hosts: all
- hosts: node1
  tasks:
    - template:
       src: /tmp/for.j2
       dest: /opt/for

一定要注意的是,如果只指定node1节点,那么node2节点的facts变量就无法获取,也就无法得到node2节点的IP地址、FQDN、主机名等信息,所以需要 再写个剧本 来先获取其他主机的信息。

ansible__2928">9 ansible 角色、集合管理

参考RH294书籍内容

9.1 理论

角色的背景说明

背景说明
随着开发的 playbook增多,您可能会发现经常可以重用以前编写的 playbook中的代码。或许,您还可以改变 play 的用途,即将其为某个应用配置 MySOL数据库的用途改为为主机名、密码和用户均不同的另一应用配置 MySQL 数据库。

但这个 play 可能比较冗长且复杂,有许多包含或导入的文件,以及用于管理各种情况的任务和处理程序。将所有这些代码复制到另- playbook 中可能并非易事。

Ansible角色 可让用户 以通用的方式 更加轻松地 重复利用 Ansible 代码。您可以采用标准化目录结构打包所有任务、变量、文件、模板,以及调配基础架构或部署应用所需的其他资源。通过复制相关的目录,将角色从一个项目复制到另一个项目,然后在 play 中调取该角色。

Ansible 角色具有下列优点(适用场景):

  • 角色可以将内容分组在一起,从而与他人轻松共享代码。
  • 角色可以定义系统类型的基本要素,如Web服务器、数据库服务器、Git存储库。
  • 角色使得较大型项目 更容易管理
  • 角色可以由不同的用户 并行开发

角色(roles)是一个包含 多个playbook 和 任务 的文件夹结构,它可以被重用共享,从而提高了Ansible的代码组织和可维护性。【也可理解为是一种模块,比如python的包,你只要pip下载下来,引用一下就可以直接用了】

角色的引用语法

roles 的名字怎么看
放角色目录的 目录名字,就是角色名
比如 我一个角色复制2份,改成不同的文件夹名,就可以当做2个角色用

引用的时候,用列表的形式

- name: 剧本1
  gather_facts: false
  hosts: 10.0.0.23
  roles:
    - hua
    - hua-2
角色(roles)的目录结构

角色目录 底下会生成对应的 文件夹、文件,但并非每个角色都拥有所有这些目录。

子目录功能
defaults此目录中的main.yml文件包含角色变量的默认值,使用角色时可以覆盖这些默认值。
这些变量的优先级最低,应该在play中更改和自定义。
files此目录包含由角色任务引用的静态文件,如https证书 等。不要在放模版
handlers此目录中的main.yml文件 用于定义角色中 需要调用 的 handlers特殊任务,可通过include引入其他的handlers文件。
meta此目录中的main.yml文件包含与 角色相关的信息,如作者、许可证、平台和可选的角色依赖项。
tasks此目录中的main.yml文件 包含 角色所 要执行的所有任务文件,可以在主文件中通过include的方式引入其他任务文件
templates此目录包含由角色任务引用的Jinja2模板。当使用角色相关的模板时,如未明确指定模板路径,则默认使用此目录中的模板
tests此目录可以包含清单和test.ymlplaybook,可用于测试角色。
vars此目录中的main.yml文件定义角色的变量值。这些变量通常用于角色内部用途。这些变量的优先级较高,在playbook中使用时不应更改。

默认路径:/etc/ansible/roles 通过修改ansible.cfg配置文件可以指定角色路径

[root@hn roles]# pwd 
/etc/ansible/roles
[root@hn roles]# ll /etc/ansible/roles/apche/
总用量 4
drwxr-xr-x. 2 root root   22  96 08:58 defaults
drwxr-xr-x. 2 root root    6  96 08:58 files
drwxr-xr-x. 2 root root   22  96 08:58 handlers
drwxr-xr-x. 2 root root   22  96 08:58 meta
-rw-r--r--. 1 root root 1328  96 08:58 README.md
drwxr-xr-x. 2 root root   22  96 08:58 tasks
drwxr-xr-x. 2 root root    6  96 08:58 templates
drwxr-xr-x. 2 root root   39  96 08:58 tests
drwxr-xr-x. 2 root root   22  96 08:58 vars
[root@hn roles]# 
集合(collection)

集合 里面包含 role角色、插件、模块

获取集合:
Ansible Galaxy - 集合

使用集合
正常写模块调用就行

role和collection

ansible 版本来说:
ansible 2.9 之前,使用的是roles角色–> 普通文件、task任务、template模板文件、handlers特殊任务、变量文件…
ansible 2.9 之后,使用是集合 collection —> roles角色、plugin插件、module模块
ansible的模块需要额外安装,通过安装集合来得到对应的模块
RHEL9引入了容器运行ansible

role
在早期版本的Ansible中,role 是Ansible社区和组织角色的方式。一个 role 通常是一个文件夹,包含一系列任务、文件、模板、变量和其他与角色相关的文件。这些文件夹通常包含在Ansible的 roles/ 目录下。

一个 role 定义了一组用于配置和管理服务器的任务,这些任务可以被重用和共享。例如,你可以创建一个名为 apache 的角色,它包含安装Apache服务器所需的所有任务。

collection
从Ansible 2.8开始,Ansible引入了 collection 概念。collection 是Ansible的新模块和角色组织方式,它取代了传统的 role。一个 collection 是一个包,包含了多个模块、角色和插件。

role 相比,collection 有以下几个优点:

  1. 模块组织collection 可以包含多个模块,而不仅仅是角色。
  2. 更好的模块管理collection 提供了更强大的模块管理功能,包括版本控制和依赖管理。
  3. 更灵活的命名空间collection 允许你创建具有特定命名空间的模块和角色,这有助于避免命名冲突。
  4. 更强大的搜索功能collection 提供了更强大的搜索功能,允许你根据模块的名称、描述和作者进行搜索。

要使用 collection,你需要安装它们。你可以使用 ansible-galaxy 命令来安装和更新 collection

总结
rolecollection 都是用于组织和分享Ansible模块和角色的概念,但 collection 是Ansible 2.8及更高版本中的新方式,它提供了更多的功能和改进。如果你使用的是Ansible 2.8或更高版本,建议使用 collection 来组织和管理你的Ansible模块和角色。

galaxy网站、文档

Ansible Galaxy - 可以下载角色、集合

角色 — Ansible 社区文档

ansiblegalaxy__3053">9.2 ansible-galaxy 角色、集合管理命令

ansible-galaxy 是 Ansible 提供的命令行工具,它可以管理 角色role集合collection

执行各种与角色和集合相关的操作。

collection  -- Manage an Ansible Galaxy collection. 管理Ansible Galaxy集合。
role     --    Manage an Ansible Galaxy role. 管理Ansible Galaxy角色。

9.2.1 常用命令

官网: https://galaxy.ansible.com/

# --角色管理--
ansible-galaxy role init 角色名				# 初始化一个 角色(roles)的目录结构
ansible-galaxy role search 角色名			# 搜索role

ansible-galaxy role install 角色名			# 从文件、URL、Ansible Galaxy安装角色
	-r 指定文件,从文件中下载
	-p 指定安装到哪个路径

ansible-galaxy role list					# 列出已安装的 roles
ansible-galaxy role info 角色名				# 查看已安装的 roles 信息
ansible-galaxy role remove 角色名			# 卸载roles




# --集合管理--
ansible-galaxy collection init            # 使用集合的基本结构 初始化 新集合。

ansible-galaxy collection list            # 显示collections_path中安装的每个集合的名称和版本

ansible-galaxy collection install         # 从文件、URL、Ansible Galaxy安装集合
ansible-galaxy collection install 集合的url路径			# 在线安装 集合
ansible-galaxy collection install 下载好的集合压缩包		# 从文件 离线安装 集合

9.2.2 举例-角色管理

初始化角色目录结构

角色的名字就是目录的名字,通过ansible-galaxy role init 要生成的角色名 可以 初始化角色目录结构,生成目录和文件

[root@hn roles]# pwd
/etc/ansible/roles
[root@hn roles]# ls
[root@hn roles]# 
# 通过`ansible-galaxy role init 要生成的角色名` 可以 初始化角色目录结构
[root@hn roles]# ansible-galaxy role init apche
- Role apche was created successfully
[root@hn roles]# 
[root@hn roles]# ls
apche
[root@hn roles]# tree /etc/ansible/roles/apche/
/etc/ansible/roles/apche/
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── README.md
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
    └── main.yml

8 directories, 8 files
[root@hn roles]# 

调用role角色执行任务

[root@controller ansible]# cat roles.yml
- hosts: all
  roles:
    - apache
安装角色roles
  1. 通过 galaxy.ansible.com 获取
    1. Ansible Galaxy - 角色
  2. 通过本地的软件包,rhel-system-roles 红帽系统角色(selinux firewalld chrony时间同步。。。。)
    1. yum install rhel-system-roles -y
    2. rpm -ql all rhel-system-roles 看释放的文件,看下装在哪
      1. /usr/share/ansible/roles 安装的角色目录
ansible-galaxy role init 角色名				# 初始化一个 角色(roles)的目录结构
ansible-galaxy role search 角色名			# 搜索role

ansible-galaxy role install 角色名			# 从文件、URL、Ansible Galaxy安装角色
	-r 指定文件,从文件中下载
	-p 指定安装到哪个路径

ansible-galaxy role list					# 列出已安装的 roles
# 安装角色,下载地址放在在一个yml文件中,安装到/etc/ansible/roles这个路径中
[root@controller ansible]# cat nginx.yml
- name: github.nginx
  src: https://github.com/noobient/ansible-galaxy-nginx/archive/main.tar.gz

[root@controller ansible]# ansible-galaxy role install -r nginx.yml -p /etc/ansible/roles
- downloading role from https://github.com/noobient/ansible-galaxy-nginx/archive/main.tar.gz
- extracting github.nginx to /etc/ansible/roles/github.nginx
- github.nginx was installed successfully
[root@controller ansible]# ls roles
apache  github.nginx  jengsala.nginx  nginx  selinux  timesync
# 指定安装目录,安装到/root/mulu1
[root@hn roles]# ansible-galaxy role install MD-SAUBAN-KHAN.dummy-mdsk-ansible-galaxy -p /root/mulu1
Starting galaxy role install process
- downloading role 'dummy-mdsk-ansible-galaxy', owned by MD-SAUBAN-KHAN
- downloading role from https://github.com/MD-SAUBAN-KHAN/dummy-mdsk-ansible-galaxy/archive/main.tar.gz
- extracting MD-SAUBAN-KHAN.dummy-mdsk-ansible-galaxy to /root/mulu1/MD-SAUBAN-KHAN.dummy-mdsk-ansible-galaxy
- MD-SAUBAN-KHAN.dummy-mdsk-ansible-galaxy (main) was installed successfully
[root@hn roles]# 
[root@hn roles]# ls /root/mulu1
1.t  2.t  3.t  MD-SAUBAN-KHAN.dummy-mdsk-ansible-galaxy
[root@hn roles]# 
查询已安装的角色
ansible-galaxy role list					# 列出已安装的 roles
ansible-galaxy role info 角色名				# 查看已安装的 roles 信息
# 列出你各个路径下的角色
[root@hn roles]# ansible-galaxy role list
# /usr/share/ansible/roles
- rhel-system-roles.metrics, (unknown version)
- linux-system-roles.ad_integration, (unknown version)
。。。。。

# /etc/ansible/roles
- apche, (unknown version)
- abc, (unknown version)
- bbb, (unknown version)
[WARNING]: - the configured path /root/.ansible/roles does not exist.
[root@hn roles]# 

include_tasks 引入task任务文件

类似python,全部代码写一个文件里,就太多了,不好浏览;
我们给他分到不同文件中,再引用,代码就看着比较简洁

include_tasks 是一个模块,它允许你在playbook中 引入(导入) 其他playbook 或 task任务文件。类似于编程中的“导入”概念

使用 include_tasks 模块可以大大提高playbook的可维护性和可重用性。它允许你将 任务 逻辑分离到 不同的文件中,并轻松地在不同的playbook中复用(引用)这些任务。

# 当任务较多时,可以将任务分开写到不同文件
[root@ansible nginx]# tree tasks/
tasks/
├── main.yml
├── nginx1.yml
└── nginx2.yml

# 通过 include_tasks 引入任务
[root@ansible tasks]# cat main.yml
- include_tasks:
  file: nginx1.yml
- include_tasks:
  file: nginx2.yml
编写角色 并执行

编写一个角色,并执行它【就跟写正常的剧本差不多,只不过更加模块化】

  1. 初始化角色目录结构
  2. 在角色目录中编写任务、变量
    1. 在 tasks 目录里 main.yml 写任务
      1. 任务1:获取date命令的输出值
      2. 任务2:打印 获取命令的输出值
      3. 任务3:打印 vars目录 里定义的变量 var_1
      4. 任务4:执行引入的外部任务 include_tasks
    2. 在 vars 目录里 写我们的变量
  3. 写剧本,引用角色,执行
# 1. 初始化角色目录结构
[root@rhel roles]# ansible-galaxy role init hua
- Role apche was created successfully
[root@rhel roles]# 
[root@rhel roles]# tree hua/
hua/
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── README.md
├── tasks
│   ├── main.yml
│   └── task_1.yml # 这里是我后面手动建立的文件
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
    └── main.yml

8 directories, 9 files
[root@rhel roles]# 
# 2. 在角色目录中编写任务、变量 
# 2.1. 在 tasks 目录里 main.yml 写任务
[root@rhel roles]# cd hua/
[root@rhel hua]# cat tasks/main.yml 
---
# tasks file for hua
- name: 任务1:获取命令的输出值
  set_fact:
    time_info: "{{ lookup('pipe', 'date +%Y-%m-%d--%H:%M:%S') }}"
- name: 任务2:打印 获取命令的输出值
  debug:
    msg: "命令的输出:{{ time_info }}"

- name: 任务3:打印 vars目录 里定义的变量 var_1
  debug:
    msg: "打印 vars目录 里定义的变量 var_1:{{ var_1 }}"
- name: 任务4:执行引入的外部任务(include_tasks)
  include_tasks:
    file: task_1.yml
[root@rhel hua]# 
[root@rhel hua]# cat tasks/task_1.yml 
- name: 外部任务1
  debug:
    msg: "外部任务1"
[root@rhel hua]# 



# 2.2. 在 vars 目录里 写我们的变量
[root@rhel hua]# cat vars/main.yml 
---
# vars file for hua
var_1: "我是变量var_1"
var_2: "我是变量var_2"
[root@rhel hua]# 
# 3. 写剧本,引用角色,执行
[root@rhel ~]# cat role_test 
- name: 剧本1
  gather_facts: false
  hosts: 10.0.0.23
  roles:
    - hua
[root@rhel ~]# ansible-playbook role_test

PLAY [剧本1] ************************************************************************

TASK [hua : 任务1:获取命令的输出值] ************************************************
ok: [10.0.0.23]

TASK [hua : 任务2:打印 获取命令的输出值] *********************************************
ok: [10.0.0.23] => {
    "msg": "命令的输出:2024-09-10--15:36:31"
}

TASK [hua : 任务3:打印 vars目录 里定义的变量 var_1] ********************************
ok: [10.0.0.23] => {
    "msg": "打印 vars目录 里定义的变量 var_1:我是变量var_1"
}

TASK [hua : 任务4:执行引入的外部任务(include_tasks)] *******************************
included: /etc/ansible/roles/hua/tasks/task_1.yml for 10.0.0.23

TASK [hua : 外部任务1] **************************************************************
ok: [10.0.0.23] => {
    "msg": "外部任务1"
}

PLAY RECAP **************************************************************************
10.0.0.23                  : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 

在执行任务前后,先执行指定任务

pre_tasks 会在 执行 playbook 之前 执行 pre_tasks 任务,
post_tasks 会在 执行 playbook 之后 执行 post_tasks 任务;

我的实验结果是 pre_taskspost_tasks 任务块是在整个 playbook 执行 之前和之后

执行上面两种特殊task任务的时候,顺序无要求。

ansible playbook play task执行顺序_ansible task 内部执行顺序-CSDN博客

角色的内容就是这个例子的: 编写角色 并执行

# pre_tasks和post_tasks 举例
[root@rhel ~]# cat role_test 
- name: 剧本1
  gather_facts: false
  hosts: 10.0.0.23
  # 执行2个角色,第二个是复制第一个的,功能一样。
  roles:
    - hua
    - hua-2
  tasks:
    - name: 常规任务1
      debug:
        msg: "常规任务1"
    - name: 常规任务2
      debug:
        msg: "常规任务2"
  
  pre_tasks:
    - name: pre_tasks 任务
      debug:
        msg: "pre_tasks 会在 执行 playbook 之前 执行 pre_tasks 任务"
  post_tasks:
    - name: post_tasks 任务
      debug:
        msg: "post_tasks 会在 执行 playbook 之后 执行 post_tasks 任务"

[root@rhel ~]# 
[root@rhel ~]# 
[root@rhel ~]# ansible --version
ansible [core 2.14.14]


# 执行过程
[root@rhel ~]# ansible-playbook role_test

PLAY [剧本1] ************************************************************************

TASK [pre_tasks 任务] ***************************************************************
ok: [10.0.0.23] => {
    "msg": "pre_tasks 会在 执行 playbook 之前 执行 pre_tasks 任务"
}

TASK [hua : 任务1:获取命令的输出值] ************************************************
ok: [10.0.0.23]

TASK [hua : 任务2:打印 获取命令的输出值] *******************************************
ok: [10.0.0.23] => {
    "msg": "命令的输出:2024-09-10--16:32:28"
}

TASK [hua : 任务3:打印 vars目录 里定义的变量 var_1] ********************************
ok: [10.0.0.23] => {
    "msg": "打印 vars目录 里定义的变量 var_1:我是变量var_1"
}

TASK [hua : 任务4:执行引入的外部任务(include_tasks)] *******************************
included: /etc/ansible/roles/hua/tasks/task_1.yml for 10.0.0.23

TASK [hua : 外部任务1] **************************************************************
ok: [10.0.0.23] => {
    "msg": "外部任务1"
}

TASK [hua-2 : 任务1:获取命令的输出值] **********************************************
ok: [10.0.0.23]

TASK [hua-2 : 任务2:打印 获取命令的输出值] *****************************************
ok: [10.0.0.23] => {
    "msg": "命令的输出:2024-09-10--16:32:28"
}

TASK [hua-2 : 任务3:打印 vars目录 里定义的变量 var_1] ******************************
ok: [10.0.0.23] => {
    "msg": "打印 vars目录 里定义的变量 var_1:我是变量var_1"
}

TASK [hua-2 : 任务4:执行引入的外部任务(include_tasks)] *****************************
included: /etc/ansible/roles/hua-2/tasks/task_1.yml for 10.0.0.23

TASK [hua-2 : 外部任务1] ************************************************************
ok: [10.0.0.23] => {
    "msg": "外部任务1"
}

TASK [常规任务1] ********************************************************************
ok: [10.0.0.23] => {
    "msg": "常规任务1"
}

TASK [常规任务2] ********************************************************************
ok: [10.0.0.23] => {
    "msg": "常规任务2"
}

TASK [post_tasks 任务] **************************************************************
ok: [10.0.0.23] => {
    "msg": "post_tasks 会在 执行 playbook 之后 执行 post_tasks 任务"
}

PLAY RECAP **************************************************************************
10.0.0.23                  : ok=14   changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@rhel ~]# 


9.2.3 举例-集合管理

安装集合

在 Ansible Galaxy - 集合 这个网址里搜要用的 集合、角色等,然后点进去,就会有提示怎么安装

如果要修改 集合的默认安装位置,进/etc/ansible/ansible.cfg 修改collections_path这、行

[root@rhel ansible]# vim ansible.cfg 

  57 ;collections_path={{ ANSIBLE_HOME ~ "/collections:/usr/share/ansible/collections" }}
 安装集合:
ansible-galaxy collection install (集合的URL 或者 集合的tar包) -p 执行路径
# 如果在线安装失败,也可以把包下载下来,进行离线安装,离线安装 直接接上压缩包名就行
[root@rhel ~]# ansible-galaxy collection install community.general.tar.fz
# 过程,在线安装 community.general
[root@rhel ~]# ansible-galaxy collection install community.general
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading https://galaxy.ansible.com/api/v3/plugin/ansible/content/published/collections/artifacts/community-general-9.3.0.tar.gz to /root/.ansible/tmp/ansible-local-372411hvy0jnl/tmpx3s0hrv1/community-general-9.3.0-dvpjtoc3
Installing 'community.general:9.3.0' to '/root/.ansible/collections/ansible_collections/community/general'
community.general:9.3.0 was installed successfully
 翻译后2句
正在将 'community.general:9.3.0' 安装到 '/root/.ansible/collections/ansible_collections/community/general'
community.general:9.3.0已成功安装
[root@rhel ~]# 
[root@rhel ~]# ansible-doc -l | grep general | wc -l
595
[root@rhel ~]# 
[root@rhel ~]# cd /root/.ansible/collections/ansible_collections/community/general
[root@rhel general]# ls
CHANGELOG.md           changelogs        docs           meta
CHANGELOG.md.license   commit-rights.md  FILES.json     plugins
CHANGELOG.rst          CONTRIBUTING.md   LICENSES       README.md
CHANGELOG.rst.license  COPYING           MANIFEST.json  tests
[root@rhel general]# 

ansiblevault__3554">10 ansible-vault 加解密文件

10.1 选项

ansible-vault create 文件名			# 创建 加密 文件【会提示输加密的密码,然后进去修改文件】

ansible-vault decrypt 文件名		    # 解密 加密文件
ansible-vault encrypt 文件名  		# 加密 已存在的文件

ansible-vault rekey 文件名 			# 修改 加密文件密码

ansible-vault view 文件名			# 查看 加密 文件【输完会显示文件内容】


# 指定密码文件【如果密码存在文件里,通过这种方式指定】
--vault-password-file=passwd.txt

10.2 例子

执行一个 加密的剧本
# 执行一个加密的剧本:

# 1. 在命令行直接输入密码 --ask-vault-pass
ansible-playbook debug.yml --ask-vault-pass

# 2. 通过 密码文件执行playbook
ansible-playbook debug.yml --vault-password-file=passwd.txt
创建 加密文件
# 创建 加密 文件【会提示输加密的密码,然后进去修改文件】
[root@rhel ~]# ansible-vault create aaaa
New Vault password: 
Confirm New Vault password: 
[root@rhel ~]# 

加密 已存在的文件
# 1. 在命令行直接输入密码
[root@hn ~]# ansible-vault encrypt 1.txt 
New Vault password: 1
Confirm New Vault password: 1
Encryption successful
[root@hn ~]# 
[root@hn ~]# 
[root@hn ~]# cat 1.txt 
$ANSIBLE_VAULT;1.1;AES256
38316337613732356165363239323833393362373865363834626661303464373838383961386434
3737383339653432373435333961。。。。。。。。


解密 已经加密的文件
[root@hn ~]# ansible-vault decrypt 1.txt 
Vault password: 
Decryption successful
[root@hn ~]# 

修改 加密文件密码
# 修改 加密文件密码
[root@rhel ~]# ansible-vault rekey aaaa
Vault password: 
New Vault password: 
Confirm New Vault password: 
Rekey successful
[root@rhel ~]# 
查看 加密文件 内容
# 查看 加密 文件【输完密码会显示文件内容】
[root@rhel ~]# ansible-vault view aaaa
Vault password: 
123456
[root@rhel ~]# 

ansiblenavigator__3646">11 ansible-navigator 导航器

11.1 说明

第 1 章 Ansible 内容导航器简介 | Red Hat Product Documentation
第 6 章 使用自动化内容导航器运行 Ansible playbook | Red Hat Product Documentation

Ansible Automation Platform - 用 Ansible Navigator 和 Execution Environment 镜像开发测试 Playbook_ansible-navigator-CSDN博客

新版 Ansible Automation Platform 为 Ansible Playbook 的开发人员提供了 ansible-navigator 命令,它可以替代以前 ansibleansible-vault、ansible-config、ansible-inventory 等多个命令。原有命令和 ansible-navigator 命令对应关系如下:

ansible 命令ansible-navigator 对应命令
ansibleansible-navigator exec – ansible
ansible-builderansible-navigator builder
ansible-configansible-navigator config
ansible-docansible-navigator doc
ansible-inventoryansible-navigator inventory
ansible-galaxyansible-navigator exec – ansible-galaxy
ansible-lintansible-navigator lint
ansible-playbookansible-navigator run
ansible-testansible-navigator exec – ansible-test
ansible-vaultansible-navigator exec – ansible-vault

11.2 选项

# 常用选项
--eei		# 指定执行的 ee镜像名称(查看的时候会显示默认值)
--eev		# 指定 要在执行环境中 绑定装载的卷

-i			# 指定 主机清单
-m			# 指定 用户界面模式(stdout 命令行输出 |interactive 交互式,就进入到图形化界面)(默认:交互式)

# 常用子命令 Subcommands【首次执行 ansible-navigator 命令 会自动下载 容器运行环境(ee镜像)】

# 使用导航器运行ansible的playbook,无法执行ad-hoc指令

# ---执行剧本---
# 每次执行剧本时,是在容器中执行,同时会在当前目录 生成日志文件;
ansible-navigator run 剧本.yml       	# 执行剧本



# ---查看---
# 查看所有的主机
ansible-navigator inventory --list -m stdout -i /etc/ansible/hosts
# 可视化模式列出所有的主机
ansible-navigator inventory --list -i /etc/ansible/hosts


ansible-navigator images				# 查看 ee镜像(Ansible Execution Environment 可靠的执行环境)
ansible-navigator --version				# 显示 ansible-navigator 的版本信息

ansible-navigator config dump       	# 显示当前的配置信息
ansible-navigator config set       		# 设置配置选项
ansible-navigator config view      		# 查看配置文件



# ---帮助---
ansible-navigator --help                # 显示帮助信息
ansible-navigator run --help			# 显示 run 命令的帮助信息


# ---查看模块文档 【没 ansible-doc 那么方便的 搜索】---
ansible-navigator doc -l			    # 查看导航器的模块
ansible-navigator doc 模块名			    # 查看 Ansible模块 的文档
# 举例:查看 vmware.vmware_rest.vcenter_vm_hardware_cpu_info模块 的帮助信息
ansible-navigator doc vmware.vmware_rest.vcenter_vm_hardware_cpu_info
[root@controller ~]# cat .ansible-navigator.yml
ansible-navigator:
 execution-environment:
  image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8
  pull:
   policy: missing

11.3 举例

ansiblenavigator_3752">安装ansible-navigator
  1. 直接用 pip 指定个国内源,安装 ansible-navigator
  2. 下载 EE 镜像(Ansible Execution Environment 可靠的执行环境)
    1. 因为 ansible-navigator 是使用容器运行 Playbook,首次运行 ansible-navigator 命令会自动下载 */ansible/creator-ee 镜像环境。
    2. 下载问题:
      1. 这里看他下载的是哪里的镜像,我这个 ghcr.io/ansible/creator-ee:v0.22.0 下的巨慢
      2. 后面看别人下载的 quay.io/ansible/ansible-navigator-demo-ee 镜像,用 podman pull 下载下来
        1. podman pull quay.io/ansible/ansible-navigator-demo-ee
      3. 如果我们下载的镜像跟它要装的不一样,要么去改配置文件。我选择把这个 容器镜像 改成它要的名字
        1. podman tag quay.io/ansible/ansible-navigator-demo-ee:latest ghcr.io/ansible/creator-ee:v0.22.0
  3. 下载ee镜像后就可以 ansible-navigator 使用了
pip install ansible-navigator -i https://mirrors.aliyun.com/pypi/simple/
ansible-navigator
[root@rhel ~]# pip install ansible-navigator -i https://mirrors.aliyun.com/pypi/simple/
....

[root@rhel ~]# 
[root@rhel ~]# ansible-navigator --version
ansible-navigator 24.2.0
[root@rhel ~]# 

### images--探索执行环境映像。 
### 如果 从红帽仓库中 拉取 ee镜像【要登录、要权限,有开发者订阅可以直接拉取】
### 这里拉取的镜像是 ghcr.io/ansible/creator-ee:v0.22.0
[root@rhel ~]# ansible-navigator imagees
---------------------------------------------------------------------
Execution environment image and pull policy overview
---------------------------------------------------------------------
Execution environment image name:     ghcr.io/ansible/creator-ee:v0.22.0
Execution environment image tag:      v0.22.0
Execution environment pull arguments: None
Execution environment pull policy:    tag
Execution environment pull needed:    True
---------------------------------------------------------------------
Updating the execution environment
---------------------------------------------------------------------
Running the command: podman pull ghcr.io/ansible/creator-ee:v0.22.0
Trying to pull ghcr.io/ansible/creator-ee:v0.22.0...
Getting image source signatures

### 安装后(下好了ee镜像)
[root@rhel ~]# ansible-navigator images

  Image                               Tag	Execution environment         Created          Size
0ansible-navigator-demo-ee           latest    True                          3 years ago      1.35 GB




^b/PgUp page up      ^f/PgDn page down      ↑↓ scroll	   esc back	 [0-9] goto	 :help help

执行剧本
# 每次执行剧本时,是在容器中执行,同时会在当前目录 生成日志文件;
ansible-navigator run 剧本.yml       	# 执行剧本

-i			# 指定 主机清单
-m			# 指定 用户界面模式(stdout 命令行输出 |interactive 交互式,就进入到图形化界面)(默认:交互式)
[root@rhel ~]# ansible-navigator run mok.yml -m stdout
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that
the implicit localhost does not match 'all'
[WARNING]: Could not match supplied host pattern, ignoring: 10.0.0.23

PLAY [剧本1] *******************************************************************
skipping: no hosts matched

PLAY RECAP *********************************************************************
[root@rhel ~]# 
# 执行剧本。指定 用户界面模式 stdout 命令行输出
[root@rhel ~]# ansible-navigator run mok.yml -i /etc/ansible/hosts -m stdout

PLAY [剧本1] *******************************************************************

TASK [任务1] *******************************************************************
changed: [10.0.0.23]

TASK [任务2] *******************************************************************
ok: [10.0.0.23] => {
    "msg": "输出结果: {'cmd': 'ls ./', 'stdout': '公共\\n模板\\n视频\\n图片\\n文档\\n下载\\n音乐\\n桌面\\nanaconda-ks.cfg\\ninitial-setup-ks.cfg\\ninstall_zabbix_server6.0.sh\\nrhel9.txt', 'stderr': '', 'rc': 0, 'start': '2024-09-07 18:40:47.722297', 'end': '2024-09-07 18:40:47.728229', 'delta': '0:00:00.005932', 'changed': True, 'stdout_lines': ['公共', '模板', '视频', '图片', '文档', '下载', '音乐', '桌面', 'anaconda-ks.cfg', 'initial-setup-ks.cfg', 'install_zabbix_server6.0.sh', 'rhel9.txt'], 'stderr_lines': [], 'ansible_facts': {'discovered_interpreter_python': '/usr/libexec/platform-python'}, 'failed': False}"
}

PLAY RECAP *********************************************************************
10.0.0.23                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
[root@rhel ~]# 
# 执行剧本。默认指定 用户界面模式 interactive 交互式,就进入到图形化界面
[root@rhel ~]# ansible-navigator run mok.yml -i /etc/ansible/hosts 

  剧本名称  确定 更改    无法访问     失败   跳过     忽略   进行中       任务计数     进度
  Play name Ok Changed Unreachable Failed Skipped Ignored In progress Task count   Progress
0│剧本1      2       1           0	0	   0	    0           0       2          Complete



^b/PgUp page up  ^f/PgDn page down  ↑↓ scroll  esc back  [0-9] goto  :help help Successful 




# --- 按对应的数字编号。按0,进入查看详细信息
  Result     Host           Number     Changed     Task    Task action       Duration
0│Ok         10.0.0.23           0     True        任务1   shell                   1s
1│Ok         10.0.0.23           1     False       任务2   debug                   0s


# --- 按对应的数字编号,还能进入查看详细信息
Play name: 剧本1:1
Task name: 任务2
Ok: 10.0.0.23 输出结果: {'cmd': 'ls ./', 'stdout': '公共\n模板\n视频\n图片\n文档\n下载\n音乐'
 0│---                                                                                     ▒
 1│duration: 0.015632│end: '2024-09-07T10:43:34.252087+00:00'3│event_loop: null                                                                        ▒
 4│host: 10.0.0.23   
 5│play: 剧本                                                                               
 6│play_pattern: 10.0.0.23                                                                  
 7│playbook: /root/mok.yml                                                                  
 8│remote_addr: 10.0.0.23                                                                   
 9│res:                                                                                    ▒
10│  _ansible_no_log: false11│  _ansible_verbose_always: true12│  changed: false13│  msg: '输出结果: {''cmd'': ''ls ./'', ''stdout'': ''公共\n模板\n视频\n图片\n文档\n下载\14''stderr'': '''', ''rc'': 0, ''start'': ''2024-09-07 18:43:34.147539'', ''end'':15''2024-09-07 18:43:34.153046'', ''delta'': ''0:00:00.005507'', ''changed'': True,   ▒
16''stdout_lines'': [''公共'', ''模板'', ''视频'', ''图片'', ''文档'', ''下载'', ''音乐17│    ''anaconda-ks.cfg'', ''initial-setup-ks.cfg'', ''install_zabbix_server6.0.sh'',     ▒
18''rhel9.txt''], ''stderr_lines'': [], ''ansible_facts'': {''discovered_interpreter_p▒
19''/usr/libexec/platform-python''}, ''failed'': False}'                              ▒
20│resolved_action: debug                                                                  ▒
21│start: '2024-09-07T10:43:34.236457+00:00'                                               ▒
22│task: 任务                                                                              ▒
23│task_action: debug                                                                      ▒
24│task_args: ''                                                                           ▒
25│task_path: /root/mok.yml:9

指定默认使用的 EE 镜像

如果要自定义默认的ee镜像,要在当前用户家目录 中创建内容如下的 .ansible-navigator.yml文件

cat > ~/.ansible-navigator.yml << EOF
ansible-navigator:
  execution-environment:
    image: registry.redhat.io/ansible-automation-platform-22/ee-supported-rhel8:latest
EOF
查看ee镜像信息

查看 ee镜像(Ansible Execution Environment 可靠的执行环境)

ansible-navigator images
[root@rhel ~]# ansible-navigator images

  Image                          Tag     Execution environment     Created	 Size
0ansible-navigator-demo-ee 	 latest  True                      3 years ago   1.35 GB



^b/PgUp page up   ^f/PgDn page down   ↑↓ scroll   esc back   [0-9] goto   :help help




--- 输入对应数字编号,查看镜像的详细信息
  Image: ansible-navigator-demo-ee:latest Description
0│Image information                       Information collected from image inspection # 镜像信息
1│General information                     OS and python version information # 操作系统和python版本信息
2│Ansible version and collections         Information about ansible and ansible collections # 关于ansibleansible集合的信息
3│Python packages                         Information about python and python packages # 关于python和python包的信息
4│Operating system packages               Information about operating system packages # 关于操作系统软件包的信息
5│Everything                              All image information # 所有镜像信息


查看主机清单的主机
# 可视化模式列出所有的主机
# 用数字选择
[root@rhel ~]# ansible-navigator inventory --list -i /etc/ansible/hosts

  Title             Description
0│Browse groups     Explore each inventory group and group members members  # 浏览每个库存 组和组成员
1│Browse hosts      Explore the inventory with a list of all hosts  # 浏览包含 所有主机列表 的资源清册




^b/PgUp page up   ^f/PgDn page down   ↑↓ scroll   esc back   [0-9] goto   :help help



--- # 浏览每个库存 组和组成员

  Name                                  Taxonomy                       Type
0│host_group                            all                            group
1│ungrouped                             all                            group


--- # 浏览包含 所有主机列表 的资源清册

  Inventory hostname
0│10.0.0.23
1│host1


# 查看所有的主机
[root@rhel ~]# ansible-navigator inventory --list -m stdout -i /etc/ansible/hosts
{
    "_meta": {
        "hostvars": {
            "10.0.0.23": {
                "rhel_name": "rhel9"
            },
            "host1": {
                "ansible_host": "10.0.0.23",
                "group_name": "itgroup"
            }
        }
    },
    "all": {
        "children": [
            "host_group",
            "ungrouped"
        ]
    },
    "host_group": {
        "hosts": [
            "host1"
        ]
    },
    "ungrouped": {
        "hosts": [
            "10.0.0.23"
        ]
    }
}
[root@rhel ~]# 

http://www.niftyadmin.cn/n/5865206.html

相关文章

SEO长尾优化实战技巧

内容概要 在搜索引擎优化&#xff08;SEO&#xff09;实践中&#xff0c;长尾关键词的精细化运营已成为提升网站精准流量与搜索排名稳定性的关键策略。相较于竞争激烈的大词&#xff0c;长尾关键词凭借其搜索意图明确、转化率高且流量波动小的特性&#xff0c;能够有效覆盖用户…

(论文)使ConvNeXt模型适应语音数据集上的音频分类

《Adapting a ConvNeXt Model to Audio Classification on AudioSet》 摘要 在计算机视觉中&#xff0c;ConvNeXt 等卷积神经网络 &#xff08;CNN&#xff09; 已经能够超越最先进的转换器&#xff0c;部分归功于深度可分离卷积 &#xff08;DSC&#xff09;。DSC 作为常规卷…

点击修改按钮图片显示有问题

问题可能出在表单数据的初始化上。在 ave-form.vue 中&#xff0c;我们需要处理一下从后端返回的图片数据&#xff0c;因为它们可能是 JSON 字符串格式。 vue:src/views/tools/fake-strategy/components/ave-form.vue// ... existing code ...Watch(value)watchValue(v: any) …

Java 大视界 -- 总结与展望:Java 大数据领域的新征程与无限可能(96)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

echart实现自写下载图片按钮

需求&#xff1a;点击自己写的下载按钮&#xff0c;下载echart提供的图片代码 <el-button style"margin-left:10px;" type"primary" plain click"saveImgae" >导出</el-button><div class"z-echartbox" style"he…

Flutter - StatefulWidget (有状态的 Widget) 和 生命周期

StatefulWidget /*** 需求&#xff1a;* 两个按钮&#xff0c;一个计数器* 这里要用到 StatefulWidget,因为 StatelessWidget 通常用来展示固定不变的数据*/ main() > runApp(MyApp());class MyApp extends StatelessWidget {overrideWidget build(BuildContext context) {…

Django ORM 的常用字段类型、外键关联的跨表引用技巧,以及 `_` 和 `__` 的使用场景

一、Django ORM 常用字段类型 1. 基础字段类型 字段类型说明示例CharField字符串字段&#xff0c;必须指定 max_lengthname models.CharField(max_length50)IntegerField整数字段age models.IntegerField()BooleanField布尔值字段is_active models.BooleanField()DateFiel…

【入门音视频】音视频基础知识

&#x1f308;前言&#x1f308; 这个系列在我学习过程中&#xff0c;对音视频知识归纳总结的笔记。因为音视频相关讲解非常稀少&#xff0c;所以我希望通过这个音视频系列&#xff0c;跟大家一起学习音视频&#xff0c;希望减少初学者在学习上的压力。同时希望也欢迎指出文章的…