第五章:Playbook 基础

深入学习 Ansible Playbook 的结构、语法和编写方法。

最后更新: 2024-01-19
页面目录

Playbook 基础

Playbook 是 Ansible 的核心组件,用于定义复杂的自动化任务和配置。

Playbook 概述

┌─────────────────────────────────────────────────────────────────┐
                      Playbook 结构                               
├─────────────────────────────────────────────────────────────────┤
                                                                  
  Playbook                                                         
  └── Play (一个或多个)                                           
      ├── name: Play 名称                                         
      ├── hosts: 目标主机                                        
      ├── become: 是否提权                                       
      ├── vars: Play 变量                                        
      ├── vars_files: 变量文件                                   
      ├── tasks: 任务列表                                        
      ├── handlers: 处理器列表                                   
      ├── pre_tasks: 前置任务                                    
      └── post_tasks: 后置任务                                   
                                                                  
└─────────────────────────────────────────────────────────────────┘

YAML 基础

YAML 语法规则

# 键值对
name: value
port: 80

# 嵌套结构
server:
  host: localhost
  port: 8080

# 列表
packages:
  - nginx
  - vim
  - git

# 复杂结构
servers:
  - name: web1
    ip: 192.168.1.10
  - name: web2
    ip: 192.168.1.11

YAML 注意事项

# 字符串引号(一般不需要)
message: "Hello World"
message: 'Hello World'

# 多行字符串
description: |
  This is a multi-line
  string that will be
  preserved as-is.

# 折叠字符串
description: >
  This will be folded
  into a single line.

# 布尔值
enabled: true
disabled: false

Playbook 基本结构

单 Play 结构

# simple-playbook.yml
---
- name: Install and configure Nginx
  hosts: webservers
  become: yes
  
  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes
      
    - name: Install Nginx
      apt:
        name: nginx
        state: present
      
    - name: Start Nginx service
      service:
        name: nginx
        state: started
        enabled: yes

多 Play 结构

# multi-playbook.yml
---
# Play 1: Web 服务器
- name: Configure Web Servers
  hosts: webservers
  become: yes
  
  tasks:
    - name: Install Nginx
      apt:
        name: nginx
        state: present
    
    - name: Copy Nginx configuration
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify: Reload Nginx
  
  handlers:
    - name: Reload Nginx
      service:
        name: nginx
        state: reloaded

# Play 2: Database 服务器
- name: Configure Database Servers
  hosts: dbservers
  become: yes
  
  tasks:
    - name: Install MySQL
      apt:
        name: mysql-server
        state: present
    
    - name: Start MySQL
      service:
        name: mysql
        state: started
        enabled: yes

Play 属性详解

hosts

# 单个主机
- name: Configure web1
  hosts: web1

# 主机组
- name: Configure webservers
  hosts: webservers

# 多个组(使用冒号)
- name: Configure web and db
  hosts: webservers:dbservers

# 排除组(使用感叹号)
- name: All except dbservers
  hosts: all:!dbservers

# 组交集(使用冒号)
- name: Common servers
  hosts: webservers:&production

# 模式匹配
- name: All web servers
  hosts: 'web*'

# 使用变量
- name: Configure dynamic hosts
  hosts: "{{ target_hosts }}"

become

# 整个 Play 启用提权
- name: Admin tasks
  hosts: all
  become: yes
  become_method: sudo
  become_user: root
  
# 任务级别覆盖
  tasks:
    - name: Install packages
      apt:
        name: nginx
      become: yes
      
    - name: Run as normal user
      command: whoami

remote_user

- name: Deploy application
  hosts: webservers
  remote_user: deploy
  become: yes
  become_user: www-data

gather_facts

# 禁用 Facts 收集(提高性能)
- name: Fast playbook
  hosts: all
  gather_facts: no
  
# 收集部分 Facts
- name: Selective facts
  hosts: all
  gather_facts:
    - ansible_distribution
    - ansible_memory_mb

Task 详解

Task 基本结构

tasks:
  - name: Task description
    module_name:
      module_argument: value
    register: result
    when: condition
    loop: ["item1", "item2"]
    tags: [tag1, tag2]

Task 执行顺序

tasks:
  # 1. 先执行 pre_tasks
  pre_tasks:
    - name: Pre-task
      debug:
        msg: "Running before main tasks"

  # 2. 执行 tasks
  tasks:
    - name: Main task
      apt:
        name: nginx
        state: present

  # 3. 执行 post_tasks
  post_tasks:
    - name: Post-task
      debug:
        msg: "Running after main tasks"

常用模块作为 Task

tasks:
  # apt 模块
  - name: Install packages
    apt:
      name:
        - nginx
        - vim
        - git
      state: present
      update_cache: yes
  
  # yum 模块
  - name: Install packages (RHEL)
    yum:
      name: httpd
      state: present
  
  # service 模块
  - name: Start service
    service:
      name: nginx
      state: started
      enabled: yes
  
  # copy 模块
  - name: Copy file
    copy:
      src: ./files/app.conf
      dest: /etc/myapp/app.conf
      owner: app
      group: app
      mode: '0644'
      backup: yes
  
  # template 模块
  - name: Generate config from template
    template:
      src: app.conf.j2
      dest: /etc/myapp/app.conf
    notify: Restart app
  
  # command 模块
  - name: Run command
    command: /usr/local/bin/setup.sh
    args:
      creates: /etc/myapp/.setup_complete
  
  # shell 模块
  - name: Run shell script
    shell: |
      cd /opt/app
      ./build.sh
    register: build_result
  
  # file 模块
  - name: Create directory
    file:
      path: /var/log/myapp
      state: directory
      owner: root
      group: root
      mode: '0755'

Playbook 执行控制

条件执行 (when)

tasks:
  # 简单条件
  - name: Install on CentOS
    yum:
      name: httpd
      state: present
    when: ansible_os_family == "RedHat"
  
  # 复合条件
  - name: Install on Debian
    apt:
      name: apache2
      state: present
    when:
      - ansible_os_family == "Debian"
      - ansible_distribution_version | int >= 20
  
  # 注册变量条件
  - name: Check if file exists
    command: test -f /etc/myapp/config.yml
    register: config_check
    ignore_errors: yes
  
  - name: Create config if not exists
    copy:
      src: default.yml
      dest: /etc/myapp/config.yml
    when: config_check.rc != 0
  
  # 变量存在性条件
  - name: Use variable if defined
    debug:
      msg: "Value is {{ custom_var }}"
    when: custom_var is defined

循环 (loop)

tasks:
  # 简单循环
  - name: Install packages
    apt:
      name: "{{ item }}"
      state: present
    loop:
      - nginx
      - vim
      - git
      - curl
  
  # 字典列表循环
  - name: Create users
    user:
      name: "{{ item.name }}"
      shell: "{{ item.shell }}"
      comment: "{{ item.comment }}"
    loop:
      - { name: 'alice', shell: '/bin/bash', comment: 'Alice User' }
      - { name: 'bob', shell: '/bin/sh', comment: 'Bob User' }
      - { name: 'charlie', shell: '/bin/bash', comment: 'Charlie User' }
  
  # 使用 loop_control
  - name: Create databases
    mysql_db:
      name: "{{ item.db_name }}"
      state: present
    loop:
      - { db_name: 'app_production', owner: 'app' }
      - { db_name: 'app_staging', owner: 'app' }
    loop_control:
      label: "{{ item.db_name }}"
  
  # with_items 简写
  - name: Create directories
    file:
      path: "{{ item }}"
      state: directory
    with_items:
      - /tmp/dir1
      - /tmp/dir2
      - /tmp/dir3
  
  # 嵌套循环
  - name: Create app directories
    file:
      path: "/opt/{{ item.app }}/{{ item.env }}"
      state: directory
    with_nested:
      - ['app1', 'app2', 'app3']
      - ['production', 'staging']

其他迭代器

tasks:
  # with_dict 字典迭代
  - name: Configure sites
    template:
      src: "{{ item.value.template }}"
      dest: "/etc/nginx/sites-available/{{ item.key }}"
    with_dict: "{{ nginx_sites }}"
  
  # with_fileglob 文件迭代
  - name: Copy all config files
    copy:
      src: "{{ item }}"
      dest: "/etc/myapp/"
      mode: '0644'
    with_fileglob:
      - "files/*.conf"
  
  # with_url  URL 迭代
  - name: Download files
    get_url:
      url: "{{ item }}"
      dest: "/tmp/downloads/"
    with_url:
      - https://example.com/file1.zip
      - https://example.com/file2.zip
  
  # with_sequence 序列迭代
  - name: Create servers
    command: "create_server.sh {{ item }}"
    with_sequence: start=1 end=5 format=web%02d

块 (block)

tasks:
  # 块定义
  - name: Block example
    block:
      - name: Install packages
        apt:
          name:
            - nginx
            - vim
          state: present
      
      - name: Configure application
        template:
          src: app.conf.j2
          dest: /etc/myapp/app.conf
    when: ansible_os_family == "Debian"
    become: yes
  
  # 块错误处理
  - name: Block with rescue
    block:
      - name: Try to deploy
        command: /usr/local/bin/deploy.sh
    rescue:
      - name: Rollback on failure
        command: /usr/local/bin/rollback.sh
    always:
      - name: Always run
        debug:
          msg: "This always runs"

Playbook 命令

基本命令

# 运行 Playbook
ansible-playbook site.yml

# 指定 Inventory
ansible-playbook -i inventory/production site.yml

# 指定运行标签
ansible-playbook site.yml --tags "install,config"

# 跳过标签
ansible-playbook site.yml --skip-tags "testing"

# 检查模式
ansible-playbook site.yml --check

# 列出任务
ansible-playbook site.yml --list-tasks

# 列出主机
ansible-playbook site.yml --list-hosts

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

# 详细输出
ansible-playbook site.yml -v      # 详细
ansible-playbook site.yml -vv     # 更详细
ansible-playbook site.yml -vvv    # 连接调试
ansible-playbook site.yml -vvvv   # 详细信息
ansible-playbook site.yml -vvvvv  # 全部信息

# 传递额外变量
ansible-playbook site.yml -e "var1=value1 var2=value2"
ansible-playbook site.yml -e @vars.yml

# 从特定步骤开始
ansible-playbook site.yml --start-at-task="Install packages"

# 逐步执行
ansible-playbook site.yml --step

Playbook 输出解读

PLAY [Configure Web Servers] *************************************************

TASK [Gathering Facts] ********************************************************
ok: [web1]
ok: [web2]

TASK [Update apt cache] ******************************************************
ok: [web1]
ok: [web2]

TASK [Install Nginx] **********************************************************
changed: [web1]
changed: [web2]

TASK [Copy Nginx configuration] **********************************************
changed: [web1]
changed: [web2]

RUNNING HANDLER [Reload Nginx] ***********************************************
changed: [web1]
changed: [web2]

PLAY RECAP ********************************************************************
web1                       : ok=5    changed=2    unreachable=0    failed=0    skipped=0
web2                       : ok=5    changed=2    unreachable=0    failed=0    skipped=0

输出状态说明

状态 说明
ok 任务执行成功,状态未改变
changed 任务执行成功,状态已改变
failed 任务执行失败
skipped 任务被跳过
unreachable 主机不可达

下一步

现在你已经掌握了 Playbook 的基础知识。接下来让我们学习 Playbook 的进阶特性。

👉 Playbook 进阶