4、配置管理自动化:Ansible基础、Playbook编写、Inventory管理、角色与变量
聊到PaaS平台的运维,配置管理自动化是绕不开的一环。你想想看,几十台、上百台机器,如果每台都手动改配置、装软件,那运维同学怕是要累到怀疑人生。我最早接触自动化配置管理时,用的是纯Shell脚本加scp,后来发现维护成本太高了——脚本一多,依赖关系乱成一锅粥。直到用了Ansible,才算是找到了真正顺手的工具。
4.1 Ansible基础:为什么是它?
Ansible最大的特点就是「无代理」。你不需要在被管理节点上装任何客户端,只要SSH能通就行。这一点在PaaS平台里特别实用——你想想,容器节点、虚拟机、物理机混在一起,如果每个都要装agent,光是版本兼容就够你头疼的。
我个人习惯把Ansible的架构理解成三部分:
- 控制节点:就是你执行命令的那台机器,装Ansible就行
- 被管理节点:目标机器,只需要Python环境和SSH
- Inventory:管理哪些机器归谁管,说白了就是主机清单
我在项目中遇到过一个问题:刚开始用Ansible时,觉得它简单,就直接在命令行敲ad-hoc命令。后来发现,临时命令虽然方便,但没法复用。你想想,每次都要敲一遍,万一漏了某个参数,整个集群就乱套了。所以,真正要上生产环境,必须用Playbook。
4.2 Playbook编写:从简单到复杂
Playbook是Ansible的核心。说白了,它就是一组任务的集合,按顺序执行。我刚开始写Playbook时,犯过一个低级错误——把所有的任务都写在一个play里。结果呢?一旦某个任务失败,整个play就停了,后面的机器全没配置上。
后来我学乖了,把Playbook拆成多个play,每个play针对一组主机。比如:
---
- name: 配置基础环境
hosts: all
tasks:
- name: 安装常用工具
yum:
name: ['vim', 'curl', 'wget']
state: present
- name: 部署Nginx
hosts: webservers
tasks:
- name: 安装Nginx
yum:
name: nginx
state: present
- name: 启动服务
service:
name: nginx
state: started
enabled: yes
你看,这样写的好处是:基础环境配置失败了,不影响后面的Nginx部署。而且每个play可以单独调试,效率高很多。
写Playbook时,我建议你注意几个点:
- 幂等性:同一个Playbook跑两次,结果应该一样。别搞成每次执行都重启服务,那生产环境就炸了
- 变量分离:别把IP、端口这些硬编码在Playbook里,用变量代替
- 错误处理:用ignore_errors或failed_when来控制失败行为
4.3 Inventory管理:主机清单的艺术
Inventory就是告诉Ansible:你要管哪些机器,它们怎么分组。默认的Inventory文件是INI格式的,但说实话,对于PaaS平台这种动态环境,静态文件根本不够用。
我常用的Inventory管理方式有三种:
| 方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 静态INI文件 | 小规模、固定环境 | 简单直观 | 变更麻烦 |
| 动态Inventory脚本 | 云环境、容器集群 | 自动发现主机 | 需要额外开发 |
| YAML格式 | 中大型项目 | 结构清晰 | 文件稍大 |
我个人习惯用YAML格式的Inventory,因为它支持更复杂的变量定义。比如:
all:
children:
webservers:
hosts:
web01:
ansible_host: 192.168.1.10
http_port: 8080
web02:
ansible_host: 192.168.1.11
http_port: 8080
dbservers:
hosts:
db01:
ansible_host: 192.168.1.20
db_port: 3306
你看,每个主机可以有自己的变量,比如端口号、IP地址。这样在Playbook里引用时,直接用{{ http_port }}就行,非常灵活。
对于PaaS平台,我建议你使用动态Inventory。比如Kubernetes集群,你可以写一个脚本,自动从API Server拉取节点信息,生成Inventory。这样新节点加入时,Ansible自动就能感知到,不用手动改文件。
4.4 角色与变量:让Playbook可复用
当你的Playbook越来越多,你会发现很多任务是重复的——比如安装JDK、配置Nginx、设置防火墙。如果每次都复制粘贴,维护起来就是噩梦。这时候,角色(Role)就派上用场了。
角色说白了就是一个目录结构,把任务、变量、模板、文件都组织起来。我常用的角色目录结构是这样的:
roles/
common/
tasks/
main.yml
handlers/
main.yml
vars/
main.yml
templates/
files/
nginx/
tasks/
main.yml
handlers/
main.yml
vars/
main.yml
templates/
nginx.conf.j2
每个角色都有自己的职责。比如common角色负责基础配置,nginx角色负责Web服务。在Playbook里引用时,只需要写:
---
- hosts: webservers
roles:
- common
- nginx
你看,多简洁!而且每个角色可以单独测试、单独版本控制。我在项目中就把所有角色放在一个Git仓库里,团队里每个人都可以复用。
变量这块,我建议你遵循Ansible的变量优先级规则:
- 命令行传入的变量优先级最高
- Playbook中定义的变量次之
- 角色中的vars目录再次之
- Inventory中的变量优先级最低
为什么会这样设计?说白了就是为了灵活。比如你在Inventory里定义了默认的Nginx端口是80,但某个特殊环境需要8080,你可以在Playbook里覆盖它,或者在命令行用-e参数传入。
嗯,这里要注意一点:变量命名一定要规范。我见过有人用port、PORT、Port混着用,结果调试了半天才发现是大小写问题。建议统一用小写加下划线,比如nginx_port、db_host。
最后,我想说一句:Ansible的配置管理自动化,核心不在于你写了多少行代码,而在于你如何设计你的Playbook和角色。好的设计,能让你的PaaS平台运维变得像呼吸一样自然。反之,如果设计得乱七八糟,那自动化反而成了负担。
我曾经接手过一个项目,前任运维写了2000多行的Playbook,全部在一个文件里。我花了整整一周才理清楚逻辑。后来我花了三天时间,把它重构成了10个角色,每个角色不超过200行。从那以后,团队里新来的同事也能快速上手了。
所以,别怕花时间在设计上。磨刀不误砍柴工,这句话在运维领域同样适用。