为什么需要自动化运维工具
服务器从 3 台涨到 30 台时,最先扛不住的不是磁盘,而是你自己。每加一台机器都要手动装一遍环境、改一遍配置、重启一遍服务;更糟的是三十台机器早就被前人的手工作业改得五花八门,谁也说不清哪台是「标准状态」。
自动化运维工具要解决的就是这件事:把「这台机器应该长什么样」写成代码,让工具去把每台机器收敛到这个样子。Ansible 在同类工具里门槛最低——它不需要在被管机器上装任何客户端,全靠 SSH 连上执行;一台控制机 + 一个 inventory 文件就能开工。
手工运维是把每台机器当成独生子照顾;自动化运维是把所有机器当成同一份配置的多份副本。
- Inventory把机器按业务、环境分组,是一切批量操作的入口。
- Playbook用 YAML 写「期望状态」,而不是一步步的执行脚本。
- 幂等同一个 Playbook 跑十遍和跑一遍结果一样,这是它敢反复执行的底气。
- 无 Agent靠 SSH 推任务,被管机零安装,上手成本极低。
Inventory:把机器组织起来
Inventory 就是一张「我管哪些机器」的清单。最简单的写法是一个 ini 或 YAML 文件,把主机按组归类:
# inventory.ini
[web]
10.0.0.11
10.0.0.12
[db]
10.0.0.21
[prod:children]
web
db
[prod:vars]
ansible_user=deploy
ansible_ssh_private_key_file=~/.ssh/deploy_key
分组不是形式主义,它直接决定你能对谁批量下命令。web 组一次发配置、db 组一次做备份、prod 整组跑合规检查——分组就是运维的「寻址能力」。机器再多也不怕,配合云的动态 Inventory(从云 API 实时拉取实例列表),新机器上线即自动纳入管理。
三个常用的临时过滤技巧:--limit web 只对 web 组操作,--limit 'db[0]' 只取组内第一台,--limit 'web: !10.0.0.12' 对 web 组但排除某台。变更窗口里先挑一台灰度,全靠 --limit 兜底。
Playbook:用 YAML 描述期望状态
Playbook 是 Ansible 的剧本。它不像 shell 脚本那样写「第一步干嘛第二步干嘛」,而是写「这台机器最终应该是什么样」。下面是一份装 Nginx 并起服务的最小剧本:
- hosts: web
become: yes
tasks:
- name: 安装 nginx
apt:
name: nginx
state: present
update_cache: yes
- name: 启动并设置开机自启
service:
name: nginx
state: started
enabled: yes
- name: 部署首页
copy:
src: files/index.html
dest: /var/www/html/index.html
几个关键约定:hosts 指定对哪组机器执行;become: yes 表示提权到 root;每个 task 有名字,执行时会打印出来,日志可读。跑之前先 ansible-playbook -C site.yml 干跑一遍(check 模式),预览会改什么再真跑,是被反复验证的好习惯。
幂等:Ansible 的核心思想
上面这份剧本,跑第一遍会真的去装 Nginx,跑第二遍它发现「已经装好了」,就什么都不做——这就是幂等:同样的操作执行一次和执行多次,系统最终状态一致。Ansible 绝大多数模块都是幂等的,跑完会用颜色告诉你哪些 task 真的改了东西(changed),哪些本来就是对的(ok)。
| 写法 | 幂等性 | 说明 |
|---|---|---|
| apt / yum / copy / service | 幂等 | 先检查当前状态,不一致才动手 |
| command / shell | 非幂等 | 原样执行,Ansible 不知道它改了什么 |
| command + creates 参数 | 可幂等化 | 文件存在就跳过,适合跑一次性脚本 |
这带来一个重要纪律:能不用 shell 就不用 shell。真要用命令时,加上 creates 或 changelog 判断条件,让它在状态已达成时自动跳过。把 Playbook 写成幂等的,你才敢在凌晨两点直接重跑它而不担心把环境改坏。
变量、模板与目录分层
真正的项目不会把所有配置写死在一个剧本里。不同环境(开发/测试/生产)、不同角色(web/db)的差异,靠变量分层解决:默认变量写在 group_vars/all.yml,生产环境的差异覆盖在 group_vars/prod.yml,单机特有配置放 host_vars/。Ansible 按约定的优先级自动合并。
配置文件里那些随环境变化的片段,用 Jinja2 模板渲染:模板文件里以变量占位,执行时按当前主机的变量值生成最终配置。一份 Nginx 配置模板,渲染到测试环境是短超时,渲染到生产是完整调优参数,这就是「一套模板,多环境分发」。
# group_vars/all.yml
nginx_port: 80
worker_processes: 4
# group_vars/prod.yml(覆盖上面的值)
worker_processes: 16
keepalive_timeout: 65
当剧本多了之后,把可复用的片段抽成 role:一个 role 就是按固定目录摆好的 tasks、templates、vars,别的剧本一句 roles: - nginx 就能引用。这是从「能跑」走向「可维护」的关键一步。
常用模块与 Ad-hoc 速查
不写剧本时,Ad-hoc 一行命令就能批量干活,是日常排障的神器:
# 批量查看所有 web 组机器的磁盘占用
ansible web -m shell -a "df -h /"
# 批量校验所有机器时间同步状态
ansible prod -m command -a "timedatectl"
# 批量把文件推送到 db 组并改属主
ansible db -m copy -a "src=my.cnf dest=/etc/my.cnf owner=root"
# 批量重启 web 组的 nginx(先灰度一台)
ansible web -m service -a "name=nginx state=restarted" --limit 10.0.0.11
几个最高频模块:ping 测连通性、setup 抓主机 Facts(操作系统、内存、CPU)、git 拉代码、unarchive 解包、uri 调 HTTP 接口。记住一点:每一类操作都先想想有没有现成模块,比临时写 shell 稳得多。
一套可落地的自动化目录结构
落到能上手的最小工程,推荐这个目录骨架:
ops-repo/
├── inventory/
│ ├── prod.ini
│ └── staging.ini
├── group_vars/
│ ├── all.yml
│ └── prod.yml
├── roles/
│ ├── common/ # 基础包、时间同步、账号
│ └── nginx/ # 装包、配置模板、起服务
├── site.yml # 总入口,引用各 role
└── ansible.cfg # 配置默认行为
从最小处起步:先写 common role 把基础环境收敛,再逐个服务补 role。不要试图第一天就把全部机器全自动接管——挑一两台非核心机器,把改配置、发文件这两件高频手动活先自动化,尝到甜头再铺开。自动化运维的价值不在于「炫酷」,而在于把你从重复的 SSH 里解放出来,让配置变更有版本、有审查、可回滚。