自动化运维 Ansible:不需要 Agent,SSH 批量部署
在服务器运维中,批量部署、配置管理等重复操作耗时费力,手动操作不仅效率低下,还易出现失误。Ansible 作为一款无 Agent、低学习成本的自动化运维工具,能通过简单的 YAML 脚本,快速实现多台服务器的统一管理,大幅提升运维效率、降低出错概率,是运维人员必备工具,以下为大家详细讲解其使用方法。
一、为什么需要自动化运维?
1.1 真实场景
场景 1:批量部署应用
你有 10 台服务器,需要部署 OpenClaw Gateway。 手动操作: 1. SSH 登录服务器 1 2. 安装 Node.js 3. 安装 OpenClaw 4. 配置文件 5. 启动服务 ... 重复 10 遍 耗时:2 小时 × 10 = 20 小时 出错概率:高(手滑、配置写错)
Ansible 方案:
# 一个 Playbook 搞定 - hosts: all tasks: - name: 安装 Node.js apt: name: nodejs state: present - name: 安装 OpenClaw npm: name: openclaw global: yes - name: 启动服务 systemd: name: openclaw state: started
执行:
ansible-playbook deploy.yml # 10 台服务器同时部署 耗时:5 分钟 出错概率:低(自动化、幂等性)
1.2 Ansible 的优势
| 对比项 | Ansible | 其他工具(Puppet/Chef) |
|---|---|---|
| 架构 | 无 Agent(SSH) | 需要 Agent |
| 学习曲线 | 低(YAML) | 高(专用语言) |
| 推送模式 | 推送(Push) | 拉取(Pull) |
| 幂等性 | 原生支持 | 需要额外配置 |
| 模块数量 | 3000+ | 较少 |
通俗理解:
- Ansible = 自动化 SSH 脚本工具。
- 写一次 Playbook,到处运行。
- 自动处理依赖、配置、服务。
1.3 适用场景
适合用 Ansible:
- 批量部署应用。
- 配置管理(统一配置)。
- 批量执行命令。
- CI/CD 部署。
- 安全加固(批量打补丁)。
不适合用 Ansible:
- 容器编排(用 K8s)。
- 实时监控(用 Prometheus)。
- 频繁变更(用配置中心)。
二、Ansible 架构详解
2.1 架构图

2.2 核心概念
| 概念 | 说明 | 类比 |
|---|---|---|
| 控制节点 | 运行 Ansible 的机器 | 指挥中心 |
| 受管节点 | 被管理的服务器 | 执行单元 |
| Inventory | 主机列表配置文件 | 通讯录 |
| Playbook | 任务清单(YAML) | 剧本 |
| Play | Playbook 中的一个阶段 | 一幕戏 |
| Task | 具体任务 | 一个动作 |
| Module | 执行模块 | 工具 |
| Handler | 触发式任务(服务重启等) | 条件反射 |
| Role | 可复用的任务集合 | 模板 |
2.3 工作原理
1. 读取 Inventory(主机列表) 2. 读取 Playbook(任务清单) 3. 通过 SSH 连接受管节点 4. 推送 Module 到受管节点 5. 执行 Module(本地执行,结果返回) 6. 收集结果,输出报告
关键点:
- 无 Agent:只需要 SSH,不需要在受管节点安装 Ansible。
- 幂等性:多次执行结果一致(不会重复安装、重复配置)。
- 模块化:3000+ 模块覆盖各种场景。
三、安装与配置
3.1 安装 Ansible
系统要求:
- 控制节点:Python 3.9+;
- 受管节点:Python 2.7+ 或 Python 3.5+、SSH Server。
Ubuntu/Debian:
# 添加官方仓库 sudo apt update sudo apt install software-properties-common sudo add-apt-repository --yes --update ppa:ansible/ansible # 安装 sudo apt install ansible # 验证 ansible --version # 输出:ansible [core 2.16.x]
CentOS/RHEL:
# 添加 EPEL 仓库 sudo dnf install epel-release # 安装 sudo dnf install ansible # 验证 ansible --version
Mac(Homebrew):
brew install ansible ansible --version
Windows(WSL):
# 在 WSL 中安装(推荐) # 参考 Ubuntu 安装步骤 # 或使用 PowerShell(不推荐,功能受限) pip install ansible
3.2 配置 SSH 密钥
为什么需要 SSH 密钥?
- 免密码登录(自动化需要);
- 更安全(比密码安全)。
生成密钥:
# 生成 RSA 密钥对 ssh-keygen -t rsa -b 4096 -C "ansible@control" # 一路回车(不设密码,自动化需要) # 输出:~/.ssh/id_rsa(私钥) # ~/.ssh/id_rsa.pub(公钥)
分发公钥到受管节点:
# 方式 1:使用 ssh-copy-id(推荐) ssh-copy-id user@192.168.1.10 ssh-copy-id user@192.168.1.11 # 方式 2:手动复制 cat ~/.ssh/id_rsa.pub | ssh user@192.168.1.10 \ "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys" # 验证 ssh user@192.168.1.10 # 应该免密码登录
测试 Ansible 连接:
# 测试所有主机
ansible all -m ping
# 输出:
# 192.168.1.10 | SUCCESS => {
# "changed": false,
# "ping": "pong"
# }
3.3 Inventory 配置
默认位置:/etc/ansible/hosts
方式 1:编辑默认文件
sudo vim /etc/ansible/hosts
# 简单列表 192.168.1.10 192.168.1.11 192.168.1.12 # 带用户名 web1.example.com ansible_user=admin web2.example.com ansible_user=admin # 带端口 db1.example.com ansible_port=2222
方式 2:分组管理(推荐)
# /etc/ansible/hosts # Web 服务器组 [webservers] web1.example.com web2.example.com # 数据库服务器组 [dbservers] db1.example.com db2.example.com # 使用变量 [webservers] web1.example.com http_port=80 web2.example.com http_port=8080 [dbservers] db1.example.com db_port=5432 db2.example.com db_port=3306 # 组变量 [webservers:vars] ansible_user=admin ansible_python_interpreter=/usr/bin/python3 [dbservers:vars] ansible_user=dba
方式 3:动态 Inventory(高级)
# 从云平台动态获取主机列表 # AWS、Azure、GCP、OpenStack 等都支持 # 示例:AWS EC2 ansible-inventory -i aws_ec2.yaml --list
方式 4:命令行指定
# 创建自定义 Inventory cat > hosts.ini << EOF [webservers] 192.168.1.10 192.168.1.11 EOF # 使用 ansible-playbook -i hosts.ini deploy.yml
3.4 Ansible 配置
配置文件优先级:
./ansible.cfg(当前目录);~/.ansible.cfg(用户目录);/etc/ansible/ansible.cfg(系统默认)。
常用配置:
# ansible.cfg
[defaults]
# Inventory 文件位置
inventory = ./hosts.ini
# 主机密钥检查(生产环境建议开启)
host_key_checking = False
# 并发数(默认 5)
forks = 10
# 超时时间
timeout = 30
# 日志文件
log_path = /var/log/ansible.log
# 角色路径
roles_path = ./roles
# 临时文件目录
remote_tmp = /tmp/ansible-${USER}
[privilege_escalation]
# 提权配置
become = True
become_method = sudo
become_user = root
become_ask_pass = False
四、第一个 Playbook
4.1 Playbook 结构
# deploy-nginx.yml
---
- name: 部署 Nginx Web 服务器
hosts: webservers # 目标主机组
become: yes # 提权为 root
vars: # 变量
nginx_port: 80
nginx_root: /var/www/html
tasks: # 任务列表
- name: 安装 Nginx
apt:
name: nginx
state: present
update_cache: yes
- name: 启动 Nginx 服务
systemd:
name: nginx
state: started
enabled: yes
- name: 配置防火墙
ufw:
rule: allow
port: "{{ nginx_port }}"
proto: tcp
- name: 创建首页
copy:
content: |
<h1>Hello from Ansible!</h1>
<p>Server: {{ inventory_hostname }}</p>
dest: "{{ nginx_root }}/index.html"
- name: 重启 Nginx(如果配置变化)
systemd:
name: nginx
state: restarted
when: nginx_config_changed # 条件执行
4.2 运行 Playbook
# 基本运行 ansible-playbook deploy-nginx.yml # 指定 Inventory ansible-playbook -i hosts.ini deploy-nginx.yml # 检查模式(不实际执行,只显示会做什么) ansible-playbook deploy-nginx.yml --check # 逐步确认执行 ansible-playbook deploy-nginx.yml --step # 限制执行的主机 ansible-playbook deploy-nginx.yml --limit web1.example.com # 传递变量 ansible-playbook deploy-nginx.yml --extra-vars "nginx_port=8080" # 显示详细输出 ansible-playbook deploy-nginx.yml -v ansible-playbook deploy-nginx.yml -vvv # 更详细
4.3 完整示例:部署 OpenClaw Gateway
# deploy-openclaw.yml
---
- name: 部署 OpenClaw Gateway
hosts: openclaw_servers
become: yes
vars:
nodejs_version: "20.x"
openclaw_version: "latest"
gateway_port: 18789
tasks:
- name: 添加 Node.js 仓库
apt_repository:
repo: "deb https://deb.nodesource.com/node_{{ nodejs_version }} nodistro main"
state: present
update_cache: yes
- name: 安装 Node.js
apt:
name: nodejs
state: present
- name: 安装 OpenClaw
npm:
name: openclaw
version: "{{ openclaw_version }}"
global: yes
- name: 创建配置目录
file:
path: /opt/openclaw
state: directory
mode: '0755'
- name: 部署配置文件
template:
src: templates/openclaw.json.j2
dest: /opt/openclaw/openclaw.json
mode: '0644'
notify: restart openclaw
- name: 创建 systemd 服务
template:
src: templates/openclaw.service.j2
dest: /etc/systemd/system/openclaw.service
mode: '0644'
notify:
- reload systemd
- restart openclaw
- name: 启动 OpenClaw 服务
systemd:
name: openclaw
state: started
enabled: yes
- name: 配置防火墙
ufw:
rule: allow
port: "{{ gateway_port }}"
proto: tcp
handlers:
- name: reload systemd
systemd:
daemon_reload: yes
- name: restart openclaw
systemd:
name: openclaw
state: restarted
4.3 Template 文件
templates/openclaw.json.j2:
{
"gateway": {
"port": {{ gateway_port }},
"bind": "0.0.0.0",
"mode": "local"
},
"channels": {
"feishu": {
"appId": "{{ feishu_app_id }}",
"appSecret": "{{ feishu_app_secret }}",
"enabled": true
}
}
}
templates/openclaw.service.j2:
[Unit] Description=OpenClaw Gateway After=network.target [Service] Type=simple User=openclaw WorkingDirectory=/opt/openclaw ExecStart=/usr/bin/openclaw gateway start Restart=always [Install] WantedBy=multi-user.target
4.4 运行部署
# 运行 Playbook ansible-playbook deploy-openclaw.yml # 输出示例: # PLAY [部署 OpenClaw Gateway] ************************************************** # # TASK [Gathering Facts] ******************************************************** # ok: [192.168.1.10] # ok: [192.168.1.11] # # TASK [添加 Node.js 仓库] ***************************************************** # changed: [192.168.1.10] # changed: [192.168.1.11] # # TASK [安装 Node.js] *********************************************************** # changed: [192.168.1.10] # changed: [192.168.1.11] # # ... # # PLAY RECAP ******************************************************************** # 192.168.1.10 : ok=10 changed=8 unreachable=0 failed=0 # 192.168.1.11 : ok=10 changed=8 unreachable=0 failed=0
五、常用模块速查
5.1 包管理
apt(Debian/Ubuntu):
- name: 安装 Nginx apt: name: nginx state: present # present/absent/latest - name: 更新所有包 apt: upgrade: dist update_cache: yes - name: 添加仓库 apt_repository: repo: "ppa:ansible/ansible" state: present
yum/dnf(CentOS/RHEL):
- name: 安装 Nginx yum: name: nginx state: present - name: 添加 EPEL 仓库 yum: name: epel-release state: present
npm:
- name: 安装 OpenClaw npm: name: openclaw global: yes state: present
pip:
- name: 安装 Python 包 pip: name: - requests - flask state: present
5.2 文件管理
copy(复制文件):
- name: 复制配置文件 copy: src: files/config.json dest: /opt/app/config.json mode: '0644' owner: app group: app backup: yes # 备份原文件
template(模板渲染):
- name: 部署配置文件 template: src: templates/config.json.j2 dest: /opt/app/config.json mode: '0644'
file(管理文件属性):
- name: 创建目录 file: path: /opt/app/data state: directory mode: '0755' owner: app group: app - name: 删除文件 file: path: /tmp/old-config.json state: absent - name: 创建软链接 file: src: /opt/app/config.json dest: /etc/app/config.json state: link
lineinfile(修改文件行):
- name: 修改 SSH 端口 lineinfile: path: /etc/ssh/sshd_config regexp: '^#?Port' line: 'Port 2222' state: present
5.3 服务管理
systemd(服务管理):
- name: 启动 Nginx systemd: name: nginx state: started # started/stopped/restarted/reloaded enabled: yes # 开机自启 daemon_reload: yes # 重新加载 systemd 配置
service(通用服务管理):
- name: 启动服务 service: name: nginx state: started
5.4 网络操作
get_url(下载文件):
- name: 下载文件 get_url: url: https://example.com/file.tar.gz dest: /tmp/file.tar.gz checksum: sha256:abc123... # 校验和 mode: '0644'
uri(HTTP 请求):
- name: 检查 API
uri:
url: http://localhost:8080/health
method: GET
return_content: yes
status_code: 200
register: health_check
- name: 发送 POST 请求
uri:
url: https://api.example.com/deploy
method: POST
body_format: json
body:
version: "1.0.0"
headers:
Authorization: "Bearer {{ api_token }}"
5.5 命令执行
command(执行命令,不支持 shell 特性):
- name: 检查磁盘空间 command: df -h / register: disk_usage
shell(执行 shell 命令,支持管道等):
- name: 获取系统信息 shell: | uname -a cat /etc/os-release register: system_info changed_when: false # 标记为不改变状态 - name: 等待服务就绪 shell: curl -f http://localhost:8080/health register: result until: result.rc == 0 retries: 10 delay: 5
5.6 用户管理
user(用户管理):
- name: 创建用户
user:
name: openclaw
shell: /bin/bash
home: /home/openclaw
groups: docker
append: yes
create_home: yes
- name: 设置 SSH 密钥
authorized_key:
user: openclaw
key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
5.7 调试模块
debug(输出信息):
- name: 显示变量
debug:
var: inventory_hostname
- name: 显示消息
debug:
msg: "部署到 {{ inventory_hostname }} 完成"
- name: 条件输出
debug:
msg: "这是生产环境"
when: env == "production"
pause(暂停执行):
- name: 确认继续 pause: prompt: "确认要重启服务吗?(Ctrl+C 取消)"
六、变量管理
6.1 变量优先级(从高到低)
1. 命令行变量(-e) 2. Play 中的 vars 3. host_vars/主机名 4. group_vars/组名 5. Inventory 变量 6. 角色默认变量 7. 事实变量(facts)
6.2 定义变量
方式 1:Play 中定义
- hosts: webservers vars: nginx_port: 80 nginx_root: /var/www/html tasks: ...
方式 2:外部文件
- hosts: webservers vars_files: - vars/common.yml - vars/webservers.yml tasks: ...
# vars/common.yml nginx_port: 80 nginx_root: /var/www/html
方式 3:group_vars
# group_vars/webservers.yml nginx_port: 80 nginx_root: /var/www/html # group_vars/dbservers.yml db_port: 5432 db_user: postgres
方式 4:host_vars
# host_vars/web1.example.com.yml nginx_port: 80 server_name: web1.example.com # host_vars/web2.example.com.yml nginx_port: 8080 server_name: web2.example.com
方式 5:Inventory 变量
[webservers] web1.example.com nginx_port=80 web2.example.com nginx_port=8080
6.3 事实变量(Facts)
收集事实:
- hosts: all
gather_facts: yes # 默认开启
tasks:
- name: 显示系统信息
debug:
msg: |
主机名:{{ ansible_hostname }}
IP 地址:{{ ansible_default_ipv4.address }}
操作系统:{{ ansible_distribution }} {{ ansible_distribution_version }}
内存:{{ ansible_memtotal_mb }} MB
CPU 核心:{{ ansible_processor_vcpus }}
常用 Facts:
ansible_hostname # 主机名 ansible_fqdn # 完整域名 ansible_default_ipv4 # 默认 IPv4 地址 ansible_distribution # 发行版(Ubuntu/CentOS) ansible_distribution_version # 版本号 ansible_memtotal_mb # 总内存(MB) ansible_processor_vcpus # CPU 核心数 ansible_architecture # 架构(x86_64)
七、Handlers(触发器)
什么是 Handlers?
- 特殊任务,只在被 notify 时执行;
- 通常用于服务重启;
- Play 结束时统一执行(多次 notify 只执行一次)。
示例:
tasks: - name: 部署 Nginx 配置 template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf notify: restart nginx # 配置变化时触发 - name: 更新 SSL 证书 copy: src: ssl.crt dest: /etc/nginx/ssl/ notify: restart nginx # 再次触发(但只重启一次) handlers: - name: restart nginx systemd: name: nginx state: restarted
八、条件判断与循环
8.1 条件判断
when 语句:
tasks: - name: 安装 Nginx(仅 Ubuntu) apt: name: nginx state: present when: ansible_distribution == "Ubuntu" - name: 安装 Nginx(仅 CentOS) yum: name: nginx state: present when: ansible_distribution == "CentOS" - name: 重启服务(仅生产环境) systemd: name: nginx state: restarted when: env == "production" # 多条件 - name: 特殊配置 template: src: special.conf.j2 dest: /etc/app/special.conf when: - ansible_memtotal_mb > 4096 - ansible_processor_vcpus > 4
8.2 循环
简单列表:
tasks:
- name: 创建多个用户
user:
name: "{{ item }}"
state: present
loop:
- alice
- bob
- charlie
字典列表:
tasks:
- name: 安装多个包
apt:
name: "{{ item.name }}"
state: "{{ item.state }}"
loop:
- { name: nginx, state: present }
- { name: mysql, state: present }
- { name: redis, state: absent }
嵌套循环:
tasks:
- name: 创建多个虚拟主机
template:
src: vhost.conf.j2
dest: "/etc/nginx/sites-available/{{ item.0 }}-{{ item.1 }}.conf"
loop: "{{ sites | product(ports) | list }}"
vars:
sites: ['site1', 'site2']
ports: [80, 443]
九、最佳实践
9.1 目录结构
project/ ├── ansible.cfg # Ansible 配置 ├── inventory/ # Inventory 文件 │ ├── production # 生产环境 │ └── development # 开发环境 ├── group_vars/ # 组变量 │ ├── all.yml │ └── webservers.yml ├── host_vars/ # 主机变量 │ └── web1.example.com.yml ├── playbooks/ # Playbook 文件 │ ├── deploy.yml │ └── backup.yml ├── roles/ # Roles │ ├── nginx/ │ └── mysql/ ├── templates/ # Jinja2 模板 │ └── nginx.conf.j2 ├── files/ # 静态文件 │ └── config.json └── scripts/ # 辅助脚本 └── validate.sh
9.2 幂等性原则
好的 Playbook:
- name: 确保 Nginx 运行 systemd: name: nginx state: started enabled: yes # 多次执行结果一致
不好的 Playbook:
- name: 启动 Nginx command: systemctl start nginx # 每次执行都会标记为 changed
9.3 错误处理
tasks: - name: 下载文件 get_url: url: https://example.com/file.tar.gz dest: /tmp/file.tar.gz ignore_errors: yes # 忽略错误继续执行 register: download_result - name: 使用备用下载地址 get_url: url: https://backup.example.com/file.tar.gz dest: /tmp/file.tar.gz when: download_result.failed
9.4 标签(Tags)
tasks: - name: 安装 Nginx apt: name: nginx tags: - nginx - install - name: 配置 Nginx template: src: nginx.conf.j2 tags: - nginx - config # 只执行特定标签 ansible-playbook deploy.yml --tags nginx ansible-playbook deploy.yml --tags config ansible-playbook deploy.yml --skip-tags install
十、常见误区
误区 1:用 command 代替模块
错误:
- name: 安装 Nginx command: apt install nginx -y
正确:
- name: 安装 Nginx apt: name: nginx state: present
原因:模块有幂等性,command 每次都会执行。
误区 2:不处理错误
错误:
- name: 下载文件 get_url: url: https://example.com/file.tar.gz dest: /tmp/file.tar.gz # 下载失败继续执行
正确:
- name: 下载文件 get_url: url: https://example.com/file.tar.gz dest: /tmp/file.tar.gz register: result failed_when: result.failed - name: 清理 file: path: /tmp/file.tar.gz state: absent when: result.failed
误区 3:硬编码变量
错误:
- name: 部署配置 template: src: config.json.j2 dest: /opt/app/config.json # 端口、路径都写死在模板里
正确:
- hosts: all
vars:
app_port: 8080
app_root: /opt/app
tasks:
- name: 部署配置
template:
src: config.json.j2
dest: "{{ app_root }}/config.json"
十一、总结
核心要点
- 无 Agent:只需要 SSH,不需要在受管节点安装 Ansible;
- 幂等性:多次执行结果一致;
- 模块化:3000+ 模块覆盖各种场景;
- YAML:易读易写,学习曲线低。
检查清单
- 配置 SSH 密钥(免密码登录);
- 配置 Inventory(主机列表);
- 测试连接(ansible all -m ping);
- 编写第一个 Playbook;
- 使用变量和模板;
- 配置 Handlers;
- 添加错误处理。
下一步学什么?
- Roles:可复用的任务集合;
- Ansible Vault:加密敏感信息;
- 动态 Inventory:从云平台获取主机列表;
- CI/CD 集成:GitHub Actions + Ansible;
- K8s 模块:用 Ansible 部署 Kubernetes 应用。
结语
本文从 Ansible 的核心价值、架构、安装配置,到 Playbook 编写、常用模块及最佳实践,全面覆盖入门必备知识。其无 Agent、幂等性、模块化的特点,能高效解决运维痛点。掌握基础用法后,可进一步学习 Roles、加密及云平台集成,逐步提升自动化运维能力,轻松应对各类运维场景。
以上关于自动化运维 Ansible:不需要 Agent,SSH 批量部署的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » 自动化运维 Ansible:不需要 Agent,SSH 批量部署
微信
支付宝