Ansible Playbook编写实战教程

1. 什么是Ansible Playbook

Ansible Playbook是使用YAML编写的配置、部署和编排文件,它定义了一系列任务(tasks)在目标主机上执行的顺序。Playbook是Ansible的核心组件,允许我们以声明式的方式描述系统状态和自动化任务。

2. 基本结构

一个典型的Ansible Playbook包含以下元素:

Play:定义要在哪些主机上执行哪些任务

Tasks:要执行的具体操作

Modules:执行任务的工具(如copy, file, service等)

Handlers:在特定条件下触发的任务

Variables:用于动态配置的数据

2.1. 示例:基本Playbook结构

---
- name: 安装和启动Nginx
  hosts: webservers
  become: yes
  vars:
    http_port: 80
    max_clients: 200

  tasks:
    - name: 安装Nginx包
      ansible.builtin.apt:
        name: nginx
        state: present

    - name: 启动Nginx服务
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: yes

  handlers:
    - name: 重启Nginx
      ansible.builtin.service:
        name: nginx
        state: restarted

3. Playbook编写步骤

定义Play基本信息

使用`name`描述Play的目的

指定目标主机`hosts`

使用`become`提权(默认为root)

定义变量

使用`vars`定义静态变量

使用`vars_files`从外部文件引入变量

支持通过`--extra-vars`命令行参数传递变量

---
- name: 配置Web服务器
  hosts: webservers
  vars_files:
    - vars/main.yml
  vars:
    app_version: 1.2.3

  tasks:
    - name: 显示应用版本
      debug:
        msg: "当前应用版本: {{ app_version }}"

编写任务

每个任务使用`name`描述

调用模块执行具体操作

支持条件判断`when`

支持循环`with_items`/`loop`

---
- name: 用户管理
  hosts: all
  tasks:
    - name: 创建开发组
      ansible.builtin.group:
        name: devops
        state: present

    - name: 创建多个用户
      ansible.builtin.user:
        name: "{{ item }}"
        groups: devops
        state: present
      loop:
        - alice
        - bob
        - charlie
      when: ansible_os_family == "Debian"

使用模板和文件

使用`template`模块处理动态配置文件

使用`copy`模块复制静态文件

支持Jinja2模板语法

---
- name: 配置Nginx站点
  hosts: webservers
  tasks:
    - name: 创建站点目录
      ansible.builtin.file:
        path: /var/www/example.com
        state: directory
        mode: '0755'

    - name: 部署配置文件
      ansible.builtin.template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/sites-available/example.com
      notify: 重启Nginx

  handlers:
    - name: 重启Nginx
      ansible.builtin.service:
        name: nginx
        state: restarted

4. 高级功能

错误处理

使用`ignore_errors`忽略错误

使用`failed_when`自定义失败条件

使用`rescue`和`always`进行错误恢复

---
- name: 安全的部署流程
  hosts: webservers
  tasks:
    - name: 部署应用新版本
      block:
        - name: 下载新版本
          get_url:
            url: "https://example.com/releases/{{ app_version }}.tar.gz"
            dest: /tmp/app.tar.gz
        - name: 解压应用
          unarchive:
            src: /tmp/app.tar.gz
            dest: /opt/myapp
            remote_src: yes
      rescue:
        - name: 回滚到上一版本
          command: /opt/scripts/rollback.sh
      always:
        - name: 清理临时文件
          file:
            path: /tmp/app.tar.gz
            state: absent

角色复用

使用`include_role`或`import_role`组织任务

创建可重用的角色结构

---
- name: 部署LAMP环境
  hosts: webservers
  roles:
    - common
    - apache
    - mysql
    - php

动态清单

从云平台API获取主机信息

支持脚本清单和动态清单插件

#!/usr/bin/env python3
# 示例动态清单脚本
import json
import subprocess

def get_ec2_instances():
    cmd = ["aws", "ec2", "describe-instances", "--query", "Reservations[*].Instances[*].{IP:PublicIpAddress,Name:Tags[?Key=='Name'].Value | [0]}", "--output", "json"]
    result = subprocess.run(cmd, capture_output=True, text=True)
    instances = json.loads(result.stdout)

    inventory = {
        "_meta": {
            "hostvars": {}
        },
        "webservers": []
    }

    for instance in instances:
        for i in instance:
            if i["IP"] and "web" in i["Name"]:
                inventory["webservers"].append(i["IP"])
                inventory["_meta"]["hostvars"][i["IP"]] = {"instance_name": i["Name"]}

    return inventory

if __name__ == "__main__":
    print(json.dumps(get_ec2_instances(), indent=2))

5. 执行和调试

运行Playbook

ansible-playbook site.yml --limit webservers --ask-become-pass

语法检查

ansible-playbook site.yml --syntax-check

测试运行(Dry Run)

ansible-playbook site.yml --check

调试技巧

使用`debug`模块打印变量

使用`-v`/`-vvv`增加详细输出

使用`step`模式逐步执行

---
- name: 调试示例
  hosts: localhost
  gather_facts: no
  vars:
    complex_var:
      - name: item1
        value: 100
      - name: item2
        value: 200

  tasks:
    - name: 显示变量结构
      debug:
        var: complex_var

    - name: 显示单个元素
      debug:
        msg: "Name: {{ item.name }} Value: {{ item.value }}"
      loop: "{{ complex_var }}"

6. 最佳实践

保持Playbook幂等性

确保多次运行结果一致

使用`state`参数定义期望状态

使用角色组织代码

   roles/
   ├── common/
   │   ├── defaults/
   │   ├── files/
   │   ├── handlers/
   │   ├── meta/
   │   ├── tasks/
   │   ├── templates/
   │   ├── tests/
   │   └── vars/
   ├── webserver/
   └── database/

版本控制

所有Playbook和角色使用Git管理

使用标签和分支管理不同环境

安全实践

使用Ansible Vault加密敏感数据

   ansible-vault encrypt secret.yml

限制特权操作(只在必要时使用become)

文档化

为每个Playbook添加说明注释

使用`ansible-doc`查看模块文档

---
# 这个Playbook用于初始化数据库服务器
# 要求:
# - 目标系统为CentOS 7+
# - 需要root权限执行
# 使用示例:
#   ansible-playbook init_db.yml --extra-vars "db_name=prod_db"

- name: 初始化数据库服务器
  hosts: dbservers
  become: yes
  vars:
    db_user: "dbadmin"
    db_pass: "{{ vault_db_password }}"  # 来自加密变量

  tasks:
    # 创建数据库用户
    - name: 创建数据库用户
      mysql_user:
        name: "{{ db_user }}"
        password: "{{ db_pass }}"
        priv: "*.*:ALL"
        state: present
      notify: 重启数据库服务

  handlers:
    - name: 重启数据库服务
      systemd:
        name: mariadb
        state: restarted

7. 总结

Ansible Playbook是实现自动化运维的核心工具,通过YAML语法定义系统状态和任务序列。掌握Playbook编写需要理解以下关键概念:

基本结构:由Play、任务、模块和处理器组成

变量管理:支持静态变量、外部文件和命令行参数

任务控制:使用条件、循环和错误处理实现复杂逻辑

模板系统:结合Jinja2实现动态配置

角色复用:组织代码为可重用单元

调试技巧:使用语法检查、Dry Run和调试模块

最佳实践包括保持幂等性、使用角色、版本控制和安全加密。随着实践深入,可以结合动态清单、异步执行和回调插件等高级功能,构建强大的自动化运维体系。通过持续学习和实践,Ansible Playbook将成为您实现基础设施即代码(IaC)的利器。

发表回复

后才能评论