Featured image of post Ansible 自动化运维:从零搭建 VPS 集群管理系统

Ansible 自动化运维:从零搭建 VPS 集群管理系统

告别手动 SSH 登录!本文手把手教你用 Ansible 实现 VPS 批量部署、配置管理和自动化运维,从入门到生产级实践,让你的每一台 VPS 都得到统一管理。

前言:为什么你需要 Ansible?

假设你有 10 台 VPS,每台都需要安装 Docker、配置 Nginx 反向代理、设置 SSL 证书、定期更新安全补丁。如果手动操作,你可能需要花费数小时甚至数天。

Ansible 让你只需编写一份配置文件,就能在所有 VPS 上执行相同的操作——这就是**基础设施即代码(Infrastructure as Code, IaC)**的核心理念。

手动运维 → 容易出错、耗时、不可复现
Ansible 运维 → 一键部署、版本可控、可审计

什么是 Ansible?

Ansible 是一款由 Red Hat 维护的开源自动化工具,具有以下特点:

  • 无代理架构:无需在目标服务器上安装任何 Agent,通过 SSH 连接即可
  • 声明式配置:你只需要描述"最终状态是什么",Ansible 会自动处理差异
  • YAML 语法:人类可读的配置语言,学习曲线平缓
  • 丰富的模块库:超过 3000 个内置模块,覆盖几乎所有运维场景

环境准备

控制节点要求

Ansible 运行在一台控制节点(Control Node)上,可以是你的本地电脑或任意一台 VPS:

组件推荐配置
操作系统Ubuntu 22.04+ / Debian 12+ / CentOS Stream 9
PythonPython 3.8+
内存2GB+
磁盘10GB+(存放 playbook 和 inventory)

受控节点要求

被管理的 VPS 只需满足:

  • Linux:Python 3 或 Python 2.7 + Jinja2
  • Windows:WinRM + PowerShell(可选)
  • 网络:控制节点能通过 SSH 访问所有受控节点

第一步:安装 Ansible

# Ubuntu/Debian
sudo apt update
sudo apt install -y python3-pip sshpass
pip3 install --user ansible

# CentOS/RHEL
sudo dnf install -y epel-release
sudo dnf install -y ansible sshpass

# 验证安装
ansible --version

第二步:配置 SSH 免密登录

这是 Ansible 高效运行的关键。在控制节点上生成 SSH 密钥并分发到所有受控节点:

# 1. 生成密钥对(如果没有的话)
ssh-keygen -t ed25519 -C "ansible@control-node" -N ""

# 2. 将公钥分发到所有 VPS
ssh-copy-id -o StrictHostKeyChecking=no user@vps1.example.com
ssh-copy-id -o StrictHostKeyChecking=no user@vps2.example.com
ssh-copy-id -o StrictHostKeyChecking.no user@vps3.example.com

# 3. 测试连接
ansible all -m ping

输出 SUCCESS 表示配置成功!

第三步:创建 Inventory 清单

Inventory 文件定义了哪些主机受 Ansible 管理,以及它们的分组方式:

# inventory.ini
[webservers]
vps1.example.com ansible_host=1.2.3.4
vps2.example.com ansible_host=5.6.7.8

[databases]
db1.example.com ansible_host=9.10.11.12

[monitoring]
mon1.example.com ansible_host=13.14.15.16

[all:vars]
ansible_user=deploy
ansible_python_interpreter=/usr/bin/python3

你可以按业务逻辑分组:

[production]
web-prod-1.example.com
web-prod-2.example.com
db-prod-1.example.com

[staging]
web-stg-1.example.com
db-stg-1.example.com

[production:children]
webservers
databases

第四步:编写第一个 Playbook

Playbook 是 Ansible 的核心——用 YAML 编写的自动化剧本。以下是一个完整的示例:

基础系统加固 Playbook

# playbook.yml
---
- name: 基础系统加固
  hosts: all
  become: true
  vars:
    timezone: Asia/Shanghai
    ntp_servers:
      - 0.cn.pool.ntp.org
      - 1.cn.pool.ntp.org
    firewall_allowed_ports:
      - 22/tcp
      - 80/tcp
      - 443/tcp

  tasks:
    - name: 更新系统包
      apt:
        update_cache: yes
        upgrade: dist
      when: ansible_os_family == "Debian"

    - name: 安装常用工具
      apt:
        name:
          - curl
          - wget
          - htop
          - jq
          - vim
          - git
          - unzip
        state: present

    - name: 配置时区
      timezone:
        timezone: "{{ timezone }}"

    - name: 配置 NTP 时间同步
      systemd:
        name: chrony
        state: started
        enabled: yes
      when: ansible_os_family == "RedHat"

    - name: 创建管理员用户
      user:
        name: admin
        groups: sudo
        shell: /bin/bash
        state: present

    - name: 配置 SSH 安全加固
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: "{{ item.regexp }}"
        line: "{{ item.line }}"
        state: present
      loop:
        - { regexp: "^#?PermitRootLogin", line: "PermitRootLogin no" }
        - { regexp: "^#?PasswordAuthentication", line: "PasswordAuthentication no" }
        - { regexp: "^#?Port ", line: "Port 22" }
      notify: 重启 SSH

    - name: 配置防火墙(UFW)
      ufw:
        rule: allow
        port: "{{ item }}"
      loop: "{{ firewall_allowed_ports }}"

  handlers:
    - name: 重启 SSH
      service:
        name: sshd
        state: restarted

运行 Playbook

# 检查语法(dry-run)
ansible-playbook playbook.yml --check --diff

# 实际执行
ansible-playbook playbook.yml -i inventory.ini -v

# 指定特定组
ansible-playbook playbook.yml -i inventory.ini --limit webservers

第五步:Docker 容器化部署

大多数现代 VPS 都运行 Docker 容器。这个 Playbook 帮你自动化整个流程:

# docker-setup.yml
---
- name: 安装和配置 Docker
  hosts: all
  become: true

  tasks:
    - name: 安装 Docker 依赖
      apt:
        name:
          - apt-transport-https
          - ca-certificates
          - curl
          - gnupg
          - lsb-release
        state: present

    - name: 添加 Docker GPG 密钥
      apt_key:
        url: https://download.docker.com/linux/ubuntu/gpg
        state: present

    - name: 添加 Docker 源
      apt_repository:
        repo: >-
          deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg]
          https://download.docker.com/linux/ubuntu
          {{ ansible_lsb.codename | lower }} stable
        state: present

    - name: 安装 Docker Engine
      apt:
        name:
          - docker-ce
          - docker-ce-cli
          - containerd.io
          - docker-compose-plugin
        state: present
        update_cache: yes

    - name: 启动 Docker 服务
      systemd:
        name: docker
        state: started
        enabled: yes

    - name: 配置 Docker 镜像加速
      copy:
        content: |
          {
            "registry-mirrors": ["https://mirror.ccs.tencentyun.com"]
          }
        dest: /etc/docker/daemon.json
      notify: 重启 Docker

    - name: 允许当前用户管理 Docker
      user:
        name: "{{ ansible_user }}"
        groups: docker
        append: yes

  handlers:
    - name: 重启 Docker
      systemd:
        name: docker
        state: restarted

第六步:Nginx 反向代理模板

使用 Ansible 的模板功能,根据变量动态生成 Nginx 配置:

# templates/nginx-site.conf.j2
server {
    listen 80;
    server_name {{ domain }};

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://{{ domain }}$request_uri;
    }
}

server {
    listen 443 ssl http2;
    server_name {{ domain }};

    ssl_certificate     /etc/letsencrypt/live/{{ domain }}/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/{{ domain }}/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;

    {% if enable_gzip %}
    gzip on;
    gzip_types text/plain application/json application/javascript text/css;
    {% endif %}

    location / {
        proxy_pass http://{{ backend_service }};
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

对应的 Playbook 任务:

- name: 部署 Nginx 站点配置
  template:
    src: nginx-site.conf.j2
    dest: /etc/nginx/sites-available/{{ domain }}
    owner: root
    group: root
    mode: '0644'
  notify: 重载 Nginx

- name: 启用站点
  file:
    src: /etc/nginx/sites-available/{{ domain }}
    dest: /etc/nginx/sites-enabled/{{ domain }}
    state: link
  notify: 重载 Nginx

第七步:SSL 证书自动续期

Let’s Encrypt 证书有效期仅 90 天,Ansible 可以帮你自动化:

# certbot-setup.yml
---
- name: 安装和配置 Certbot
  hosts: all
  become: true

  tasks:
    - name: 安装 Certbot
      apt:
        name: certbot
        state: present

    - name: 申请 SSL 证书
      community.general.certbot:
        command: certificate_only
        email: admin@{{ domain }}
        domains:
          - "{{ domain }}"
          - "www.{{ domain }}"
        challenge_hook: "systemctl reload nginx"
        account_sid: my-account
        csr: /etc/ssl/certs/{{ domain }}.csr
        state: present

    - name: 创建证书续期脚本
      copy:
        content: |
          #!/bin/bash
          certbot renew --quiet --post-hook "systemctl reload nginx"
          logger "Certificate renewal completed"
        dest: /usr/local/bin/renew-certs.sh
        mode: '0755'

    - name: 添加 Cron 定时任务(每天凌晨3点检查)
      cron:
        name: "自动续期SSL证书"
        minute: "0"
        hour: "3"
        job: "/usr/local/bin/renew-certs.sh >> /var/log/certbot-renew.log 2>&1"

第八步:多环境管理

生产环境和开发环境使用不同的配置,Ansible 通过变量文件轻松管理:

project/
├── inventory/
│   ├── production.ini
│   ├── staging.ini
│   └── development.ini
├── group_vars/
│   ├── all.yml          # 全局变量
│   ├── webservers.yml   # Web 服务器变量
│   └── databases.yml    # 数据库变量
├── host_vars/
│   ├── vps1.example.com.yml
│   └── db1.example.com.yml
├── roles/
│   ├── nginx/
│   ├── docker/
│   ├── certbot/
│   └── monitoring/
└── site.yml             # 主 Playbook

变量优先级

# group_vars/all.yml
ntp_servers:
  - 0.cn.pool.ntp.org
  - 1.cn.pool.ntp.org

# host_vars/vps1.example.com.yml(覆盖全局)
ntp_servers:
  - ntp1.mycompany.com
  - ntp2.mycompany.com

第九步:监控与告警

在每台 VPS 上部署 Uptime Kuma 或 Prometheus Node Exporter:

# monitoring-setup.yml
---
- name: 部署监控代理
  hosts: all
  become: true

  tasks:
    - name: 部署 Node Exporter
      docker_container:
        name: node-exporter
        image: prom/node-exporter:latest
        restart_policy: always
        ports:
          - "9100:9100"
        volumes:
          - /proc:/host/proc:ro
          - /sys:/host/sys:ro
          - /:/rootfs:ro

    - name: 部署 Uptime Kuma
      docker_container:
        name: uptime-kuma
        image: louislam/uptime-kuma:1
        restart_policy: always
        ports:
          - "3001:3001"
        volumes:
          - uptime-kuma-data:/app/data

第十步:CI/CD 集成

将 Ansible 集成到 GitHub Actions,每次代码变更自动部署:

# .github/workflows/deploy.yml
name: Deploy to VPS

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: 安装 Ansible
        run: pip3 install ansible

      - name: 配置 SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          ssh-keyscan -H ${{ secrets.VPS_HOST }} >> ~/.ssh/known_hosts

      - name: 运行 Playbook
        run: |
          ansible-playbook site.yml \
            -i inventory/production.ini \
            -e "deploy_version=${{ github.sha }}" \
            -v

最佳实践与避坑指南

✅ 应该做的

  1. 使用角色(Roles)组织代码:将每个功能封装为独立角色,便于复用
  2. 先 dry-run 再执行:始终使用 --check --diff 预览变更
  3. 版本控制 Playbook:像管理代码一样管理基础设施
  4. 最小权限原则:SSH 用户使用 sudo 而非 root
  5. 使用 Ansible Vault 加密敏感信息:密码、API Key 等绝不明文存储
# 加密敏感变量
ansible-vault encrypt group_vars/secrets.yml
ansible-vault edit group_vars/secrets.yml  # 编辑加密文件

# 运行时解密
ansible-playbook site.yml --ask-vault-pass

❌ 避免的

  1. 不要过度使用 raw 模块:优先使用标准模块
  2. 不要在 Playbook 中硬编码 IP 地址:使用 Inventory 管理
  3. 不要忽略错误处理:使用 ignore_errors 要谨慎
  4. 不要一次性修改太多主机:先在单台机器上测试
  5. 不要跳过文档:每个 Playbook 都应该有清晰的注释

成本对比:手动 vs Ansible

指标手动运维Ansible 自动化
10 台 VPS 初始配置~8 小时~30 分钟
批量安全补丁更新~4 小时~5 分钟
新增服务器上线~2 小时~10 分钟
出错概率高(人为失误)极低(一致执行)
审计追踪完整日志记录
月度节省时间-20+ 小时

进阶:Ansible AWX / Tower

当管理规模超过 50 台 VPS 时,可以考虑引入 AWX(Ansible 的企业版 Web UI):

# awx-deploy.yml
---
- name: 部署 AWX 管理面板
  hosts: localhost
  connection: local
  tasks:
    - name: 克隆 AWX 仓库
      git:
        repo: https://github.com/ansible/awx.git
        dest: /opt/awx
        version: latest

    - name: 构建 AWX
      shell: make deploy
      args:
        chdir: /opt/awx

AWX 提供:

  • Web 图形界面管理所有 Playbook
  • 可视化 Inventory 和主机分组
  • 定时任务和调度器
  • 用户权限管理
  • 执行历史和日志审计

总结

Ansible 是你 VPS 运维的瑞士军刀——一次编写,处处执行。从单机加固到多机集群,从容器编排到证书管理,Ansible 都能胜任。

行动建议:

  1. 今天就在你的第一台 VPS 上安装 Ansible
  2. 将你最常重复的操作写成 Playbook
  3. 逐步将其他 VPS 纳入管理
  4. 使用 Vault 保护敏感信息
  5. 将 Playbook 推入 Git 版本控制

记住:最好的自动化,是从解决一个痛点开始的。 不要试图一次性自动化一切,从小处着手,逐步扩展。


喜欢这篇文章?关注 selfvps.net 获取更多自托管和 VPS 省钱技巧!

📺 看视频版教程 → DuckDB Lab YouTube

Subscribe for more DuckDB & AI automation tutorials