ansible —— 无 agent 的批量配置管理
ansible —— 无 agent 的批量配置管理
分类:07 自动化与版本 适用:Ansible 2.9 / 2.10+(collections 拆分后)/ ansible-core 2.1x 一句话:基于 SSH,不用在目标机装 agent,就能对上百台机器批量执行命令、下发配置。
1. 它解决什么问题
- 100 台机器要同时改一个参数、装一个包、重启一个服务。
- 配置要可重复执行(幂等),而不是"跑一次脚本,第二次报错"。
- 变更要有记录、能灰度、能回滚。
- 临时排查:一次性在所有 web 节点上执行某条命令。
2. 安装与最小结构
# 控制机(不装在被管节点)
pip install ansible
# 或
apt install -y ansible
dnf install -y ansible
project/
├── ansible.cfg
├── inventory/
│ ├── hosts.ini
│ └── group_vars/
├── playbooks/
│ └── site.yml
└── roles/
ansible.cfg:
[defaults]
inventory = ./inventory/hosts.ini
host_key_checking = False
forks = 20
timeout = 30
stdout_callback = yaml
retry_files_enabled = False
interpreter_python = auto_silent
[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False
3. Inventory
# inventory/hosts.ini
[web]
web01 ansible_host=10.0.0.11
web02 ansible_host=10.0.0.12
[db]
db01 ansible_host=10.0.0.21 ansible_user=dba
[all:vars]
ansible_user=ops
ansible_ssh_private_key_file=~/.ssh/id_ed25519
[web:vars]
nginx_worker=4
YAML 格式(更适合动态):
all:
children:
web:
hosts:
web01: { ansible_host: 10.0.0.11 }
web02: { ansible_host: 10.0.0.12 }
vars:
nginx_worker: 4
动态 inventory(云环境):
ansible-inventory -i inventory/ --graph # 查看解析结果
ansible-inventory --list # JSON 输出
4. Ad-hoc(一次性命令)
ansible all -m ping # 连通性
ansible web -m command -a 'uptime'
ansible web -m shell -a 'df -h | grep /$' # 支持管道
ansible web -m command -a 'systemctl status nginx'
ansible web -m setup -a 'filter=ansible_memory_mb' # 取 facts
ansible web -a 'hostname' # 默认 command
ansible all --list-hosts
常用参数:
-i inventory/hosts.ini # 指定 inventory
-u ops # 用户
-b / --become # sudo
-K # 询问 sudo 密码
-l web01 # 限制主机
--limit 'web:!web02' # 排除
-f 20 # 并发
-o # 单行输出
-v / -vvv # 详细
--check # 预演(dry-run)
--diff # 显示文件差异
command vs shell
| 模块 | 特点 |
|---|---|
command |
不经过 shell,不支持管道/重定向/变量展开,更安全 |
shell |
经过 /bin/sh,支持管道与通配 |
raw |
不依赖 Python,用于目标机无 Python 的场景 |
5. Playbook
---
- name: 部署 nginx
hosts: web
become: true
vars:
nginx_port: 8080
tasks:
- name: 安装 nginx
ansible.builtin.package:
name: nginx
state: present
- name: 下发配置
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: "0644"
backup: true
notify: reload nginx
- name: 启动并开机自启
ansible.builtin.service:
name: nginx
state: started
enabled: true
handlers:
- name: reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
常用模块
| 模块 | 作用 | 示例 |
|---|---|---|
ping |
连通性 | ansible.builtin.ping |
copy |
拷文件 | src/dest/mode |
template |
Jinja2 模板 | src=*.j2 |
file |
目录/权限/软链 | state: directory |
lineinfile |
改一行 | regexp + line |
blockinfile |
改一段 | marker |
replace |
正则替换 | regexp/replace |
package / yum / apt |
装包 | state: present |
service / systemd |
服务 | state/enabled/daemon_reload |
user / group |
用户 | name/uid/shell |
cron |
定时任务 | minute/job |
get_url |
下载 | url/dest/checksum |
unarchive |
解压 | src/dest/remote_src |
stat |
判断存在 | register + when |
find |
查找 | patterns/age |
synchronize |
rsync | 需装 rsync |
command / shell |
执行 | 配合 creates/changed_when |
uri |
HTTP 请求 | url/status_code |
docker_container |
容器 | 需 collection |
setup |
收集 facts | gather_facts |
变量与条件
- name: 只在 Debian 上执行
ansible.builtin.apt:
name: nginx
state: present
when: ansible_os_family == "Debian"
- name: 注册结果
ansible.builtin.command: /usr/bin/check_health
register: health
changed_when: false
failed_when: health.rc not in [0, 2]
- name: 条件执行
ansible.builtin.debug:
msg: "服务异常"
when: health.rc != 0
- name: 循环
ansible.builtin.user:
name: "{{ item }}"
state: present
loop:
- alice
- bob
幂等写法
# 不要这样(每次都 changed)
- ansible.builtin.shell: echo "1" >> /tmp/f
# 应该这样
- ansible.builtin.lineinfile:
path: /tmp/f
line: "1"
command/shell 加 creates 或 changed_when 控制幂等:
- ansible.builtin.command: /opt/app/init.sh
args:
creates: /opt/app/.initialized
6. 执行控制
ansible-playbook -i inventory/hosts.ini playbooks/site.yml
ansible-playbook site.yml --check --diff # 预演 + 差异
ansible-playbook site.yml --limit web01 # 只对一台
ansible-playbook site.yml --tags config # 只跑指定 tag
ansible-playbook site.yml --skip-tags slow
ansible-playbook site.yml -e "version=1.2.3" # 传变量
ansible-playbook site.yml -e @vars.yml
ansible-playbook site.yml --syntax-check
ansible-playbook site.yml --list-tasks
ansible-playbook site.yml -f 50 -vv
灰度与并发
- hosts: web
serial: 2 # 每批 2 台
max_fail_percentage: 30 # 失败超 30% 停止
serial:
- 1 # 先 1 台
- 5 # 再 5 台
- "30%" # 最后 30%
委派与一次性
- name: 在负载均衡上摘除
ansible.builtin.command: "lb remove {{ inventory_hostname }}"
delegate_to: lb01
run_once: true
7. Vault(加密敏感变量)
ansible-vault create group_vars/prod/vault.yml
ansible-vault edit group_vars/prod/vault.yml
ansible-vault encrypt secrets.yml
ansible-vault decrypt secrets.yml
ansible-vault rekey secrets.yml
ansible-vault view secrets.yml
# 使用
ansible-playbook site.yml --ask-vault-pass
ansible-playbook site.yml --vault-password-file ~/.vault_pass
~/.vault_pass 权限设 600,不要提交到 git。
8. 实战场景
8.1 批量巡检
ansible all -m shell -a 'echo "== $(hostname)"; uptime; df -h / | tail -1; free -m | grep Mem' -o
8.2 批量下发 hosts 文件
- hosts: all
become: true
tasks:
- name: 生成 hosts
ansible.builtin.template:
src: hosts.j2
dest: /etc/hosts
backup: true
notify: flush dns
handlers:
- name: flush dns
ansible.builtin.command: systemctl restart systemd-resolved
when: ansible_os_family == "Debian"
模板里可直接用 inventory 主机名:
{% for h in groups['all'] %}
{{ hostvars[h]['ansible_host'] }} {{ h }}
{% endfor %}
8.3 灰度发布
- hosts: web
serial: 1
max_fail_percentage: 0
tasks:
- name: 摘流量
...
- name: 更新二进制
ansible.builtin.copy:
src: "app-{{ version }}"
dest: /opt/app/app
mode: "0755"
- name: 重启
ansible.builtin.service:
name: app
state: restarted
- name: 健康检查
ansible.builtin.uri:
url: http://127.0.0.1:8080/health
status_code: 200
retries: 5
delay: 3
8.4 只改配置不重启(用 handler)
handler 只在任务 changed 时触发,天然避免无谓重启。
8.5 排查连接问题
ansible web -m ping -vvv
ansible web -m setup -a 'filter=ansible_python*'
# 常见:python 路径、sudo 需要密码、密钥权限
9. 常见坑
- 目标机没 Python:报
Missing sudo password之外的python not found,用raw模块先装,或ansible_python_interpreter指定路径。 command不支持管道:需要管道用shell,但注意幂等性。shell每次都 changed:加creates或changed_when: false。become需要密码:-K或配NOPASSWDsudo。- host key 校验失败:首次连接交互确认,
host_key_checking = False或预置known_hosts。 forks太小导致慢:默认 5,调到 20~50。- 变量优先级混乱:命令行
-e> play vars > host_vars > group_vars > role defaults。记住这个顺序。 group_vars目录名必须与组名一致。template的 Jinja2 未定义变量静默为空:加-e或用| default()。lineinfile重复添加:regexp写不准会每次追加,用insertafter/insertbefore配合。handler不执行:任务没 changed 就不触发,或 handler 名拼错。- CentOS 7 的 Python 2:ansible-core 2.17+ 已不支持 Python 2 目标机,老系统需用 ansible 2.9。
- collection 未安装:
community.docker、ansible.posix要ansible-galaxy collection install。 synchronize方向:默认从控制机推到目标,拉取要mode: pull。--check不是万能的:command/shell在 check 模式下默认跳过,会给出误导性的"无变化"。
10. 速查表
ansible all -m ping
ansible web -m shell -a 'uptime' -o
ansible web -m setup -a 'filter=ansible_*'
ansible-inventory --graph
ansible-doc -l | grep <kw>
ansible-doc <module>
ansible-playbook site.yml
ansible-playbook site.yml --check --diff
ansible-playbook site.yml --limit web01
ansible-playbook site.yml --tags config --skip-tags slow
ansible-playbook site.yml -e "v=1" -e @vars.yml
ansible-playbook site.yml --syntax-check --list-tasks
ansible-vault create/edit/view/encrypt/decrypt/rekey
# 核心模块
copy / template / file / lineinfile / blockinfile / replace
package / service / user / cron / get_url / unarchive
command(creates) / shell(changed_when) / uri / stat / find