前言

管 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 篇。