当您管理多台 VPS 服务器时,逐一通过 SSH 登录既无法扩展,也难以重复操作。Ansible 是一款配置管理工具,让您只需编写一次"配方",即可通过 SSH 在所有服务器上同时执行——无需在目标机器上安装任何代理程序。
什么是 Ansible,为什么要使用它?
Ansible 是 Red Hat 旗下的开源 IT 自动化工具。它使用 YAML 编写 Ansible Playbook(剧本)——这是一种声明式文件,用于描述系统应处于的状态(例如:必须安装 Nginx、某个配置文件必须存在、某个用户必须已创建)。Ansible 以幂等方式使系统达到所声明的状态——多次运行同一 Playbook(剧本)始终产生相同结果。
- 无代理 — 仅通过 SSH 通信,无需在被管理节点上安装任何程序
- 基于 YAML — 可读性强,无需深厚编程基础即可编写
- 幂等性 — 可安全重复运行,不会产生意外副作用
- 庞大的生态系统 — 3000+ 个内置模块,几乎覆盖所有任务
前提条件
- 控制节点 — 运行 Ansible 的机器(macOS、Linux 或管理用 VPS),需要 Python 3.8+
- 被管理节点 — 目标 VPS 服务器(Ubuntu 20.04/22.04/24.04 或 Debian),必须安装 Python 3
- SSH 密钥 — 您的公钥必须存在于所有被管理节点的
~/.ssh/authorized_keys中
在控制节点上安装 Ansible
Ubuntu / Debian
sudo apt update
sudo apt install -y ansible
macOS(Homebrew)
brew install ansible
Python pip(任意操作系统)
pip3 install ansible
验证安装结果:
ansible --version
# ansible [core 2.17.x]
创建清单文件
清单文件告诉 Ansible 有哪些服务器以及如何连接它们。
inventory.ini(基本格式)
[webservers]
web1 ansible_host=203.0.113.10 ansible_user=root
web2 ansible_host=203.0.113.11 ansible_user=root
[databases]
db1 ansible_host=203.0.113.20 ansible_user=root
[all:vars]
ansible_ssh_private_key_file=~/.ssh/id_ed25519
测试连通性
ansible -i inventory.ini all -m ping
每台主机返回 pong 响应,即确认连接正常。
编写第一个 Playbook(剧本)— 安装 Nginx
创建 setup-nginx.yml:
---
- name: Install and enable Nginx
hosts: webservers
become: yes
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install Nginx
apt:
name: nginx
state: present
- name: Start Nginx and enable on boot
service:
name: nginx
state: started
enabled: yes
- name: Allow port 80 in UFW
ufw:
rule: allow
port: '80'
proto: tcp
运行 Playbook(剧本):
ansible-playbook -i inventory.ini setup-nginx.yml
幂等性实践:运行此 Playbook(剧本)10 次,Nginx 也只会安装一次。Ansible 首先检查实际状态——如果软件包已存在,任务显示"ok"而非"changed",不会执行任何操作。
变量——让 Playbook(剧本)更灵活
---
- name: Install Web Stack
hosts: webservers
become: yes
vars:
php_version: "8.3"
app_user: "www-data"
tasks:
- name: Install PHP {{ php_version }}
apt:
name: "php{{ php_version }}-fpm"
state: present
将变量分离到文件中
# vars/main.yml
php_version: "8.3"
mysql_root_password: "SecurePass123"
app_domain: "example.com"
# In playbook
vars_files:
- vars/main.yml
处理程序(Handlers)— 仅在发生变更时执行任务
处理程序是一种特殊任务,只有在被其他任务"通知"时才会运行——非常适合在配置变更后重启服务。
tasks:
- name: Copy Nginx config
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart Nginx
handlers:
- name: Restart Nginx
service:
name: nginx
state: restarted
若配置未发生变更,Nginx 将不会重启——从而减少不必要的停机时间。
角色(Roles)— 组织大型 Playbook(剧本)
ansible-galaxy init roles/nginx
# Creates structure:
# roles/nginx/
# tasks/main.yml
# handlers/main.yml
# templates/
# vars/main.yml
# defaults/main.yml
新 VPS 安全加固 Playbook(剧本)
---
- name: Setup new VPS
hosts: new_servers
become: yes
tasks:
- name: Full system upgrade
apt:
upgrade: dist
update_cache: yes
- name: Install essential packages
apt:
name:
- ufw
- fail2ban
- unattended-upgrades
- curl
- git
state: present
- name: Create deploy user
user:
name: deploy
shell: /bin/bash
groups: sudo
append: yes
- name: Add SSH public key
authorized_key:
user: deploy
key: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"
- name: Disable root SSH login
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^PermitRootLogin'
line: 'PermitRootLogin no'
notify: Restart SSH
- name: Enable UFW
ufw:
state: enabled
policy: deny
- name: Allow SSH port
ufw:
rule: allow
port: '22'
proto: tcp
handlers:
- name: Restart SSH
service:
name: ssh
state: restarted
即席命令(Ad-hoc Commands)
# Check disk usage on all servers
ansible -i inventory.ini all -m shell -a "df -h /"
# Restart Nginx on webservers
ansible -i inventory.ini webservers -m service -a "name=nginx state=restarted" --become
# Copy a file to all servers
ansible -i inventory.ini all -m copy -a "src=app.conf dest=/etc/app.conf"
Ansible Galaxy:社区维护的角色可在 galaxy.ansible.com 获取。使用 ansible-galaxy install geerlingguy.mysql 安装后,可直接在 Playbook(剧本)中使用。
实用技巧
- 使用
--check(模拟运行)在应用前预览变更:ansible-playbook --check setup.yml - 添加
--diff可查看将要变更的具体文件内容 - 将清单和 Playbook(剧本)存储在 Git 中,便于版本控制和团队协作
- 提交前使用
ansible-vault encrypt vars/secrets.yml加密密码 - 为任务起具描述性的名称——可读性好的日志让故障排查更轻松
Ansible Vault — 安全加密密钥
当 Playbook(剧本)中包含密码、API 密钥或其他凭证时,请在提交版本控制前使用 Ansible Vault 对文件进行加密。这样既能将密钥排除在代码仓库之外,又能让 Ansible 在运行时正常访问。
创建并加密变量文件
# Create an encrypted secrets file
ansible-vault create vars/secrets.yml
# Edit an already-encrypted file
ansible-vault edit vars/secrets.yml
# Encrypt an existing plain-text file
ansible-vault encrypt vars/secrets.yml
# Temporarily decrypt to inspect content
ansible-vault decrypt vars/secrets.yml
在 Playbook(剧本)中引用加密文件
vars_files:
- vars/main.yml
- vars/secrets.yml # 加密文件 — Ansible 在运行时自动解密
# 以交互方式输入 Vault 密码运行
ansible-playbook -i inventory.ini deploy.yml --ask-vault-pass
# 使用密码文件运行(适合 CI/CD 流水线)
ansible-playbook -i inventory.ini deploy.yml --vault-password-file ~/.vault_pass
最佳实践:将加密后的 vars/secrets.yml 提交到 Git——加密使其可以安全存储在版本控制中。切勿将未加密的 secrets.yml 加入 .gitignore,否则日后可能意外提交明文版本。
Ansible 在 CI/CD 流水线中的应用
将 Ansible 与 CI/CD 工具集成后,每次推送代码时,即可自动部署到所有 VPS 服务器——无需任何手动操作。
GitHub Actions 工作流示例
# .github/workflows/deploy.yml
name: Deploy with Ansible
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Ansible
run: pip3 install ansible
- name: Write SSH Key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
- name: Run Ansible Playbook
run: |
ansible-playbook -i inventory.ini deploy.yml \
--vault-password-file <(echo "${{ secrets.VAULT_PASS }}")
配置此工作流后,每次合并到 main 分支都会自动触发对清单中所有服务器的部署——无需手动 SSH 操作。
| CI/CD 工具 | Ansible 集成方式 | 复杂度 |
|---|---|---|
| GitHub Actions | 在运行器中执行 pip install ansible | 低 |
| GitLab CI | 使用预装 Ansible 的 Docker镜像 | 低 |
| Jenkins | Ansible 插件或 shell 步骤 | 中 |
| AWX / Ansible Tower | 内置集成——专为此场景设计 | 高(企业级) |
常见问题排查
以下是初次使用 Ansible 时最常遇到的问题及其解决方案。
SSH 连接被拒绝
# Debug with maximum verbosity to see exact SSH commands
ansible -i inventory.ini all -m ping -vvv
# Add host fingerprint to known_hosts before first run
ssh-keyscan -H 203.0.113.10 >> ~/.ssh/known_hosts
执行任务时权限被拒绝
# 需要 root 权限的任务必须包含 become: yes
- name: Install package
apt:
name: nginx
state: present
become: yes # 特权操作必须
# 或在 play 层面为所有任务设置 become
- hosts: webservers
become: yes
被管理节点未检测到 Python
# 在清单中明确指定 Python 解释器路径
web1 ansible_host=203.0.113.10 ansible_python_interpreter=/usr/bin/python3
# 或在组变量中全局设置
[all:vars]
ansible_python_interpreter=/usr/bin/python3
调试技巧:在任何 Ansible 命令后添加 -v、-vv 或 -vvv 可提升日志详细程度。在 -vvv 级别,您能看到 Ansible 实际执行的 SSH 命令——对诊断连接问题非常有价值。
Ansible 与其他配置管理工具的对比
配置管理领域有多款工具相互竞争。了解各自的权衡取舍,有助于根据基础设施规模和团队技能选择合适的工具。
| 工具 | 是否需要代理 | 配置语言 | 适用场景 |
|---|---|---|---|
| Ansible | 否(无代理) | YAML | 任意规模,入门简单 |
| Puppet | 是 | DSL | 大型企业服务器集群 |
| Chef | 是 | Ruby DSL | 熟悉 Ruby 的团队 |
| Terraform | 否 | HCL | 云基础设施资源供给 |
当您优先考虑简洁性、零代理开销和可读的 YAML 配置时,Ansible 是管理 VPS 服务器的最实用选择。它通过您已在使用的 SSH 连接进行操作,让任何规模的团队都能逐步采用,风险极低。