Ansible Playbook编写教程

1. Ansible简介与核心概念

Ansible是一个开源的IT自动化工具,用于配置管理、应用部署、任务执行和基础设施编排。Playbook是Ansible的核心组件,使用YAML语言编写,定义了一系列要在目标主机上执行的任务。

1.1. 核心概念解析

Inventory(清单):定义要管理的主机列表和主机组

Modules(模块):执行具体任务的独立工具

Tasks(任务):调用模块完成的具体操作

Playbooks(剧本):多个任务的有序集合

Handlers(处理器):在特定变化时触发的特殊任务

Roles(角色):结构化组织Playbook的方法

Variables(变量):存储可配置值的数据结构

2. Playbook基础结构

一个基本的Playbook结构包含:

文档开始标记 ---

一个或多个play(以 - name: 开头)

每个play包含:

目标主机(hosts

任务列表(tasks

可选的变量、处理器等

2.1. 示例:最简单的Playbook

---
- name: 基础系统更新
  hosts: webservers
  become: yes

  tasks:
    - name: 更新所有软件包
      apt:
        update_cache: yes
        upgrade: dist

解释:

---:YAML文档开始标记

- name::定义一个play,名称为"基础系统更新"

hosts: webservers:指定该play在名为webservers的主机组上执行

become: yes:使用sudo权限执行任务

tasks::任务列表的开始

- name::定义一个任务,名称为"更新所有软件包"

apt::使用apt模块(适用于Debian/Ubuntu系统)

update_cache: yes:更新软件包缓存

upgrade: dist:进行发行版升级

3. 详细Playbook编写指南

3.1. Inventory文件管理

Inventory定义了要管理的主机。可以创建为静态文件或动态生成。

#### 静态Inventory示例 (inventory.ini)

[webservers]
192.168.1.10
192.168.1.11

[databases]
192.168.1.20

[all:vars]
ansible_user=ubuntu
ansible_ssh_private_key_file=~/.ssh/id_rsa

解释:

[webservers]:定义主机组webservers,包含两台主机

[databases]:定义主机组databases,包含一台主机

[all:vars]:为所有主机定义变量

ansible_user:默认SSH用户

ansible_ssh_private_key_file:SSH私钥路径

#### 动态Inventory

可以通过脚本从云服务获取主机列表,例如AWS动态inventory脚本。

3.2. 常用模块详解

#### a. 包管理模块

apt模块(Debian/Ubuntu):

- name: 安装Nginx
  apt:
    name: nginx
    state: present
    update_cache: yes

yum模块(RHEL/CentOS):

- name: 安装httpd
  yum:
    name: httpd
    state: present

参数说明:

name:软件包名称

state:状态(present/absent/latest)

update_cache:是否更新包缓存

#### b. 服务管理模块

- name: 确保Nginx服务运行
  service:
    name: nginx
    state: started
    enabled: yes

参数说明:

name:服务名称

state:状态(started/stopped/restarted/reloaded)

enabled:是否开机自启

#### c. 文件管理模块

- name: 创建目录
  file:
    path: /opt/myapp
    state: directory
    mode: '0755'
    owner: www-data
    group: www-data

参数说明:

path:文件/目录路径

state:状态(file/directory/link/absent)

mode:权限模式(八进制格式)

owner:所有者

group:所属组

#### d. 复制文件模块

- name: 复制配置文件
  copy:
    src: files/nginx.conf
    dest: /etc/nginx/nginx.conf
    backup: yes
    validate: 'nginx -t %s'

参数说明:

src:源文件路径(相对于Playbook目录)

dest:目标路径

backup:是否备份原文件

validate:验证命令(%s会被临时文件名替换)

#### e. 模板模块

- name: 生成配置文件
  template:
    src: templates/app.conf.j2
    dest: /etc/app.conf
    mode: '0644'

模板文件示例(templates/app.conf.j2):

# {{ ansible_managed }}
ServerName {{ server_name }}
DocumentRoot {{ doc_root }}

LogLevel {{ log_level }}

参数说明:

src:模板文件路径(以.j2为后缀)

dest:目标文件路径

模板中使用Jinja2语法:{{ variable }}

#### f. 命令执行模块

- name: 检查磁盘空间
  command: df -h
  register: disk_info

- name: 显示磁盘信息
  debug:
    msg: "{{ disk_info.stdout_lines }}"

参数说明:

command:执行简单命令

register:将命令输出保存到变量

shell:执行复杂命令(支持管道、重定向等)

debug:调试模块,用于输出变量值

3.3. 变量系统

#### 定义变量的方式

在Playbook中直接定义:

- name: 使用变量示例
  hosts: all
  vars:
    package_name: nginx
    service_name: nginx
    doc_root: /var/www/html

  tasks:
    - name: 安装{{ package_name }}
      apt:
        name: "{{ package_name }}"
        state: present

在变量文件中定义:

创建vars/main.yml

---
package_name: nginx
service_name: nginx
doc_root: /var/www/html

在Playbook中引用:

- name: 使用变量文件
  hosts: all
  vars_files:
    - vars/main.yml

从命令行传递变量:

ansible-playbook playbook.yml --extra-vars "package_name=apache2"

#### 变量优先级

命令行值 (-e--extra-vars)

Inventory中的变量

Play中的vars

角色中的变量

系统事实 (ansible_facts)

#### 使用Facts(系统事实)

- name: 显示系统信息
  hosts: all
  tasks:
    - name: 显示操作系统信息
      debug:
        msg: "系统是 {{ ansible_facts['distribution'] }} {{ ansible_facts['distribution_version'] }}"

常用Facts:

ansible_facts['os_family']:操作系统家族(如Debian、RedHat)

ansible_facts['architecture']:系统架构(如x86_64)

ansible_facts['memtotal_mb']:总内存(MB)

ansible_facts['processor_vcpus']:CPU核心数

3.4. 任务控制结构

#### a. 条件判断(when)

- name: 仅在Debian系统上安装
  apt:
    name: apache2
    state: present
  when: ansible_facts['os_family'] == "Debian"

复杂条件示例:

- name: 满足多个条件时执行
  apt:
    name: nginx
    state: present
  when:
    - ansible_facts['os_family'] == "Debian"
    - ansible_facts['architecture'] == "x86_64"
    - nginx_install is defined

#### b. 循环(with_items/loop)

简单列表循环:

- name: 安装多个软件包
  apt:
    name: "{{ item }}"
    state: present
  loop:
    - nginx
    - mysql-server
    - php-fpm

字典列表循环:

- name: 创建多个用户
  user:
    name: "{{ item.name }}"
    uid: "{{ item.uid }}"
    groups: "{{ item.groups }}"
  loop:
    - { name: 'user1', uid: 1001, groups: 'admin' }
    - { name: 'user2', uid: 1002, groups: 'developer' }

#### c. 错误处理

忽略错误:

- name: 可能失败的任务
  command: /some/risky/command
  ignore_errors: yes

自定义失败条件:

- name: 检查磁盘空间
  command: df -h /
  register: disk_usage
  failed_when: "'90%' in disk_usage.stdout"

任务重试机制:

- name: 重试直到服务启动
  uri:
    url: http://localhost
  register: result
  until: result.status == 200
  retries: 5
  delay: 10

3.5. 处理器(Handlers)

处理器是特殊的任务,只有在被通知时才会执行,通常用于服务重启等操作。

示例:

- name: 配置Nginx
  hosts: webservers

  tasks:
    - name: 更新Nginx配置
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify: 重启Nginx

  handlers:
    - name: 重启Nginx
      service:
        name: nginx
        state: restarted

解释:

当模板文件发生变化时,会触发"重启Nginx"处理器

处理器定义在handlers:部分

一个处理器可以被多个任务通知

3.6. 角色(Roles)

角色是一种结构化组织Playbook的方法,将变量、任务、文件等分离到独立目录。

#### 角色目录结构

nginx/
├── defaults/          # 默认变量(低优先级)
│   └── main.yml
├── files/            # 静态文件
├── handlers/         # 处理器
│   └── main.yml
├── meta/             # 角色依赖
│   └── main.yml
├── tasks/            # 任务
│   └── main.yml
├── templates/        # 模板文件
└── vars/             # 变量(高优先级)
    └── main.yml

#### 创建Nginx角色

tasks/main.yml:

---
- name: 安装Nginx
  apt:
    name: nginx
    state: present

- name: 配置Nginx
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  notify: 重启Nginx

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

handlers/main.yml:

---
- name: 重启Nginx
  service:
    name: nginx
    state: restarted

vars/main.yml:

---
worker_processes: "{{ ansible_processor_vcpus }}"
listen_port: 80

templates/nginx.conf.j2:

user www-data;
worker_processes {{ worker_processes }};
pid /run/nginx.pid;

events {
    worker_connections 768;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    server {
        listen {{ listen_port }};

        location / {
            root /var/www/html;
            index index.html;
        }
    }
}

#### 使用角色

site.yml:

---
- name: 配置Web服务器
  hosts: webservers
  become: yes

  roles:
    - nginx
    - { role: php-fpm, when: install_php is defined }

传递变量给角色:

---
- name: 自定义角色变量
  hosts: webservers

  roles:
    - role: nginx
      vars:
        worker_processes: 4
        listen_port: 8080

3.7. 高级技巧

#### a. 包含和导入文件

include_tasks(动态包含):

- name: 包含任务
  include_tasks: setup_{{ ansible_os_family }}.yml

import_tasks(静态导入):

- name: 导入任务
  import_tasks: common_tasks.yml

include_role(动态包含角色):

- name: 包含角色
  include_role:
    name: nginx

#### b. 标签(Tags)

- name: 带标签的Playbook
  hosts: all

  tasks:
    - name: 安装软件
      apt:
        name: nginx
      tags: ['install', 'web']

    - name: 配置防火墙
      ufw:
        rule: allow
        name: 'Nginx Full'
      tags: ['firewall']

执行特定标签的任务:

ansible-playbook playbook.yml --tags "install"
ansible-playbook playbook.yml --skip-tags "firewall"

#### c. 异步任务

- name: 长时间运行的任务
  command: /usr/bin/long_running_script
  async: 300  # 超时时间(秒)
  poll: 30    # 轮询间隔(秒)

#### d. 委托任务(Delegate)

- name: 在负载均衡器上执行
  command: /usr/bin/restart_haproxy
  delegate_to: lb01

#### e. 本地执行任务

- name: 在控制节点上执行
  command: echo "This runs on the control machine"
  connection: local

3.8. 完整示例:部署WordPress应用

#### 目录结构

wordpress_deploy/
├── group_vars/
│   └── webservers.yml
├── roles/
│   ├── mysql/
│   │   ├── tasks/
│   │   │   └── main.yml
│   │   └── handlers/
│   │       └── main.yml
│   ├── nginx/
│   │   ├── tasks/
│   │   │   └── main.yml
│   │   ├── handlers/
│   │   │   └── main.yml
│   │   └── templates/
│   │       └── nginx.conf.j2
│   └── php/
│       ├── tasks/
│       │   └── main.yml
│       └── handlers/
│           └── main.yml
├── site.yml
└── inventory.ini

#### inventory.ini

[webservers]
192.168.1.10
192.168.1.11

[databases]
192.168.1.20

#### group_vars/webservers.yml

---
mysql_root_password: "SecurePassword123!"
wp_db_name: "wordpress"
wp_db_user: "wp_user"
wp_db_password: "WpUserPassword!"
wp_url: "example.com"
wp_title: "My WordPress Site"
wp_admin_user: "admin"
wp_admin_password: "AdminPassword123!"
wp_admin_email: "admin@example.com"

#### site.yml

---
- name: 配置数据库服务器
  hosts: databases
  become: yes
  roles:
    - mysql

- name: 配置Web服务器
  hosts: webservers
  become: yes
  roles:
    - nginx
    - php

#### roles/mysql/tasks/main.yml

---
- name: 安装MySQL
  apt:
    name: "{{ item }}"
    state: present
  loop:
    - mysql-server
    - python3-pymysql

- name: 启动MySQL服务
  service:
    name: mysql
    state: started
    enabled: yes

- name: 创建WordPress数据库
  mysql_db:
    name: "{{ wp_db_name }}"
    state: present
    login_user: root
    login_password: "{{ mysql_root_password }}"

- name: 创建WordPress用户
  mysql_user:
    name: "{{ wp_db_user }}"
    password: "{{ wp_db_password }}"
    priv: "{{ wp_db_name }}.*:ALL"
    host: '%'
    login_user: root
    login_password: "{{ mysql_root_password }}"

#### roles/nginx/tasks/main.yml

---
- name: 安装Nginx
  apt:
    name: nginx
    state: present

- name: 创建Nginx配置
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/sites-available/wordpress
    notify: 重启Nginx

- name: 启用WordPress站点
  file:
    src: /etc/nginx/sites-available/wordpress
    dest: /etc/nginx/sites-enabled/wordpress
    state: link
  notify: 重启Nginx

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

- name: 移除默认站点
  file:
    path: /etc/nginx/sites-enabled/default
    state: absent
  notify: 重启Nginx

#### roles/nginx/templates/nginx.conf.j2

server {
    listen 80;
    server_name {{ wp_url }};

    root /var/www/wordpress;
    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
    }

    location ~ /\.ht {
        deny all;
    }
}

#### roles/php/tasks/main.yml

---
- name: 安装PHP和扩展
  apt:
    name: "{{ item }}"
    state: present
  loop:
    - php-fpm
    - php-mysql
    - php-gd
    - php-xml
    - php-curl
    - php-mbstring
    - php-xmlrpc
    - php-soap
    - php-intl
    - php-zip

- name: 启动PHP-FPM
  service:
    name: php7.4-fpm
    state: started
    enabled: yes

- name: 下载WordPress
  unarchive:
    src: https://wordpress.org/latest.tar.gz
    dest: /var/www/
    remote_src: yes
    owner: www-data
    group: www-data
    creates: /var/www/wordpress

- name: 配置WordPress
  template:
    src: wp-config.php.j2
    dest: /var/www/wordpress/wp-config.php
    owner: www-data
    group: www-data

#### roles/php/handlers/main.yml

---
- name: 重启PHP-FPM
  service:
    name: php7.4-fpm
    state: restarted

#### roles/php/templates/wp-config.php.j2

<?php
define('DB_NAME', '{{ wp_db_name }}');
define('DB_USER', '{{ wp_db_user }}');
define('DB_PASSWORD', '{{ wp_db_password }}');
define('DB_HOST', '192.168.1.20');
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');

define('AUTH_KEY',         'put your unique phrase here');
define('SECURE_AUTH_KEY',  'put your unique phrase here');
define('LOGGED_IN_KEY',    'put your unique phrase here');
define('NONCE_KEY',        'put your unique phrase here');
define('AUTH_SALT',        'put your unique phrase here');
define('SECURE_AUTH_SALT', 'put your unique phrase here');
define('LOGGED_IN_SALT',   'put your unique phrase here');
define('NONCE_SALT',       'put your unique phrase here');

$table_prefix = 'wp_';

define('WP_DEBUG', false);

if ( !defined('ABSPATH') )
    define('ABSPATH', dirname(__FILE__) . '/');

require_once(ABSPATH . 'wp-settings.php');

#### 执行部署

ansible-playbook -i inventory.ini site.yml

4. 总结

本教程全面介绍了Ansible Playbook的编写方法,从基础概念到高级技巧,包括:

核心概念:解释了Ansible的基本组成部分和Playbook的作用

基础结构:展示了Playbook的基本格式和组成部分

模块详解:介绍了常用模块如包管理、服务管理、文件操作等

变量系统:讲解了变量的定义、使用方式和优先级

任务控制:演示了条件判断、循环和错误处理

6.

发表回复

后才能评论