阅读时间 : 14 minutes

两个 Pro 订阅,一台机器,一张银行卡,且没有网络冲突。Docker 是如何让我在不购买第二台 PC 的情况下将云推理能力翻倍的。

简介

我已经使用 Ollama Pro 几个月了,为我的 OpenCode 会话提供云端模型。问题在于,即使拥有 Pro 订阅,当并行启动多个代理时,也会很快受到速率限制的限制。显而易见的解决方案:再订阅一个。

但这里,这就是悲剧。

Ollama 使用独一无二的 SSH 密钥来标识每台机器 — 它著名的 Device Key。同一操作系统上的两个实例将共享同样的身份,且无法将两个专业账户绑定到同一设备上。正如你所料,我并不打算为此再买一台笔记本电脑。

解决方案:作弊。让 Ollama 误以为它在两台不同的机器上运行,而实际上它们共享同一个 CPU、相同的 RAM 和相同的网络连接。Docker 将为我们提供一个完美的隔离气泡。

@startuml
!theme plain
skinparam BoxPadding 10
skinparam DefaultFontSize 12

left to right direction

node "物理机" {
  frame "Ollama 实例 A (原生)" as Native {
    database "身份\n/usr/share/ollama/.ollama/id_ed25519" as KeyA
    portin "API\n:11434" as PortA
  }

  frame "Ollama 实例 B (Docker)" as Docker {
    database "身份\n~/ollama-b-data/id_ed25519" as KeyB
    portin "API\n:11435" as PortB
  }
}

cloud "Ollama 云" {
  actor "专业账号 A\n(email 1)" as AccountA
  actor "专业账号 B\n(email 2)" as AccountB
}

KeyA --> AccountA : "键 SSH A"
KeyB --> AccountB : "键 SSH B"
PortA --> AccountA : "请求 A"
PortB --> AccountB : "请求 B"

note bottom of Docker
隔离容器:独立的身份 SSH,独立的网络端口,持久卷
end note
@enduml

为什么两次都是同样的事情?

在你把我当成精神病患者之前,让我向你解释一下用例。

只需一个Pro订阅,我就可以启动一个像`deepseek-v4-pro:cloud`在一个 OpenCode 会话中。当我想要 两个 同时会话时,问题就出现了。速率限制的配额导致第二个会话变慢或被直接拒绝。

拥有两个独立的专业版订阅:

  • 会话 OpenCode 1 → 专业账户 A`localhost:11434`)

  • 会话 OpenCode 2 → 专业版账户 B`localhost:11435`)

每个会话都有自己的配额、自己的上下文,并且不会相互干扰。这是人类的多处理。

两个 Pro 订阅 = 两个不同的电子邮件。同一张银行卡可以使用 — Ollama 支付系统不会阻止来自同一付款方式的多重订阅。我已经确认了。

问题的核心:设备密钥

当你安装 Ollama 时,一对 SSH ed25519 密钥将在身份目录中生成:

$ cat /usr/share/ollama/.ollama/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...

此密钥在上传到Ollama服务器时`ollama signin`她说 « 这台机器属于某个专业账户 »。如果您的两个实例共享同一文件`id_ed25519`, 它们共享相同的身份。游戏结束。

诀窍:给我们的第二个实例分配它自己的身份目录,隔离在 Docker 卷中。

准备工作 — 逐步进行

先决条件

  • 一个功能正常的 Ollama 原生安装(官方脚本`curl | sh`)

  • Docker 已安装并正常运行

  • Portainer 或 docker-compose 用于部署

  • 两个 Ollama 账户,分别使用两个不同的电子邮件

我使用了版本`0.20.2`— 由官方脚本提供的那个以及对应的 Docker 镜像。

步骤 1:创建卷

主机上的一个简单目录将作为实例 B 的身份的持久卷:

mkdir -p ~/ollama-b-data

此文件夹将在 Docker 容器中以…​方式挂载`/root/.ollama`, 那里 Ollama 存储其身份密钥、历史记录和模型。

步骤 2 : 启动 Docker 容器

# docker-compose.yml
services:
  ollama-instance-b:
    image: ollama/ollama:0.20.2
    container_name: ollama-instance-b
    ports:
      - "11435:11434"
    volumes:
      - /home/cheroliv/ollama-b-data:/root/.ollama
    environment:
      - OLLAMA_HOST=0.0.0.0
    restart: always

这里正在发生什么:

  • 港口11435主机的映射到11434内部容器。 因此,Docker 实例监听在`:11435`与正在占用的本地实例无冲突`:11434`.

  • 体积`~/ollama-b-data`安装在`/root/.ollama`— 这是存储 SSH 身份的地方。

  • `OLLAMA_HOST=0.0.0.0`允许容器接受外部连接。

我通过 Portainer 部署了这个堆栈,一个简单`docker compose up -d`同样也能正常工作。

步骤3:获取公钥

容器在首次启动时是空的,Ollama 会在卷中自动生成一对新的 SSH 密钥。我们获取公钥:

$ docker exec ollama-instance-b cat /root/.ollama/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDDQ+dvnfmuo49q5O8LOlvgZ39SKORFw47ry9k4H2jPc

我确认此密钥与原生实例的密钥*不同*:

# Instance native
$ cat /usr/share/ollama/.ollama/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGyF...(différente)

# Instance Docker
$ cat ~/ollama-b-data/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDDQ...(différente)

两把不同的钥匙,两个分离的身份。一切已成定局。

如果您的容器重新启动,它 重新使用 卷中存在的密钥。身份是持久的。您不会丢失与专业帐户的关联。

步骤 4:在 Ollama.com 上保存密钥

转到 https://ollama.com/settings/keys → 添加 SSH 密钥。将 Docker 实例的公钥粘贴进去并确认。

然后,将此身份关联到 Pro B 账户 :

$ docker exec -it ollama-instance-b ollama signin

浏览器打开,使用账户 B 的邮箱登录,然后令牌与容器的 SSH 密钥关联。检查一切正常:

$ docker exec -it ollama-instance-b ollama signin
User: cherolivpro

步骤 5:用免费小模型进行测试

在提到专业版订阅的价格之前,我想确认管道是否正常工作。我拉取一个小型的本地免费模型来验证连接:

$ docker exec ollama-instance-b ollama pull qwen3:0.6b
pulling manifest
pulling 7f4030143c1c: 100% ▕██████████████████▏ 522 MB
success

$ curl -s http://localhost:11435/api/tags
{"models":[{"name":"qwen3:0.6b","model":"qwen3:0.6b",...}]}

港口`11435`它响应,模型已就绪。B 实例处于活跃状态。

一旦放心,我就切换到真正的云端Pro模型的拉取:

$ docker exec ollama-instance-b ollama pull deepseek-v4-pro:cloud
pulling manifest
pulling 31c3059e137e: 100% ▕██████████████████▏  344 B
success

344 字节用于云模型的清单 — 正常,推理是在服务器端进行的,而不是在本地。这就是最终测试:

$ curl -s http://localhost:11435/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"deepseek-v4-pro:cloud","messages":[{"role":"user","content":"Dis bonjour en une phrase courte."}]}'

{
  "id": "chatcmpl-480",
  "model": "deepseek-v4-pro",
  "choices": [{
    "message": { "content": "Bonjour !" },
    "finish_reason": "stop"
  }],
  "usage": { "total_tokens": 181 }
}

您好,实例B。

API的陷阱 `/v1/models

我在一个愚蠢的细节上浪费了20分钟。当我配置提供者时`ollama-b`在 OpenCode 中,模型选择器里什么也没有显示出来。什么也没有。一丁点儿都没有。

原因 ? L’API`/v1/models`实例 B 正在返回`{"object":"list","data":null}`而不是`{"object":"list","data":[]}`当没有模型被拉取时。值`null`导致 OpenCode 端的解析崩溃,它没有显示提供者。

解决方案:在 OpenCode 配置中*显式*声明模型,而不是依赖动态发现。

~/.config/opencode/opencode.json
{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "ollama": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "Ollama (local)",
      "options": { "baseURL": "http://localhost:11434/v1" },
      "models": {
        "gemma4:e2b": { "name": "Gemma 4 E2B (local)" }
      }
    },
    "ollama-b": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "Ollama Instance B (Docker)",
      "options": { "baseURL": "http://localhost:11435/v1" },
      "models": {
        "qwen3:0.6b": { "name": "Qwen3 0.6B (B)" },
        "deepseek-v4-pro:cloud": { "name": "DeepSeek V4 Pro (B)" }
      }
    }
  }
}

通过此显式声明,OpenCode 立即看到提供程序 B 及其模型。重启会话后,选择器`/models`显示两个实例并排。

如果您看不到您的自定义提供程序在`/models`, 不要花三个小时重启终端。手动声明模型在`opencode.json`— 这能立即解决问题。

结果:两个会话,两个账户,零冲突

最终,我可以同时启动两个 OpenCode 会话:

Session 1 → /models → Ollama (local) → deepseek-v4-pro:cloud → Compte A
Session 2 → /models → Ollama Instance B (Docker) → deepseek-v4-pro:cloud → Compte B

每个会话都有自己的配额和速率限制,互不干扰。同样的机器,同样的信用卡,使用两个不同的电子邮件。

而最好的? Docker 容器是`restart: always`. 它能在系统重启后存活,无需人工干预。

学到的教训

  1. Docker 隔离一切,甚至身份— 简单的卷绑定挂载即可为容器提供自己的 SSH 密钥集,使其在 Ollama 眼中与不同的物理机无法区分。

  2. 两封邮件,同一张银行卡— Ollama 不会阻止使用同一付款方式的多重订阅。只有电子邮件必须不同。

  3. 免费试用后再付费 — Un ollama pull qwen3:0.6b (modèle libre, 522 Mo) permet de valider toute la chaîne réseau sans débourser un centime. Vous validez le plomberie d’abord, vous activez le Pro ensuite.

  4. /v1/models` 与 data: null 导致 OpenCode 出错— 如果您配置一个自定义提供程序与`"models": {}`OpenCode 尝试通过 API 发现这些模型。如果 API 响应`data: null`, 提供程序未出现。请显式声明模型。

  5. 两个账户,这就是人类的多处理— 一个 Pro 账户 = 一个活跃的 OpenCode 会话。两个账户 = 两个并行会话。对于同时处理多个 Gradle 项目的人来说,这是个游戏规则改变者。

相关文章