R / Richie全部文章 ↑

Linux · 5 分钟阅读

Playbook

Playbook = 用 YAML 描述"远端该做什么"。比 ad-hoc 命令更适合长期维护、版本控制、复用。本篇把 Playbook 的写法 + 流程控制 + 变量体系一次讲清。


1. 核心元素

元素 作用
hosts 目标主机 / 组
tasks 任务列表
vars 变量
templates Jinja2 模板
handlers 变更通知触发器
tags 任务标签

2. YAML 速记

  • 缩进 2 空格,不允许 Tab
  • 文件以 --- 起头
  • 字符串里含特殊字符用引号

3. 完整示例

---
- name: 部署 Apache
  hosts: web
  remote_user: root
  vars:
    http_port: 8088

  tasks:
    - name: 创建测试文件
      ansible.builtin.file:
        path: /tmp/playtest.txt
        state: touch

    - name: 创建系统用户
      ansible.builtin.user:
        name: test02
        system: true
        shell: /sbin/nologin

    - name: 安装 httpd
      ansible.builtin.yum:
        name: httpd
        state: present

    - name: 推送 httpd 配置
      ansible.builtin.template:
        src: ./httpd.conf
        dest: /etc/httpd/conf/httpd.conf
      notify:
        - restart apache

    - name: 复制测试页
      ansible.builtin.copy:
        src: /var/www/html/index.html
        dest: /var/www/html/index.html

    - name: 启动 httpd
      ansible.builtin.service:
        name: httpd
        state: started

  handlers:
    - name: restart apache
      ansible.builtin.service:
        name: httpd
        state: restarted

httpd.conf 模板(httpd.conf.j2)里直接用 {{ http_port }} 引用变量:

Listen {{ http_port }}

执行:

ansible-playbook playbook01.yml

验证:

ansible 192.168.1.31 -m shell -a 'ls /tmp/playtest.txt && id test02'
curl 192.168.1.31:8088

4. 运行选项

选项 作用
--syntax-check 只检查语法
--check / -C Dry-run
--list-hosts 列出受影响主机
--list-tags 列出所有 tag
--list-tasks 列出所有 task
--limit web1 限制主机
-f 10 并发数(默认 5)
-t deploy 只跑某 tag
-vvv 调试输出
ansible-playbook site.yml -C
ansible-playbook site.yml --limit web
ansible-playbook site.yml -t deploy
ansible-playbook site.yml -vvv

5. 元素属性

5.1 主机与用户

- hosts: webservers:dbservers
  remote_user: deploy

tasks:
  - name: df -h
    remote_user: test
    ansible.builtin.shell: df -h

  - name: install package
    become: true
    become_user: admin
    ansible.builtin.yum:
      name: httpd
      state: present

sudo: yes 在 2.8 起改名 become: true,老写法已废弃。

5.2 任务列表

tasks:
  - name: create file
    ansible.builtin.file:
      path: /tmp/test01.txt
      state: touch
  - name: create user
    ansible.builtin.user:
      name: test001
      state: present

5.3 Handlers

tasks:
  - name: 推送配置
    ansible.builtin.template:
      src: httpd.conf.j2
      dest: /etc/httpd/conf/httpd.conf
    notify: restart apache

handlers:
  - name: restart apache
    ansible.builtin.service:
      name: httpd
      state: restarted

Handler 只在被 notify 时触发,且一个 Play 内只跑一次(即使被多次通知)。


6. 变量

6.1 命令行

ansible-playbook test.yml -e "version=1.0.0"
ansible-playbook test.yml -e "@vars.yml"

6.2 Playbook 内

- hosts: web
  vars:
    http_port: 80
    max_clients: 200

6.3 变量文件

# group_vars/webservers.yml
---
db_name: myapp
db_user: admin

6.4 规则

  • 名字只能字母、数字、下划线,字母开头
  • 模板里用 {{ var }}
  • 命令行 -e 优先级最高
  • group_vars/ 比 host_vars/ 优先级低

6.5 特殊变量

ansible_hostname           # 主机名
ansible_distribution       # OS
ansible_os_family          # OS 家族
ansible_architecture       # 架构

注册变量:

- name: 检查服务
  ansible.builtin.command: systemctl status httpd
  register: svc
  changed_when: false

- name: 打印结果
  ansible.builtin.debug:
    var: svc.stdout

6.6 作用域

级别 文件 作用
全局 group_vars/all.yml 全部主机
组 group_vars/<group>.yml 该组
主机 host_vars/<host>.yml 单台
任务 task 内 vars 仅本任务

别在 hosts 文件里写变量,迁移到 YAML 格式的 host_vars/,可读性更好。


7. 条件判断:when

when / loop / block 是 Playbook 三大控制结构。2026 年新写法已经统一成 loop,with_items / with_dict 等老循环基本弃用。

7.1 单一条件

- name: 安装 Apache
  ansible.builtin.yum:
    name: httpd
    state: present
  when: ansible_os_family == "RedHat"

7.2 组合条件

- name: CentOS 7 专属配置
  ansible.builtin.template:
    src: service.conf.j2
    dest: /etc/service.conf
  when:
    - ansible_distribution == "CentOS"
    - ansible_distribution_major_version == "7"

- name: 安装 RHEL 系包
  ansible.builtin.yum:
    name: httpd
    state: present
  when: >
    ansible_distribution == "CentOS" or
    ansible_distribution == "RedHat"

7.3 注册变量 + 条件

- name: 检查服务
  ansible.builtin.command: systemctl status httpd
  register: svc
  changed_when: false

- name: 重启
  ansible.builtin.service:
    name: httpd
    state: restarted
  when: svc.rc != 0

7.4 变量存在性

- name: 配置数据库
  ansible.builtin.template:
    src: db.conf.j2
    dest: /etc/db.conf
  when: db_password is defined

8. 循环:loop(推荐)

8.1 基本列表

- name: 安装多个包
  ansible.builtin.yum:
    name: "{{ item }}"
    state: present
  loop:
    - httpd
    - php
    - mariadb-server

8.2 字典循环

- name: 创建用户
  ansible.builtin.user:
    name: "{{ item.key }}"
    groups: "{{ item.value.groups }}"
    shell: "{{ item.value.shell }}"
  loop: "{{ users | dict2items }}"
  vars:
    users:
      admin: {groups: wheel, shell: /bin/bash}
      dev:   {groups: developers, shell: /bin/zsh}

8.3 文件匹配

- name: 复制配置
  ansible.builtin.copy:
    src: "{{ item }}"
    dest: /etc/app/
  loop: "{{ lookup('fileglob', 'files/*.conf', wantlist=True) }}"

老语法 with_fileglob、with_dict 仍能用,但 2.5+ 起官方推荐 loop。

8.4 数字序列

- name: 建目录
  ansible.builtin.file:
    path: "/data/dir{{ item }}"
    state: directory
  loop: "{{ range(1, 6) | list }}"

9. 错误处理

9.1 忽略失败

- name: 尝试命令
  ansible.builtin.command: /bin/false
  ignore_errors: true

9.2 自定义失败条件

- name: 检查服务
  ansible.builtin.command: systemctl status httpd
  register: result
  failed_when: "'active' not in result.stdout"
  changed_when: false

9.3 block / rescue / always

- name: 部署
  block:
    - ansible.builtin.yum: {name: app, state: present}
    - ansible.builtin.service: {name: app, state: started}
  rescue:
    - ansible.builtin.yum: {name: app, state: absent}
  always:
    - ansible.builtin.mail:
        to: admin@example.com
        subject: 部署结果

10. 任务控制

10.1 Tags

tasks:
  - name: 安装
    ansible.builtin.yum: {name: httpd, state: present}
    tags: install

  - name: 配置
    ansible.builtin.template:
      src: httpd.conf.j2
      dest: /etc/httpd.conf
    tags: [config, httpd]

10.2 委派

- name: 加入负载均衡
  ansible.builtin.shell: /usr/local/bin/add_to_lb.sh {{ inventory_hostname }}
  delegate_to: localhost

10.3 异步

- name: 长时间任务
  ansible.builtin.command: /usr/bin/long_running
  async: 3600      # 1 小时
  poll: 0          # 不阻塞,立即返回

11. 几条提醒

  • when 里别写复杂逻辑,能放到 set_fact 里就提出来
  • 避免在循环里嵌套 with_*,两层 loop 比嵌套清晰
  • 异步任务记得加 wait_for 或在最后一步收尾

12. 参考