4、配置管理自动化:Ansible基础、Playbook编写、Inventory管理、角色与变量

聊到PaaS平台的运维,配置管理自动化是绕不开的一环。你想想看,几十台、上百台机器,如果每台都手动改配置、装软件,那运维同学怕是要累到怀疑人生。我最早接触自动化配置管理时,用的是纯Shell脚本加scp,后来发现维护成本太高了——脚本一多,依赖关系乱成一锅粥。直到用了Ansible,才算是找到了真正顺手的工具。

4.1 Ansible基础:为什么是它?

Ansible最大的特点就是「无代理」。你不需要在被管理节点上装任何客户端,只要SSH能通就行。这一点在PaaS平台里特别实用——你想想,容器节点、虚拟机、物理机混在一起,如果每个都要装agent,光是版本兼容就够你头疼的。

我个人习惯把Ansible的架构理解成三部分:

  • 控制节点:就是你执行命令的那台机器,装Ansible就行
  • 被管理节点:目标机器,只需要Python环境和SSH
  • Inventory:管理哪些机器归谁管,说白了就是主机清单

我在项目中遇到过一个问题:刚开始用Ansible时,觉得它简单,就直接在命令行敲ad-hoc命令。后来发现,临时命令虽然方便,但没法复用。你想想,每次都要敲一遍,万一漏了某个参数,整个集群就乱套了。所以,真正要上生产环境,必须用Playbook。

小提示:Ansible的模块化设计非常棒。比如你要拷贝文件,用copy模块;要执行命令,用command或shell模块。别自己写脚本去实现这些功能,直接用现成的,既稳定又安全。

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来控制失败行为
注意:我曾经在生产环境上吃过亏——Playbook里用了shell模块执行一个脚本,脚本里有个rm -rf命令,结果变量没传对,直接把根目录删了。嗯,从那以后,我规定团队里禁止在Playbook里用rm命令,必须用file模块的state=absent。

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参数传入。

核心要点:角色的本质是「封装」。把重复的逻辑封装成角色,把变化的参数暴露成变量。这样你的Playbook就像搭积木一样,组合不同的角色就能完成不同的任务。

嗯,这里要注意一点:变量命名一定要规范。我见过有人用portPORTPort混着用,结果调试了半天才发现是大小写问题。建议统一用小写加下划线,比如nginx_portdb_host

最后,我想说一句:Ansible的配置管理自动化,核心不在于你写了多少行代码,而在于你如何设计你的Playbook和角色。好的设计,能让你的PaaS平台运维变得像呼吸一样自然。反之,如果设计得乱七八糟,那自动化反而成了负担。

我曾经接手过一个项目,前任运维写了2000多行的Playbook,全部在一个文件里。我花了整整一周才理清楚逻辑。后来我花了三天时间,把它重构成了10个角色,每个角色不超过200行。从那以后,团队里新来的同事也能快速上手了。

所以,别怕花时间在设计上。磨刀不误砍柴工,这句话在运维领域同样适用。