前言
在如今的IT运维工作中,重复的服务器配置、软件安装、服务启停等工作占用了大量时间,而且人工操作容易出错。Ansible作为一款轻量级自动化工具,无需在目标主机安装代理,只需通过SSH即可实现远程管理,而Playbook则是Ansible的核心——它用简单的YAML格式,把复杂的运维任务写成“剧本”,让基础设施管理实现“代码化”,既高效又可靠。
本文将从基础概念到实战案例,一步步教你编写和使用Ansible Playbook,即使是运维新手也能轻松上手。
一、Playbook 基础概念
1.1 Playbook 结构概述
Playbook 本质是一个YAML格式的文件,里面可以包含多个“剧本(Play)”,每个Play专门针对一组主机执行一系列任务。一个完整的Playbook通常由以下5个核心部分组成,就像搭建房子需要不同的建材一样:
- Tasks(任务):最核心的部分,每个任务调用一个Ansible模块(比如安装软件、启动服务),在目标主机上执行具体操作。
- Variables(变量):相当于“万能占位符”,可以定义重复使用的值(比如软件名称、端口号),让Playbook更灵活,不用重复修改代码。
- Templates(模板):基于Jinja2引擎的配置文件模板,能把变量动态替换成实际值,比如不同主机的Apache配置可以用同一个模板生成。
- Handlers(处理器):专门响应任务变更的“触发器”,比如配置文件修改后,通过它触发服务重启,而且不管触发多少次,最后只执行一次,避免重复操作。
- Roles(角色):把任务、变量、模板等按功能拆分到不同目录,形成模块化结构,比如把“Apache配置”“MySQL安装”做成独立角色,方便重复使用和维护。
二、Playbook 示例解析
2.1 基础Playbook示例
下面用一个完整的示例,带你看懂Playbook的基本结构——这个剧本会在目标主机上关闭防火墙、安装Apache、配置文件并启动服务:
# 文件名:test1.yaml---# 标记YAML文件开始,可省略-name:部署Apache服务# 剧本名称,方便识别,可省略gather_facts:false# 不收集主机信息,加快执行速度,可省略hosts:webservers# 目标主机组(在Ansible主机清单中定义)remote_user:root# 用root用户在目标主机执行操作tasks:# 任务列表,按顺序执行-name:测试主机连通性# 任务名称,自定义ping:# 调用ping模块,测试SSH连接是否正常-name:关闭SELinuxcommand:'/sbin/setenforce 0'# command模块执行系统命令,无需key=value格式ignore_errors:True# 忽略命令执行失败(比如SELinux已关闭),避免剧本中断-name:关闭防火墙service:name=firewalld state=stopped enabled=no# service模块管理系统服务,key=value格式传参-name:安装Apacheyum:name=httpd state=latest# yum模块安装最新版httpd-name:复制Apache配置文件copy:src=/opt/httpd.conf dest=/etc/httpd/conf/httpd.conf# 复制本地配置文件到目标主机notify:"重启Apache"# 若该任务执行后配置文件有变更,触发名为“重启Apache”的处理器-name:启动Apache服务service:name=httpd state=started enabled=yes# 启动服务并设置开机自启handlers:# 处理器列表,仅在被notify触发时执行-name:重启Apache# 名称必须和notify中的一致service:name=httpd state=restarted# 重启Apache服务这里有个关键知识点:Handlers不会立即执行,要等当前剧本中所有普通任务都完成后,才会统一执行被触发的Handlers。这样即使多次修改配置文件触发notify,也只会重启一次服务,效率更高。
再补充一个安装MySQL的简单示例,帮你巩固基础:
# 文件名:install_mysql.yaml----name:安装MariaDB数据库hosts:dbservers# 目标主机组为数据库服务器tasks:-name:安装MariaDB服务yum:name:[mariadb,mariadb-server]# 安装两个软件包state:latest-name:启动MariaDB服务service:name:mariadbstate:startedenabled:yes三、Playbook 执行与管理
3.1 命令行运行 Playbook
写好Playbook后,通过ansible-playbook命令执行,常用参数如下,直接套用即可:
# 1. 直接运行Playbookansible-playbook test1.yaml# 2. 检查语法错误(编写后必做,避免执行失败)ansible-playbook test1.yaml --syntax-check# 3. 查看剧本中的所有任务,不实际执行ansible-playbook test1.yaml --list-task# 4. 查看剧本会影响哪些主机ansible-playbook test1.yaml --list-hosts# 5. 从指定任务开始执行(比如前面任务已完成,无需重复执行)ansible-playbook test1.yaml --start-at-task='安装Apache'执行后会显示每个任务的结果:ok(无变更)、changed(有变更)、failed(失败),一目了然。
3.2 认证相关参数
如果目标主机需要SSH密码登录,或者执行任务需要sudo权限(非root用户),可以用以下参数:
# 提示输入SSH登录密码ansible-playbook test1.yaml -k# 提示输入sudo密码(执行sudo命令时需要)ansible-playbook test1.yaml -K四、变量定义与使用
变量能让Playbook摆脱“硬编码”,一次编写多次复用,比如换个软件名称、端口号,不用修改任务,只需改变量即可。
4.1 变量定义方法
最常用的是在Playbook中用vars关键字定义,直接看示例:
# 文件名:test2.yaml----name:用变量创建用户和组hosts:dbserversremote_user:rootvars:# 定义变量groupname:mysql# 组名变量username:nginx# 用户名变量tasks:-name:创建mysql组group:name:"{{ groupname }}"# 引用变量,用{{ 变量名 }}格式system:yes# 系统组gid:306# 组ID-name:创建nginx用户user:name:"{{ username }}"uid:306group:"{{ groupname }}"# 引用组名变量-name:保存主机IP到文件copy:content:"{{ ansible_default_ipv4.address }}"# 引用Ansible内置变量(获取主机IP)dest:/opt/host_ip.txtAnsible还有很多内置变量(比如主机名、内存大小等),可以通过ansible 主机名 -m setup命令查看所有内置变量。
4.2 命令行传递变量
如果临时需要修改变量值,不用改Playbook,直接用-e参数在命令行传递,优先级最高:
# 临时将用户名改为tom,覆盖Playbook中定义的nginxansible-playbook test2.yaml -e"username=tom"这里要提一下Ansible的幂等性:不管执行多少次Playbook,结果都是一致的,不会出现重复创建用户、重复安装软件的问题,非常安全。
五、条件判断与循环
5.1 条件判断 when
有时候我们需要“满足特定条件才执行任务”,比如只给IP为192.168.10.14的主机重启,或者内存大于4G才安装数据库。Ansible中用when指令实现条件判断,值为true时执行任务。
常用场景示例:
- 按IP地址执行任务:
----name:只重启指定IP的主机hosts:allremote_user:roottasks:-name:重启主机command:/sbin/shutdown-r nowwhen:ansible_default_ipv4.address == "192.168.10.14"# 变量不用加{{ }}- 按主机配置执行任务(比如内存≥4G装MariaDB,CPU≥2核装Apache):
----name:按主机配置安装软件hosts:allgather_facts:true# 必须开启(默认开启),才能获取内存、CPU等信息remote_user:roottasks:-name:内存≥4G安装MariaDByum:name=[mariadb,mariadb-server]state=presentwhen:ansible_memtotal_mb>= 4096# ansible_memtotal_mb是内置变量,代表总内存(MB)-name:内存不足提示debug:msg="内存只有{{ansible_memtotal_mb}}MB,不安装MariaDB"when:ansible_memtotal_mb < 4096-name:CPU≥2核安装Apacheyum:name=httpd state=presentwhen:ansible_processor_cores>= 2# 内置变量,CPU核心数-name:CPU核心不足提示debug:msg="CPU只有{{ansible_processor_cores}}核,不安装Apache"when:ansible_processor_cores < 2- 按主机名执行任务:
# 需先在Ansible主机清单中配置主机名映射when:inventory_hostname == "web01"# inventory_hostname是内置变量,代表主机名5.2 迭代循环 with_items 和 loop
如果需要重复执行同一个任务(比如创建多个目录、多个用户),不用写多个任务,用循环就能搞定。Ansible支持with_items和loop,功能完全一样,loop是较新的推荐用法。
常用场景示例:
- 创建多个目录:
----name:批量创建目录hosts:dbserversremote_user:roottasks:-name:创建/tmp/test1和/tmp/test2目录file:path:"{{ item }}"# item代表循环中的每个值state:directory# 状态为目录loop:# 循环列表-/tmp/test1-/tmp/test2- 创建多个用户(带不同组):
----name:批量创建用户hosts:dbserversremote_user:roottasks:-name:创建test1和test2用户user:name:"{{ item.name }}"# 引用循环中的name字段state:presentgroups:"{{ item.groups }}"# 引用循环中的groups字段loop:-name:test1groups:wheel# test1用户加入wheel组-name:test2groups:root# test2用户加入root组执行后,目标主机上会成功创建两个用户,且所属组符合配置。
六、模板系统 Templates
有时候不同主机的配置文件只有少数参数不同(比如端口号、网站根目录),如果每个主机写一个配置文件,维护起来很麻烦。这时用Jinja2模板就能解决——一个模板文件,配合变量,自动生成不同主机的配置文件。
6.1 Jinja2 模板引擎
Jinja2是基于Python的模板引擎,核心作用是“变量替换”:在模板文件中用{{ 变量名 }}标记需要替换的内容,Ansible执行时会把变量换成实际值,生成最终的配置文件。
6.2 模板使用示例
以Apache配置文件为例,步骤如下:
第一步:创建Jinja2模板文件
先复制默认的Apache配置文件,改名为.j2后缀(标识为模板文件):
# 复制默认配置文件到/opt目录,并重命名cp/etc/httpd/conf/httpd.conf /opt/httpd.conf.j2编辑模板文件,把需要动态修改的参数换成变量:
vim/opt/httpd.conf.j2# 42行:监听端口,替换为{{ http_port }}Listen{{http_port}}# 95行:服务器名,替换为{{ server_name }}ServerName{{server_name}}# 119行:网站根目录,替换为{{ root_dir }}DocumentRoot"{{ root_dir }}"第二步:在主机清单中定义变量
编辑Ansible主机清单(默认路径/etc/ansible/hosts),给webservers组的主机指定变量值:
vim/etc/ansible/hosts[webservers]192.168.10.14http_port=80server_name=www.test.com:80root_dir=/etc/httpd/htdocs192.168.10.15http_port=8080server_name=www.demo.com:8080root_dir=/opt/htdocs这样不同主机的变量值不同,生成的配置文件也会不一样。
第三步:编写Playbook使用模板
# 文件名:template_demo.yaml----hosts:webserversremote_user:rootvars:service:httpd# 定义服务名变量tasks:-name:安装Apacheyum:name={{service}}state=latest-name:部署动态配置文件template:src:/opt/httpd.conf.j2# 模板文件路径dest:/etc/httpd/conf/httpd.conf# 目标主机的配置文件路径notify:重启Apache# 配置文件变更后触发重启-name:创建网站根目录file:path={{root_dir}}state=directory# 引用主机清单中的root_dir变量-name:启动Apache服务service:name={{service}}state=started enabled=yeshandlers:-name:重启Apacheservice:name={{service}}state=restarted第四步:执行并验证
ansible-playbook template_demo.yaml# 验证配置文件是否生效(以192.168.10.14为例)ansible192.168.10.14 -a'grep -E "Listen|ServerName|DocumentRoot" /etc/httpd/conf/httpd.conf'执行后会看到,配置文件中的变量已经被替换成主机清单中定义的值,实现了“一个模板,多机复用”。
七、Tags 模块
如果Playbook中有很多任务,而你只想执行其中几个(比如只更新配置文件,不安装软件),用Tags就能实现——给任务打标签,执行时指定标签即可。
7.1 Tags 的作用
- 给单个或多个任务打标签,执行Playbook时可指定只运行带该标签的任务;
- 特殊标签
always:无论指定哪个标签,带always的任务都会执行(比如备份文件)。
7.2 Tags 使用示例
# 文件名:tags_demo.yaml----hosts:webserversremote_user:roottasks:-name:备份原有配置文件copy:src=/etc/httpd/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf.baktags:always# 始终执行,不管指定哪个标签-name:安装Apacheyum:name=httpd state=latesttags:install# 标签名为install-name:更新Apache配置文件copy:src=/opt/httpd.conf dest=/etc/httpd/conf/httpd.conftags:config# 标签名为config-name:重启Apacheservice:name=httpd state=restartedtags:restart# 标签名为restart7.3 执行指定Tags
# 1. 只执行带config标签的任务(备份任务会自动执行,因为有always标签)ansible-playbook tags_demo.yaml --tags="config"# 2. 执行install和restart两个标签的任务ansible-playbook tags_demo.yaml --tags="install,restart"# 3. 只执行always标签的任务(仅备份配置文件)ansible-playbook tags_demo.yaml --tags="always"Tags在大型Playbook中非常实用,能节省执行时间,避免不必要的操作。
八、Roles 模块
当你的Playbook越来越复杂(比如要管理Web、数据库、缓存等多种服务),把所有任务都写在一个文件里,维护起来会非常困难。Roles就是为了解决这个问题——把不同服务的配置拆分成独立的“角色”,结构清晰,可重复使用。
8.1 Roles 概念与优势
Roles 是Ansible的模块化组织方式,把任务、变量、模板等按功能拆分到固定目录中,优势很明显:
- 结构清晰:每个角色负责一个具体功能(比如httpd角色管Apache,mysql角色管数据库);
- 复用性高:一个角色可以在多个项目中使用,不用重复写代码;
- 易于维护:修改某个服务的配置,只需改对应的角色目录,不影响其他部分。
8.2 Roles 目录结构
每个角色都有固定的目录结构,创建后如下(以web和db两个角色为例):
roles/ # 总角色目录(默认路径/etc/ansible/roles) ├── web/ # Apache角色目录 │ ├── files/ # 存放copy/script模块需要的文件(比如静态配置文件) │ ├── templates/ # 存放Jinja2模板文件 │ ├── tasks/ # 任务目录,必须有main.yml(定义角色的任务) │ ├── handlers/ # 处理器目录,必须有main.yml │ ├── vars/ # 变量目录,main.yml中定义角色专属变量 │ ├── defaults/ # 默认变量目录,优先级低于vars │ └── meta/ # 依赖关系目录,main.yml中定义角色依赖 └── db/ # MySQL角色目录 ├── files/ ├── templates/ ├── tasks/ ├── handlers/ ├── vars/ ├── defaults/ └── meta/核心是tasks/main.yml(角色的任务入口)和vars/main.yml(角色的变量定义),其他目录用不到可以为空或不创建。
8.3 使用Roles的步骤
- 创建总角色目录(yum安装Ansible后默认已有):
mkdir -p /etc/ansible/roles; - 创建具体角色目录(比如httpd、mysql、php);
- 在每个角色目录下创建子目录(files、tasks、vars等);
- 在tasks、vars等目录下创建main.yml文件,编写任务和变量;
- 编写site.yml文件,调用不同角色;
- 执行site.yml。
8.4 Roles 实战案例
下面通过部署LAMP(Linux+Apache+MySQL+PHP)环境,带你实战Roles的使用。
8.4.1 创建项目目录
先创建三个角色(httpd、mysql、php)的目录和必要文件:
# 创建角色目录和子目录mkdir-p /etc/ansible/roles/{httpd,mysql,php}/{files,templates,tasks,handlers,vars,defaults,meta}# 在每个角色的tasks、vars等目录创建main.ymltouch/etc/ansible/roles/httpd/{tasks,vars,handlers}/main.ymltouch/etc/ansible/roles/mysql/{tasks,vars,handlers}/main.ymltouch/etc/ansible/roles/php/{tasks,vars,handlers}/main.yml8.4.2 编写httpd模块(Apache角色)
- 编写任务文件(定义安装、启动Apache):
vim/etc/ansible/roles/httpd/tasks/main.yml - name: 安装Apache软件 yum:name={{pkg}}state=latest - name: 启动Apache服务并设置开机自启 service:name={{svc}}state=startedenabled=yes- 编写变量文件(定义软件名和服务名):
vim/etc/ansible/roles/httpd/vars/main.yml pkg: httpd# 软件包名svc: httpd# 服务名8.4.3 编写mysql模块(MySQL角色)
- 编写任务文件:
vim/etc/ansible/roles/mysql/tasks/main.yml - name: 安装MariaDB软件 yum:name={{pkg}}state=latest - name: 启动MariaDB服务并设置开机自启 service:name={{svc}}state=startedenabled=yes- 编写变量文件:
vim/etc/ansible/roles/mysql/vars/main.yml pkg: - mariadb# MariaDB客户端- mariadb-server# MariaDB服务端svc: mariadb# 服务名8.4.4 编写php模块(PHP角色)
- 编写任务文件:
vim/etc/ansible/roles/php/tasks/main.yml - name: 安装PHP软件 yum:name={{pkg}}state=latest - name: 启动php-fpm服务并设置开机自启 service:name={{svc}}state=startedenabled=yes- 编写变量文件:
vim/etc/ansible/roles/php/vars/main.yml pkg: - php# PHP核心- php-fpm# PHP进程管理器svc: php-fpm# 服务名8.4.5 编写roles剧本(调用角色)
创建site.yml文件,指定哪些主机调用哪些角色:
vim/etc/ansible/site.yml --- - name: 部署LAMP环境 hosts: webservers# 目标主机组(需要在/etc/ansible/hosts中定义)remote_user: root roles: - httpd# 调用Apache角色- mysql# 调用MySQL角色- php# 调用PHP角色执行并验证
# 进入Ansible配置目录执行cd/etc/ansible ansible-playbook site.yml# 验证服务是否正常运行(在目标主机执行)systemctl is-active httpd# 输出active表示正常systemctl is-active mariadb systemctl is-active php-fpm执行成功后,目标主机就部署好了LAMP环境,整个过程结构清晰,后续要修改Apache配置,只需改httpd角色的相关文件即可。
总结
Ansible Playbook 是运维自动化的“瑞士军刀”,通过YAML格式的简单语法,把复杂的运维任务变成可复用、可维护的“代码”。本文从基础结构入手,逐步讲解了变量、条件判断、循环、模板、Tags和Roles等核心功能,每个部分都配有实战示例,新手也能跟着操作。
核心要点回顾:
- Playbook的核心是“任务”,通过模块实现具体操作;
- 变量、模板让Playbook更灵活,避免重复代码;
- Tags能精准执行指定任务,提高效率;
- Roles是模块化的关键,适合管理复杂环境。