本文将指导您如何在高性能计算(HPC)集群上,利用Apptainer容器来运行OllamaOllama是一个流行的工具,用于在本地运行开源大语言模型(LLM)。这种设置对于以下情况非常理想:您在HPC集群或共享GPU系统上工作,无法直接使用Docker;常规的Ollama设置不起作用或无法安装Ollama应用程序;您没有root权限。通过本文,您将学会如何在Apptainer容器中运行带有GPU支持的Ollama服务器,并能从Python或CLI访问它。

理解Apptainer与HPC环境

在HPC环境中,资源管理和安全性是至关重要的。传统的容器化技术,如Docker,通常需要root权限来构建和运行容器,这在共享的HPC集群中是不可接受的。Apptainer应运而生,它是一个专为HPC环境设计的容器解决方案。Apptainer允许用户在没有root权限的情况下运行容器,这使得它成为在Docker不可用或不适合的场景下的理想选择。想象一个拥有数百个用户的超级计算机,每个人都需要运行不同的应用程序和库版本。如果每个用户都试图安装自己的依赖项,很快就会导致系统冲突和不稳定。Apptainer通过将每个应用程序及其依赖项隔离在容器中来解决这个问题,确保环境的一致性和可重复性。

实际上,许多HPC集群都禁止直接使用Docker,原因在于Docker守护进程需要root权限,这可能会带来安全风险。Apptainer则不同,它采用用户空间技术,无需root权限即可运行容器。这意味着用户可以在不影响系统安全性的前提下,自由地使用容器来管理他们的应用程序和依赖项。 例如,一个生物信息学研究团队需要在HPC集群上运行一个复杂的基因组分析流程。该流程依赖于多个不同的软件工具,每个工具都需要特定的库和版本。通过使用Apptainer,研究团队可以将所有这些工具打包到一个容器中,并在HPC集群上轻松部署和运行,而无需担心依赖项冲突或权限问题。

构建Ollama容器镜像:绕过Docker的限制

要在HPC集群上使用Apptainer运行Ollama,首先需要构建Ollama的容器镜像。由于我们假设无法直接使用Docker,因此我们将使用Docker Hub上的官方Ollama镜像作为构建源。使用以下命令:

apptainer build ollama.sif docker://ollama/ollama

这条命令会从Docker Hub下载ollama/ollama镜像,并将其转换为Apptainer镜像格式(.sif文件)。将这个ollama.sif文件放置在您希望存放所有Ollama相关文件的目录中。 .sif文件是Apptainer的单文件镜像格式,它包含了运行Ollama所需的所有依赖项和文件系统。

例如,某个研究人员希望在HPC集群上测试不同版本的Ollama。通过这种方法,他们可以创建多个不同的ollama.sif镜像,每个镜像对应一个Ollama版本,并在不同的作业中轻松切换和测试,而无需担心版本冲突或依赖项问题。这种方法的优势在于它允许用户在受限的环境中利用Docker Hub上丰富的镜像资源,同时避免了Docker本身的权限问题。

准备目录结构:模型文件的本地缓存

为了避免重复下载模型,建议创建一个目录来存储Ollama的本地模型缓存文件。假设您使用以下目录结构:

~/ollama-container/
├── ollama.sif
└── models/

将所有本地模型缓存文件放入models/目录中。这可以显著加快后续的模型加载速度,并减少网络流量。 Ollama默认会将下载的模型存储在用户目录下的一个隐藏文件夹中。但是,在HPC环境中,用户目录通常是共享的,并且容量有限。将模型文件存储在单独的目录中,可以更好地管理存储空间,并避免与其他用户的模型文件发生冲突。

一个具体的例子是,如果需要在Ollama中使用llama2:latestcode llama:latest两个模型。您可以事先使用ollama pull llama2:latestollama pull code llama:latest在本地下载这些模型,然后将它们拷贝到~/ollama-container/models/目录下。这样,当您在Apptainer容器中运行Ollama时,它将直接从本地加载这些模型,而无需重新下载。

创建启动脚本:配置GPU和模型路径

接下来,创建一个Shell脚本来启动Apptainer容器中的Ollama服务器。将以下内容保存为start_ollama_server.sh

#!/bin/bash

# Configuration
CONTAINER_IMAGE="~/ollama-container/ollama.sif"
INSTANCE_NAME="ollama-$USER"
MODEL_PATH="~/ollama-container/models"
PORT=11434

# Start Apptainer instance with GPU and writable tempfs
apptainer instance start \
  --nv \
  --writable-tmpfs \
  --bind "$MODEL_PATH" \
  "$CONTAINER_IMAGE" "$INSTANCE_NAME"

# Start Ollama serve inside the container in the background
apptainer exec instance://$INSTANCE_NAME \
  bash -c "export OLLAMA_MODELS=$MODEL_PATH && ollama serve &"

echo "🦙 Ollama is now serving at http://localhost:$PORT"

然后,运行以下命令使其可执行并启动服务器:

chmod +x start_ollama_server.sh
bash start_ollama_server.sh

您应该会看到:

🦙 Ollama is now serving at http://localhost:11434...

这个脚本做了以下几件事:

  • 配置变量: 定义了容器镜像路径、实例名称、模型路径和端口号。
  • 启动Apptainer实例: 使用apptainer instance start命令启动一个Apptainer实例。
    • --nv:启用GPU支持。
    • --writable-tmpfs:创建一个可写临时文件系统,用于存储Ollama运行时生成的文件。
    • --bind "$MODEL_PATH":将本地模型目录绑定到容器中,使得Ollama可以访问本地模型文件。
    • "$CONTAINER_IMAGE":指定要使用的容器镜像。
    • "$INSTANCE_NAME":指定实例的名称。
  • 在容器中启动Ollama服务器: 使用apptainer exec命令在容器中执行ollama serve命令。
    • export OLLAMA_MODELS=$MODEL_PATH:设置环境变量OLLAMA_MODELS,告诉Ollama在哪里查找模型文件。
    • ollama serve &:启动Ollama服务器,并将其放入后台运行。
  • 打印提示信息: 告诉用户Ollama服务器正在运行的地址。

INSTANCE_NAME使用了ollama-$USER的命名方式,有效地将每个用户的Ollama实例隔离开来,避免了命名冲突,尤其是在多人共享的HPC环境中,这种命名方式尤为重要。--writable-tmpfs选项则是在HPC集群中提升性能的一个关键手段。由于HPC集群的共享存储通常速度较慢,将Ollama的临时文件存储在内存中的临时文件系统中,可以显著提高读写速度,从而提升Ollama的整体性能。

从Python访问Ollama服务器:实时的聊天体验

安装官方Ollama Python库:

pip install ollama

以下是如何与正在运行的模型进行聊天的示例:

import ollama

ollama.base_url = "http://localhost:11434"
model_name = "llama2:latest"
prompt = "Write a simple Python program to print something"

# Check if model already exists
models = ollama.list().get("models", [])
model_names = [model.model for model in models]
if model_name not in model_names:
    try:
        # Attempt to pull the model if it does not exist
        print(f"Pulling model '{model_name}'...")
        ollama.pull(model=model_name)
    except Exception as e:
        print(f"Could not pull model: {e}")

# Stream output token by token
for chunk in ollama.chat(
    model=model_name,
    messages=[{"role": "user", "content": prompt}],
    stream=True,
):
    print(chunk["message"]["content"], end="", flush=True)

print("\n" + "=" * 20)

这段代码首先设置Ollama服务器的地址。然后,它指定要使用的模型名称,并定义一个提示。代码会检查模型是否已经存在,如果不存在,则尝试下载模型。最后,它使用ollama.chat函数与模型进行聊天,并以流式方式输出结果,模拟ChatGPT的实时生成体验。

利用stream=True参数实现的token-level生成,是提升用户交互体验的关键。例如,在一个代码生成场景中,如果代码是一次性全部生成,用户可能需要等待较长时间才能看到结果。而通过流式输出,用户可以立即看到代码的生成过程,从而更快地理解和反馈。

解决证书错误:Conda的妙用

在受限的环境中(大多数HPC集群),当您想使用Ollama拉取新模型时,可能会遇到以下错误:

tls: failed to verify certificate: x509: certificate signed by unknown authority (status code: 500)

这通常是因为您的系统无法验证SSL证书,并且通常无法通过apt-get或使用sudo来更新它们。

我发现最实用的解决方法是通过conda-forge安装Ollama。为此,您需要在您的系统上安装conda。然后,从您的conda环境中运行:

conda install -c conda-forge ollama

安装完成后,您可以通过运行以下命令来设置要存储模型的位置:

export OLLAMA_MODELS=${DESIRED_LOCATION_TO_KEEP_THE_MODEL}

然后,使用以下命令拉取所需的模型:

ollama pull ${MODEL_NAME}

这种方法避免了证书问题,并且在受限的HPC环境中运行良好。

在HPC集群中,由于安全策略和权限限制,系统管理员通常不会允许用户随意修改系统级的证书信任列表。因此,传统的证书更新方法(如apt-get update ca-certificates)通常是不可行的。Conda作为一个用户级别的包管理工具,允许用户在自己的环境中安装和管理依赖项,而无需root权限。通过conda-forge安装Ollama,可以绕过系统级的证书验证,从而避免了证书错误。OLLAMA_MODELS环境变量,它可以灵活地指定模型存储位置,这在HPC环境中非常有用,因为用户可以根据自己的存储配额和性能需求,将模型存储在不同的位置。

停止服务器:清理资源

当您完成操作后,停止Apptainer实例:

apptainer instance stop ollama-$USER

这将停止Ollama服务器并释放占用的资源。 清理资源是HPC集群上负责任的做法。停止Apptainer实例可以释放GPU资源,使其可供其他用户使用。

总结:HPC环境中的Ollama部署

本文详细介绍了如何在HPC集群上使用Apptainer运行Ollama,并提供了解决常见问题的技巧。通过这种方法,您可以在受限的环境中利用Ollama的强大功能,并在本地运行大语言模型。从构建Ollama容器镜像,到配置GPU支持,再到解决证书错误,本文涵盖了Ollama部署的各个方面。 掌握了这些技能,您就可以在HPC集群上构建自己的本地LLM应用,并充分利用HPC集群的计算资源。 未来,随着大语言模型的不断发展,Apptainer和Ollama将在HPC环境中发挥越来越重要的作用,为科研人员和工程师提供强大的计算和分析工具。