前言
管 3 台机器可以 SSH 手敲,管 30 台就需要 Ansible 了。无 Agent 架构(SSH 即通道)、声明式剧本(写"要什么"而不是"怎么做"),是自动化运维入门性价比最高的工具。
一、架构与安装
控制机(装 Ansible) --SSH--> 被管机们(啥都不用装)
dnf install -y python3-pip
pip3 install ansible-core # 或 dnf install -y ansible
# 前提: 控制机密钥能登录被管机(SSH 加固篇配过)
ssh-keygen -t ed25519
ssh-copy-id root@10.0.1.11 # 对每台被管机执行
二、Inventory:管谁
# /etc/ansible/hosts 或项目里的 inventory.ini
[webservers]
10.0.1.11 ansible_host=10.0.1.11 hostname=web01
10.0.1.12 hostname=web02
[dbservers]
10.0.2.21 hostname=db01
[prod:children] # 组嵌套
webservers
dbservers
[prod:vars] # 组变量
env=production
连通性测试:
ansible all -i inventory.ini -m ping
# 10.0.1.11 | SUCCESS => {"changed": false, "ping": "pong"}
三、Ad-hoc:临时命令(替代批量 SSH)
# -m 模块, -a 参数
ansible webservers -i inventory.ini -m command -a "uptime"
ansible webservers -m shell -a "df -h | grep data" # 需要管道时
# 最常用模块速览
ansible all -m setup -a "filter=ansible_memory_mb" # 采集 facts(系统信息)
ansible all -m copy -a "src=app.conf dest=/etc/app/app.conf"
ansible all -m yum -a "name=nginx state=present" # 装包
ansible all -m service -a "name=nginx state=started enabled=yes"
ansible all -m user -a "name=zy groups=wheel"
ansible all -m file -a "path=/opt/app state=directory mode=0755"
ansible all -m fetch -a "src=/etc/nginx/nginx.conf dest=backup/ flat=yes" # 拉文件
四、Playbook:可复用的剧本
deploy_nginx.yml:
---
- name: 批量部署 Nginx 并上线静态站点
hosts: webservers
become: yes # 提权(sudo)
vars:
nginx_port: 80
site_dir: /var/www/site
tasks:
- name: 安装 nginx
ansible.builtin.dnf:
name: nginx
state: present
- name: 创建站点目录
ansible.builtin.file:
path: "{{ site_dir }}"
state: directory
mode: "0755"
- name: 分发站点文件
ansible.builtin.copy:
src: files/index.html
dest: "{{ site_dir }}/index.html"
notify: restart nginx # 变更时通知 handler
- name: 渲染 nginx 配置
ansible.builtin.template:
src: templates/site.conf.j2
dest: /etc/nginx/conf.d/site.conf
notify: restart nginx
- name: 启动并自启 nginx
ansible.builtin.service:
name: nginx
state: started
enabled: yes
handlers: # 被通知才执行, 多次通知只跑一次
- name: restart nginx
ansible.builtin.service:
name: nginx
state: restarted
templates/site.conf.j2(Jinja2 模板,喝出变量的味道):
server {
listen {{ nginx_port }};
server_name {{ hostname }}.example.com;
root {{ site_dir }};
index index.html;
}
ansible-playbook -i inventory.ini deploy_nginx.yml
# 先演练看会发生什么(不做真改动)
ansible-playbook --check --diff -i inventory.ini deploy_nginx.yml
五、变量与条件循环
tasks:
- name: 只在 CentOS 上执行
ansible.builtin.dnf:
name: htop
state: present
when: ansible_facts['os_family'] == "RedHat"
- name: 批量建用户
ansible.builtin.user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
loop:
- { name: alice, groups: wheel }
- { name: bob, groups: docker }
- name: 收集事实并打印
ansible.builtin.setup:
register: facts
- name: 依据注册结果决定动作
ansible.builtin.debug:
msg: "内存充足"
when: facts.ansible_facts.memtotal_mb > 4000
- name: 失败即停(自定义条件)
ansible.builtin.fail:
msg: "磁盘太小, 终止部署"
when: ansible_facts['devices']['vdb'] is not defined
六、Roles:工程化组织
项目大了把 Playbook 拆成角色,目录即规范:
site.yml # 入口: 引用各 role
inventory/
roles/
└── nginx/
├── tasks/main.yml # 核心任务
├── templates/ # j2 模板
├── files/ # 静态文件
├── vars/main.yml # 角色变量
├── handlers/main.yml
└── defaults/main.yml # 可被覆盖的默认值
# site.yml
- hosts: webservers
roles:
- role: nginx
vars:
nginx_port: 8080 # 覆盖 defaults
配置查找顺序(高 → 低):-e 命令行 > playbook vars > role vars > role defaults。
也可以用现成轮子:ansible-galaxy install geerlingguy.nginx。
七、实战场景清单
| 场景 | 核心模块 |
|---|---|
| 批量改配置 | template + notify handler |
| 批量打补丁 | dnf name=* state=latest |
| 发布应用 | copy/synchronize + service restart |
| 巡检采集 | setup + fetch |
| 用户管理 | user / authorized_key |
| 定时巡检 | ansible-pull 或 cron 调 playbook |
八、常见坑
| 现象 | 原因 |
|---|---|
UNREACHABLE |
SSH 不通/密钥没配/端口不对 |
| 改了配置没重启 | 忘写 notify/handlers |
| 幂等性差,二跑报错 | 用模块而非 shell;shell 任务加 creates= 参数 |
| 变量不生效 | 查找顺序覆盖了;-v 看 variable 来源 |
| 模板渲染错 | Jinja2 语法;{{ }} 里引号嵌套问题 |
小结
- ad-hoc 管临时事,Playbook 管复用事,Roles 管工程事
- 幂等是灵魂:同一剧本跑 N 遍,结果一致
- 上线前永远
--check演练 - handler + notify 实现"按需重启"
本文是「Linux 运维」系列第 8 篇。