Hugging Face Spaces × Docker 部署 Ollama 小模型全流程

意外富翁 · 2个月前 · 技术 · 61 · 0

Ollama 装进 Hugging Face Spaces 的免费 Docker 容器,让 1-4B 小模型一键上线,随时通过 HTTPS 调用。


1 创建 Spaces 容器

  1. 登录 https://huggingface.co → 右上角 New Space
  2. Space 名称随意(例 ollama-gemma)、Docker 类型、硬件选 CPU basic(免费 2 vCPU / 16 GB,足够 1-4B 量化模型)。
  3. Public(私有需 PRO),点 Create Space

2 本地克隆仓库(后面要推送)

git clone https://huggingface.co/spaces/<你的用户名>/ollama-gemma
cd ollama-gemma

3 一键可用文件清单(全部放在仓库根目录)

文件 作用
Dockerfile 基于 ollama/ollama 官方镜像,装 Python、依赖、暴露 7860
entrypoint.sh 启动 ollama 服务 → 拉模型 → 启动 FastAPI 代理
app.py 透传 /generate 接口,把客户端 JSON 原样发给 Ollama
requirements.txt 仅 3 行:fastapi / uvicorn / requests

下面直接给出内容,复制即可。

3.1 Dockerfile

# 使用 ollama 官方镜像作为底包
FROM ollama/ollama:latest

# 1. 安装系统依赖(curl + python3)
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        curl \
        python3 \
        python3-pip \
        python3-venv \
        python-is-python3 && \
    rm -rf /var/lib/apt/lists/*

# 2. 创建并启用虚拟环境(避开 PEP-668 限制)
RUN python3 -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

# 3. 复制 requirements 并安装 Python 依赖
COPY requirements.txt /tmp/
RUN pip install --upgrade pip && \
    pip install -r /tmp/requirements.txt

# 4. 复制业务代码
WORKDIR /code
COPY . .

# 5. 暴露 Hugging Face 要求的端口
EXPOSE 7860

# 6. 启动脚本
ENTRYPOINT ["bash", "entrypoint.sh"]

3.2 entrypoint.sh

#!/bin/bash
set -e

# 设置环境变量
export OLLAMA_MAX_LOADED_MODELS=2
echo "Set OLLAMA_MAX_LOADED_MODELS=$OLLAMA_MAX_LOADED_MODELS"
echo "Set OLLAMA_KEEP_ALIVE=-1"

echo "Starting Ollama service..."
# 1. 后台启动 ollama 服务
ollama serve &
OLLAMA_PID=$!

# 等待服务就绪(最多等待120秒)
echo "Waiting for Ollama to be ready..."
COUNTER=0
until curl -s http://localhost:11434/api/tags > /dev/null 2>&1; do
  echo "Waiting for Ollama... ($COUNTER/120)"
  sleep 3
  COUNTER=$((COUNTER + 3))
  if [ $COUNTER -ge 120 ]; then
    echo "ERROR: Ollama failed to start within 120 seconds"
    kill $OLLAMA_PID 2>/dev/null || true
    exit 1
  fi
done

echo "Ollama is ready!"

# 2. 拉取模型
echo "Pulling models..."
ollama pull nomic-embed-text || echo "Failed to pull nomic-embed-text"
ollama pull qwen2.5:1.5b || echo "Failed to pull qwen2.5:1.5b"

# 3. 保持模型在内存中运行
echo "Loading models into memory..."
# 启动 nomic-embed-text 模型并保持运行
ollama run nomic-embed-text > /dev/null 2>&1 &
EMBED_PID=$!

# 启动 qwen2.5:1.5b 模型并保持运行
ollama run qwen2.5:1.5b > /dev/null 2>&1 &
CHAT_PID=$!

# 等待模型加载完成
echo "Waiting for models to load into memory..."
sleep 15

# 验证模型是否可用
echo "Checking available models..."
curl -s http://localhost:11434/api/tags

# 4. 启动 FastAPI 接口
echo "Starting FastAPI server..."
exec uvicorn app:app --host 0.0.0.0 --port 7860

3.3 app.py(透传版)

# app.py
import requests
from fastapi import FastAPI, Request, Response
from fastapi.middleware.cors import CORSMiddleware

OLLAMA_BASE_URL = "http://localhost:11434"

app = FastAPI(title="Ollama-raw-proxy")

# 可选:解决浏览器跨域
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.post("/api/{endpoint}")
async def proxy_ollama_dynamic(endpoint: str, raw: Request):
    """
    动态转发请求到 Ollama API
    例如: /api/chat → http://localhost:11434/api/chat
         /api/generate → http://localhost:11434/api/generate
         /api/embed → http://localhost:11434/api/embed
    """
    # 构建目标 URL
    target_url = f"{OLLAMA_BASE_URL}/api/{endpoint}"
    
    body = await raw.body()               # bytes,保持原样
    headers = {"Content-Type": "application/json"}
    
    # 转发请求到对应的 Ollama API
    r = requests.post(target_url,
                      data=body,
                      headers=headers,
                      timeout=60)
    
    return Response(content=r.content,
                    status_code=r.status_code,
                    headers=r.headers)

# 保持向后兼容的别名路由
@app.post("/chat")
async def proxy_ollama_chat(raw: Request):
    """向后兼容:/chat → /api/chat"""
    return await proxy_ollama_dynamic("chat", raw)

@app.post("/embed")
async def proxy_ollama_embed(raw: Request):
    """向后兼容:/embed → /api/embed"""
    return await proxy_ollama_dynamic("embed", raw)

@app.get("/")
def root():
    return {"message": "Ollama raw proxy is running"}

3.4 requirements.txt

fastapi
uvicorn[standard]
requests

4 推送并部署

git add .
git commit -m "feat: ollama qwen3 1.7b on HF Spaces"
git push origin main

Space 页面会实时刷构建日志,看到 Ollama is running 说明容器已就绪。


5 快速测试

5.1 查看模型列表

curl https://<你的-space>.hf.space/api/tags

5.2 生成文本

curl https://<你的-space>.hf.space/generate \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen2.5:1.5b","prompt":"天空为什么是蓝色的?","stream":false}'

返回示例

{"model":"qwen2.5:1.5b","response":"天空呈现蓝色是由于瑞利散射现象……","done":true}

5.3 Python 客户端

import requests
url = "https://<你的-space>.hf.space/generate"
payload = {
  "model": "qwen2.5:1.5b",
  "prompt": "用三句话介绍量子计算",
  "stream": False
}
print(requests.post(url, json=payload).json()["response"])

6 常见问题速查

现象 解决
curl: command not found Dockerfile 里 apt install curl
externally-managed-environment 用 python3-venv 或 --break-system-packages
502/504 模型尚未拉完,看日志等 1-2 分钟
想跑 7B+ Spaces 硬件切到 GPU(收费)再拉 qwen3:7b

7 进阶玩法

  1. 换模型:把 entrypoint.sh 里的 ollama pull qwen2.5:1.5b 改成 gemma3:1bnomic-embed-text:latest 等,重新 push 即可。
  2. 流式输出:把 stream=true 并在客户端逐行读取 response 字段。
  3. 嵌入模型:用 /api/embeddings 端点,见 示例。

至此,你已经拥有了一个公网可访问、24 h 在线、免费 CPU 运行的 Ollama 推理节点。
把接口地址填到 ChatBox、Open-WebUI、AnythingLLM 等客户端,就能随时与本地/线上小模型对话啦!

已复制到剪贴板

评论 0 条

暂无评论,来种下第一颗种子。