本文记录一套可复用的远程大模型部署流程:使用 Colab CLI 按需创建 A100 runtime,从 Google Drive 加载 Qwen3.8-27B-Uncensored-Q8_0,使用 llama.cpp 提供 OpenAI 兼容 API,再通过 Cloudflare Tunnel 从本地客户端访问。

核心结构:

1
2
3
4
5
6
7
本地电脑:Colab CLI / 启动脚本 / OpenAI 客户端
│
├── Colab A100 runtime:llama-server 推理
│
├── Google Drive:GGUF 模型、编译 binary、凭据
│
└── Cloudflare Tunnel:临时公网访问

Colab 的 /content 是临时高速磁盘,Google Drive 是持久存储。模型放 Drive,启动时复制到 /content,服务停止后释放 GPU。

一、准备条件

需要:

  • Google 账号和可用的 Colab GPU 配额。
  • 已认证的 Colab CLI。
  • Google Drive 至少约 30 GB 可用空间存放 Q8_0 模型。
  • A100、H100 或其他支持 CUDA 的 GPU runtime。
  • 本地安装 colab CLI。

Q8_0 模型约 29.05 GB。A100 40 GB 可以运行,但上下文空间需要保守设置;A100 80 GB 更适合 64K context。

GPU 选择:

GPU 建议
A100 80 GB 首选,适合 Q8_0 和 64K context
A100 40 GB 可以运行,建议 16K–32K context
H100 性能更好,取决于账户配额
L4 可以尝试较小量化,Q8_0 需要谨慎评估
T4 不建议运行这个 Q8_0 模型
CPU / TPU 不适合本流程

二、安装和认证 Colab CLI

检查 CLI:

1
2
command -v colab
colab --help

首次使用 OAuth2:

1
colab --auth=oauth2 sessions

CLI 会输出 Google 授权链接。打开链接,完成登录和授权,再按提示提交 authorization code。

检查当前账号:

1
colab whoami

OAuth2 token 通常保存在:

1
~/.config/colab-cli/token.json

部分 Colab CLI 版本没有独立的 logout 命令。注销时应先在 Google 侧撤销 token,再删除本地 token 文件;不要删除 Google Cloud ADC,除非你也要注销 ADC。

不要把 authorization code、access token、refresh token 或 API key 写入公开仓库。

三、把模型下载到 Google Drive

模型仓库:

1
JonathanColetti/Qwen3.8-27B-Uncensored-GGUF

本次选择:

1
Qwen3.8-27B-Uncensored-Q8_0.gguf

使用 crhywun/colab-llm 提供的 notebooks/01_download_to_drive.ipynb,或者在 Colab CPU runtime 中执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
!pip install -q -U "huggingface_hub[hf_transfer]"

from google.colab import drive
drive.mount('/content/drive')

from huggingface_hub import hf_hub_download

REPO = "JonathanColetti/Qwen3.8-27B-Uncensored-GGUF"
FILE = "Qwen3.8-27B-Uncensored-Q8_0.gguf"
LOCAL = "/content/qwen38_download"
DRIVE_DIR = "/content/drive/MyDrive/colab/Qwen3.8-27B-Uncensored-GGUF"

model_path = hf_hub_download(
repo_id=REPO,
filename=FILE,
local_dir=LOCAL,
)

!mkdir -p "{DRIVE_DIR}"
!rsync -ah --partial --progress \
"{model_path}" "{DRIVE_DIR}/"

最终路径:

1
My Drive/colab/Qwen3.8-27B-Uncensored-GGUF/Qwen3.8-27B-Uncensored-Q8_0.gguf

实际复制结果:

1
2
bytes: 29047084448
sha256: fb2cb9aa5407264aec6698faf41183f337809352abe0bd8fb750c0b689f4ced2

建议保存模型 README 和下载信息,方便以后核对来源和文件完整性。

四、按需创建 A100 runtime

1
colab new -s qwen38-server --gpu a100 --high-mem

查看状态:

1
2
colab status -s qwen38-server
colab sessions

新 runtime 的 /content 为空,不包含上一次运行的模型和进程。每次启动都要重新挂载 Drive,并把模型复制到本地盘。

如果 runtime 是通过 Colab 网页创建的,notebook 名称不一定是 CLI session name。colab sessions 中显示的 assignment 名称和网页标题可能不同。为便于自动化,建议一直使用 colab new -s ... 创建 runtime。

验证 GPU:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
colab exec -s qwen38-server --timeout 60 -f /dev/stdin <<'PY'
import platform
import shutil
import torch

print('platform:', platform.platform())
print('free disk GB:', round(shutil.disk_usage('/').free / 1024**3, 1))
print('torch:', torch.__version__)
print('cuda:', torch.cuda.is_available())
if torch.cuda.is_available():
print('gpu:', torch.cuda.get_device_name(0))
print('vram GB:', round(
torch.cuda.get_device_properties(0).total_memory / 1024**3, 1
))
PY

五、挂载 Google Drive

可以在当前 runtime 的 Colab 页面执行:

1
2
from google.colab import drive
drive.mount('/content/drive')

也可以使用 CLI:

1
colab drivemount -s qwen38-server /content/drive

首次挂载会打开 Google 授权页。点击允许后,回到 CLI 按回车,让 Colab 完成 credentials propagation。

挂载完成后确认:

1
2
3
4
5
6
7
8
9
colab exec -s qwen38-server --timeout 60 -f /dev/stdin <<'PY'
import os

model = '/content/drive/MyDrive/colab/Qwen3.8-27B-Uncensored-GGUF/Qwen3.8-27B-Uncensored-Q8_0.gguf'
print('drive:', os.path.isdir('/content/drive/MyDrive'))
print('model:', os.path.exists(model))
if os.path.exists(model):
print('bytes:', os.path.getsize(model))
PY

如果出现 ValueError: mount failed,通常是授权页面还没完成、回车提交太早,或者授权发生在另一个 runtime。重新执行 colab drivemount,确认使用同一个 session。

六、复制模型到 runtime 本地盘

从 Drive 直接加载模型速度较慢,推荐复制到 /content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
colab exec -s qwen38-server --timeout 900 -f /dev/stdin <<'PY'
import os
import subprocess

source = '/content/drive/MyDrive/colab/Qwen3.8-27B-Uncensored-GGUF/Qwen3.8-27B-Uncensored-Q8_0.gguf'
target = '/content/models/Qwen3.8-27B-Uncensored-Q8_0.gguf'

os.makedirs('/content/models', exist_ok=True)
subprocess.run([
'rsync', '-ah', '--partial', '--info=progress2',
source, target,
], check=True)
print('model ready:', os.path.getsize(target), 'bytes')
PY

rsync --partial 支持中断后继续。模型不会重新下载,只会从 Drive 复制。

七、准备 llama.cpp

colab-llm 使用 llama.cpp 的 llama-server。A100 的 compute capability 是 8.0,对应 sm80。

第一次可以编译:

1
2
3
4
5
6
7
8
!git clone --depth 1 https://github.com/ggml-org/llama.cpp /content/llama.cpp
!cmake -S /content/llama.cpp -B /content/llama.cpp/build \
-DGGML_CUDA=ON \
-DCMAKE_CUDA_ARCHITECTURES=80 \
-DBUILD_SHARED_LIBS=OFF \
-DLLAMA_CURL=OFF \
-DCMAKE_BUILD_TYPE=Release
!cmake --build /content/llama.cpp/build --target llama-server -j 4

首次 CUDA 编译可能很慢。不要盲目使用 -j $(nproc),长时间前台编译可能造成 CLI 等待超时或 runtime 被回收。

编译完成后保存 binary:

1
2
3
!mkdir -p /content/drive/MyDrive/colab/llama-bin
!cp /content/llama.cpp/build/bin/llama-server \
/content/drive/MyDrive/colab/llama-bin/llama-server-sm80

以后新 runtime 直接复用:

1
2
3
4
5
6
7
8
9
10
colab exec -s qwen38-server --timeout 60 -f /dev/stdin <<'PY'
import os
import shutil

source = '/content/drive/MyDrive/colab/llama-bin/llama-server-sm80'
target = '/content/llama-server'
shutil.copy(source, target)
os.chmod(target, 0o755)
print('llama-server ready')
PY

八、启动 Qwen API

A100 80 GB 的实际测试配置:

  • Qwen3.8-27B-Uncensored-Q8_0
  • context 65,536
  • KV cache q8_0
  • 单并发
  • -ngl 99
  • A100-SXM4-80GB 显存使用约 29,228 MiB
  • /health 约 10 秒通过

启动命令:

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
37
import secrets
import subprocess
import time
import urllib.request

SERVER = '/content/llama-server'
MODEL = '/content/models/Qwen3.8-27B-Uncensored-Q8_0.gguf'
API_KEY = secrets.token_urlsafe(32)

command = [
SERVER,
'-m', MODEL,
'--alias', 'qwen3.8-27b',
'--api-key', API_KEY,
'-ngl', '99',
'-c', '65536',
'-ctk', 'q8_0',
'-ctv', 'q8_0',
'-np', '1',
'--jinja',
'--host', '127.0.0.1',
'--port', '8000',
]

log = open('/content/llama-server.log', 'w')
subprocess.Popen(command, stdout=log, stderr=subprocess.STDOUT)

for _ in range(90):
try:
if urllib.request.urlopen(
'http://127.0.0.1:8000/health', timeout=3
).status == 200:
print('server ready')
print('API key:', API_KEY)
break
except Exception:
time.sleep(2)

参数说明:

  • -ngl 99:尽可能把模型层放入 GPU。
  • -c 65536:上下文长度 65,536。
  • -ctk q8_0 -ctv q8_0:量化 KV cache,降低显存压力。
  • -np 1:单并发,把显存优先给一个请求。
  • --jinja:启用模型 chat template。
  • --api-key:启用 API 鉴权。

A100 40 GB 建议从 16K 或 32K context 开始。显存不足时依次尝试:

1
65536 → 32768 → 16384

先不要启用 speculative decoding。draft-mtp 需要额外下载 draft model,应先确认主模型稳定。

九、测试 OpenAI 兼容 API

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
import json
import urllib.request

payload = {
'model': 'qwen3.8-27b',
'messages': [
{'role': 'user', 'content': '用三句话介绍一下你自己。'}
],
'max_tokens': 512,
'temperature': 0.7,
}

request = urllib.request.Request(
'http://127.0.0.1:8000/v1/chat/completions',
data=json.dumps(payload).encode(),
headers={
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + API_KEY,
},
method='POST',
)

response = urllib.request.urlopen(request, timeout=90)
result = json.loads(response.read())
print(result['choices'][0]['message']['content'])

返回 HTTP 200 只说明模型加载、GPU 推理和 API server 正常。还应测试格式化输出、翻译、中文理解和困难问题。

十、实测能力和适用边界

本次对 Qwen3.8-27B-Uncensored-Q8_0 做了 8 项测试,temperature 0–0.2,A100 80 GB,context 65,536,reasoning 开启:

测试 结果 耗时 说明
17 × 23 通过 2.9 秒 输出 391
三段论陷阱题 通过 3.1 秒 识别“所有猫都是狗”的陷阱
JSON 指令遵循 通过 4.8 秒 只输出合法 JSON
翻译和长指令 通过 6.0 秒 只输出译文
中文成语和现代场景造句 通过 5.1 秒 中文理解良好
负温度系数物理常识 通过 3.5 秒 回答到位
数字谜题 ab7+394=7cb 失败 52 秒以上 推理耗尽 2048 tokens,content 为空
交替真假话逻辑题 失败 52 秒以上 同样耗尽推理预算,content 为空

结论:基础问答、代码、翻译和结构化输出可用,常见响应约 3–6 秒;困难或含歧义的长链条推理不稳定。

两道失败题本身也有问题:数字谜题经手工验证无解,真假话题没有指定交替者相位,属于欠定问题。模型没有快速识别病态题,而是持续思考直到耗尽 token。

测试中把 max_tokens 提高到 8192,或加入 /no_think,仍没有解决这两题的循环;英文改写也能复现。因此更像模型在困难/病态问题上的行为缺陷,而不是中文 prompt 单独造成。

生产调用建议:

  • 使用 stream=True,及时接收 token。
  • 普通任务把 max_tokens 控制在 1024–2048。
  • 对困难题设置客户端 timeout 和空答案重试。
  • 提示模型先判断题目是否有解、条件是否充分。
  • 不把这个配置当成长链条数学推理服务。

完整测试记录:qwen38-server3-model-test-report.md。

十一、用 Cloudflare Tunnel 暴露公网 API

只在 Colab 内使用时,不需要隧道。需要本地 Chatbox、Cherry Studio、Open WebUI 或 Python 客户端访问时,可以启动临时隧道:

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
import os
import re
import subprocess
import time

if not os.path.exists('/content/cloudflared'):
subprocess.run([
'wget', '-q',
'https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64',
'-O', '/content/cloudflared',
], check=True)
os.chmod('/content/cloudflared', 0o755)

log = open('/content/cloudflared.log', 'w')
subprocess.Popen([
'/content/cloudflared', 'tunnel', '--no-autoupdate',
'--url', 'http://127.0.0.1:8000',
], stdout=log, stderr=subprocess.STDOUT)

public_url = None
for _ in range(60):
text = open('/content/cloudflared.log').read()
match = re.search(r'https://[a-z0-9-]+\\.trycloudflare\\.com', text)
if match:
public_url = match.group(0)
break
time.sleep(1)

print('Public API:', public_url + '/v1')

公网自检:

1
2
3
4
5
6
7
request = urllib.request.Request(
public_url + '/v1/models',
headers={'Authorization': 'Bearer ' + API_KEY},
)
response = urllib.request.urlopen(request, timeout=30)
print(response.status)
print(response.read().decode())

本次实测公网 /health 和 /v1/models 均返回 HTTP 200。具体 trycloudflare.com 地址只代表当次 runtime,不能写死到客户端配置;应从脚本生成的 ${SESSION}-public.txt 读取。

Cloudflare 免费隧道的超时边界

测试发现,非流式长推理超过约 100 秒时,Cloudflare 免费临时隧道可能返回 HTTP 524。模型可能仍在推理,但公网请求已经超时。因此公网调用必须使用流式响应:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from openai import OpenAI

client = OpenAI(
base_url='https://你的隧道地址.trycloudflare.com/v1',
api_key='你的 API key',
timeout=120,
)

stream = client.chat.completions.create(
model='qwen3.8-27b',
messages=[{'role': 'user', 'content': '你好。'}],
max_tokens=2048,
stream=True,
)

for chunk in stream:
if not chunk.choices:
continue
print(chunk.choices[0].delta.content or '', end='', flush=True)

流式调用不能修复模型推理循环,所以仍需设置 max_tokens 和客户端超时。需要长期、私有访问时,可以改用 Tailscale;但 Tailscale 也不能防止 Colab runtime 被回收。

十二、本地客户端配置

安装 SDK:

1
pip install openai

OpenAI 兼容配置:

1
2
3
Base URL: https://你的隧道地址.trycloudflare.com/v1
Model: qwen3.8-27b
API Key: Drive 凭据文件中的 api_key

本地 Python:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from openai import OpenAI

client = OpenAI(
base_url='https://你的隧道地址.trycloudflare.com/v1',
api_key='从 Drive 凭据文件读取',
timeout=120,
)

stream = client.chat.completions.create(
model='qwen3.8-27b',
messages=[
{'role': 'user', 'content': '写一段关于远程 GPU 的短文。'}
],
max_tokens=2048,
stream=True,
)

for chunk in stream:
if not chunk.choices:
continue
print(chunk.choices[0].delta.content or '', end='', flush=True)

不要把 API key 写入 Git、公开 notebook 或文章正文。推荐保存到权限受控的 Drive 文件或 Colab Secrets。

十三、按需启动脚本

本次流程已整理为:start_qwen_colab.sh

1
2
3
4
5
6
7
8
9
10
11
# 创建或复用 A100、挂载 Drive、启动模型
./start_qwen_colab.sh start

# 启动模型并建立临时 Cloudflare Tunnel
./start_qwen_colab.sh tunnel

# 查看 runtime 和 API 状态
./start_qwen_colab.sh status

# 停止模型、隧道并释放 runtime
./start_qwen_colab.sh stop

脚本默认使用:

1
2
3
4
Session: qwen38-server
GPU: a100
Context: 65536
Model: Qwen3.8-27B-Uncensored-Q8_0.gguf

自定义 session 或 context:

1
2
3
QWEN_COLAB_SESSION=qwen38-server4 \
QWEN_COLAB_CONTEXT=32768 \
./start_qwen_colab.sh tunnel

脚本自动完成:

  1. 创建或复用 runtime。
  2. 挂载 Google Drive。
  3. 把模型复制到 /content。
  4. 复用 Drive 中的 llama-server-sm80。
  5. 启动 llama-server。
  6. 可选启动 Cloudflare Tunnel。
  7. 把 API key 和公网地址写入 Drive。

十四、Tailscale 私有访问

Tailscale 可以替代临时 Cloudflare Tunnel,提供 Tailnet 内的私有访问:

1
本地电脑 ── Tailnet ── Colab runtime ── llama-server:8000

它不会公开 trycloudflare.com 地址,也不受 Cloudflare 免费隧道的 524 超时影响。但需要 Tailscale auth key,并且 runtime 回收后 Tailscale 节点也会离线。

在 runtime 中安装:

1
!curl -fsSL https://tailscale.com/install.sh | sh

启动 userspace daemon:

1
2
3
4
5
6
7
8
9
10
11
12
import subprocess
import time

socket_path = '/tmp/tailscaled.sock'
subprocess.Popen([
'sudo', 'tailscaled',
'--tun=userspace-networking',
f'--socket={socket_path}',
'--state=/tmp/tailscale-state',
], stdout=open('/tmp/tailscaled.log', 'w'),
stderr=subprocess.STDOUT)
time.sleep(3)

使用 Colab Secret 读取 auth key:

1
2
3
4
5
6
7
8
9
from google.colab import userdata

auth_key = userdata.get('TAILSCALE_AUTH_KEY')
subprocess.run([
'sudo', 'tailscale', f'--socket={socket_path}', 'up',
f'--authkey={auth_key}',
'--hostname=colab-qwen',
'--accept-routes=false',
], check=True)

将本地 llama-server 代理到 Tailscale:

1
2
!sudo tailscale --socket=/tmp/tailscaled.sock serve --bg http://127.0.0.1:8000
!sudo tailscale --socket=/tmp/tailscaled.sock serve status

Tailscale 只解决私有网络访问,不保证 Colab 常驻。它也必须加入启动脚本,每次新 runtime 重新登录。

十五、停止服务和释放资源

使用脚本:

1
./start_qwen_colab.sh stop

手动停止:

1
2
3
4
5
6
7
8
9
colab exec -s qwen38-server --timeout 30 -f /dev/stdin <<'PY'
import subprocess

subprocess.run(['pkill', '-f', '/content/llama-server'])
subprocess.run(['pkill', '-f', '/content/cloudflared'])
subprocess.run(['pkill', '-f', 'tailscaled'])
PY

colab stop -s qwen38-server

释放 runtime 后:

  • /content/models 消失。
  • /content/llama-server 消失。
  • llama-server、Tunnel 和 Tailscale 停止。
  • 临时公网地址失效。
  • Drive 中的模型、binary、配置和凭据保留。

十六、Colab runtime 能否按需启动

可以,但不能保证永久运行。Colab 可能因以下原因回收 runtime:

  • 空闲超时。
  • 达到账号或套餐的最长运行时间。
  • GPU 资源紧张或被后端抢占。
  • 计算单元耗尽。
  • kernel 崩溃或长任务异常。

监控只能减少空闲回收,不能阻止后端重启、资源抢占或最长运行时间:

1
2
3
4
5
6
7
8
9
10
11
import time
import urllib.request

while True:
try:
print(time.strftime('%H:%M'), urllib.request.urlopen(
'http://127.0.0.1:8000/health', timeout=10
).status, flush=True)
except Exception as e:
print('server error:', e, flush=True)
time.sleep(240)

真正需要长期在线时,应使用 RunPod、Vast.ai、Lambda Cloud、GCP Compute Engine、AWS EC2 或自有 GPU 服务器。Colab 适合低成本、按需启动;持续服务应使用长期运行的 GPU VM。

十七、完整流程总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Colab OAuth2 登录
↓
下载 Qwen3.8-27B Q8_0 到 Google Drive
↓
创建 A100 High-RAM runtime
↓
挂载 Drive
↓
复制模型到 /content
↓
复用或编译 llama-server-sm80
↓
启动 llama-server
↓
本地 HTTP 200 健康检查
↓
能力测试和参数限制
↓
可选 Cloudflare Tunnel 或 Tailscale
↓
本地 OpenAI 客户端流式调用
↓
停止服务并释放 runtime

关键经验:

  • 模型和 binary 放 Google Drive,避免重复下载和编译。
  • /content 只作为临时高速缓存。
  • Q8_0 在 A100 80 GB 上实测可运行,64K context 使用约 29 GB 显存。
  • 公网访问使用流式请求,避免 Cloudflare 免费隧道的长请求超时。
  • 对困难推理设置 max_tokens、timeout 和空答案处理。
  • Tailscale 提供私有访问,但不能防止 Colab runtime 回收。
  • 使用结束执行 stop,否则 GPU 会继续消耗计算单元。

这样,Colab 就能作为一个按需启动的远程 Qwen 推理工作站:Drive 保存持久资源,Colab 提供临时 GPU,llama.cpp 提供 API,客户端通过流式 OpenAI 兼容接口访问模型。