路线图

23-Ansible

星辉 2026-07-02 阅读 4 min 684 字 路线图
23-Ansible 封面

Ansible 是开源的配置管理和批量运维自动化工具。与 Terraform 管理云资源不同,Ansible 专注于操作系统层面的配置——装软件、改配置、启服务。本章覆盖 Ansible 的核心概念、Playbook/Role 工程化、动态 Inventory、Vault 加密,以及与 Terraform 的定位区别。

核心优势#

  • 无 Agent:不需要在目标机器上安装客户端,只要 SSH 通就能管。接手已有机器时不用先装 Agent。
  • SSH 推送:控制机推送任务到目标机执行,权限边界清晰。相比 Puppet/Chef 的 pull 模式,push 模式在紧急场景响应更快。
  • 幂等性:大多数模块设计为幂等,执行一次和执行十次效果相同。package 模块检查软件包是否已安装,file 模块检查文件是否已存在。
  • YAML 描述:声明式描述目标状态,直观且易 Code Review。

Inventory 管理#

Inventory 定义了 Ansible 要管理的主机列表及分组。

静态 Inventory#

ini
# inventory/hosts
[web]
web-01.example.com
web-02.example.com ansible_port=2222

[db]
db-01.example.com ansible_user=ubuntu ansible_become=true

[production:children]
web
db

[web:vars]
nginx_worker_processes=4
app_env=production

动态 Inventory(AWS EC2)#

机器在云上动态扩缩,不可能手动维护 Inventory。Ansible 提供 aws_ec2 插件:

yaml
# inventory/aws_ec2.yml
plugin: aws_ec2
regions:
  - us-west-2
filters:
  instance-state-name: running
  tag:Environment: production
keyed_groups:
  - key: tags.Role       # 按 Tag:Role 自动分组
    prefix: role
  - key: instance_type   # 按实例类型分组
    prefix: type
hostnames:
  - private-ip-address   # 内网 IP 作为主机名
bash
ansible-inventory -i inventory/aws_ec2.yml --list
ansible-inventory -i inventory/aws_ec2.yml --graph

变量组织#

text
inventory/
├── hosts.yml
├── group_vars/
│   ├── all.yml          # 所有主机共用
│   ├── web.yml          # web 组变量
│   └── production.yml
└── host_vars/
    └── db-01.example.com.yml   # 单台主机变量

变量优先级:host_vars > group_vars/<specific-group> > group_vars/all。

常用模块速查#

yaml
# copy:上传文件
- name: Upload config file
  copy:
    src: files/nginx.conf
    dest: /etc/nginx/nginx.conf
    owner: root
    mode: '0644'
    backup: yes

# template:渲染 Jinja2 模板后上传
- name: Render and upload template
  template:
    src: templates/app.conf.j2
    dest: /etc/app/app.conf

# file:创建目录/设置权限
- name: Create log directory
  file:
    path: /var/log/myapp
    state: directory
    owner: www-data
    mode: '0755'

# yum/apt:包管理
- name: Install packages
  apt:
    name: "{{ packages }}"
    state: present
    update_cache: yes
  vars:
    packages: [curl, jq, net-tools]

# service:服务管理
- name: Ensure nginx is running and enabled
  service:
    name: nginx
    state: started
    enabled: yes

# command/shell:执行命令
- name: Check disk usage
  command: df -h /data
  register: disk_info
  changed_when: false    # 查询操作不算 changed

Playbook 结构#

一个完整的 Playbook 示例——部署 Node Exporter:

yaml
- name: Deploy Prometheus Node Exporter
  hosts: all
  become: true
  vars:
    node_exporter_version: "1.7.0"
    install_dir: "/opt/node_exporter"

  pre_tasks:
    - name: Check if node_exporter is already installed
      stat:
        path: "{{ install_dir }}/node_exporter"
      register: binary_stat

  tasks:
    - name: Create node_exporter user
      user:
        name: node_exporter
        system: yes
        shell: /usr/sbin/nologin

    - name: Download node_exporter
      get_url:
        url: "https://github.com/prometheus/node_exporter/releases/download/v{{ node_exporter_version }}/node_exporter-{{ node_exporter_version }}.linux-amd64.tar.gz"
        dest: "/tmp/node_exporter.tar.gz"
      when: not binary_stat.stat.exists

    - name: Create systemd service
      template:
        src: templates/node_exporter.service.j2
        dest: /etc/systemd/system/node_exporter.service
      notify:
        - Reload systemd
        - Restart node_exporter

    - name: Ensure node_exporter is running
      service:
        name: node_exporter
        state: started
        enabled: yes

  handlers:
    - name: Reload systemd
      systemd:
        daemon_reload: yes
    - name: Restart node_exporter
      service:
        name: node_exporter
        state: restarted

执行:

bash
# 语法检查
ansible-playbook playbooks/deploy-node-exporter.yml --syntax-check

# dry run(不实际执行,只显示会做什么)
ansible-playbook playbooks/deploy-node-exporter.yml --check --diff

# 只在特定主机组执行
ansible-playbook playbooks/deploy-node-exporter.yml -l web

# 只执行特定 tags
ansible-playbook playbooks/deploy-node-exporter.yml --tags "install,config"

Role 工程化#

当多个 Playbook 有重复逻辑时,抽成 Role。Role 是标准化的目录结构,可跨 Playbook 复用:

text
roles/
└── node_exporter/
    ├── defaults/
    │   └── main.yml      # 默认变量(优先级最低,可被覆盖)
    ├── vars/
    │   └── main.yml      # 内部变量(优先级较高,不对外暴露)
    ├── tasks/
    │   ├── main.yml      # 任务入口
    │   ├── install.yml
    │   └── configure.yml
    ├── handlers/
    │   └── main.yml
    ├── templates/
    │   └── node_exporter.service.j2
    └── meta/
        └── main.yml      # 依赖声明
yaml
# 在 Playbook 中使用 Role
- name: Setup monitoring
  hosts: all
  become: true
  roles:
    - role: node_exporter
      vars:
        node_exporter_version: "1.8.0"
    - role: filebeat
      when: ansible_os_family == "Debian"

经验法则:可配置的参数放 defaults/,不对外的内部常量才放 vars/。vars/ 优先级比 group_vars 还高,放错了会导致外部传入的覆盖不生效。

Jinja2 模板#

ini
# templates/node_exporter.service.j2
[Unit]
Description=Prometheus Node Exporter
After=network.target

[Service]
User={{ node_exporter_user }}
Type=simple
ExecStart={{ install_dir }}/node_exporter \
  --web.listen-address=:{{ listen_port }}
Restart=on-failure

[Install]
WantedBy=multi-user.target

Ansible Vault:加密敏感变量#

数据库密码、API Token 不应该明文存在代码仓库:

bash
# 创建加密的变量文件
ansible-vault create group_vars/production/vault.yml

# 编辑加密文件
ansible-vault edit group_vars/production/vault.yml

# 加密已有明文文件
ansible-vault encrypt group_vars/production/secrets.yml

# 执行时提供密码
ansible-playbook site.yml --ask-vault-pass

# 从文件读取密码(CI/CD 场景)
ansible-playbook site.yml --vault-password-file ~/.vault_pass

幂等性陷阱#

command 和 shell 模块本身不知道命令是否改变了什么,默认每次报 changed。需要显式告诉 Ansible 什么情况算 changed:

yaml
# 查询操作,永远不算 changed
- name: Get current timezone
  command: timedatectl show --property=Timezone
  register: tz_result
  changed_when: false

# 配合 creates 参数实现幂等(文件存在时跳过)
- name: Initialize once
  command: /opt/scripts/one_time_setup.sh
  args:
    creates: /opt/.setup_done

vs Terraform#

维度AnsibleTerraform
管理对象操作系统配置(装软件、改配置)云基础设施(VPC、ECS、RDS)
状态管理无 state(每次执行检查当前状态)有 state(记录资源 ID 和依赖)
幂等机制模块内置检查声明式 + state diff
执行模式SSH pushAPI 调用
适用场景机器初始化、配置同步、批量运维云资源创建/变更/销毁

协作模式:Terraform 创建云资源(ECS 实例、安全组),Ansible 配置机器(装 Docker、部署应用、改内核参数)。两者互补不冲突。

性能优化#

大批量执行时默认并发数只有 5,200 台机器要跑 40 批。优化 ansible.cfg:

ini
[defaults]
forks = 50                    # 并发数调大
host_key_checking = False

[ssh_connection]
pipelining = True             # 减少 SSH 连接次数,显著提速
control_path_dir = /tmp/ansible-ssh
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o ConnectTimeout=10

pipelining = True 能提升 2-3 倍性能,但需要目标机器 sudoers 没有 requiretty。

小结#

Ansible 的无 Agent + SSH push + 幂等模块模型让它成为机器级配置管理的首选。Inventory 管理是基础——静态 Inventory 适合固定机器,动态 Inventory(aws_ec2 插件)适合云上动态扩缩。Playbook 是任务编排,Role 是复用单元——可配置参数放 defaults/,内部常量放 vars/。Vault 加密敏感变量,命令模块要显式设 changed_when 保证幂等。与 Terraform 互补——Terraform 管云资源,Ansible 管机器配置。