跳过正文

本地运行大语言模型(二):vLLM和Ollama的比较及部署Open WebUI前端

·3949 字·8 分钟
锦李本鲤
作者
锦李本鲤
光锥之内,皆为命运。
大语言模型系列 - 这篇文章属于一个选集。
§ 2: 本文

缘起
#

之前我们讨论过自建大语言模型服务,当时使用的是Ollama作为后端服务,使用Lobechat作为前端,通过API调用后端的Ollama来实现对话。

最近我在尝试使用OpenClaw,但它烧Token的速度实在太快了,就想着自己运行一个本地的大语言模型来提供Token。跟ChatGPT聊过之后,它告诉我这种情况下最好使用vLLM而非Ollama,因为vLLM提供与OpenAI兼容的API,支持Copilot,且性能更好。于是我就试着转向了vLLM。

简介
#

vLLM
#

vLLM和Ollama都是在自己电脑上或者在服务器上运行大语言模型(LLM)的工具,然而他们的使用场景却有所不同。简单来讲,vLLM致力于高效地运行大语言模型,而Ollama专注于使大语言模型方便地运行起来。

另外,vLLM提供与OpenAI兼容的API,如果我们要运行的服务依赖于OpenAI之类的商业模型API,而且支持用户自定义模型提供商,那我们就可以接入我们自己部署的vLLM。

Ollama
#

关于Ollama的介绍和部署请参考本系列的第一篇文章。

由于Ollama使用的是一些自定义的REST API,和OpenAI的API不兼容,不能直接接入很多使用大语言模型的服务。不过,有开发者开发了一款可以调用Ollama并兼容OpenAI API的工具LiteLLM

我们只需要将这个工具和Ollama容器一块儿部署,再做一些设置,它就可以提供与OpenAI的API兼容的API。

vLLM和Ollama的比较及注意事项
#

  1. 显存占用

    在运行时,我们能感知到的vLLM与Ollama最大的区别是,部署vLLM需要将整个模型加载到GPU的显存里,即使模型处于闲置(未被调用)的状态,vLLM依然会占用这部分显存。

    也就是说,vLLM几乎不能和其他重度使用显卡的程序一块儿运行。例如,运行vLLM后,如果你想打游戏,那就需要先下线vLLM。

    而Ollama则没有这个问题。Ollama只有在被调用时才会把模型加载到显存里,在你想打游戏时,你不需要关闭Ollama,只要在你打游戏期间不去调用Ollama,那它就能和你的游戏相安无事。

  2. 可调用模型数量

    在部署Ollama后,我们可以下载多个模型,用户可以决定调用哪一个。

    但在部署vLLM后,加载到显存中的模型是固定的,一个vLLM容器只能加载一个模型,要想选择模型调用,就需要同时部署多个vLLM容器。这对于我们个人用户显然是不现实的,我们的显卡一般是做不到的。如果想加载多个模型,那就只能用很小的模型,意义不大。

部署大语言模型及前端
#

总览
#

我有两台有16GB显存显卡的电脑,因此我选择在电脑A上部署vLLM,在电脑B上部署带有vLLM接口的Ollama(我们暂且称其为vOllama)。通过域名将vLLM和vOllama的接口暴露于公网。

在任一台电脑上(可以是A或B,也可以是另一台电脑C)上部署Open WebUI前端,它可以方便地接入自己部署的大语言模型。

vLLM部署
#

docker-compose
#

docker-compose.yml文件如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
services:
  vllm:
    image: vllm/vllm-openai:v0.17.0
    container_name: vllm
    restart: unless-stopped
    runtime: nvidia
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    environment:
      - HUGGING_FACE_HUB_TOKEN=${HF_TOKEN}
      - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
      - HF_HUB_DISABLE_XET=1
      - HF_HUB_ENABLE_HF_TRANSFER=0
      - VLLM_API_KEY=${VLLM_API_KEY:-dummy}
      - TZ=${TZ:-UTC}
    volumes:
      - ~/.cache/huggingface:/root/.cache/huggingface
    ipc: host
    command: >
      ${MODEL}
      ${MODEL_PARAMETERS}
    networks:
      traefik-net:
        aliases:
          - vllm

networks:
  traefik-net:
    external: true

这里的MODELMODEL_PARAMETERS和其他变量在环境变量文件.env文件中定义:

1
2
3
4
5
HF_TOKEN=hf_your_token
MODEL=Qwen/Qwen2.5-Coder-14B-Instruct-AWQ
MODEL_PARAMETERS="--host 0.0.0.0 --port 8000 --gpu-memory-utilization 0.81 --dtype auto --kv-cache-dtype fp8 --enforce-eager"
VLLM_API_KEY=your_api_key
TZ=Europe/Paris

其中,HF_TOKEN是Huggingface的token,我们需要它以便从Huggingface下载大语言模型。

vLLM只能加载一个模型,我们选了Qwen/Qwen2.5-Coder-14B-Instruct-AWQ,加载它大约需要14GB的显存。

测试
#

在命令行中输入类似下面的命令来测试我们的vLLM是否部署成功:

1
2
3
4
5
6
7
curl https://vllm.example/v1/chat/completions \
  -H "Authorization: Bearer your_vllm_api_key" \   
  -H "Content-Type: application/json" \
  -d '{
    "model": "Qwen/Qwen2.5-Coder-14B-Instruct-AWQ",
    "messages": [{"role": "user", "content": "Hello"}]
  }'

如果收到类似下面的回复就表示vLLM已成功部署并可以通过域名访问:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
{
  "id": "chatcmpl-82d9387336a446bd",
  "object": "chat.completion",
  "created": 1774276907,
  "model": "Qwen/Qwen2.5-Coder-14B-Instruct-AWQ",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello! How can I assist you today?",
        "refusal": null,
        "annotations": null,
        "audio": null,
        "function_call": null,
        "tool_calls": [],
        "reasoning": null
      },
      "logprobs": null,
      "finish_reason": "stop",
      "stop_reason": null,
      "token_ids": null
    }
  ],
  "service_tier": null,
  "system_fingerprint": null,
  "usage": {
    "prompt_tokens": 30,
    "total_tokens": 40,
    "completion_tokens": 10,
    "prompt_tokens_details": null
  },
  "prompt_logprobs": null,
  "prompt_token_ids": null,
  "kv_transfer_params": null
}

在命令行里这个JSON会被压缩在一行显示。

vOllama部署
#

配置文件
#

docker-compose.yml文件如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
services:
  ollama:
    container_name: ollama
    image: docker.1ms.run/ollama/ollama:0.17.7
    environment:
      - OLLAMA_ORIGINS=*
      - OLLAMA_HOST=0.0.0.0
      - TZ=America/New_York
    deploy:
      resources:
        reservations:
          devices:
          - driver: nvidia
            capabilities: ["gpu"]
            count: all
    volumes:
      - ./ollama-data:/root/.ollama
    restart: always
    networks:
      - traefik-net

  litellm:
    image: ghcr.io/berriai/litellm:main-latest
    container_name: litellm
    restart: unless-stopped
    command: ["--config", "/app/config.yaml"]
    volumes:
      - ./litellm-config.yaml:/app/config.yaml
    networks:
      - traefik-net

networks:
  traefik-net:
    external: true

这里我们部署了ollamalitellm两个容器,它们都使用我们之前创建的网络traefik-net,我们依然使用Cloudflare Tunnel来反向代理vOllama服务。

litellm需要一个配置文件litellm-config.yml,如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
model_list:
  - model_name: qwen3.5:0.8b
    litellm_params:
      model: ollama/qwen3.5:0.8b
      api_base: http://ollama:11434

  - model_name: mistral:7b
    litellm_params:
      model: ollama/mistral:7b
      api_base: http://ollama:11434

general_settings:
  master_key: your_ollama_api_key

配置文件主要有以下作用:

  • model_list告诉litellm我们在ollama里面运行了哪些大语言模型。当我们给Ollama下载安装了其他模型,我们需要更新这个文件以加入新的模型。
  • 由于我们将模型暴露在了公网上,我们需要设置一个API key(即master_key)来控制访问,不提供API key的不能调用我们的模型。

安装大语言模型
#

  1. 使用docker compose up -d来部署Ollama容器。
  2. 部署成功后,Ollama还是空的,里面没有下载任何大语言模型。我们使用docker exec -it ollama ollama run qwen3.5:0.8b来下载用运行qwen3.5:0.8b这个模型。
  3. 安装完成后可以继续下载其他模型
  4. 使用docker exec -it ollama ollama list可以列出安装了哪些模型。

测试
#

可以使用如下命令测试是否部署成功:

1
2
3
4
5
6
7
curl https://ollama.example/v1/chat/completions \
  -H "Authorization: Bearer your_ollama_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen3.5:0.8b",
    "messages": [{"role": "user", "content": "Hello"}]
  }'

如果收到类似下面的回复就表示vOllama已成功部署并可以通过域名访问:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
{
  "id": "chatcmpl-5844b507-f967-4412-b8fa-815c4e3235b1",
  "created": 1774276549,
  "model": "qwen3.5:0.8b",
  "object": "chat.completion",
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "message": {
        "content": "Greetings! How's going? 😊 Feel free to ask me anything—whether you need help, just wanted to say hello, or maybe something else. I'm here for fun and helpful!",
        "role": "assistant"
      }
    }
  ],
  "usage": {
    "completion_tokens": 111,
    "prompt_tokens": 15,
    "total_tokens": 126
  }
}

在命令行里这个JSON会被压缩在一行显示。

Open WebUI部署
#

只用curl或者API来调用部署的大模型显然是不方便的,我们需要部署一个网页版的客户端。这里我们选用一个开源的客户端Open WebUI

Open WebUI简介
#

Open WebUI是一个开源的大语言模型网页版客户端,风格跟OpenUI的ChatGPT页面很像。它允许我们接入自己部署的vLLM或者Ollama模型,也支持接入商用的各种模型(需要API key)。

Open WebUI部署
#

docker-compose.yml文件如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
version: "3.9"

services:
  open-webui:
    image: ghcr.io/open-webui/open-webui:0.8.10
    container_name: open-webui
    volumes:
      - ${DATA_DIR}:/app/backend/data
    environment:
      - OPENAI_API_BASE_URL=http://vllm:8000/v1
      - OPENAI_API_KEY=${VLLM_API_KEY:-dummy}
      - TZ=${TZ:-UTC}
    restart: unless-stopped
    networks:
      traefik-net:
        aliases:
          - open-webui

networks:
  traefik-net:
    external: true

这里的OPENAI_API_BASE_URLOPENAI_API_KEY填了我们在本地部署的vLLM。不填也可以,待会儿可以在图形界面设置。

使用下面的命令启动open-webui容器:

1
docker compose up -d

Open WebUI配置
#

设置反向代理后,我们就可以通过域名访问Open WebUI了。第一次访问时,Open WebUI会要求我们设置一个管理员账号和密码。

用管理员账号登录后,点击右上角的头像,可以看到"Admin Panel"选项,点击进入可以设置Open WebUI所需要连接的后端模型服务。

Open WebUI Admin Panel

在“Admin Panel"的"Settings"页面里,有”Connections"选项,在这里可以添加我们之前部署的Ollama服务、vLLM服务,或者vOllama服务,以及商用的大语言模型服务(如OpenAI、Azure、Anthropic等)。

添加完成后,点击"settings"页面里的"Models"选项,你可以看到Ollama服务或者vLLM服务中可用的模型。这些模型默认的权限是"private",我们可以点击某个模型右侧的编辑按钮,在模型具体设置里点击"Access"选项,具体设置哪些用户可以访问这个模型。

最后,在设置好模型后,就可以像使用ChatGPT一样使用Open WebUI了,输入问题后,Open WebUI会调用我们之前部署的vLLM或者Ollama服务来获取回复:

Open WebUI Chat

问题解决
#

Ollama安装Qwen3.6
#

在使用Ollama安装去审核版Qwen3.6模型Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive时,可能会遇到安装失败的问题。报错信息如下:

1
llama_model_load: error loading model: error loading model architecture: unknown model architecture: 'qwen35moe'

这个问题在GitHub上有讨论:

https://github.com/ollama/ollama/issues/14503

其原因是在下载Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive模型完成后,Ollama生成的模型文件Modelfile并不正确。里面有两行From /root/.ollama/xxx,这表明Ollama试图加载两个模型文件,但实际上只需要加载一个模型文件。我们需要将第二个From /root/.ollama/xxx删除掉或用#注释掉,保存后重新运行这个模型就可以了。

但实际上Modelfile似乎并没有被显式地创建,我们需要进入容器

1
docker exec -it ollama bash

然后在容器内内执行下面的命令来生成这个文件:

1
ollama show --modelfile hf.co/HauhauCS/Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive:IQ2_M | cat > /root/Modelfile

注释掉或者删除掉第二行From /root/.ollama/xxx后,保存退出,然后重新创建这个模型:

1
ollama create Qwen3.6-35B-A3B-Uncensored-IQ2M -f /root/Modelfile
大语言模型系列 - 这篇文章属于一个选集。
§ 2: 本文