在人工智能领域,本地运行大型语言模型(LLM)的需求日益增长。许多在线指南侧重于 Linux 环境下的 Ray 和 vLLM 的使用,但本文将介绍一种在 Windows 环境下,利用 WSL(Windows Subsystem for Linux)镜像网络连接两台笔记本电脑,从而搭建 Ray 集群并高效运行 LLM 的方法。这种方案结合了 Windows 的易用性和 Linux 的灵活性,为开发者提供了一个更友好的选择。
WSL 镜像网络:连接虚拟与现实的桥梁
传统的 WSL2 使用虚拟化网络,为每个 Linux 环境分配一个虚拟 IP 地址,导致 Ray 集群中的 master 节点(GCS)难以直接与 worker 节点通信。数据必须先经过 Windows 主机的 IP 地址,再到达 WSL 实例。端口转发虽然可行,但对于 Ray 动态的心跳机制而言,需要配置大量的转发规则,非常不便。
WSL 镜像网络 解决了这个问题。通过启用镜像网络模式,WSL 实例可以像网络中的一等公民一样,直接使用笔记本电脑的 IP 地址进行通信。这意味着 Ray 集群中的节点可以互相发现和连接,无需复杂的网络配置。
具体操作步骤如下:
- 在记事本中输入以下内容:
[wsl2]
networkingMode=mirrored
- 将文件保存为
.wslconfig
,并确保保存类型为“所有文件”。 - 将文件保存到用户配置文件目录下(可通过在搜索栏输入
%USERPROFILE%
找到)。 - 以管理员身份打开 PowerShell。
- 输入以下命令,允许所有传入连接(在私有网络中进行快速测试时可以采用,但不建议在公共网络中使用,因为存在安全风险):
Set-NetFirewallHyperVVMSetting -Name "{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}" -DefaultInboundAction Allow
通常端口范围是 10000-80000,因为 NCCL 和 GLOO 层也需要执行握手。
- 打开“高级安全 Windows Defender 防火墙”。
- 在左侧窗格中点击“入站规则”,并启用“文件和打印机共享(回显请求 – IPv4-In)”和“文件和打印机共享(回显请求 – ICMPv6-In)”,确保同时针对“专用”和“域”启用。这允许笔记本电脑互相 ping 通。
- 要确认笔记本电脑之间的网络连接是否正常,可以使用
ping 192.168.xx.xx
命令测试。可以通过在 PowerShell 中输入ipconfig
命令,查找无线网络适配器的 IP 地址。 - 要确认 WSL 镜像网络是否成功,可以在 WSL Ubuntu 中输入
hostname -I
命令,检查显示的 IP 地址是否与 PowerShell 中显示的 IP 地址一致。
NCCL & GLOO:构建高性能通信桥梁
NCCL(NVIDIA Collective Communications Library)和 Gloo 是 PyTorch 等库的通信后端,Ray 可以利用它们进行高性能的集体通信,例如 LLM 训练中的 AllReduce 和 AllGather 操作。通过优化节点间的通信,NCCL 和 Gloo 可以显著提高 LLM 训练和推理的效率。
在配置 NCCL 和 Gloo 之前,需要确定每个笔记本电脑上的网络接口。在 WSL 中输入 ip addr show
命令,会显示一系列网络端口及其代码。选择支持广播和组播通信的端口。
例如:
ip addr show
找到类似 eth2
的网络接口,并记录其名称。注意: 不同笔记本电脑上的网络接口名称可能不同。这个名称将在后续的 Ray 集群初始化中使用。
Ray 集群初始化:连接两台笔记本的算力
在完成网络配置后,就可以初始化 Ray 集群了。Ray 是一个分布式计算框架,可以轻松地在多台机器上并行运行任务。
以下是在两台笔记本上初始化 Ray 集群的步骤:
在 Laptop W(Master 节点)上:
- 创建 conda 环境:
conda create -n multi_node python=3.12
conda 环境需要在两台笔记本上完全一致。
- 激活 conda 环境:
conda activate multi_node
- 在 conda 环境中安装 Ray 和 vLLM。
- 设置环境变量并启动 Ray 集群:
export NCCL_DEBUG=INFO
export GLOO_DEBUG=2
export VLLM_LOGGING_LEVEL=DEBUG
export NCCL_SOCKET_IFNAME=<网络接口名称>
export GLOO_SOCKET_IFNAME=<网络接口名称>
export NCCL_ASYNC_ERROR_HANDLING=1
export RAY_memory_monitor_refresh_ms=0
ray start --head --port=6379
将 <网络接口名称>
替换为在上一节中找到的网络接口名称。
- 使用
ray status
命令检查集群状态,确认 master 节点已启动。
在 Laptop V(Worker 节点)上:
- 重复 Laptop W 上的步骤 1-3,创建并激活相同的 conda 环境,并安装 Ray 和 vLLM。
- 设置环境变量并连接到 Ray 集群:
export NCCL_DEBUG=INFO
export GLOO_DEBUG=2
export VLLM_LOGGING_LEVEL=DEBUG
export NCCL_SOCKET_IFNAME=<网络接口名称>
export GLOO_SOCKET_IFNAME=<网络接口名称>
export NCCL_ASYNC_ERROR_HANDLING=1
export RAY_memory_monitor_refresh_ms=0
ray start --address=<Laptop W 的 IP 地址>
将 <网络接口名称>
替换为 Laptop V 上的网络接口名称,并将 <Laptop W 的 IP 地址>
替换为 Laptop W 的 IP 地址。
- 使用
ray status
命令检查集群状态,确认 worker 节点已成功连接到集群。
集群 QA:验证 Ray 集群的连通性
为了确保 Ray 集群正常工作,可以使用提供的 cluster_check.py
脚本进行 QA 测试。这个脚本会在集群中的所有节点上分配资源,并执行一个简单的 for 循环。
import ray
import time
import socket
import os
import platform
try:
if ray.is_initialized():
ray.shutdown()
ray.init(address="auto", ignore_reinit_error=True)
print(f"Successfully connected to Ray cluster.")
print(f"Cluster resources: {ray.cluster_resources()}")
@ray.remote(num_cpus=1)
def get_node_info_and_sleep(task_id):
time.sleep(1)
current_node_id = ray.get_runtime_context().get_node_id()
current_hostname = socket.gethostname()
current_pid = os.getpid()
return {
"task_id": task_id,
"hostname": current_hostname,
"pid": current_pid,
"ray_node_id": str(current_node_id)
}
total_cpus = ray.cluster_resources().get("CPU", 0)
num_tasks_to_launch = int(total_cpus * 2) if total_cpus > 0 else 16
print(f"\nLaunching {num_tasks_to_launch} remote tasks to distribute across nodes...")
futures = [get_node_info_and_sleep.remote(i) for i in range(num_tasks_to_launch)]
print("\nRetrieving results:")
for i, future in enumerate(futures):
node_info = ray.get(future)
print(f"Task {node_info['task_id']} executed on: Hostname={node_info['hostname']}, PID={node_info['pid']}, NodeID={node_info['ray_node_id']}")
print("\nAll tasks completed.")
except Exception as e:
print(f"\nAn error occurred: {e}")
import traceback
traceback.print_exc()
finally:
if ray.is_initialized():
ray.shutdown()
print("\nRay script finished and shut down connection.")
运行此脚本后,它应该显示任务已成功在两个节点上执行,从而验证 Ray 集群的连通性。此外,还可以使用 vLLM 提供的 sanity_script.py
脚本来验证 NCCL 和 Gloo 通信层是否正常工作。
vLLM 初始化:启动 LLM 服务
最后,我们就可以在 Ray 集群上启动 vLLM 服务,并运行 LLM 模型了。vLLM 是一个快速、易于使用的 LLM 推理框架,可以显著提高 LLM 推理的速度和效率。
在 Laptop W(Master 节点)上运行以下命令:
vllm serve microsoft/Phi-3-mini-4k-instruct \
--host 0.0.0.0 \
--port 8000 \
--tensor-parallel-size 1 \
--pipeline-parallel-size 2 \
--gpu-memory-utilization 0.98 \
--swap-space 4
这个命令会在 Laptop W 上启动 vLLM 服务,并加载 microsoft/Phi-3-mini-4k-instruct
模型。--tensor-parallel-size
参数指定每个笔记本电脑上的 GPU 数量,--pipeline-parallel-size
参数指定集群中的节点数量。--gpu-memory-utilization
参数指定 GPU 内存的使用率,--swap-space
参数指定交换空间的大小。
如果模型成功加载,您应该会在终端中看到类似以下的输出:
INFO 2024-01-01 00:00:00 vllm.model_executor.model_loader: Loading model...
INFO 2024-01-01 00:00:00 vllm.model_executor.model_loader: Model loaded in 10.0 seconds.
INFO 2024-01-01 00:00:00 vllm.engine.ray_utils: Starting vLLM engine...
INFO 2024-01-01 00:00:00 vllm.engine.ray_utils: vLLM engine started successfully.
INFO 2024-01-01 00:00:00 vllm.entrypoints.api_server: Running on http://0.0.0.0:8000
如果出现错误,可能是由于 GPU 内存不足、CPU 不足,或者 NCCL 和 Gloo 配置不正确导致的。
LLM 推理:体验多节点 LLM 的强大算力
在 vLLM 服务启动后,可以使用以下脚本进行 LLM 推理:
#!/bin/bash
# --- Configuration ---
# Use your LAN IP (e.g., "192.168.18.88")
VLLM_SERVER_IP="192.168.18.88"
VLLM_SERVER_PORT="8000"
# --- Define your prompts following the ChatML format for Phi-3 Instruct ---
PROMPT_TEXT_1="<|user|>\nWrite what you think are the best scenes of the bee movie<|end|>\n<|assistant|>"
# --- Choose which prompt to use ---
SELECTED_PROMPT="${PROMPT_TEXT_1}"
# --- Generation Parameters ---
MAX_TOKENS=256
TEMPERATURE=0.7
N_COMPLETIONS=1
# Stop sequences for Phi-3 Instruct
STOP_SEQUENCES='["<|endoftext|>", "<|end|>"]'
# --- Construct the JSON payload ---
JSON_PAYLOAD=$(printf '{
"prompt": "%s",
"max_tokens": %d,
"temperature": %.1f,
"n": %d,
"stop": %s
}' "${SELECTED_PROMPT}" "${MAX_TOKENS}" "${TEMPERATURE}" "${N_COMPLETIONS}" "${STOP_SEQUENCES}")
# --- Execute the curl command ---
echo "Querying vLLM server at http://${VLLM_SERVER_IP}:${VLLM_SERVER_PORT}"
echo "Prompt: $(echo "${SELECTED_PROMPT}" | sed 's/<|user|>//g; s/<|end|>//g; s/<|assistant|>//g')" # Print cleaned prompt for readability
JSON_PAYLOAD='{ "prompt": "Your input prompt here", "max_tokens": 2000, "temperature": 0.7, "n": 1}'
curl -X POST \
-H "Content-Type: application/json" \
-d "${JSON_PAYLOAD}" \
http://${VLLM_SERVER_IP}:${VLLM_SERVER_PORT}/v1/completions
echo ""
将 VLLM_SERVER_IP
替换为 Laptop W 的 IP 地址,并将提示语替换为您想要使用的提示语。运行此脚本后,您应该会在终端中看到 LLM 生成的文本。
在运行 LLM 推理时,您可以在任务管理器中查看两个笔记本电脑的 GPU 使用率。如果一切正常,您应该会看到两个 GPU 都在运行,表明 LLM 推理已成功分布在两个节点上。
结论:低成本构建 LLM 本地算力集群
通过利用 WSL 镜像网络,结合 Ray 集群和 vLLM,我们可以在两台笔记本电脑上搭建一个低成本的 LLM 本地算力集群,实现高效的 LLM 训练和推理。这种方案不仅降低了硬件成本,还提高了开发效率,为 LLM 爱好者和开发者提供了一个新的选择。希望本文能够帮助您成功搭建自己的 LLM 本地算力集群,并尽情探索 LLM 的强大功能。