学习Ansible Playbook 核心语法

一、变量:让 Playbook 更灵活

变量是 Playbook 的 "数据容器",用于存储和复用信息(如主机地址、软件版本、文件路径等),避免重复编写固定值。Ansible 中的变量按作用范围可分为五大类,不同类型的变量适用场景不同,且存在明确的优先级规则。

1.1 变量的命名规则

在定义变量前,需遵守以下规则:

  • 变量名由字母、数字和下划线组成,必须以字母开头 (如web_port合法,123_port非法);
  • 不能使用 Ansible 的保留关键字(如taskplayvars等)。

1.2 五大变量类型及使用场景

(1)全局变量:临时传递的变量

全局变量通过ansibleansible-playbook命令的-e参数手动传递,适用于临时修改变量值的场景(如临时指定目标主机、软件版本)。

使用示例

  • 传递单个键值对: bash

    复制代码
    # 临时指定user变量的值为"admin",并执行debug模块
    ansible all -i localhost, -m debug -a "msg='当前用户:{{ user }}'" -e "user=admin"
  • 传递 JSON/YAML 文件:先创建vars.json文件:

    json

    复制代码
    {"name": "nginx", "version": "1.24.0"}

    再通过-e @文件路径传递:

    bash

    复制代码
    ansible-playbook -i hosts deploy.yml -e @vars.json
(2)剧本变量:定义在 Playbook 中的变量

剧本变量直接在 Playbook 内定义,作用范围仅限当前 Playbook,适用于固定在任务中的变量(如默认安装路径、服务名)。定义方式有两种:

  • 通过vars属性直接定义

    yaml

    复制代码
    ---
    - name: 安装Nginx
      hosts: web_servers
      vars:
        nginx_pkg: nginx  # 软件包名
        nginx_conf: /etc/nginx/nginx.conf  # 配置文件路径
      tasks:
        - name: 安装Nginx包
          yum:
            name: "{{ nginx_pkg }}"
            state: present
  • 通过vars_files引入外部文件 :当变量较多时,可将变量抽离到单独的 YAML 文件(如vars/nginx.yml):

    yaml

    复制代码
    # vars/nginx.yml
    nginx_pkg: nginx
    nginx_conf: /etc/nginx/nginx.conf

    再在 Playbook 中引入:

    yaml

    复制代码
    ---
    - name: 安装Nginx
      hosts: web_servers
      vars_files:
        - vars/nginx.yml  # 引入外部变量文件
      tasks:
        - name: 安装Nginx包
          yum:
            name: "{{ nginx_pkg }}"
            state: present
(3)资产变量:针对主机 / 主机组的变量

资产变量定义在 Ansible 的 inventory(资产清单)中,分为主机变量 (仅对单个主机生效)和主机组变量(对整个主机组生效),适用于不同主机的个性化配置(如不同主机的 SSH 端口、数据库密码)。

使用示例 :编辑 inventory 文件hosts

ini

复制代码
# 主机组:web_servers
[web_servers]
192.168.1.101 ssh_port=2222  # 主机变量:该主机的SSH端口为2222
192.168.1.102

# 主机组变量:对web_servers组所有主机生效
[web_servers:vars]
db_host=192.168.1.200  # 数据库地址
db_port=3306           # 数据库端口

验证变量:

bash

复制代码
# 查看192.168.1.101的ssh_port变量
ansible 192.168.1.101 -i hosts -m debug -a "var=ssh_port"
# 查看web_servers组的db_host变量
ansible web_servers -i hosts -m debug -a "var=db_host"
(4)Facts 变量:自动收集的主机信息

Facts 变量是 Ansible 通过setup模块自动收集的被管理主机信息(如操作系统版本、IP 地址、内存大小),无需手动定义,适用于基于主机状态的动态配置(如根据操作系统选择安装命令)。

使用示例

  • 手动收集 Facts 信息: bash

    复制代码
    # 收集localhost的所有Facts信息
    ansible localhost -m setup
    # 过滤内存相关信息(使用filter参数)
    ansible localhost -m setup -a "filter=*memory*"
  • 在 Playbook 中使用 Facts: yaml

    复制代码
    ---
    - name: 打印主机信息
      hosts: all
      tasks:
        - name: 输出操作系统版本和IP地址
          debug:
            msg: "操作系统:{{ ansible_os_family }},IP地址:{{ ansible_default_ipv4.address }}"

若 Playbook 中无需使用 Facts,可关闭收集以提升执行速度:

yaml

复制代码
---
- name: 无需Facts的任务
  hosts: all
  gather_facts: no  # 关闭Facts收集
  tasks:
    - name: 安装Nginx
      yum: name=nginx state=present
(5)注册变量:保存任务执行结果

注册变量通过register关键字定义,用于保存某个任务的执行结果(如命令输出、模块返回值),适用于基于前序任务结果的后续操作(如判断命令是否执行成功、根据输出内容触发后续任务)。

使用示例

yaml

复制代码
---
- name: 检查Nginx服务状态
  hosts: web_servers
  tasks:
    - name: 执行systemctl is-active命令
      command: /usr/bin/systemctl is-active nginx
      register: nginx_status  # 将命令结果保存到nginx_status变量
      ignore_errors: yes      # 忽略命令执行失败(如Nginx未启动)
    
    - name: 输出Nginx状态
      debug:
        msg: "Nginx状态:{{ nginx_status.stdout }}"  # 打印命令标准输出

1.3 变量优先级规则

当不同类型的变量重名时,优先级从高到低为:全局变量(-e参数) > 剧本变量(vars/vars_files) > 主机变量 > 主机组变量

例如:若全局变量、剧本变量、主机组变量均定义了user,最终生效的是全局变量。

二、条件语句:根据状态执行任务

条件语句用于根据变量、Facts 或前序任务结果,决定是否执行某个任务,核心关键字是when。常见场景包括:基于操作系统选择安装命令、判断服务状态决定是否重启、检查文件是否存在等。

2.1 when关键字的基本使用

when后面跟随 Python 表达式,表达式结果为true时执行任务,false时跳过任务。

示例 1:根据操作系统安装软件

yaml

复制代码
---
- name: 跨系统安装Nginx
  hosts: all
  tasks:
    # RedHat/CentOS系统使用yum安装
    - name: 安装Nginx(RedHat系列)
      yum:
        name: nginx
        state: present
      when: ansible_os_family == "RedHat"  # 条件:操作系统为RedHat系列
    
    # Debian/Ubuntu系统使用apt安装
    - name: 安装Nginx(Debian系列)
      apt:
        name: nginx
        state: present
      when: ansible_os_family == "Debian"  # 条件:操作系统为Debian系列

示例 2:根据注册变量结果执行任务

yaml

复制代码
---
- name: 检查并重启Postfix服务
  hosts: mail_servers
  tasks:
    - name: 获取Postfix状态
      command: systemctl is-active postfix
      register: postfix_status
      ignore_errors: yes
    
    # 若Postfix运行中(返回码rc=0),则重启Apache
    - name: 重启Apache
      service:
        name: httpd
        state: restarted
      when: postfix_status.rc == 0

2.2 常用运算符

when表达式中,可使用比较运算符和逻辑运算符组合条件:

类型 运算符 说明 示例
比较运算符 == 等于 ansible_machine == "x86_64"
!= 不等于 ansible_distribution != "Ubuntu"
>/</>=/<= 大小比较(数字 / 版本号) ansible_memory_mb.real.total > 2048
逻辑运算符 and 逻辑与(同时满足) a == 1 and b == 2
or 逻辑或(满足一个即可) a == 1 or b == 2
not 逻辑非(取反) not (a == 1)
() 组合条件(提升优先级) (os == "RedHat" and ver == "7") or (os == "Ubuntu" and ver == "20.04")

示例:组合条件判断

yaml

复制代码
---
- name: 版本兼容性检查
  hosts: web_servers
  tasks:
    - name: 仅在CentOS 7或Ubuntu 20.04上执行
      debug:
        msg: "当前系统符合要求"
      when:
        (ansible_distribution == "CentOS" and ansible_distribution_version == "7") or
        (ansible_distribution == "Ubuntu" and ansible_distribution_version == "20.04")

2.3 条件判断与 Tests

Ansible 支持使用 Jinja2 模板的tests功能,实现更丰富的条件判断(如判断变量是否定义、路径是否存在、字符串是否为小写)。

常用 Tests 场景:
Tests 说明 示例
defined/undefined 判断变量是否已定义 / 未定义 when: nginx_port is defined
exists 判断路径是否存在(注意:检查主控端路径) when: "/etc/nginx" is exists
file/directory 判断路径是否为文件 / 目录 when: "/etc/nginx/nginx.conf" is file
lower/upper 判断字符串是否全为小写 / 大写 when: "hello" is lower
even/odd 判断数字是否为偶数 / 奇数 when: 6 is even
version 版本号比较 when: ansible_distribution_version is version("7.5", "ge") # 大于等于 7.5

示例:使用 Tests 判断文件是否存在

yaml

复制代码
---
- name: 检查Nginx配置文件
  hosts: web_servers
  vars:
    nginx_conf_path: /etc/nginx/nginx.conf
  tasks:
    - name: 若配置文件存在,则备份
      copy:
        src: "{{ nginx_conf_path }}"
        dest: "{{ nginx_conf_path }}.bak"
        remote_src: yes
      when: nginx_conf_path is exists  # 判断文件是否存在

2.4 条件块:block/rescue/always

当多个任务需要使用相同条件时,可通过block将任务分组,避免重复编写when。此外,block还支持rescue(任务失败时执行)和always(无论成功 / 失败都执行),实现错误处理。

示例 1:批量执行条件任务

yaml

复制代码
---
- name: 配置Ubuntu 16.04的DNS
  hosts: web_servers
  tasks:
    - name: 通用DNS配置(所有系统)
      template:
        src: resolv.conf.j2
        dest: /etc/resolv.conf
    
    # 仅Ubuntu 16.04执行以下两个任务
    - block:
        - name: 配置Ubuntu 16.04的DNS基础文件
          template:
            src: resolv.conf.j2
            dest: /etc/resolvconf/resolv.conf.d/base
        
        - name: 重启resolvconf服务
          service:
            name: resolvconf
            state: restarted
      when: ansible_distribution == "Ubuntu" and ansible_distribution_major_version == "16"

示例 2:错误处理(rescue/always

yaml

复制代码
---
- name: 检查/testdir目录
  hosts: all
  tasks:
    - block:
        # 尝试列出/testdir目录(若目录不存在则失败)
        - name: 列出/testdir内容
          command: ls /testdir
      rescue:
        # 若block任务失败,执行此处(如提示目录不存在)
        - name: 目录不存在时的提示
          debug:
            msg: "/testdir目录不存在!"
      always:
        # 无论block成功/失败,都执行此处(如记录日志)
        - name: 记录任务执行时间
          debug:
            msg: "任务执行时间:{{ ansible_date_time.iso8601 }}"

三、循环语句:批量执行重复任务

循环语句用于批量执行相同操作(如批量安装软件、创建用户、复制文件),核心关键字是loop(Ansible 2.5 + 推荐),替代了旧版的with_*语法(如with_itemswith_dict)。

3.1 loop关键字的基本使用

loop接收一个列表,通过{``{ item }}引用列表中的每个元素,实现循环执行。

示例 1:批量启动服务

yaml

复制代码
---
- name: 启动多个服务
  hosts: web_servers
  tasks:
    - name: 启动httpd、postfix、sshd服务
      service:
        name: "{{ item }}"
        state: started
      loop:
        - httpd
        - postfix
        - sshd

示例 2:循环使用变量列表 先定义变量列表,再在loop中引用:

yaml

复制代码
---
- name: 批量创建用户
  hosts: web_servers
  vars:
    # 用户列表:每个元素是字典(包含用户名和所属组)
    users:
      - { name: "user1", groups: "wheel" }
      - { name: "user2", groups: "www" }
      - { name: "user3", groups: "ftp" }
  tasks:
    - name: 创建用户并指定所属组
      user:
        name: "{{ item.name }}"
        groups: "{{ item.groups }}"
        state: present
      loop: "{{ users }}"  # 循环用户列表

3.2 循环中注册变量

若需保存循环中每个任务的执行结果,可在循环任务中使用register,结果会以列表形式存储在变量中。

示例:批量检查文件是否存在

yaml

复制代码
---
- name: 检查多个文件
  hosts: localhost
  tasks:
    - name: 检查/etc/hosts、/etc/passwd、/etc/group
      stat:
        path: "{{ item }}"
      loop:
        - /etc/hosts
        - /etc/passwd
        - /etc/group
      register: file_check_result  # 保存循环结果
    
    - name: 输出文件检查结果
      debug:
        msg: "文件{{ item.item }}是否存在:{{ item.stat.exists }}"
      loop: "{{ file_check_result.results }}"  # 遍历循环结果列表

3.3 旧版循环语法(with_*

Ansible 2.5 以前使用with_*语法实现循环,目前仍兼容但不推荐。以下是常见旧版循环的使用场景:

语法 说明 示例
with_items 简单列表循环(等同于loop with_items: [1,2,3]
with_dict 循环字典(遍历键值对) with_dict: { "a":1, "b":2 }
with_fileglob 循环指定目录的文件(主控端) with_fileglob: "/root/*.pub"
with_lines 循环命令输出或文件内容(逐行) with_lines: "cat /tmp/users.txt"
with_sequence 生成整数序列(指定起始 / 结束 / 步长) with_sequence: start=1 end=5 stride=2

示例:使用with_dict循环字典

yaml

复制代码
---
- name: 遍历用户字典
  hosts: localhost
  vars:
    users:
      alice:
        age: 25
        role: developer
      bob:
        age: 30
        role: tester
  tasks:
    - name: 输出用户信息
      debug:
        msg: "用户名:{{ item.key }},年龄:{{ item.value.age }},角色:{{ item.value.role }}"
      with_dict: "{{ users }}"  # 循环字典

3.4 循环与条件结合

可在循环中使用when,实现 "筛选式循环"(仅执行满足条件的循环项)。

示例:仅安装内存大于 2GB 的主机的 Nginx

yaml

复制代码
---
- name: 按需安装Nginx
  hosts: all
  tasks:
    - name: 内存大于2GB时安装Nginx
      yum:
        name: nginx
        state: present
      # 条件:内存总量(MB)> 2048
      when: ansible_memory_mb.real.total > 2048

示例:循环中筛选元素

yaml

复制代码
---
- name: 仅打印大于5的数字
  hosts: localhost
  tasks:
    - name: 输出5以上的数字
      command: echo "{{ item }}"
      loop: [1,2,3,4,5,6,7,8]
      when: item > 5  # 仅循环项大于5时执行

四、总结

变量、条件语句和循环语句是 Ansible Playbook 的核心能力,三者结合可实现复杂的自动化场景:

  • 变量:解决数据复用问题,让 Playbook 更灵活;
  • 条件语句:解决 "按需执行" 问题,让任务更智能;
  • 循环语句:解决 "重复操作" 问题,让代码更简洁。
相关推荐
('-')2 小时前
《从根上理解MySQL是怎样运行的》第三章学习笔记
笔记·学习·mysql
LaoZhangGong1232 小时前
TCP数据包格式分析
网络·网络协议·tcp/ip·以太网
老蒋新思维2 小时前
创客匠人 2025 高峰论谈(11.22-25):AI 智能体重构创始人 IP 打造与知识变现的管理逻辑
大数据·网络·人工智能·网络协议·tcp/ip·重构·知识付费
星释3 小时前
Rust 练习册 31:啤酒歌与字符串格式化艺术
开发语言·网络·rust
基于底层的菜鸟4 小时前
网络抓包工具——tcpdump &&icmpv6抓包
网络·测试工具·tcpdump
Nan_Shu_6146 小时前
学习:ES6(2)
前端·学习·es6
tang777896 小时前
如何保护网络隐私?从理解代理IP开始
网络·tcp/ip·php
河铃旅鹿7 小时前
Android开发-java版:Framgent
android·java·笔记·学习
自动化代码美学9 小时前
【Python3.13】官网学习之控制流
开发语言·windows·python·学习