在大型 GPU 算力集群中,为了提升中小显存占用任务的吞吐量,NVIDIA MPS(Multi-Process Service,多进程服务) 是一个几乎必选的方案。配合 Slurm 的 gres/mps 机制,多任务可以物理共享单张 GPU,并实现算力和显存的硬隔离。
然而,在多节点、高并发的生产环境下,MPS 的维护往往是个灾难:
- 用户作业异常退出导致 MPS 守护进程(
nvidia-cuda-mps-control)残留或僵死。 /tmp/nvidia-mps下的 IPC 套接字文件权限被污染,导致后续作业无法连接。- Slurm 登记的 MPS 资源与节点物理运行的 MPS 状态出现漂移,导致调度器“空放”任务或报
Resource temporarily unavailable。
本文将分享如何通过 Ansible 编排,实现多节点 Slurm 集群下 GPU MPS 的批量一键部署、状态复位以及自动化健康巡检。
一、 核心架构:Slurm 与 MPS 的生命周期管理
在开始写 Ansible Playbook 之前,必须理清 Slurm 调度下 MPS 的运行逻辑。
通常有两种管理模式:
- Slurm 托管模式(推荐):在
slurm.conf中配置GresTypes=gpu,mps,并在gres.conf中定义 MPS 资源。Slurm 在拉起作业时,通过PrologSlurmctld或task/cgroup插件动态启动和限制 MPS。 - 系统级常驻模式:在每个 GPU 节点上,通过 Systemd 静态拉起一个全局的
nvidia-cuda-mps-control守护进程,供所有作业共享。
本文方案主要针对第二种常驻模式,以及第一种模式下需要对底层节点进行大面积体检和残留清理的场景。
二、 Ansible 批量维护:MPS 状态一键复位与清理
当节点出现 MPS 僵死或者权限混乱时,我们需要一个“干净”的复位流程。一个合格的复位动作不能简单地 kill -9,必须遵循 NVIDIA 规范,优雅地退出演算,清理残留,再重新初始化。
1. 基础环境定义 (group_vars/gpu_nodes.yml)
首先定义基础环境变量,确保所有节点的 IPC 路径和日志路径一致:
---
mps_control_bin: "/usr/bin/nvidia-cuda-mps-control"
mps_ipc_dir: "/tmp/nvidia-mps"
mps_log_dir: "/var/log/nvidia-mps"
# 限制单个 MPS 客户端的最大可用算力百分比(可选,默认不限制)
mps_active_thread_percentage: 100
2. 复位与拉起 Playbook (reset_mps.yml)
这个 Playbook 实现了以下逻辑:
- 优雅停止现有的 MPS 控制进程。
- 强行清理可能残留的僵尸进程(如
nvidia-cuda-mps-server)。 - 清理并重新创建合规权限的
/tmp共享目录。 - 启动 MPS 守护进程。
---
- name: Batch Maintain and Reset GPU MPS Status
hosts: gpu_nodes
become: yes
gather_facts: yes
tasks:
- name: 1. Ensure NVIDIA kernel modules are loaded
ansible.builtin.shell: lsmod | grep nvidia
register: nvidia_driver_loaded
failed_when: nvidia_driver_loaded.rc != 0
changed_when: false
- name: 2. Stop running MPS control daemon gracefully
ansible.builtin.shell: |
echo "quit" | {{ mps_control_bin }}
failed_when: false
changed_when: true
- name: 3. Force kill any zombie MPS server processes
ansible.builtin.shell: |
killall -9 nvidia-cuda-mps-control nvidia-cuda-mps-server
failed_when: false
register: kill_zombies
changed_when: "'killed' in kill_zombies.stdout"
- name: 4. Clean up corrupted IPC directory and log directory
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- "{{ mps_ipc_dir }}"
- "{{ mps_log_dir }}"
- name: 5. Recreate IPC and Log directories with correct permissions
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: '0777' # 必须是0777,允许不同系统用户提交的作业往此写入IPC套接字
loop:
- "{{ mps_ipc_dir }}"
- "{{ mps_log_dir }}"
- name: 6. Start nvidia-cuda-mps-control in background
ansible.builtin.shell: |
export CUDA_VISIBLE_DEVICES=0,1,2,3 # 根据节点实际显卡数量调整,或通过fact动态获取
export CUDA_MPS_PIPE_DIRECTORY={{ mps_ipc_dir }}
export CUDA_MPS_LOG_DIRECTORY={{ mps_log_dir }}
{{ mps_control_bin }} -d
environment:
CUDA_VISIBLE_DEVICES: "{{ ansible_env.CUDA_VISIBLE_DEVICES | default('0') }}"
async: 10
poll: 0
changed_when: true
- name: 7. Verify MPS control daemon is active
ansible.builtin.wait_for:
path: "{{ mps_ipc_dir }}/control"
state: present
timeout: 5
三、 Ansible 批量健康巡检:主动发现故障节点
运维的最高境界是“主动发现”。我们需要一个巡检 Playbook,每天定时运行,过滤出那些“表面上在线,但 MPS 已经坏掉”的亚健康节点,并自动向 Slurm 发送 drain 信号,避免坏节点继续接单。
巡检指标设计:
- 进程存在性:
nvidia-cuda-mps-control是否在运行? - GPU 状态响应:通过
nvidia-smi检查 GPU 驱模块是否异常(如掉卡/D-State)。 - MPS 服务响应:向控制套接字发送
get_default_active_thread_percentage指令,看是否能正常返回。 - 共享目录权限:
/tmp/nvidia-mps是否被某个用户的作业改写成了私有所有者(导致其他用户报错)。
巡检 Playbook (check_mps_health.yml)
---
- name: GPU MPS Multi-Node Health Audit
hosts: gpu_nodes
become: yes
gather_facts: false
tasks:
- name: Audit 1 - Check MPS control process
ansible.builtin.command: pgrep nvidia-cuda-mps
register: mps_process_check
ignore_errors: yes
- name: Audit 2 - Query MPS control daemon response
ansible.builtin.shell: |
export CUDA_MPS_PIPE_DIRECTORY={{ mps_ipc_dir }}
echo "get_default_active_thread_percentage" | {{ mps_control_bin }}
register: mps_cmd_check
ignore_errors: yes
changed_when: false
- name: Audit 3 - Check IPC socket path permissions
ansible.builtin.stat:
path: "{{ mps_ipc_dir }}"
register: mps_dir_stat
- name: Audit 4 - Check if Slurm daemon is synchronized
ansible.builtin.shell: scontrol show node {{ inventory_hostname }} | grep -E "Gres|State"
delegate_to: "{{ groups['slurm_master'][0] }}"
register: slurm_node_state
ignore_errors: yes
changed_when: false
- name: Aggregate Health Report and Handle Failures
block:
- name: Identify bad nodes
ansible.builtin.set_fact:
mps_unhealthy: >-
{{
mps_process_check.rc != 0 or
'100' not in mps_cmd_check.stdout or
mps_dir_stat.stat.mode != '0777'
}}
- name: Print warning for unhealthy node
ansible.builtin.debug:
msg: >
[WARNING] Node {{ inventory_hostname }} is UNHEALTHY!
Process: {{ 'ALIVE' if mps_process_check.rc == 0 else 'DEAD' }},
Response: {{ mps_cmd_check.stdout | default('NO_RESPONSE') }},
Dir Mode: {{ mps_dir_stat.stat.mode }}
when: mps_unhealthy
- name: Automatically DRAIN unhealthy node in Slurm
delegate_to: "{{ groups['slurm_master'][0] }}"
ansible.builtin.command: >
scontrol update NodeName={{ inventory_hostname }}
State=DRAIN Reason="MPS Health Check Failed via Ansible"
when: mps_unhealthy and trigger_drain | default(false) | bool
四、 进阶:如何规避 MPS 常见的“坑”
在用 Ansible 落地上述方案时,根据生产实践,有几个必须要踩的坑和避坑指南:
1. 动态 CUDA_VISIBLE_DEVICES 绑定
在多卡节点上,如果有些卡需要跑 MPS,有些卡需要跑独占模式(Exclusive),绝对不能粗暴地在全局拉起整个 MPS。
- 最佳实践:在 Ansible 中,通过调用
nvidia-smi --query-gpu=index,uuid --format=csv动态读取节点上的 GPU 拓扑。 - 在 Ansible 模板中,针对每张需要启动 MPS 的 GPU,单独指定不同的
CUDA_MPS_PIPE_DIRECTORY=/tmp/nvidia-mps_${GPU_INDEX},实现物理隔离下的 MPS 管理。
2. 避免 /tmp 目录被 systemd-tmpfiles 清理
CentOS/Ubuntu 默认会定期清理 /tmp 下超过一定时间未访问的文件。如果你的 nvidia-mps 套接字长时间没有新任务连接,可能会被系统误删,导致后续作业提交直接挂掉。
- 解决方法:通过 Ansible 部署一个
/etc/tmpfiles.d/nvidia-mps.conf配置文件,内容如下:
这会告诉系统:永远不要清理这个目录。x /tmp/nvidia-mps x /tmp/nvidia-mps/*
3. Slurm Epilog 中的自动收尾
除了定期用 Ansible 巡检,最好在 Slurm 的 Epilog 脚本中加入一行轻量级清理逻辑。当用户的最后一个 MPS 作业退出时,使用 Ansible 下发的管理脚本自动检测,若无活动连接,则执行 echo quit。这能极大减轻日常维护的压力。
五、 总结
利用 Ansible 对 Slurm GPU 集群进行批量 MPS 运维,其核心在于**“规范化路径”与“状态自愈”**。通过上文提供的复位与健康巡检 Playbook,你可以将日常排查异构算力节点故障的时间缩短到分钟级,甚至通过结合 Slurm 的 scontrol 命令实现故障节点的“无感下线与自动修复”,保障大模型训练/推理集群的高可用性。