#DevOps

配置管理:Ansible 入门实战

SSH 上去手动装环境?慢、不可复现、还容易漏。Ansible 用声明式 YAML + SSH 自动化管理服务器,无需 agent。本文讲清 Ansible 的核心概念(inventory/playbook/task/role)、常用模块、一个实战 playbook(装 Nginx + 配置 + 启动),以及和 Terraform 的分工(基建 vs 配置)。

本文要点

  • 主题聚焦:DevOps
  • 适合人群:独立开发者、全栈工程师、AI 应用开发者
  • 核心价值:快速理解背景、实践路径、常见误区和工程建议
作者 @admin·发布于 2026年4月21日·更新于 2026年7月23日

我见过不少团队上线新机器的流程:SSH 上去 apt install nginx,手动改 nginx.conf,粘一段配置,systemctl restart,再 ufw allow 80。一台机器十分钟,十台机器一小时,中途被打断回来就忘了改到哪一步。下次扩容,新人照着"上次怎么搞的"微信群记录复刻,环境就不一致了——线上是 A 版配置,新机器是 B 版,玄学故障就此埋下。配置漂移(Configuration Drift)是手动运维的原罪:慢、不可复现、不一致三连。Ansible 就是来治这个病的——用一份声明式 YAML 描述"机器应该长什么样",然后 SSH 上去把它变成现实,可复跑、可 review、可入库。

一、Ansible 是什么:SSH 上的声明式自动化

一句话定义:Ansible 是个用 YAML 描述、靠 SSH 执行的配置管理工具。你写一份"机器应该处于什么状态"的清单,它负责把机器变成那个状态。

和shell 脚本的本质区别在于声明式 vs 命令式。Shell 脚本是"先 apt install,再 cp 配置,再 systemctl start"——只关心动作,不管结果,跑两遍可能装两次、报错或覆盖。Ansible 是"确保 nginx 已安装、配置文件内容如下、服务处于 running"——只关心期望状态,跑一遍和跑十遍结果一致,这就是幂等。

这也是它和 Puppet/Chef 的关键差异之一:那些工具需要在目标机上跑一个常驻 agent,你得先装 agent、和中心服务通信,复杂度陡增。Ansible 只要目标机能 SSH 上去、装了 Python(几乎都有),就完事了——控制端跑 Ansible,被控端啥都不用准备。对临时接管一台新机器、或管理异构小集群,这是压倒性的易用性优势。

二、核心概念:inventory、playbook、task、module、role

五个词搞懂 Ansible 的全部心智模型。

inventory —— "管哪些机器"。一个 INI/YAML 文件,列出目标主机和分组:

ini
# inventory.ini
[web]
web1.example.com ansible_host=10.0.0.1
web2.example.com ansible_host=10.0.0.2

[db]
db1.example.com ansible_host=10.0.0.10

[prod:children]
web
db

分组可以嵌套(production 包含 web 和 db),一台机器可以属于多个组。所有操作都按组来,天然支持批量。

playbook —— "对这堆机器做什么"。一个 YAML 文件,由若干个 play 组成,每个 play 把一组主机和一组任务绑在一起:

yaml
- name: 部署 Web 服务器
  hosts: web
  become: yes          # sudo 提权
  tasks:
    - name: 装 Nginx
      ansible.builtin.apt:
        name: nginx
        state: present

task —— playbook 里的一个步骤,调用一个 module,带一组参数。name 字段是给人看的,执行时 Ansible 会打印它,所以起名要能看出意图,别写"执行任务1"这种废话。

module —— task 实际干活的单元。Ansible 内置几百个模块,覆盖装包、拷文件、起服务、改用户、操作云厂商 API 等等。模块是幂等的——apt 模块知道"包已装就跳过",不是傻乎乎再装一遍。

role —— 把一个大 playbook 按职责拆成可复用的单元。装 Nginx 是一个 role,装 Redis 是另一个 role,一台机器的 tasks 里只要 include_role: nginx,具体怎么装被封装在 role 内部。Role 可以打包分享,Ansible Galaxy 上有现成的,自己写也简单。后面会专门讲。

三、幂等 + 无 agent:Ansible 的两个底座

为什么 Ansible 能取代大部分手写运维脚本?靠这两条。

幂等(idempotent) 是核心。重复执行同一份 playbook,只有当机器当前状态和期望状态不一致时才真正改动,一致就跳过。执行日志会告诉你每个 task 是 changed 还是 ok——一次健康的复跑应该几乎全是 ok。这意味着:你可以放心地把同一个 playbook 每天跑一遍,自动收敛配置漂移,谁手动改了机器都会被"纠正"回来。手动 SSH 改东西在这套机制下变成了反模式,因为下次 playbook 一跑就给你还原。

无 agent 是易用性的底座。控制端到被控端只走 SSH,被控不需要常驻进程、不需要和中心节点通信、不需要开额外端口。这意味着:没有任何东西在目标机上"装上去就卸不下来",Ansible 本身不留痕迹(除了它执行的改动);接管一台云厂商临时开的机器、一台客户内网机器,只要 SSH 通就能管;被控的 Python 通常系统自带,少数老系统没装就 raw 模块先装上。代价是 SSH 的开销——对几千台规模、需要极强集中调度的场景,Puppet/Chef 的 agent 模型更合适,但对绝大多数中小团队,Ansible 的简单就是正义。

声明式 YAML 是表达层的好处:可读、可 diff、可进 Git,配置变更走 PR review,历史可追溯。运维终于可以像写代码一样工作。

四、常用模块速查

日常 90% 的需求用这十来个模块就够了,记熟它们比到处翻文档重要。

模块干什么典型用法
apt / yum / dnf装包(Debian / RHEL 系)name: nginx state: present
copy拷本地文件到远端src: nginx.conf dest: /etc/nginx/nginx.conf
template用 Jinja2 渲染后拷过去同 copy,但 src 是 .j2 模板
service / systemd服务管理name: nginx state: started enabled: yes
file文件/目录/权限/符号链接path: /var/www state: directory mode: '0755'
user / group用户和组管理name: deploy shell: /bin/bash
git拉代码repo: ... dest: /srv/app version: main
command / shell跑任意命令最后的选择,破坏幂等
debug打印调试信息msg: "{{ var }}"
assert断言,失败即停校验前置条件

command 和 shell 要单独说一句:尽量别用。它们默认不幂等,你写个 command: systemctl restart nginx 每次都会执行,即便没必要。能用专门模块代替的就别用 shell——service 代替 systemctl、file 代替 mkdir/chmod、apt 代替 apt-get。真要用,就配 creates/changed_when 显式告诉 Ansible "什么情况下算它改了东西",把幂等性找补回来。

五、实战 playbook:装 Nginx + 拷配置 + 启动 + 开防火墙

把上面这些组合起来,写一份能直接跑的生产级 playbook。目标:给 [web] 组的机器装 Nginx,拷一份自定义配置,确保服务启动并开机自启,放行 80 和 443。

yaml
---
# site.yml — Web 服务器基线配置
- name: 配置 Web 服务器
  hosts: web
  become: yes
  vars:
    nginx_worker_processes: auto

  tasks:
    - name: 更新 apt 缓存并安装 Nginx
      ansible.builtin.apt:
        name: nginx
        state: present
        update_cache: yes
        cache_valid_time: 3600    # 一小时内不重复 update,省时间

    - name: 拷贝 Nginx 配置文件
      ansible.builtin.copy:
        src: files/nginx.conf
        dest: /etc/nginx/nginx.conf
        owner: root
        group: root
        mode: '0644'
        validate: 'nginx -t -c %s'   # 拷贝前先语法检查,炸了就不应用
      notify: reload nginx           # 配置变了才触发,没变不 reload

    - name: 确保 Nginx 已启动并开机自启
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: yes

    - name: 放行 HTTP/HTTPS 流量
      community.general.ufw:
        rule: allow
        port: "{{ item }}"
        proto: tcp
      loop:
        - '80'
        - '443'

  handlers:
    - name: reload nginx
      ansible.builtin.service:
        name: nginx
        state: reloaded

这份 playbook 几乎每个细节都值得说道:

  • update_cache: yes + cache_valid_time:装包前先 apt update,但一小时内的缓存不重复刷,平衡正确性和速度。
  • validate: 'nginx -t -c %s':%s 是 Ansible 替换的临时文件路径。配置语法错了,这一步直接失败,不会把坏文件覆盖上去——生产配置必须带校验,这是和"手 SCP 一把梭"的关键区别。
  • notify: reload nginx + handlers:handler 只在该 task changed 时才执行,且在所有 task 跑完后统一执行一次。这意味着配置没改就不会触发 reload,跑十遍 playbook 不会无意义地 reload 十次——幂等性的精髓。
  • loop:一个 task 批量处理多个端口/用户/包,比写三个重复 task 干净。
  • become: yes:全局 sudo 提权,省得每个 task 都写。

执行就一行命令:

bash
ansible-playbook -i inventory.ini site.yml

输出会告诉你每台机器每个任务是 ok 还是 changed 还是 failed。一份 YAML 描述、一条命令收敛,这才是运维该有的样子。

六、variables + Jinja2 template:按机器生成配置

playbook 写死配置只能管一类机器。真实场景里,web1 和 web2 的 worker 数可能不同、上游地址不同、域名不同——这就需要变量和模板。

变量来源很多:host_vars/ 目录按主机放、group_vars/ 按组放、inventory 里直接写、playbook 里 vars 声明、命令行 -e 临时注入。优先级有十几个层级,记住一句话:越具体的越优先,host_vars 压过 group_vars 压过 playbook 默认值。

模板用 Jinja2,文件后缀 .j2,用 template 模块渲染:

jinja2
{# files/nginx.conf.j2 #}
worker_processes {{ nginx_worker_processes | default('auto') }};

upstream app {
{% for host in groups['app'] %}
    server {{ hostvars[host]['ansible_host'] }}:8080;
{% endfor %}
}

server {
    listen 80;
    server_name {{ server_name }};
    location / {
        proxy_pass http://app;
    }
}
yaml
# group_vars/web.yml
server_name: example.com
nginx_worker_processes: 4

这段模板最强大的是 for host in groups['app']——它能遍历 inventory 里 app 组的所有机器,自动把它们的 IP 写进 upstream。扩容时往 inventory 加一行新机器、跑一遍 playbook,Nginx 的 upstream 自动更新,完全不用手动改配置。这是 Ansible 把"基础设施即数据"做实的典型场景:inventory 既是事实来源,又驱动配置生成。

七、role 组织:把大 playbook 拆成可复用单元

playbook 超过两三百行就难维护了,真实项目用 role 拆。一个 role 是一个标准目录结构:

text
roles/
  nginx/
    tasks/main.yml      # 主任务列表
    handlers/main.yml   # 这个 role 的 handler
    templates/          # Jinja2 模板
    files/              # 静态文件
    vars/main.yml       # role 内部变量
    defaults/main.yml   # 可被覆盖的默认值
    meta/main.yml       # 依赖声明、元信息

一个 role 封装"装并配置某个组件"的完整逻辑,默认值放 defaults/(优先级最低,谁都能覆盖),内部约定放 vars/。在 playbook 里引入:

yaml
- hosts: web
  roles:
    - role: nginx
    - role: geerlingguy.redis       # 从 Galaxy 装的现成 role

role 的价值是复用和标准化。你写的 nginx role 可以丢到下一个项目直接用;团队里"装 Postgres"有了标准做法,谁都不许再手 SSH 装一遍。Ansible Galaxy 上有几千个社区 role,小团队不值得自己造的轮子(装 Docker、配 Kafka)直接拿来用,但生产用之前务必读一遍它的 task,社区 role 质量参差,别盲目信任。

八、Ansible vs Terraform:基建 vs 配置

最后讲个绕不开的问题:既然都是"基础设施即代码",Ansible 和 Terraform 啥区别?能不能只用一个?

分工是这样的:

  • Terraform 管创建基础设施:开机器、建 VPC、挂磁盘、配安全组、买域名解析。它面向云厂商 API,状态是"哪些资源已经存在",声明式地 diff 出增删。
  • Ansible 管配置已有机器:在 Terraform 开出来的机器上装软件、改配置、起服务。它面向SSH,状态是"机器里装了什么",声明式地收敛。

一个典型生产链路:Terraform 跑 apply,开出 3 台 EC2,把它们的 IP 写到一个动态 inventory 脚本里;Ansible 接管,跑 playbook 给这 3 台机器装 Nginx、拷配置、启动服务。Terraform 建,Ansible 配,各管一段,组合用。

为什么不全用 Terraform?Terraform 的 provisioner(本地脚本/远程执行)又弱又不推荐,而且它不该管机器内部状态——机器里装没装某个包不是 Terraform 该关心的事。为什么不全用 Ansible?它能开云资源(有云厂商模块),但状态管理远不如 Terraform 的 state file 清晰,改一台机器的 IP 用 Ansible 管就是灾难。让工具各司其职:建机器归 Terraform,配机器归 Ansible。

一个值得提的趋势: immutable infrastructure / Packer + Terraform 的思路是"机器一旦开出来就不再改",要更新就开新镜像滚动替换,Ansible 只在镜像构建阶段跑一次。这种模式下 Ansible 退化成镜像工厂,运维不再需要"在线收敛"。但它对小团队、对状态服务(数据库)仍不适用,在线配置管理仍是大多数场景的刚需,Ansible 仍是默认选择。

小结

  1. 手动 SSH 运维的原罪是配置漂移:慢、不可复现、不一致。Ansible 用一份声明式 YAML 描述期望状态,SSH 上去收敛,根治这个病。
  2. 五个概念就够:inventory(管哪些机器)、playbook(做什么)、task(一个步骤)、module(干活的单元)、role(可复用单元)。心智模型极简。
  3. 幂等 + 无 agent 是底座:幂等让你敢反复跑,自动纠正手动改动;无 agent 让你 SSH 通就能管,接管任意机器零成本。这是 Ansible 易用性的根源。
  4. 常用模块记熟比翻文档重要:apt/copy/template/service/file 是高频五件套;command/shell 是最后选择,真要用就配 changed_when 找补幂等。
  5. 生产 playbook 必带 validate 校验和 notify/handler 触发:配置先验证语法再应用,只在真正变更时 reload——这是和"手 SCP 一把梭"的本质区别。
  6. Ansible 和 Terraform 分工明确:Terraform 建机器(管云 API),Ansible 配机器(管 SSH),生产链路里组合用,各司其职。

一句话:运维自动化的黄金法则——如果一台机器的状态不能用一份 YAML 描述出来,那它就不算真正在你掌控之中。 能被代码描述的才能被复现、被 review、被信任;靠人脑和聊天记录维系的"配置",迟早会以一次凌晨故障的形式找上门。

Related

相关推荐