# 自动化运维Day03:Ansible模块进阶(setup,debug),四种常用变量,进阶语法;Ansible Roles(角色)

一、实验环境

沿用前两天的环境

使用 template 模板机 克隆下方 4 台虚拟机,配置好 IP 地址后,通过 WindTerm 远程连接虚拟机,作为 Ansible 控制节点与被管理节点。


二、Ansible 模块进阶

2.1 setup 模块

  • setup 模块 用于采集被管理主机的系统事实信息,这些信息被称为 Facts
  • ansible_facts 变量用于存储采集到的系统信息,供后续任务调用。
  • 每次执行 Playbook 时,默认第一个任务就是 Gathering Facts (收集事实信息)。
  • 使用 setup 模块可以手动查看或过滤这些 Facts 信息

常用命令示例

bash 复制代码
# 查看所有 Facts 信息
ansible test -m setup
192.168.8.11 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.1.11"
... 省略部分内容...
  • 找出下列facts信息(有父子关系时使用.分隔)
    • ansible_bios_version(版本号)
    • ansible_memtotal_mb(内存)
    • ansible_hostname(主机名)
bash 复制代码
# 过滤指定 Facts(使用 filter 参数)
ansible web1 -m setup -a "filter=ansible_bios_version" #过滤版本号
ansible web1 -m setup -a "filter=ansible_memtotal_mb"  #过滤内存
ansible web1 -m setup -a "filter=ansible_hostname"     #过滤主机名

常用 Facts 变量 (有父子关系时使用 . 分隔调用):

  • ansible_bios_version
  • ansible_memtotal_mb
  • ansible_hostname
  • ansible_all_ipv4_addresses
  • ansible_ens160.ipv4.address

2.2 debug 模块

debug 模块 可以显示变量的值。debug取的值源自于与setup模块获取的Facts。主要用于辅助排错和验证变量内容。

常用参数

  • msg:要输出的信息(引用变量时需要使用 {``{ }}

示例 Playbook

yaml 复制代码
---
- hosts: web1
  tasks:
    - debug:
        msg: "主机名是:{{ ansible_hostname }}"

    - debug:
        msg: "总内存大小:{{ ansible_memtotal_mb }}"

补充:var模块也可以直接调用变量且不需要加双花括号,但是不能和字符串一起使用,只能单独调用字符串


三、Ansible 变量

Ansible 支持22种定义变量的方式,本节介绍其中最常用的四种,并按优先级从高到低排序:

  • 变量文件(vars_files:) :通过 vars_files: 引入外部YAML文件加载的变量
  • Playbook 变量(vars:) :在 Play 中使用 vars: 定义, Play 级别的变量,仅在单个play生效
  • Host Facts 变量(系统常数) :由 setup 模块收集的系统信息
  • Inventory 变量(主机变量和组变量):静态定义在主机清单(Inventory)文件,或者 host_vars/、group_vars/ 目录中的变量。

补充:官方文档的详细优先级顺序(了解):

3.1 Inventory 变量

Inventory 变量是在主机清单配置文件(hosts)中为特定主机(组)定义的变量。

hosts 文件示例

ini 复制代码
[webserver]
web[1:2]

[db]
db1 myvar1="hello the world"    myvar2="content"       #为db1生成两个变量
#写在哪个主机后边,这个变量就属于谁

[cluster:children]                       #children代表下面是组成员
webserver
db

[webserver:vars]                        # 为webserver组定义变量,改组成员均可调用。vars 是固定关键字,代表下面的是变量
yourname="tina"

编写调用 Inventory 变量的 Playbook,执行playbook验证:

yaml 复制代码
---
- hosts: db1
  tasks:
    - name: use inventory vars
      shell: echo {{ myvar1 }} > /tmp/{{ myvar2 }}  # 这里 {{}} 不在开头,所以不需要加双引号

- hosts: webserver
  tasks:
    - name: use inventory vars yourname
      user:
        name: "{{ yourname }}"          # {{}} 开头时需要加双引号

3.2 Host Facts 变量

Host Facts 变量可以直接调用 setup 模块收集到的系统信息。

示例 Playbook

yaml 复制代码
---
- hosts: web1
  tasks:
    - name: Host Facts 变量应用
      copy:
        content: "{{ ansible_hostname }}:{{ ansible_bios_version }}"
        dest: /tmp/facts.txt

3.3 Playbook 变量

在 Playbook 中使用 vars 关键字定义变量。

注意:仅在当前 play 内有效

示例 Playbook

yaml 复制代码
---
- hosts: web1
  vars:
    iname: heal
    ipass: '123456'                     # 密码必须是字符串,需要加引号
  tasks:
    - name: Use variables create user.
      user:
        name: "{{ iname }}"
        password: "{{ ipass | password_hash('sha512') }}"

3.4 变量文件

将变量单独定义在一个 YAML 文件中,在 Playbook 中通过 vars_files 调用。

变量文件 variables.yml

注意格式为键值对

yaml 复制代码
---
iname: cloud
ipass: '123456'

调用变量文件的 Playbook

yaml 复制代码
---
- hosts: web1
  vars_files: variables.yml  #调用YAML变量文件
  tasks:
    - name: create user.
      user:
        name: "{{ iname }}"                                  # iname 来自变量文件
        password: "{{ ipass | password_hash('sha512') }}"    # ipass 来自变量文件

四、Ansible 进阶

4.1 template 模块

  • template 模块作用类似于copy,都可以拷贝文件。

  • copy 模块只能复制静态文件

  • template 模块主要用于生成动态文件,并且可以调用文件中的变量

    如果希望每个主机拷贝的文件内容不一样(例如包含各自的 IP 或主机名),就需要使用 template 模块。

  • template 模块结合 Jinja2 模板引擎 ,可以实现动态内容生成。

    • Jinja2 表达式语法: {``{ 变量 }}
    • 之前在 Playbook 中调用变量,实际上也是 Jinja2 的功能。
    • template 模块的主要作用是生成动态配置文件

示例 1:为 webserver 组中的主机生成不同内容的首页

模板文件 index.html

html 复制代码
Welcome to {{ ansible_hostname }} on {{ ansible_ens160.ipv4.address }}.
# 模板文件中调用变量不需要双引号

Playbook:

yaml 复制代码
---
- hosts: webserver
  tasks:
    - name: use template copy index.html to webserver.
      template:
        src: ~/ansible/template/index.html
        dest: /usr/local/nginx/html/index.html

示例 2:使用自定义变量

  • template 使用 .j2 后缀模板文件
    • 例如:nginx.conf.j2
    • 里面可以写 {{ 变量名 }}

模板文件 source.j2

jinja2 复制代码
{{ welcome }} {{ iname }} ...

Playbook:

yaml 复制代码
---
- hosts: webserver
  vars:
    welcome: 'hello'      #为变量赋值  
    iname: 'jack'         #为变量赋值  
  tasks:
    - name: 使用template模块将文件复制到远程主机
      template:
        src: ~/ansible/template/source.j2    #调用文件,调用文件中的变量
        dest: /tmp/

4.2 Ansible 高级语法(重要)

4.2.1 error 处理机制

默认情况下,Ansible 遇到错误会立即停止 Playbook 的执行。

忽略单个任务的错误

yaml 复制代码
---
- hosts: web1
  tasks:
    - name: start a service that does not exist.
      service:
        name: hehe                              
        state: started
      ignore_errors: true                        # 与模块对齐,则仅针对当前模块任务,忽略错误

    - name: touch a file.
      file:
        path: /tmp/service.txt
        state: touch

忽略整个play的错误

yaml 复制代码
---
- hosts: web1
  ignore_errors: true                            # 当前 Play 内忽略错误
  tasks:
    - name: start a service that does not exist.
      service:
        name: hehe
        state: started

    - name: touch a file.
      file:
        path: /tmp/service.txt
        state: touch

4.2.2 handlers

handlers是一种特殊的任务,只在被其他任务通过 notify 通知后(任务处于changed 状态时),才在 Playbook 结束时执行一次,通常用于触发服务的重启或重载等收尾操作。

注意:即使被多次 notify,也只执行一次。

handlers 的特点

  • 仅当任务触发(notify)handlers 时才执行(不触发不执行)。
  • 多个任务 notify 同一个 handler 时,只会执行一次。
  • 仅当任务的执行状态为 changed 时,handlers 才会执行
  • handlers 任务在当前play内的所有其他 tasks 执行完毕后才执行

注意:一个play只能有一个handlers(但一个剧本可以有多个play)

示例

yaml 复制代码
---
- hosts: web1
  tasks:
    - name: 创建一个目录          #多次执行该任务,playbook状态将不再是changed
      file:
        path: /tmp/parents/subdir/
        state: directory
      notify: touch file          # notify 后面的名称必须与 handlers 中的任务名称一致
      #notify和模块处于同一层级
  
  handlers:       #handlers和tasks属于同一层级
    - name: touch file
      file:
        path: /tmp/parents/subdir/new.txt
        state: touch

注意:notify 后面的名称必须与 handlers 中的任务名称一致

细节补充:即使使用了ignore_errors: true,也不会影响handlers的触发条件,只有changed状态可以触发

4.2.3 when 条件判断

使用 when 可以为任务添加执行条件,只有条件为真时才执行该任务。

常用比较操作符==!=>>=<<=

多个条件可以使用 andor 连接。

注意when 表达式中引用变量时不需要 使用 {``{ }}

示例 1:内存不足 700MB 时停止 NetworkManager 服务

yaml 复制代码
---
- hosts: web1
  tasks:
    - name: 检查内存,内存不足 700MB 时停止 NetworkManager 服务
      service:
        name: NetworkManager
        state: stopped
      when: ansible_memfree_mb < 700

示例 2:判断操作系统为 Rocky 8 时创建文件(多行条件)

yaml 复制代码
---
- hosts: web1
  tasks:
    - name: 判断操作系统为 Rocky 8 时创建文件
      file:
        path: /tmp/when.txt
        state: touch
      when: >
        ansible_distribution == "Rocky" #注意:字符串要加双引号
        and
        ansible_distribution_major_version == "8"

注意!虽然when写在模块尾部,但是优先进行判断

补充:when主流写法写在模块尾部

bash 复制代码
- name: 安装 Nginx
  apt:
    name: nginx
    state: present
  when: ansible_os_family == "Debian"  #  习惯写在尾部

非主流也可以写在头部,并且更符合执行逻辑:

bash 复制代码
- name: 安装 Nginx
  when: ansible_os_family == "Debian"  # 👈 其实写在头部完全合法
  apt:
    name: nginx
    state: present

4.2.4 block 任务块

使用 block 可以将多个任务组合成一个组,方便统一管理。

block 还支持 rescuealways 子句:

  • rescue:block 中任务执行失败时执行的补救任务。
  • always:无论 block 是否成功,都会执行的任务。

基础 block 示例

yaml 复制代码
---
- hosts: db1
  tasks:
    - name: 定义一组任务
      block:   
        - name: 安装 httpd
          yum:
            name: httpd
            state: present
        - name: start httpd
          service:
            name: httpd
            state: started
      when: ansible_distribution == "Rocky" #满足此条件时,执行block任务块

rescue + always 示例

yaml 复制代码
---
- hosts: web1
  tasks:
    - block:
        - name: 任务块创建 test1.txt
          file:
            path: /tmp/test1.txt
            state: touch
      rescue:
        - name: 任务块执行失败会创建 test2.txt
          file:
            path: /tmp/test2.txt
            state: touch
      always:
        - name: 任何情况下都会创建 test3.txt
          file:
            path: /tmp/test3.txt
            state: touch

直接运行剧本,会有test1.txt和test3.txt

将block改成: path: /tmp/abc/test1.txt,会有test2.txt和test3.txt

为什么当block报错整个任务不会停止?因为整个 block: + rescue: + always: 算 Ansible 的「一个任务单元」,单个任务失败不会让剧本停止,且rescue 就是专门用来捕获 block 里的失败,不让任务中断


4.2.5 loop 循环

当多个任务使用相同模块时,可以使用 loop 循环来避免重复编写。

简单列表循环

yaml 复制代码
---
- hosts: test
  tasks:
    - name: 借助loop循环依次创建school,legend,life目录
      file:
        path: /tmp/{{ item }}                # item 是循环中的固定关键字
        state: directory
      loop:                                  #loop中的所有元素会逐个丢给item
        - School
        - Legend
        - Life

复杂字典列表循环

yaml 复制代码
---
- hosts: web1
  tasks:
    - name: 使用loop循环完成用户的批量创建,并设置密码
      user:
        name: "{{ item.iname }}"  #注意,这里'tiem'属于固定格式
        password: "{{ item.ipass | password_hash('sha512') }}"
      loop:                                  # 循环成员为字典
        - { iname: 'term', ipass: '123456' }
        - { iname: 'amy', ipass: '654321' }

这里item是固定格式,必须通过 item.元素 来调用定义的字典元素


五、Ansible Roles

5.1 Roles 概述

在实际生产环境中,随着功能增多,Playbook 文件会越来越多,且可能互相调用变量文件等,管理起来非常困难。

Ansible 从 1.2 版本开始支持 Roles

Roles 是管理ansible文件的一种规范(目录结构),用于更好地组织和管理 Playbook、变量、模板、handlers 等文件。

我们需要使用命令创建角色,修改目录下的对应文件内容或者添加内容,最后在剧本中调用它。

ansible的角色(role)像是做软件开发一样。开发是做一堆配置文件,功能文件,然后再main文件中进行集成;role就像是把配置文件,资源文件放到对应目录,然后在剧本中集成/调用

5.2 Roles 规范的目录结构

目录 / 文件 作用说明
defaults/main.yml 定义变量的缺省值(即变量默认值,优先级较低)
files/ 存放静态文件(普通文件)
handlers/main.yml 定义 handlers
meta/main.yml 角色元数据(作者、版本、依赖等)
README.md 整个角色的说明文档
tasks/main.yml 定义任务的核心文件
templates/ 存放 Jinja2 动态模板文件
vars/main.yml 定义变量(优先级较高)

不同模块会自动到对应目录下查找数据。

标准的目录结构:

bash 复制代码
role_name/
├── defaults/      # 默认变量(优先级最低)
│   └── main.yml
├── vars/          # 角色变量(优先级高于 defaults)
│   └── main.yml
├── tasks/         # 主要任务列表(必须有)
│   └── main.yml
├── handlers/      # 触发器,用于触发服务重启等
│   └── main.yml
├── templates/     # Jinja2 模板文件(.j2)
│   └── *.j2
├── files/         # 静态文件,直接拷贝
│   └── *
├── meta/          # 角色元数据(依赖、作者、版本等)
│   └── main.yml
└── README.md      # 角色说明文档(可选)

5.3 Roles 应用

5.3.1 创建 Role

使用 ansible-galaxy init <rolename> (init初始化) 命令可以创建、管理自己的roles

bash 复制代码
mkdir ~/ansible/roles

ansible-galaxy init ~/ansible/roles/issue
#创建一个Role,该Role的目的是使用模板修改远程主机的/etc/issue文件

tree ~/ansible/roles/issue/
#查看目录结构,如果没有tree命令则需要使用dnf安装该软件

会发现生成了这样结构的目录:

5.3.2 修改 Role
  • 定义issue文件的模板文件

模板文件 templates/issue.j2

bash 复制代码
[root@control ansible]# vim   ~/ansible/roles/issue/templates/issue.j2

This is the system {{ ansible_hostname }}
Today's date is: {{ ansible_date_time.date }}
Contact to {{ admin }}

定义变量文件 vars/main.yml

yaml 复制代码
[root@control ansible]# vim ~/ansible/roles/issue/vars/main.yml
#定义变量admin
---
admin: yoyo@test.com

编写任务文件 tasks/main.yml(注意!Role 中的任务文件不需要写 tasks: 关键词):

Role的各个文件之间 相互调用不需要写路径,直接写文件名即可

yaml 复制代码
[root@control ansible]#vim ~/ansible/roles/issue/tasks/main.yml

---
- name: delever issue file
  template:
    src: issue.j2
    dest: /etc/issue
5.3.3 在 Playbook 中调用 Role

在Playbook中调用角色有两种方法,我们使用第二种方法。

方法一 :在role同级目录创建 Playbook 直接调用。

方法二 :在 ansible.cfg 中指定 roles_path=路径

bash 复制代码
[root@control ansible]# vim  ~/ansible/ansible.cfg 
[defaults]
inventory = ~/ansible/hosts
remote_user = alice
roles_path = ~/ansible/roles        #指定Roles读取位置
[privilege_escalation]
become=True
become_method=sudo
become_user=root
become_ask_pass=False

编写playbook文件,通过roles关键词调用role(调用角色使用角色名即可)

bash 复制代码
[root@control ansible]# cat  ~/ansible/issue.yml
---
- hosts: web2
  roles:                 #调用Role
    - issue
#   - role2              #支持加载多个Role
[root@control ansible]# ansible-playbook  issue.yml

六、总结

本节重点掌握内容:

  • 特殊模块setupdebugtemplate
  • 变量管理:Inventory 变量、Host Facts 变量、Playbook 变量、变量文件
  • 高级语法 :error 处理机制(ignore_errors)、handlers、when 条件判断、block 任务块、loop 循环
  • Roles:角色作用、标准目录结构、角色创建与调用

相关推荐
赵民勇2 小时前
Linux strings命令详解
linux·运维
敲代码的瓦龙2 小时前
操作系统?Android与Linux!!!
android·linux·运维
茶乡浪子2 小时前
同子网基于IPv4网络静态VXLAN配置示例(下)
运维·网络·数据中心·vxlan·evpn·华为vxlan·华为数据中心网络
专注API从业者2 小时前
电商选品效率翻倍!基于 Open Claw + 淘宝商品 API 实现自动化监控选品(附完整可运行代码)
大数据·运维·数据结构·数据库·自动化
云计算磊哥@3 小时前
运维开发宝典025-MySQL01数据库的安装和配置
运维·数据库·运维开发
Dxy12393102163 小时前
BAT 窗口不输出日志:三种静默方案,从半隐藏到完全消失
linux·运维·服务器
kida_yuan3 小时前
不想花钱写了一个 Flask 知识库
运维·python
Tian_Hang3 小时前
Linux基础知识(一)
linux·运维·服务器
古月开发4 小时前
本地化 AI 论文查重与润色工具部署指南
人工智能·自动化