跳转至

第五章:Playbook 剧本

什么是 Playbook?

Playbook 是 Ansible 的核心概念,是用 YAML 格式编写的自动化剧本,定义了一系列要在目标主机上执行的任务。

基本结构

- name: Play 名称
  hosts: 目标主机
  become: yes/no
  vars:
    变量定义
  tasks:
    - name: 任务名称
      模块:
        参数: 

简单示例

- name: Install and configure nginx
  hosts: webservers
  become: yes

  tasks:
    - name: Install nginx
      ansible.builtin.apt:
        name: nginx
        state: present
        update_cache: yes

    - name: Start nginx
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: yes

    - name: Copy index.html
      ansible.builtin.copy:
        src: index.html
        dest: /var/www/html/index.html
        mode: '0644'

Playbook 语法

Play 定义

- name: 第一个 Play
  hosts: webservers
  remote_user: admin
  become: yes
  become_method: sudo
  become_user: root
  gather_facts: yes

  tasks:
    - name: Task 1
      ...

- name: 第二个 Play
  hosts: dbservers
  become: yes

  tasks:
    - name: Task 1
      ...

任务定义

tasks:
  # 基本任务
  - name: Install nginx
    ansible.builtin.apt:
      name: nginx
      state: present

  # 带条件的任务
  - name: Install nginx on Ubuntu
    ansible.builtin.apt:
      name: nginx
      state: present
    when: ansible_distribution == "Ubuntu"

  # 带循环的任务
  - name: Install multiple packages
    ansible.builtin.apt:
      name: "{{ item }}"
      state: present
    loop:
      - nginx
      - vim
      - git

  # 带标签的任务
  - name: Install nginx
    ansible.builtin.apt:
      name: nginx
      state: present
    tags:
      - install
      - nginx

变量

定义变量

- name: Using variables
  hosts: webservers
  vars:
    http_port: 80
    server_name: example.com
    packages:
      - nginx
      - vim
      - git

  tasks:
    - name: Print variable
      ansible.builtin.debug:
        msg: "HTTP Port is {{ http_port }}"

    - name: Install packages
      ansible.builtin.apt:
        name: "{{ packages }}"
        state: present

变量文件

# vars/main.yml
http_port: 80
server_name: example.com
- name: Include variables
  hosts: webservers
  vars_files:
    - vars/main.yml

  tasks:
    - name: Print variable
      ansible.builtin.debug:
        msg: "Server name is {{ server_name }}"

变量优先级

1. 命令行变量 (-e)
2. Playbook vars
3. Playbook vars_files
4. host_vars
5. group_vars
6. Inventory 主机变量
7. Inventory 组变量
8. Role defaults

Facts 变量

- name: Using facts
  hosts: all

  tasks:
    - name: Print OS
      ansible.builtin.debug:
        msg: "OS is {{ ansible_distribution }} {{ ansible_distribution_version }}"

    - name: Print IP
      ansible.builtin.debug:
        msg: "IP is {{ ansible_default_ipv4.address }}"

    - name: Print memory
      ansible.builtin.debug:
        msg: "Total memory is {{ ansible_memtotal_mb }} MB"

条件判断

when 条件

tasks:
  - name: Install nginx on Ubuntu
    ansible.builtin.apt:
      name: nginx
      state: present
    when: ansible_distribution == "Ubuntu"

  - name: Install nginx on CentOS
    ansible.builtin.yum:
      name: nginx
      state: present
    when: ansible_distribution == "CentOS"

  - name: Install on production
    ansible.builtin.apt:
      name: nginx
      state: present
    when: env == "production"

  - name: Multiple conditions
    ansible.builtin.apt:
      name: nginx
      state: present
    when:
      - ansible_distribution == "Ubuntu"
      - ansible_distribution_version == "22.04"

条件运算符

# 比较运算符
when: ansible_memtotal_mb > 1024
when: ansible_distribution == "Ubuntu"
when: ansible_distribution != "CentOS"

# 逻辑运算符
when: ansible_distribution == "Ubuntu" and ansible_distribution_version == "22.04"
when: ansible_distribution == "Ubuntu" or ansible_distribution == "Debian"
when: not ansible_check_mode

# 列表包含
when: "'nginx' in installed_packages"
when: "'apache' not in installed_packages"

# 变量定义检查
when: my_var is defined
when: my_var is not defined

循环

loop 循环

# 基本循环
- name: Install packages
  ansible.builtin.apt:
    name: "{{ item }}"
    state: present
  loop:
    - nginx
    - vim
    - git

# 字典循环
- name: Create users
  ansible.builtin.user:
    name: "{{ item.name }}"
    group: "{{ item.group }}"
    shell: "{{ item.shell }}"
  loop:
    - { name: 'user1', group: 'developers', shell: '/bin/bash' }
    - { name: 'user2', group: 'admins', shell: '/bin/bash' }

# 循环索引
- name: Print with index
  ansible.builtin.debug:
    msg: "Item {{ index }} is {{ item }}"
  loop:
    - nginx
    - vim
  loop_control:
    index_var: index

with_items(旧语法)

- name: Install packages
  ansible.builtin.apt:
    name: "{{ item }}"
    state: present
  with_items:
    - nginx
    - vim
    - git

with_dict

- name: Create users
  ansible.builtin.user:
    name: "{{ item.key }}"
    comment: "{{ item.value.comment }}"
  with_dict:
    user1:
      comment: "User One"
    user2:
      comment: "User Two"

Handlers

Handler 是特殊的任务,只在被通知时执行。

- name: Configure nginx
  hosts: webservers
  become: yes

  handlers:
    - name: Restart nginx
      ansible.builtin.service:
        name: nginx
        state: restarted

    - name: Reload nginx
      ansible.builtin.service:
        name: nginx
        state: reloaded

  tasks:
    - name: Copy nginx config
      ansible.builtin.copy:
        src: nginx.conf
        dest: /etc/nginx/nginx.conf
      notify: Restart nginx

    - name: Copy site config
      ansible.builtin.template:
        src: site.conf.j2
        dest: /etc/nginx/sites-available/default
      notify:
        - Reload nginx
        - Restart nginx

模块

template 模块

- name: Deploy config
  ansible.builtin.template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    mode: '0644'
    backup: yes
    validate: nginx -t -c %s
# nginx.conf.j2
user www-data;
worker_processes {{ ansible_processor_vcpus }};

http {
    server {
        listen {{ http_port }};
        server_name {{ server_name }};

        location / {
            proxy_pass http://{{ backend_host }}:{{ backend_port }};
        }
    }
}

block 模块

tasks:
  - name: Handle errors
    block:
      - name: Try to do something
        ansible.builtin.command: /bin/false

      - name: This won't run
        ansible.builtin.debug:
          msg: "This won't run"

    rescue:
      - name: Handle error
        ansible.builtin.debug:
          msg: "An error occurred"

    always:
      - name: Always run
        ansible.builtin.debug:
          msg: "This always runs"

Tags

- name: Deploy application
  hosts: webservers
  become: yes

  tasks:
    - name: Install nginx
      ansible.builtin.apt:
        name: nginx
        state: present
      tags:
        - install
        - nginx

    - name: Configure nginx
      ansible.builtin.template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      tags:
        - config
        - nginx

    - name: Start nginx
      ansible.builtin.service:
        name: nginx
        state: started
      tags:
        - service
        - nginx
# 运行指定标签的任务
ansible-playbook site.yml --tags install
ansible-playbook site.yml --tags nginx,config

# 跳过指定标签
ansible-playbook site.yml --skip-tags install

Include 和 Import

include_tasks

# tasks/main.yml
- name: Install nginx
  ansible.builtin.apt:
    name: nginx
    state: present

- name: Start nginx
  ansible.builtin.service:
    name: nginx
    state: started
# playbook.yml
- name: Deploy nginx
  hosts: webservers
  tasks:
    - name: Include nginx tasks
      ansible.builtin.include_tasks: tasks/main.yml

import_tasks

- name: Deploy nginx
  hosts: webservers
  tasks:
    - name: Import nginx tasks
      ansible.builtin.import_tasks: tasks/main.yml

include_vars

- name: Include variables
  ansible.builtin.include_vars:
    file: vars/main.yml

实战示例

完整 Web 服务器部署

- name: Deploy Web Server
  hosts: webservers
  become: yes
  vars:
    nginx_version: "1.24"
    http_port: 80
    server_name: example.com

  handlers:
    - name: Restart nginx
      ansible.builtin.service:
        name: nginx
        state: restarted

    - name: Reload nginx
      ansible.builtin.service:
        name: nginx
        state: reloaded

  tasks:
    - name: Update apt cache
      ansible.builtin.apt:
        update_cache: yes
        cache_valid_time: 3600

    - name: Install dependencies
      ansible.builtin.apt:
        name:
          - nginx
          - python3-pip
          - git
        state: present

    - name: Create web directory
      ansible.builtin.file:
        path: /var/www/{{ server_name }}
        state: directory
        owner: www-data
        group: www-data
        mode: '0755'

    - name: Deploy nginx config
      ansible.builtin.template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/sites-available/{{ server_name }}
        mode: '0644'
      notify: Reload nginx

    - name: Enable site
      ansible.builtin.file:
        src: /etc/nginx/sites-available/{{ server_name }}
        dest: /etc/nginx/sites-enabled/{{ server_name }}
        state: link
      notify: Reload nginx

    - name: Remove default site
      ansible.builtin.file:
        path: /etc/nginx/sites-enabled/default
        state: absent
      notify: Reload nginx

    - name: Start nginx
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: yes

    - name: Check nginx status
      ansible.builtin.uri:
        url: "http://localhost:{{ http_port }}"
        method: GET
        status_code: 200
      register: result
      until: result.status == 200
      retries: 5
      delay: 10

小结

本章学习了:

  • ✅ Playbook 基本结构
  • ✅ 变量定义和使用
  • ✅ 条件判断
  • ✅ 循环
  • ✅ Handlers
  • ✅ Tags
  • ✅ Include 和 Import

下一章

第六章:Roles 角色 - 学习如何组织和复用 Playbook。