Ollama:简化LLM本地部署

Ollama是2026年最受欢迎的本地LLM部署工具之一。它以极简的命令行界面和自动化的模型管理,让在本地运行大模型变得前所未有的简单。但从"能跑"到"生产可用"之间,还有大量工程细节需要处理。

安装与环境准备

系统要求

# Linux安装
curl -fsSL https://ollama.com/install.sh | sh

# 验证GPU支持
nvidia-smi  # 确认GPU可用
ollama --version

GPU显存规划

不同模型的显存需求:

模型参数量FP16显存INT4显存推荐GPU
Qwen-3-7B7B14GB5GBRTX 4060 8GB+
Llama-3-8B8B16GB6GBRTX 4070 12GB+
Qwen-3-32B32B64GB20GBRTX 4090 24GB+
Llama-3-70B70B140GB40GB2×A100 80GB

Ollama服务配置

# 自定义模型存储路径
export OLLAMA_MODELS=/data/ollama/models

# 监听所有网络接口(生产环境配合防火墙)
export OLLAMA_HOST=0.0.0.0:11434

# 并发请求数
export OLLAMA_NUM_PARALLEL=4

# 上下文长度
export OLLAMA_CONTEXT_LENGTH=8192

# GPU层数(-1为全部卸载到GPU)
export OLLAMA_NUM_GPU=-1

# 启动服务
ollama serve

模型管理

Modelfile自定义

# 基于Qwen-3创建自定义模型
FROM qwen3:32b

# 系统提示词
SYSTEM """
你是一个专业的技术助手。请提供准确、简洁的回答。
如果不确定,请明确说明。
"""

# 参数调优
PARAMETER temperature 0.7
PARAMETER top_p 0.9
PARAMETER top_k 40
PARAMETER num_ctx 8192
PARAMETER num_gpu 50
PARAMETER stop "<|im_end|>"
PARAMETER stop "<|endoftext|>"

# 模板
TEMPLATE """
{{ if .System }}<|im_start|>system
{{ .System }}<|im_end|>
{{ end }}{{ range .Messages }}{{ if eq .Role "user" }}<|im_start|>user
{{ .Content }}<|im_end|>
{{ end }}{{ if eq .Role "assistant" }}<|im_start|>assistant
{{ .Content }}<|im_end|>
{{ end }}{{ end }}<|im_start|>assistant
"""
# 构建自定义模型
ollama create my-qwen -f Modelfile

# 运行
ollama run my-qwen

模型量化

# Ollama自动选择量化级别
ollama pull llama3:70b          # 默认INT4量化
ollama pull llama3:70b-q8_0     # 指定INT8
ollama pull llama3:70b-fp16     # FP16精度

# 从GGUF文件导入
ollama create my-model --file ./model.gguf

API服务

REST API

import requests

# 基础对话
response = requests.post(
    "http://localhost:11434/api/chat",
    json={
        "model": "my-qwen",
        "messages": [
            {"role": "user", "content": "解释Transformer的注意力机制"}
        ],
        "stream": False,
        "options": {
            "temperature": 0.7,
            "num_ctx": 8192,
        }
    }
)
print(response.json()["message"]["content"])

# 流式响应
response = requests.post(
    "http://localhost:11434/api/chat",
    json={"model": "my-qwen", "messages": [...], "stream": True},
    stream=True
)
for line in response.iter_lines():
    if line:
        chunk = json.loads(line)
        print(chunk["message"]["content"], end="", flush=True)

OpenAI兼容API

Ollama提供OpenAI兼容接口,方便迁移现有应用:

from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama"  # 任意值
)

response = client.chat.completions.create(
    model="my-qwen",
    messages=[{"role": "user", "content": "Hello"}],
    stream=True
)

生产环境配置

Nginx反向代理

upstream ollama_backend {
    server 127.0.0.1:11434;
    # 可以添加多个后端实现负载均衡
    # server 10.0.0.2:11434;
}

server {
    listen 443 ssl;
    server_name llm.example.com;
    
    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;
    
    location / {
        proxy_pass http://ollama_backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        
        # SSE支持(流式响应)
        proxy_buffering off;
        proxy_cache off;
        chunked_transfer_encoding on;
        
        # 超时设置(LLM生成可能较慢)
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
    }
    
    # 速率限制
    limit_req zone=api burst=10 nodelay;
}

Systemd服务

# /etc/systemd/system/ollama.service
[Unit]
Description=Ollama Service
After=network.target

[Service]
Type=simple
User=ollama
Group=ollama
Environment="OLLAMA_HOST=0.0.0.0:11434"
Environment="OLLAMA_MODELS=/data/ollama/models"
Environment="OLLAMA_NUM_PARALLEL=4"
ExecStart=/usr/local/bin/ollama serve
Restart=always
RestartSec=3

# 安全限制
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/data/ollama

# 资源限制
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

Docker部署

# docker-compose.yml
version: '3.8'
services:
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    ports:
      - "11434:11434"
    volumes:
      - ollama_data:/root/.ollama
    environment:
      - OLLAMA_HOST=0.0.0.0:11434
      - OLLAMA_NUM_PARALLEL=4
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    restart: unless-stopped

  # 可选:Web UI
  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    ports:
      - "3000:8080"
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
    volumes:
      - webui_data:/app/backend/data
    depends_on:
      - ollama

volumes:
  ollama_data:
  webui_data:

监控与日志

Prometheus指标

# 自定义指标导出
from prometheus_client import Counter, Histogram, start_http_server

REQUEST_COUNT = Counter('ollama_requests_total', 'Total requests')
REQUEST_LATENCY = Histogram('ollama_request_duration_seconds', 'Request duration')
ACTIVE_REQUESTS = Counter('ollama_active_requests', 'Currently active requests')

@app.route("/api/chat", methods=["POST"])
def chat():
    REQUEST_COUNT.inc()
    ACTIVE_REQUESTS.inc()
    start_time = time.time()
    
    try:
        response = proxy_to_ollama(request.json)
        REQUEST_LATENCY.observe(time.time() - start_time)
        return response
    finally:
        ACTIVE_REQUESTS.dec()

日志管理

import logging
import json

class OllamaLogger:
    def __init__(self):
        self.logger = logging.getLogger("ollama")
        handler = logging.FileHandler("/var/log/ollama/app.log")
        handler.setFormatter(logging.Formatter(
            '%(asctime)s - %(levelname)s - %(message)s'
        ))
        self.logger.addHandler(handler)
    
    def log_request(self, model, prompt_length, response_length, duration):
        self.logger.info(json.dumps({
            "model": model,
            "prompt_tokens": prompt_length,
            "response_tokens": response_length,
            "duration_ms": duration,
            "timestamp": datetime.now().isoformat()
        }))

性能优化

模型预热

# 启动时预加载模型
curl http://localhost:11434/api/generate -d '{
    "model": "my-qwen",
    "prompt": "warmup",
    "keep_alive": -1
}'

# keep_alive: -1 表示永久保持模型在内存中
# keep_alive: 0 表示使用后立即卸载
# keep_alive: 300 表示保持5分钟

批处理优化

import asyncio
import aiohttp

async def batch_generate(prompts, model="my-qwen"):
    """并发批处理"""
    async with aiohttp.ClientSession() as session:
        tasks = []
        for prompt in prompts:
            task = asyncio.create_task(
                generate_one(session, model, prompt)
            )
            tasks.append(task)
        
        results = await asyncio.gather(*tasks)
        return results

async def generate_one(session, model, prompt):
    async with session.post(
        "http://localhost:11434/api/generate",
        json={"model": model, "prompt": prompt, "stream": False}
    ) as resp:
        return await resp.json()

多GPU负载均衡

# 指定GPU
CUDA_VISIBLE_DEVICES=0 ollama serve  # GPU 0
CUDA_VISIBLE_DEVICES=1 ollama serve  # GPU 1(不同端口)

# 或使用Ollama内置多GPU支持(自动分配)
ollama serve  # 自动检测所有GPU

安全加固

API认证

from fastapi import FastAPI, HTTPException, Security
from fastapi.security import APIKeyHeader

app = FastAPI()
api_key_header = APIKeyHeader(name="X-API-Key")

VALID_API_KEYS = {"your-secret-key"}

async def verify_api_key(api_key: str = Security(api_key_header)):
    if api_key not in VALID_API_KEYS:
        raise HTTPException(status_code=403, detail="Invalid API key")
    return api_key

@app.post("/api/chat")
async def chat(request: dict, api_key: str = Security(verify_api_key)):
    # 代理到Ollama
    return await proxy_to_ollama(request)

内容过滤

class ContentFilter:
    def __init__(self):
        self.blocked_patterns = [
            r"忽略.*指令",
            r"ignore.*instruction",
            # 添加更多模式
        ]
    
    def check_input(self, text):
        for pattern in self.blocked_patterns:
            if re.search(pattern, text, re.IGNORECASE):
                return False, "输入包含可疑内容"
        return True, None
    
    def check_output(self, text):
        # 检查输出是否包含敏感信息
        if self.contains_pii(text):
            return False, "输出包含敏感信息"
        return True, None

常见问题排查

GPU内存不足

# 减少GPU层数
PARAMETER num_gpu 20  # 只将20层卸载到GPU

# 使用更激进的量化
ollama pull model:q4_0  # INT4量化

# 减少并发数
export OLLAMA_NUM_PARALLEL=1

响应速度慢

# 检查是否所有层在GPU上
ollama ps  # 查看模型加载状态

# 确保keep_alive设置合理
curl http://localhost:11434/api/generate -d '{
    "model": "my-qwen",
    "prompt": "test",
    "keep_alive": -1
}'

模型加载失败

# 检查磁盘空间
df -h /data/ollama/models

# 清理未使用的模型
ollama rm old-model

# 检查模型文件完整性
ollama show my-qwen --modelfile

结语

Ollama以其简洁的设计和强大的功能,成为本地LLM部署的首选工具。从开发测试到生产部署,合理配置模型参数、监控系统健康、优化请求处理,是构建可靠LLM服务的关键。

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。