# 自动化运维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:角色作用、标准目录结构、角色创建与调用

相关推荐
乘云数字DATABUFF3 天前
5分钟部署开源APM Databuff:OpenTelemetry全链路追踪入门实战
运维·后端
悠然南风4 天前
Ansible常见模块总结及LDAP Role 编写与调试
ansible
荣--4 天前
一键部署不是为了省时间 —— 它是把"买来的 PaaS"变成"自己的平台"的拐点
运维·zabbix·工程化·一键部署·平台化·边界设计
江华森4 天前
动手实战学 Docker — 从零到集群编排完全指南
运维
Avan_菜菜5 天前
FRP 内网穿透完整实战:从 HTTP 映射到 HTTPS 自签代理
运维·nginx·https
SelectDB6 天前
Litefuse 开源并推出单进程轻量模式,25 秒就能跑起来的 Agent 可观测与评估平台
运维·后端·自动化运维
XIAOHEZIcode8 天前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户0328472220708 天前
如何搭建本地yum源(上)
运维
大树8811 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠11 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql