Ansible Playbook 基础 #

什么是 Playbook? #

Playbook 是 Ansible 的核心组件,使用 YAML 格式编写,定义了一系列自动化任务。它描述了在哪些主机上执行什么任务,是 Ansible 自动化的脚本文件。

text
┌─────────────────────────────────────────────────────────────┐
│                      Playbook 结构                           │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  Playbook                                                    │
│  ├── Play 1                                                  │
│  │   ├── hosts: 目标主机                                     │
│  │   ├── vars: 变量                                          │
│  │   └── tasks: 任务列表                                     │
│  │       ├── Task 1                                          │
│  │       ├── Task 2                                          │
│  │       └── ...                                             │
│  │                                                           │
│  └── Play 2                                                  │
│      ├── hosts: 目标主机                                     │
│      └── tasks: 任务列表                                     │
│                                                              │
└─────────────────────────────────────────────────────────────┘

YAML 语法基础 #

基本规则 #

yaml
# YAML 基本规则

# 1. 使用缩进表示层级关系(空格,不用 Tab)
# 2. 缩进必须一致
# 3. 大小写敏感
# 4. 文件以 --- 开始(可选)
# 5. 注释使用 #

---
# 键值对
key: value

# 列表
- item1
- item2
- item3

# 字典
person:
  name: John
  age: 30
  city: Beijing

# 列表中的字典
users:
  - name: Alice
    age: 25
  - name: Bob
    age: 30

# 字典中的列表
server:
  name: web1
  ports:
    - 80
    - 443

YAML 在 Ansible 中的应用 #

yaml
---
# Playbook 示例
- name: Configure web servers
  hosts: webservers
  become: yes
  
  vars:
    nginx_port: 80
    document_root: /var/www/html
  
  tasks:
    - name: Install Nginx
      apt:
        name: nginx
        state: present
        update_cache: yes
    
    - name: Start Nginx
      service:
        name: nginx
        state: started
        enabled: yes

Playbook 结构 #

基本结构 #

yaml
---
- name: Play 名称
  hosts: 目标主机
  vars:                    # 变量定义(可选)
    var1: value1
  vars_files:              # 变量文件(可选)
    - vars.yml
  become: yes              # 是否提权(可选)
  gather_facts: yes        # 是否收集 Facts(可选)
  
  tasks:                   # 任务列表
    - name: Task 1
      module_name:
        parameter: value
    
    - name: Task 2
      module_name:
        parameter: value
  
  handlers:                # 处理器(可选)
    - name: Handler 1
      module_name:
        parameter: value

多个 Play #

yaml
---
# Play 1: 配置 Web 服务器
- name: Configure web servers
  hosts: webservers
  become: yes
  tasks:
    - name: Install Nginx
      apt:
        name: nginx
        state: present

# Play 2: 配置数据库服务器
- name: Configure database servers
  hosts: databases
  become: yes
  tasks:
    - name: Install MySQL
      apt:
        name: mysql-server
        state: present

# Play 3: 配置缓存服务器
- name: Configure cache servers
  hosts: cache
  become: yes
  tasks:
    - name: Install Redis
      apt:
        name: redis-server
        state: present

第一个 Playbook #

创建 Playbook #

yaml
# site.yml
---
- name: My first playbook
  hosts: all
  become: yes
  
  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes
    
    - name: Install Nginx
      apt:
        name: nginx
        state: present
    
    - name: Start Nginx
      service:
        name: nginx
        state: started
        enabled: yes
    
    - name: Create index page
      copy:
        content: "Hello Ansible!"
        dest: /var/www/html/index.html

执行 Playbook #

bash
# 基本执行
ansible-playbook site.yml

# 指定清单文件
ansible-playbook -i inventory/hosts site.yml

# 检查语法
ansible-playbook --syntax-check site.yml

# 模拟执行(不实际更改)
ansible-playbook --check site.yml

# 详细输出
ansible-playbook -v site.yml
ansible-playbook -vv site.yml
ansible-playbook -vvv site.yml

# 指定标签
ansible-playbook --tags "install" site.yml

# 从特定任务开始
ansible-playbook --start-at-task "Start Nginx" site.yml

任务编写 #

任务基本格式 #

yaml
tasks:
  - name: 任务描述
    module_name:
      parameter1: value1
      parameter2: value2

常用任务示例 #

yaml
tasks:
  # 安装软件包
  - name: Install Nginx
    apt:
      name: nginx
      state: present
      update_cache: yes

  # 复制文件
  - name: Copy configuration file
    copy:
      src: files/nginx.conf
      dest: /etc/nginx/nginx.conf
      owner: root
      group: root
      mode: '0644'
    notify: Restart Nginx

  # 使用模板
  - name: Deploy application config
    template:
      src: templates/app.conf.j2
      dest: /etc/app/app.conf
      backup: yes

  # 管理服务
  - name: Ensure Nginx is running
    service:
      name: nginx
      state: started
      enabled: yes

  # 创建用户
  - name: Create application user
    user:
      name: appuser
      shell: /bin/bash
      groups: sudo
      append: yes

  # 创建目录
  - name: Create application directory
    file:
      path: /var/www/app
      state: directory
      owner: appuser
      group: appuser
      mode: '0755'

  # 执行命令
  - name: Check application status
    command: /usr/bin/app status
    register: app_status
    changed_when: false

  # 执行 Shell 命令
  - name: Get system information
    shell: uname -a
    register: system_info

  # 调试输出
  - name: Display system information
    debug:
      msg: "System: {{ system_info.stdout }}"

任务属性 #

yaml
tasks:
  - name: Task with all attributes
    copy:
      src: file.txt
      dest: /tmp/file.txt
    
    # 条件执行
    when: ansible_facts['os_family'] == "Debian"
    
    # 循环
    loop:
      - item1
      - item2
    
    # 提权
    become: yes
    become_user: root
    
    # 忽略错误
    ignore_errors: yes
    
    # 注册结果
    register: result
    
    # 更改条件
    changed_when: "'success' in result.stdout"
    
    # 失败条件
    failed_when: "'error' in result.stdout"
    
    # 通知处理器
    notify: Restart Service
    
    # 标签
    tags:
      - install
      - config
    
    # 重试
    retries: 3
    delay: 10
    until: result is success

变量使用 #

定义变量 #

yaml
---
- name: Use variables
  hosts: webservers
  become: yes
  
  # 在 Play 中定义变量
  vars:
    app_name: myapp
    app_port: 8080
    app_user: appuser
  
  # 使用变量文件
  vars_files:
    - vars/app.yml
    - vars/secrets.yml
  
  tasks:
    - name: Create application user
      user:
        name: "{{ app_user }}"
        shell: /bin/bash
    
    - name: Create application directory
      file:
        path: "/var/www/{{ app_name }}"
        state: directory
        owner: "{{ app_user }}"
    
    - name: Configure application port
      lineinfile:
        path: /etc/app/config.yml
        line: "port: {{ app_port }}"
        create: yes

变量文件 #

yaml
# vars/app.yml
---
app_name: myapp
app_version: 1.0.0
app_port: 8080
app_user: appuser
app_dir: /var/www/myapp

database:
  host: localhost
  port: 3306
  name: app_production
  user: appuser
  password: "{{ vault_db_password }}"

使用 Facts #

yaml
---
- name: Use facts
  hosts: all
  
  tasks:
    - name: Show system information
      debug:
        msg: |
          Hostname: {{ ansible_facts['hostname'] }}
          OS: {{ ansible_facts['distribution'] }} {{ ansible_facts['distribution_version'] }}
          IP: {{ ansible_facts['default_ipv4']['address'] }}
          Memory: {{ ansible_facts['memtotal_mb'] }} MB
    
    - name: Install package based on OS
      apt:
        name: nginx
        state: present
      when: ansible_facts['os_family'] == "Debian"
    
    - name: Install package on RedHat
      yum:
        name: nginx
        state: present
      when: ansible_facts['os_family'] == "RedHat"

Handlers #

定义 Handlers #

yaml
---
- name: Use handlers
  hosts: webservers
  become: yes
  
  tasks:
    - name: Install Nginx
      apt:
        name: nginx
        state: present
      notify: Restart Nginx
    
    - name: Copy Nginx config
      copy:
        src: files/nginx.conf
        dest: /etc/nginx/nginx.conf
      notify: 
        - Restart Nginx
        - Reload Firewall
    
    - name: Copy site config
      template:
        src: templates/site.conf.j2
        dest: /etc/nginx/sites-available/default
      notify: Restart Nginx
  
  handlers:
    - name: Restart Nginx
      service:
        name: nginx
        state: restarted
    
    - name: Reload Nginx
      service:
        name: nginx
        state: reloaded
    
    - name: Reload Firewall
      command: ufw reload

Handler 特点 #

yaml
# Handler 只在任务产生变更时触发
# Handler 在所有任务执行完后执行
# Handler 只执行一次(即使被多次通知)
# Handler 按定义顺序执行

tasks:
  - name: Task 1
    copy:
      src: file1
      dest: /tmp/file1
    notify: Handler 1    # 如果变更,触发 Handler 1
  
  - name: Task 2
    copy:
      src: file2
      dest: /tmp/file2
    notify: Handler 1    # 如果变更,再次触发 Handler 1

handlers:
  - name: Handler 1
    debug:
      msg: "Handler 1 executed"
    # 即使被通知两次,也只执行一次

执行控制 #

条件判断 #

yaml
---
- name: Conditional execution
  hosts: all
  become: yes
  
  tasks:
    # 基本条件
    - name: Install on Debian
      apt:
        name: nginx
        state: present
      when: ansible_facts['os_family'] == "Debian"
    
    # 多条件(AND)
    - name: Install on Ubuntu 20.04
      apt:
        name: nginx
        state: present
      when: 
        - ansible_facts['distribution'] == "Ubuntu"
        - ansible_facts['distribution_version'] == "20.04"
    
    # 多条件(OR)
    - name: Install on Debian or Ubuntu
      apt:
        name: nginx
        state: present
      when: ansible_facts['os_family'] == "Debian" or ansible_facts['os_family'] == "Ubuntu"
    
    # 条件组合
    - name: Complex condition
      apt:
        name: nginx
        state: present
      when: >
        (ansible_facts['distribution'] == "Ubuntu" and ansible_facts['distribution_version'] == "20.04") or
        (ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "10")
    
    # 变量条件
    - name: Deploy to production
      debug:
        msg: "Deploying to production"
      when: environment == "production"
    
    # 布尔值条件
    - name: Run if enabled
      debug:
        msg: "Feature is enabled"
      when: feature_enabled | bool
    
    # 检查变量是否定义
    - name: Run if variable is defined
      debug:
        msg: "Variable is {{ my_var }}"
      when: my_var is defined
    
    # 检查变量是否未定义
    - name: Run if variable is undefined
      debug:
        msg: "Variable is not defined"
      when: my_var is undefined

循环 #

yaml
---
- name: Use loops
  hosts: all
  become: yes
  
  tasks:
    # 基本循环
    - name: Install multiple packages
      apt:
        name: "{{ item }}"
        state: present
      loop:
        - nginx
        - git
        - curl
        - vim
    
    # 循环字典列表
    - name: Create multiple users
      user:
        name: "{{ item.name }}"
        groups: "{{ item.groups }}"
        shell: "{{ item.shell }}"
      loop:
        - { name: 'user1', groups: 'sudo', shell: '/bin/bash' }
        - { name: 'user2', groups: 'docker', shell: '/bin/bash' }
        - { name: 'user3', groups: 'www-data', shell: '/bin/sh' }
    
    # 循环范围
    - name: Create numbered directories
      file:
        path: "/tmp/dir{{ item }}"
        state: directory
      loop: "{{ range(1, 6) | list }}"
    
    # 循环字典
    - name: Create files from dictionary
      copy:
        content: "{{ item.value }}"
        dest: "/tmp/{{ item.key }}.txt"
      loop: "{{ files_dict | dict2items }}"
      vars:
        files_dict:
          file1: "Content of file 1"
          file2: "Content of file 2"
          file3: "Content of file 3"
    
    # 嵌套循环
    - name: Nested loops
      debug:
        msg: "{{ item[0] }} - {{ item[1] }}"
      loop: "{{ ['a', 'b'] | product(['1', '2']) | list }}"
    
    # 注册循环结果
    - name: Loop with register
      command: "echo {{ item }}"
      loop:
        - one
        - two
        - three
      register: echo_results
    
    - name: Show loop results
      debug:
        msg: "{{ item.stdout }}"
      loop: "{{ echo_results.results }}"

标签 #

定义标签 #

yaml
---
- name: Use tags
  hosts: webservers
  become: yes
  
  tasks:
    - name: Install Nginx
      apt:
        name: nginx
        state: present
      tags:
        - install
        - nginx
    
    - name: Configure Nginx
      copy:
        src: files/nginx.conf
        dest: /etc/nginx/nginx.conf
      tags:
        - config
        - nginx
    
    - name: Start Nginx
      service:
        name: nginx
        state: started
        enabled: yes
      tags:
        - service
        - nginx
    
    - name: Deploy application
      copy:
        src: files/app/
        dest: /var/www/app/
      tags:
        - deploy
        - app

使用标签 #

bash
# 只执行特定标签的任务
ansible-playbook site.yml --tags "install"

# 执行多个标签的任务
ansible-playbook site.yml --tags "install,config"

# 跳过特定标签的任务
ansible-playbook site.yml --skip-tags "deploy"

# 列出所有标签
ansible-playbook site.yml --list-tags

包含和导入 #

包含任务 #

yaml
# tasks/install.yml
---
- name: Install Nginx
  apt:
    name: nginx
    state: present

- name: Install Git
  apt:
    name: git
    state: present

# site.yml
---
- name: Include tasks
  hosts: webservers
  become: yes
  
  tasks:
    - name: Include install tasks
      include_tasks: tasks/install.yml
    
    - name: Configure application
      include_tasks: tasks/config.yml
      vars:
        app_name: myapp

导入任务 #

yaml
# site.yml
---
- name: Import tasks
  hosts: webservers
  become: yes
  
  tasks:
    - name: Import install tasks
      import_tasks: tasks/install.yml
    
    - name: Import config tasks
      import_tasks: tasks/config.yml

包含 vs 导入 #

text
┌─────────────────────────────────────────────────────────────┐
│                include_tasks vs import_tasks                 │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  include_tasks              import_tasks                    │
│  ──────────────             ──────────────                  │
│  运行时处理                  解析时处理                       │
│  可使用变量                  不能使用变量                     │
│  可循环                      不能循环                         │
│  可条件包含                  不能条件导入                     │
│  任务列表中                  可在任意位置                     │
│                                                              │
└─────────────────────────────────────────────────────────────┘

调试技巧 #

debug 模块 #

yaml
tasks:
  # 输出消息
  - name: Debug message
    debug:
      msg: "Current host is {{ inventory_hostname }}"
  
  # 输出变量
  - name: Debug variable
    debug:
      var: ansible_facts
  
  # 输出表达式结果
  - name: Debug expression
    debug:
      msg: "Result is {{ 1 + 2 }}"
  
  # 条件调试
  - name: Conditional debug
    debug:
      msg: "This is production"
    when: environment == "production"

注册结果 #

yaml
tasks:
  - name: Run command
    command: ls -la /tmp
    register: ls_result
  
  - name: Show command output
    debug:
      msg: "{{ ls_result.stdout }}"
  
  - name: Show all result data
    debug:
      var: ls_result

详细输出 #

bash
# 一级详细
ansible-playbook site.yml -v

# 二级详细
ansible-playbook site.yml -vv

# 三级详细
ansible-playbook site.yml -vvv

# 连接调试
ansible-playbook site.yml -vvvv

完整示例 #

yaml
# site.yml
---
- name: Deploy Web Application
  hosts: webservers
  become: yes
  gather_facts: yes
  
  vars:
    app_name: myapp
    app_version: 1.0.0
    app_user: appuser
    app_dir: /var/www/myapp
    nginx_port: 80
  
  vars_files:
    - vars/secrets.yml
  
  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes
        cache_valid_time: 3600
      tags: always
    
    - name: Install required packages
      apt:
        name:
          - nginx
          - git
          - curl
        state: present
      tags: install
    
    - name: Create application user
      user:
        name: "{{ app_user }}"
        shell: /bin/bash
        system: yes
        create_home: yes
      tags: setup
    
    - name: Create application directory
      file:
        path: "{{ app_dir }}"
        state: directory
        owner: "{{ app_user }}"
        group: "{{ app_user }}"
        mode: '0755'
      tags: setup
    
    - name: Copy Nginx configuration
      template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/nginx.conf
        owner: root
        group: root
        mode: '0644'
        backup: yes
      notify: Restart Nginx
      tags: config
    
    - name: Copy application files
      copy:
        src: files/app/
        dest: "{{ app_dir }}/"
        owner: "{{ app_user }}"
        group: "{{ app_user }}"
      notify: Restart Nginx
      tags: deploy
    
    - name: Ensure Nginx is running
      service:
        name: nginx
        state: started
        enabled: yes
      tags: service
    
    - name: Check application status
      uri:
        url: "http://localhost:{{ nginx_port }}"
        method: GET
        status_code: 200
      register: health_check
      retries: 3
      delay: 5
      until: health_check.status == 200
      tags: verify
    
    - name: Display deployment info
      debug:
        msg: |
          Application {{ app_name }} v{{ app_version }} deployed successfully!
          URL: http://{{ ansible_facts['default_ipv4']['address'] }}:{{ nginx_port }}
      tags: verify
  
  handlers:
    - name: Restart Nginx
      service:
        name: nginx
        state: restarted

下一步 #

现在你已经掌握了 Playbook 的基础知识,接下来学习 Playbook 进阶 了解更高级的任务编排技巧!

最后更新:2026-03-29