文章

Ansible 配置管理实战

Ansible 配置管理实战

概述

Ansible 是 Red Hat 开发的开源配置管理工具,通过 SSH/WinRM 协议远程管理服务器,无需在目标节点安装 Agent。它用 YAML 编写 Playbook,描述系统的”期望状态”,自动实现幂等性。

一句话:Terraform 管”资源有没有”(创建 VPC、EC2),Ansible 管”资源配置对不对”(装包、改配置、启服务)。

Ansible 架构

graph TB
    subgraph "控制节点 (Control Node)"
        Playbook["Playbook<br/>YAML 剧本"]
        Inventory["Inventory<br/>主机清单"]
        Modules["Modules<br/>执行单元"]
        Roles["Roles<br/>可复用角色"]
    end

    subgraph "被管节点 (Managed Nodes)"
        N1["GPU 服务器 01<br/>10.0.1.10"]
        N2["GPU 服务器 02<br/>10.0.1.11"]
        N3["GPU 服务器 03<br/>10.0.1.12"]
        N4["存储服务器<br/>10.0.2.10"]
    end

    Playbook --> Modules
    Inventory --> Modules
    Roles --> Playbook
    Modules -->|"SSH (port 22)"| N1
    Modules -->|"SSH"| N2
    Modules -->|"SSH"| N3
    Modules -->|"SSH"| N4

    style Modules fill:#e8f5e9,stroke:#4caf50

核心概念

概念说明类比
Inventory被管主机清单(INI/YAML)通讯录
PlaybookYAML 格式的配置剧本剧本
Task最小执行单元(调用 Module)一句台词
Module执行具体操作(yum/apt/copy/service…)演员技能
Role一组 Task + 变量 + 模板的集合,可复用一幕戏
Handler只在 Task 通知时触发的操作(如重启服务)幕后道具
Variable变量(host_vars / group_vars / extra_vars)剧本参数
Fact自动收集的目标主机信息(OS、IP、CPU…)角色资料

Inventory 管理

静态 Inventory

# inventory/production.ini
[gpu_servers]
gpu-01 ansible_host=10.0.1.10 ansible_user=root
gpu-02 ansible_host=10.0.1.11 ansible_user=root
gpu-03 ansible_host=10.0.1.12 ansible_user=root

[storage_servers]
storage-01 ansible_host=10.0.2.10 ansible_user=ubuntu

[gpu_servers:vars]
gpu_type=a100
nvidia_driver_version=550
cuda_version=12.4

[storage_servers:vars]
nfs_enabled=true

# 元组:包含所有服务器
[all:children]
gpu_servers
storage_servers

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

动态 Inventory

云环境中主机动态变化,用动态 Inventory 自动发现:

# inventory/aws_ec2.yml — AWS 动态 Inventory
plugin: aws_ec2
regions:
  - ap-southeast-1
  - us-west-2

# 按标签分组
keyed_groups:
  - key: tags.Role
    prefix: role
  - key: tags.Environment
    prefix: env
  - key: tags.GPU
    prefix: gpu

# 过滤条件
filters:
  tag:Environment: production
  instance-state-name: running

# 主机变量
compose:
  ansible_host: public_ip_address
  ansible_user: "'ec2-user'"
# inventory/gcp_compute.yml — GCP 动态 Inventory
plugin: google.cloud.gcp_compute
projects:
  - my-project-id
zones:
  - asia-southeast1-a
  - asia-southeast1-b
filters:
  - status = RUNNING
  - labels.environment = production
keyed_groups:
  - key: labels.gpu_type
    prefix: gpu
hostnames:
  - name
compose:
  ansible_host: network_interfaces[0].access_configs[0].natIP
# 使用动态 Inventory
ansible-inventory -i inventory/aws_ec2.yml --graph
ansible-playbook -i inventory/aws_ec2.yml site.yml --limit role_gpu

Playbook 核心语法

基本结构

---
# site.yml — 主入口 Playbook
- name: 配置 GPU 服务器集群
  hosts: gpu_servers
  become: yes                    # sudo 提权
  gather_facts: yes              # 收集主机信息

  vars:
    nvidia_driver_version: "550"
    cuda_version: "12.4"
    docker_version: "25.0"

  pre_tasks:
    - name: 确保系统包索引更新
      apt:
        update_cache: yes
      when: ansible_os_family == "Debian"

    - name: 检查 GPU 是否存在
      shell: nvidia-smi --query-gpu=name --format=csv,noheader
      register: gpu_check
      changed_when: false
      failed_when: false

    - name: 显示 GPU 信息
      debug:
        msg: "检测到 GPU: {{ gpu_check.stdout_lines }}"
      when: gpu_check.rc == 0

  roles:
    - role: nvidia_driver
      when: gpu_check.rc == 0
    - role: cuda_toolkit
      when: gpu_check.rc == 0
    - role: docker
    - role: nvidia_container_toolkit
      when: gpu_check.rc == 0
    - role: k8s_worker

  post_tasks:
    - name: 验证 NVIDIA 驱动
      command: nvidia-smi
      register: nvidia_smi
      changed_when: false

    - name: 显示 GPU 状态
      debug:
        var: nvidia_smi.stdout_lines

常用 Module 速查

Module用途示例
apt / yum包管理apt: name=nginx state=present
copy拷贝文件copy: src=app.conf dest=/etc/nginx/
templateJinja2 模板渲染template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
service / systemd服务管理systemd: name=nginx state=restarted enabled=yes
file文件/目录管理file: path=/data state=directory mode=0755
user / group用户管理user: name=app shell=/bin/bash
gitGit 操作git: repo=... dest=/opt/app version=main
command / shell执行命令shell: nvidia-smi register: result
lineinfile单行编辑lineinfile: path=/etc/hosts line='...'
blockinfile多行块编辑blockinfile: path=/etc/sysctl.conf ...
mount挂载管理mount: src=/dev/sdb path=/data fstype=ext4 state=mounted
cron定时任务cron: name="backup" hour=2 job="/opt/backup.sh"
uriHTTP 请求uri: url=http://localhost/health status=200
debug调试输出debug: var=result.stdout
set_fact设置变量set_fact: gpu_count={{ gpu_check.stdout_lines | length }}

变量与 Jinja2 模板

# group_vars/gpu_servers.yml
gpu_config:
  driver_version: "550"
  cuda_version: "12.4"
  persistence_mode: true
  power_limit: 350          # Watts
  auto_boost: 0

nvidia_container_runtime:
  repo_url: "https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo"
  packages:
    - nvidia-container-toolkit
  docker_runtime: "nvidia"

k8s_node_labels:
  gpu: "true"
  gpu-type: "a100"
  nvidia.com/gpu.present: "true"
{# templates/nvidia-persistence.service.j2 #}
[Unit]
Description=NVIDIA Persistence Daemon
After=network.target

[Service]
Type=forking
ExecStart=/usr/bin/nvidia-persistenced --persistence-mode -u root
ExecStop=/usr/bin/nvidia-persistenced --persistence-mode -u root --no-persistence-mode
Restart=always

[Install]
WantedBy=multi-user.target
{# templates/daemon.json.j2 — Docker 配置 with NVIDIA runtime #}
{
  "runtimes": {
    "nvidia": {
      "path": "nvidia-container-runtime",
      "runtimeArgs": []
    }
  },
  "default-runtime": "nvidia",
  "data-root": "{{ docker_data_root | default('/var/lib/docker') }}",
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  },
  "registry-mirrors": [
    "https://mirror.example.com"
  ]
}

Handler — 事件触发的操作

- name: 配置 Docker daemon
  template:
    src: daemon.json.j2
    dest: /etc/docker/daemon.json
  notify: restart docker          # 通知 handler

  handlers:
    - name: restart docker
      systemd:
        name: docker
        state: restarted
        daemon_reload: yes

Handler 只在 Task 状态为 changed 时触发,且在 Play 结束时统一执行。同一个 handler 被通知多次只执行一次。

条件与循环

# 条件判断
- name: Ubuntu 系统安装 GPU 驱动
  apt:
    deb: "https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/nvidia-driver-{{ nvidia_driver_version }}_open_{{ cuda_version }}.deb"
  when:
    - ansible_os_family == "Debian"
    - gpu_check.rc == 0

# 循环
- name: 创建数据目录
  file:
    path: "/data/{{ item }}"
    state: directory
    owner: "{{ app_user }}"
    mode: "0755"
  loop:
    - logs
    - cache
    - tmp
    - models

# 循环 + 字典
- name: 配置 sysctl 参数
  sysctl:
    name: "{{ item.key }}"
    value: "{{ item.value }}"
    state: present
    reload: yes
  loop:
    - { key: "net.core.somaxconn", value: "65535" }
    - { key: "net.ipv4.tcp_max_syn_backlog", value: "65535" }
    - { key: "vm.max_map_count", value: "262144" }
    - { key: "fs.file-max", value: "2097152" }

Role 开发与复用

Role 目录结构

roles/
└── nvidia_driver/
    ├── defaults/             # 默认变量(优先级最低,可被覆盖)
    │   └── main.yml
    ├── vars/                 # 角色变量(优先级高于 defaults)
    │   └── main.yml
    ├── tasks/                # 主任务
    │   └── main.yml
    ├── handlers/             # Handler
    │   └── main.yml
    ├── templates/            # Jinja2 模板
    │   ├── nvidia-persistence.service.j2
    │   └── nvidia-modprobe.conf.j2
    ├── files/                # 静态文件
    │   └── blacklist-nouveau.conf
    └── meta/                 # 元数据(依赖、作者)
        └── main.yml

实战 Role:NVIDIA GPU 驱动安装

# roles/nvidia_driver/defaults/main.yml
---
nvidia_driver_version: "550"
cuda_version: "12.4"
nvidia_persistence_mode: true
nvidia_power_limit: 350          # Watts, 0 = 不修改
nvidia_auto_boost: 0
nouveau_blacklist: true

# roles/nvidia_driver/tasks/main.yml
---
- name: 屏蔽 Nouveau 开源驱动
  copy:
    src: blacklist-nouveau.conf
    dest: /etc/modprobe.d/blacklist-nouveau.conf
  when: nouveau_blacklist
  notify: update initramfs

- name: 安装内核头文件和编译工具
  apt:
    name:
      - linux-headers-{{ ansible_kernel }}
      - build-essential
      - dkms
    state: present
  when: ansible_os_family == "Debian"

- name: 添加 NVIDIA 官方 APT 源
  apt:
    deb: "https://developer.download.nvidia.com/compute/cuda/repos/ubuntu{{ ansible_distribution_version | replace('.', '') }}/{{ ansible_architecture }}/cuda-keyring_1.1-1_all.deb"
    state: present
  when: ansible_os_family == "Debian"

- name: 安装 NVIDIA 驱动
  apt:
    name: "nvidia-driver-{{ nvidia_driver_version }}"
    state: present
    update_cache: yes
  register: driver_install
  when: ansible_os_family == "Debian"

- name: 安装 CUDA Toolkit
  apt:
    name: "cuda-toolkit-{{ cuda_version }}"
    state: present
  when: ansible_os_family == "Debian"

- name: 加载 NVIDIA 内核模块
  modprobe:
    name: nvidia
    state: present
  when: driver_install.changed

- name: 启用 NVIDIA 持久化模式
  command: "nvidia-smi -pm {{ 1 if nvidia_persistence_mode else 0 }}"
  changed_when: false
  when: nvidia_persistence_mode

- name: 设置 GPU 功耗限制
  command: "nvidia-smi -pl {{ nvidia_power_limit }}"
  when: nvidia_power_limit | int > 0
  changed_when: false

- name: 配置 GPU 自动 Boost
  command: "nvidia-smi -ac {{ item }}"
  loop: "{{ gpu_application_clocks | default([]) }}"
  changed_when: false
  when: nvidia_auto_boost | int == 0

- name: 安装 nvidia-persistenced
  template:
    src: nvidia-persistence.service.j2
    dest: /etc/systemd/system/nvidia-persistenced.service
  notify: enable persistenced

# roles/nvidia_driver/handlers/main.yml
---
- name: update initramfs
  command: update-initramfs -u

- name: enable persistenced
  systemd:
    name: nvidia-persistenced
    state: started
    enabled: yes
    daemon_reload: yes

实战 Role:Docker + NVIDIA Container Toolkit

# roles/docker/tasks/main.yml
---
- name: 卸载旧版本 Docker
  apt:
    name: "{{ item }}"
    state: absent
  loop:
    - docker
    - docker-engine
    - docker.io
    - containerd
    - runc

- name: 安装 Docker 依赖
  apt:
    name:
      - ca-certificates
      - curl
      - gnupg
      - lsb-release
    state: present
    update_cache: yes

- name: 添加 Docker 官方 GPG Key
  apt_key:
    url: "https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg"
    state: present

- name: 添加 Docker APT 源
  apt_repository:
    repo: "deb [arch=amd64] https://download.docker.com/linux/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} stable"
    state: present
    update_cache: yes

- name: 安装 Docker
  apt:
    name: "docker-ce{{ '='+docker_version+'*' if docker_version else '' }}"
    state: present
  register: docker_install

- name: 配置 Docker daemon
  template:
    src: daemon.json.j2
    dest: /etc/docker/daemon.json
  notify: restart docker

- name: 配置 Docker 日志轮转
  copy:
    src: docker-logrotate
    dest: /etc/logrotate.d/docker
  when: docker_logrotate | default(true)

- name: 启动并设置开机自启
  systemd:
    name: docker
    state: started
    enabled: yes

# NVIDIA Container Toolkit
- name: 添加 NVIDIA Container Toolkit 源
  apt_key:
    url: "https://nvidia.github.io/libnvidia-container/gpgkey"
    state: present
  when: "'gpu' in group_names"

- name: 添加 NVIDIA Container Toolkit APT 源
  apt_repository:
    repo: "deb https://nvidia.github.io/libnvidia-container/stable/deb/$(ARCH) /"
    state: present
    update_cache: yes
  when: "'gpu' in group_names"

- name: 安装 NVIDIA Container Toolkit
  apt:
    name: nvidia-container-toolkit
    state: present
  when: "'gpu' in group_names"
  notify: restart docker

- name: 配置 Docker 使用 NVIDIA Runtime
  command: nvidia-ctk runtime configure --runtime=docker
  when: "'gpu' in group_names"
  notify: restart docker

实战 Role:K8s Worker 节点准备

# roles/k8s_worker/tasks/main.yml
---
- name: 禁用 Swap
  swapoff:
    name: swap
    state: absent

- name: 注释 fstab 中的 swap
  replace:
    path: /etc/fstab
    regexp: '^([^#].*swap.*)$'
    replace: '#\1'

- name: 加载内核模块
  modprobe:
    name: "{{ item }}"
    state: present
  loop:
    - overlay
    - br_netfilter

- name: 持久化内核模块加载
  copy:
    dest: /etc/modules-load.d/k8s.conf
    content: |
      overlay
      br_netfilter

- name: 配置 sysctl for K8s
  sysctl:
    name: "{{ item.key }}"
    value: "{{ item.value }}"
    state: present
    reload: yes
  loop:
    - { key: "net.bridge.bridge-nf-call-iptables", value: "1" }
    - { key: "net.bridge.bridge-nf-call-ip6tables", value: "1" }
    - { key: "net.ipv4.ip_forward", value: "1" }

- name: 添加 Kubernetes APT 源
  apt_key:
    url: "https://pkgs.k8s.io/core:/stable:/v{{ k8s_version }}/deb/Release.key"
    state: present
  when: ansible_os_family == "Debian"

- name: 添加 Kubernetes APT 源
  apt_repository:
    repo: "deb https://pkgs.k8s.io/core:/stable:/v{{ k8s_version }}/deb/ /"
    state: present
    update_cache: yes

- name: 安装 containerd
  apt:
    name: containerd
    state: present

- name: 配置 containerd
  shell: |
    containerd config default > /etc/containerd/config.toml
    sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
  args:
    creates: /etc/containerd/config.toml
  notify: restart containerd

- name: 安装 Kubernetes 组件
  apt:
    name:
      - kubelet
      - kubeadm
      - kubectl
    state: present

- name: 标记 Kubernetes 包不自动升级
  dpkg_selections:
    name: "{{ item }}"
    selection: hold
  loop:
    - kubelet
    - kubeadm
    - kubectl

- name: 设置节点标签和 taint
  template:
    src: kubelet-extra-args.service.j2
    dest: /etc/systemd/system/kubelet.service.d/11-extra-args.conf
  notify: restart kubelet

Ansible 与 Terraform 协作模式

模式一:Terraform 创建资源 → Ansible 配置

Terraform Plan:
  1. 创建 VPC / 子网 / 安全组
  2. 创建 EC2 / GPU 实例
  3. 输出实例 IP → 写入 Ansible Inventory

Ansible Playbook:
  4. SSH 到实例 → 安装 NVIDIA 驱动 / Docker / K8s
# Terraform 输出动态 Inventory
resource "local_file" "ansible_inventory" {
  content = templatefile("inventory.tmpl", {
    gpu_servers = aws_instance.gpu[*].public_ip
    region      = var.region
  })
  filename = "${path.module}/../ansible/inventory/terraform_hosts.ini"
}
{# inventory.tmpl #}
[gpu_servers]
%{ for ip in gpu_servers ~}
gpu-${index(gpu_servers, ip) + 1} ansible_host=${ip} ansible_user=ubuntu
%{ endfor ~}

[gpu_servers:vars]
region=${region}
# ansible/site.yml — 使用 Terraform 生成的 Inventory
- name: 配置 GPU 服务器
  hosts: gpu_servers
  become: yes
  roles:
    - nvidia_driver
    - docker
    - nvidia_container_toolkit
    - k8s_worker

模式二:Terraform user_data + Ansible Playbook

# Terraform 用 user_data 触发 Ansible Pull 模式
resource "aws_instance" "gpu" {
  user_data = templatefile("user_data.sh.tpl", {
    playbook_repo = "https://github.com/org/ansible-playbooks.git"
    playbook_name = "gpu-server.yml"
  })
}
#!/bin/bash
# user_data.sh.tpl — 实例启动时自动拉取并执行 Playbook
apt-get update && apt-get install -y ansible git
git clone ${playbook_repo} /opt/ansible
cd /opt/ansible
ansible-playbook ${playbook_name} --connection=local -i localhost,

模式三:Ansible 管理已有基础设施

# 适合裸金属服务器 — 不经过 Terraform,直接 Ansible 管理
- name: 全量配置 GPU 集群
  import_playbook: site.yml
  vars:
    target: gpu_servers

Ansible AWX / Tower 自动化

AWX 架构

graph TB
    subgraph "AWX 架构"
        Web["Web UI<br/>可视化管理"]
        API["REST API<br/>程序化接口"]
        DB["PostgreSQL<br/>存储任务/凭据"]
        Redis["Redis<br/>任务队列"]
        Exec["Execution Environment<br/>容器化执行"]
    end

    Git["Git 仓库<br/>Playbook 源码"]
    Cloud["云平台<br/>AWS/GCP/裸金属"]

    Web --> API
    API --> DB
    API --> Redis
    Redis --> Exec
    Exec -->|"SSH"| Cloud
    Git -->|"拉取"| Exec

    style Exec fill:#e8f5e9,stroke:#4caf50

AWX 核心概念

概念说明
ProjectGit 仓库连接(存储 Playbook)
Inventory主机清单(可动态同步云资源)
CredentialSSH/云 API 凭据(加密存储)
Job TemplatePlaybook + Inventory + Credential 的组合
Workflow多个 Job Template 的编排(DAG)
Survey运行时输入表单(变量参数化)
Execution Environment容器化的执行环境(自定义 Python 依赖)

AWX Job Template 示例

# 通过 API 创建 Job Template
---
name: "GPU 服务器初始化"
description: "安装 NVIDIA 驱动、CUDA、Docker、K8s"
organization: "SRE Team"
project: "ansible-playbooks"
playbook: "site.yml"
inventory: "GPU Production"
credential: "SSH - GPU Servers"
limit: "gpu_servers"
verbosity: 2
job_tags: "nvidia,docker,k8s"
skip_tags: ""
use_fact_cache: true
survey_enabled: true
survey_spec:
  name: ""
  description: "选择配置范围"
  spec:
    - question_name: "安装范围"
      question_description: "选择要执行的 Role"
      required: true
      type: "multiplechoice"
      variable: "install_scope"
      choices:
        - "all"
        - "nvidia_only"
        - "k8s_only"
      default: "all"

最佳实践与常见坑

最佳实践

实践说明示例
幂等性优先确保重复执行不产生副作用creates/changed_when 控制
Role 模块化每个 Role 只做一件事nvidia_driver / docker / k8s_worker 分开
变量分层defaults < group_vars < host_vars < extra_vars敏感变量用 Vault 加密
使用 Tags按 Tag 选择性执行--tags nvidia 只执行 GPU 相关
Fact Caching缓存 Fact 避免每次收集fact_caching = jsonfile 或 redis
Limit 控制范围先小范围测试再全量--limit gpu-01 先测试一台
Check 模式Dry-run 预览变更--check --diff

常见坑

问题根因解决方案
SSH Error: Permission deniedSSH Key 未配置或用户不对ansible all -m ping 先测连通性
Playbook 卡住不动SSH 超时 / 目标主机无响应--timeout=30 + ansible_ssh_timeout
Fact 收集慢主机多或网络差gather_facts: no + 手动获取需要的信息
模块找不到Python 版本不匹配ansible_python_interpreter=/usr/bin/python3
修改变量不生效变量优先级覆盖检查 ansible-inventory --host <host>
Handler 未触发Task 状态不是 changed确认 Task 确实修改了目标
apt 模块锁冲突另一个 apt 进程在运行apt: update_cache 再安装

性能优化

# ansible.cfg — 性能相关配置
[defaults]
# 并发控制
forks = 50                    # 默认 5,大规模可调高

# SSH 优化
host_key_checking = False
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o ConnectionAttempts=3
pipelining = True             # 减少 SSH 连接次数(需 sudo requiretty=False)

# Fact 缓存
gathering = smart
fact_caching = redis
fact_caching_timeout = 86400

# 超时
timeout = 30
ansible_ssh_timeout = 10
# 使用 Mitogen 加速 Ansible(10 倍性能提升)
pip install mitogen
# ansible.cfg
[defaults]
strategy = mitogen_linear
strategy_plugins = /path/to/ansible_mitogen/plugins/strategy

关联知识

参考资源

学习时间

阶段时间备注
Ansible 基础2026-08-03Inventory、Playbook、Module、Role
GPU 配置实战2026-08-03NVIDIA 驱动、CUDA、Docker、K8s Worker
AWX 自动化2026-08-03Job Template、Workflow、Survey

状态: ✅ 已完成 学习时间: 2026-08-03