自动化运维 Ansible Playbook 编写
Ansible Playbook 入门:从零掌握自动化部署与配置管理
Ansible 是一款简单强大的自动化运维工具,而 Playbook 是 Ansible 的核心。它使用 YAML 语法,将一组任务编排为可重复执行的“剧本”,实现对多台服务器的配置管理、应用部署和任务编排。本教程将带你从基础概念到实战编写,快速上手 Playbook。
1. 环境准备与基础概念
在开始编写 Playbook 之前,请确保已安装 Ansible(推荐使用 Python 的 pip 安装):
pip install ansible
并通过 ansible --version 确认版本。你需要一个控制节点(你的电脑)和至少一台被管理节点(远程服务器),控制节点可通过 SSH 免密登录到被管理端。
1.1 什么是 Inventory?
Inventory(清单)定义了要管理的主机列表,可以是静态文件或动态脚本。最简单的静态 Inventory 文件 hosts.ini:
[web]
192.168.1.10
192.168.1.11
[db]
db.example.com ansible_user=root
方括号内的为组名,组下每一行是一台主机,可附加连接变量。
1.2 什么是 Playbook?
Playbook 是一个以 .yml 或 .yaml 结尾的文本文件,它包含一个或多个 play(剧本),每个 play 针对一组主机执行一系列任务。一个极简的 Playbook 示例 ping.yml:
---
- name: 测试连通性
hosts: web
tasks:
- name: Ping 主机
ansible.builtin.ping:
执行命令:ansible-playbook -i hosts.ini ping.yml。
2. Playbook 核心结构详解
一个结构完整的 Playbook 包含 Play 定义、主机与用户、变量、任务列表等部分。
2.1 YAML 语法快速回顾
- 使用缩进表示层级关系,通常用两个空格。
- 键值对用冒号分隔,冒号后必须有空格。
- 列表项以短横线开头,同样需要空格。
- 可使用
true/false表示布尔值。
错误示范:name:value(缺少空格)。正确写法:name: value。
2.2 Playbook 组成要素
---
- name: 部署 Web 应用 # Play 的名称(可选但推荐)
hosts: web # 目标主机组
become: yes # 是否提权(如 sudo)
vars: # 定义变量
http_port: 80
tasks: # 任务列表
- name: 安装 Nginx # 任务的描述
ansible.builtin.yum: # 使用的模块
name: nginx
state: present
- name: 启动服务
ansible.builtin.service:
name: nginx
state: started
- hosts:字符串,支持通配符、逻辑与或(如
web:&staging)。 - tasks:按顺序执行,一个任务调用一个模块。
- 模块:Ansible 提供的工具单元,例如
copy、file、template、command。推荐使用完全限定集合名(FQCN),如ansible.builtin.yum,以确保清晰。
3. 常用模块实践
3.1 文件与目录操作
- 创建目录并设置权限:
- name: 确保应用目录存在
ansible.builtin.file:
path: /opt/myapp
state: directory
owner: appuser
group: appgroup
mode: '0755'
- 拷贝文件到远程主机:
- name: 上传配置文件
ansible.builtin.copy:
src: files/app.conf
dest: /etc/myapp/app.conf
owner: root
mode: '0644'
3.2 包管理
根据操作系统使用相应模块,例如在 CentOS/RHEL 上用 yum,Ubuntu 上用 apt:
- name: 安装多个软件包
ansible.builtin.yum:
name:
- git
- curl
- vim
state: present
3.3 服务管理
- name: 重启并设置开机自启
ansible.builtin.service:
name: nginx
state: restarted
enabled: yes
3.4 执行命令与脚本
尽量避免使用 command 或 shell 模块,优先使用专用模块以保证幂等性。但必要时:
- name: 运行自定义脚本
ansible.builtin.shell: /opt/scripts/setup.sh >> /var/log/setup.log
args:
creates: /opt/scripts/setup.done # 幂等性标记
4. 变量与 Facts
4.1 定义和使用变量
变量可在多处定义:Playbook vars 块、外部文件、Inventory、命令行等。
- hosts: web
vars:
app_version: 2.4.0
download_url: "https://example.com/release/{{ app_version }}/app.tar.gz"
tasks:
- name: 下载应用包
ansible.builtin.get_url:
url: "{{ download_url }}"
dest: /tmp/app.tar.gz
使用 {{ }} 引用变量。可在单独 YAML 文件中定义变量并通过 vars_files 导入:
vars_files:
- vars/global.yml
4.2 注册变量与 Facts
使用 register 保存任务输出结果;ansible_facts 包含目标主机的信息(IP、系统版本等)。
- name: 获取磁盘使用情况
ansible.builtin.shell: df -h /
register: disk_usage
- name: 打印结果
ansible.builtin.debug:
msg: "根分区磁盘使用状况 {{ disk_usage.stdout }}"
自定义 Facts 可放在 /etc/ansible/facts.d/ 下,动态生成主机信息。
5. 条件判断与循环
5.1 when 条件
根据变量或事实决定任务是否执行:
- name: 仅在 CentOS 上安装 yum 插件
ansible.builtin.yum:
name: yum-plugin-fastestmirror
state: present
when: ansible_os_family == "RedHat"
支持标准比较运算符,逻辑组合 and / or / not。
5.2 循环
早期使用 with_items,现在推荐 loop:
- name: 创建多个用户
ansible.builtin.user:
name: "{{ item }}"
state: present
loop:
- alice
- bob
- charlie
结合字典列表:
- name: 安装指定版本的包
ansible.builtin.apt:
name: "{{ item.name }}"
state: "{{ item.state }}"
loop:
- { name: nginx, state: latest }
- { name: git, state: present }
6. 模板与 Jinja2
使用 template 模块可将本地模板文件渲染后传送到远程主机,模板使用 Jinja2 语法。
模板文件 nginx.conf.j2:
server {
listen {{ nginx_port }};
server_name {{ server_name }};
...
}
任务定义:
- name: 渲染 nginx 配置
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
mode: '0644'
notify: restart nginx
notify 用于触发 handler,后文会讲到。
6.1 Jinja2 常用功能
- 过滤器:
{{ some_var | default('N/A') }}、{{ list | join(',') }}。 - 条件表达式:可在模板内使用
{% if %}...{% endif %}。 - 循环:
{% for item in items %}...{% endfor %}。
7. Handlers 与错误处理
7.1 Handlers
Handlers 类似于被触发的“回调任务”,仅在发生变化时执行,常用于重启服务。
tasks:
- name: 更新配置文件
ansible.builtin.template:
src: app.conf.j2
dest: /etc/myapp/app.conf
notify: restart myapp
handlers:
- name: restart myapp
ansible.builtin.service:
name: myapp
state: restarted
多个任务可通知同一个 handler,handler 会在任务执行结束后统一运行(仅运行一次)。
7.2 错误控制
- ignore_errors:任务失败时继续执行。
- failed_when:自定义失败条件。
- block/rescue/always:类似 try/except/finally 结构。
- block:
- name: 尝试执行可能失败的操作
ansible.builtin.command: /bin/false
rescue:
- name: 出错时清理
ansible.builtin.debug:
msg: "操作失败,执行回滚"
always:
- name: 始终执行的任务
ansible.builtin.debug:
msg: "完成"
8. 角色 (Roles) 与组织最佳实践
当 Playbook 变得复杂时,应使用角色对任务、变量、模板、文件等进行模块化组织。一个角色目录结构如下:
roles/
common/
tasks/
main.yml
handlers/
main.yml
templates/
files/
vars/
main.yml
defaults/
main.yml
定义角色后,在 Playbook 中使用:
---
- hosts: web
roles:
- common
- nginx
角色内部 tasks/main.yml 会自动加载,变量优先级:vars > defaults,并可通过 vars 文件夹覆盖默认值。Ansible Galaxy 可以分享和安装社区角色。
9. 实战示例:部署一个静态网站
将所有知识串联起来:
- Inventory
hosts.ini:
[web]
webserver ansible_host=192.168.1.100 ansible_user=deploy
- Playbook
deploy.yml:
---
- name: 部署静态网站
hosts: web
become: yes
vars:
site_domain: example.com
site_root: /var/www/example
tasks:
- name: 确保站点目录存在
ansible.builtin.file:
path: "{{ site_root }}"
state: directory
owner: www-data
group: www-data
- name: 上传站点文件
ansible.builtin.copy:
src: site/
dest: "{{ site_root }}"
- name: 配置 Nginx 虚拟主机
ansible.builtin.template:
src: nginx-vhost.conf.j2
dest: /etc/nginx/sites-available/{{ site_domain }}
notify: reload nginx
- name: 启用站点
ansible.builtin.file:
src: /etc/nginx/sites-available/{{ site_domain }}
dest: /etc/nginx/sites-enabled/{{ site_domain }}
state: link
notify: reload nginx
handlers:
- name: reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
- 模板
nginx-vhost.conf.j2:
server {
listen 80;
server_name {{ site_domain }};
root {{ site_root }};
index index.html;
}
运行:ansible-playbook -i hosts.ini deploy.yml 即可完成部署。
10. 检查、调试与优化
- 语法检查:
ansible-playbook deploy.yml --syntax-check - 试运行:
--check模式模拟执行,--diff显示文件变更。 - 详细输出:增加
-v、-vvv查看调试信息。 - 标签 (tags):给任务打标签,选择性执行部分任务:
执行时使用- name: 更新代码 tags: deploy ...-t deploy或--skip-tags。 - 并行与策略:通过
forks控制并发数,--limit限定主机。
不断练习并参考官方文档,你就能编写出健壮、可维护的 Ansible Playbook,轻松管理成千上万台服务器。