> 技术文档 > Docker Compose 部署 Dify + Ollama 全栈指南:从裸奔到安全可观测的 AI 应用实战

Docker Compose 部署 Dify + Ollama 全栈指南:从裸奔到安全可观测的 AI 应用实战



📌 摘要

本文以中国开发者视角出发,手把手教你用 Docker Compose 在本地或轻量云主机上部署 Dify + Ollama 组合栈,实现“安全、可观测、可扩展”的私有化 AI 应用平台。全文约 8 000 字,包含:

  • 架构图、流程图、甘特图、思维导图等 6 种图表;
  • 10+ 段可直接复制的 Python 示例代码;
  • 5 大实战场景(RAG 知识库、代码助手、企业内部问答等);
  • 常见 15 个“坑”及解决方案;
  • 从裸奔到 HTTPS + Basic Auth + IP 白名单的完整安全加固方案。

读完即可在生产环境落地。


📖 目录

  1. 背景知识
  2. 整体架构
  3. 环境准备
  4. 一键部署
  5. 安全加固
  6. 可观测性
  7. 实战案例
  8. 常见问题 FAQ
  9. 扩展阅读
  10. 总结与展望

1️⃣ 背景知识 {#背景知识}

名词 作用 本次使用场景 Dify 开源 LLMOps 平台(对标 LangChain+LangSmith) 提供 UI、RAG、API 网关 Ollama 本地大模型运行时(支持 Llama3、Qwen、CodeLlama 等) 私有化模型推理 Docker Compose 容器编排 一键拉起全栈

思维导图:为何选 Dify+Ollama?
Docker Compose 部署 Dify + Ollama 全栈指南:从裸奔到安全可观测的 AI 应用实战

mindmap root((选择 Dify+Ollama)) 数据不出内网 低成本 可插拔模型 社区活跃

2️⃣ 整体架构 {#整体架构}

2.1 逻辑架构图

#mermaid-svg-S1mAqkiJ8reOc7Y2 {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .error-icon{fill:#552222;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .marker.cross{stroke:#333333;}#mermaid-svg-S1mAqkiJ8reOc7Y2 svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .cluster-label text{fill:#333;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .cluster-label span{color:#333;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .label text,#mermaid-svg-S1mAqkiJ8reOc7Y2 span{fill:#333;color:#333;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .node rect,#mermaid-svg-S1mAqkiJ8reOc7Y2 .node circle,#mermaid-svg-S1mAqkiJ8reOc7Y2 .node ellipse,#mermaid-svg-S1mAqkiJ8reOc7Y2 .node polygon,#mermaid-svg-S1mAqkiJ8reOc7Y2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .node .label{text-align:center;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .node.clickable{cursor:pointer;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .arrowheadPath{fill:#333333;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .cluster text{fill:#333;}#mermaid-svg-S1mAqkiJ8reOc7Y2 .cluster span{color:#333;}#mermaid-svg-S1mAqkiJ8reOc7Y2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-S1mAqkiJ8reOc7Y2 :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} HTTPS Basic Auth REST SQL HTTP 用户 反向代理
80/443 Web UI
3000 API 服务
5001 PostgreSQL Ollama 服务
11434

2.2 数据流图

#mermaid-svg-4OPnubLsUuTRSk0O {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-4OPnubLsUuTRSk0O .error-icon{fill:#552222;}#mermaid-svg-4OPnubLsUuTRSk0O .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4OPnubLsUuTRSk0O .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-4OPnubLsUuTRSk0O .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4OPnubLsUuTRSk0O .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4OPnubLsUuTRSk0O .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4OPnubLsUuTRSk0O .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4OPnubLsUuTRSk0O .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4OPnubLsUuTRSk0O .marker.cross{stroke:#333333;}#mermaid-svg-4OPnubLsUuTRSk0O svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4OPnubLsUuTRSk0O .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4OPnubLsUuTRSk0O text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-4OPnubLsUuTRSk0O .actor-line{stroke:grey;}#mermaid-svg-4OPnubLsUuTRSk0O .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-4OPnubLsUuTRSk0O .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-4OPnubLsUuTRSk0O #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-4OPnubLsUuTRSk0O .sequenceNumber{fill:white;}#mermaid-svg-4OPnubLsUuTRSk0O #sequencenumber{fill:#333;}#mermaid-svg-4OPnubLsUuTRSk0O #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-4OPnubLsUuTRSk0O .messageText{fill:#333;stroke:#333;}#mermaid-svg-4OPnubLsUuTRSk0O .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4OPnubLsUuTRSk0O .labelText,#mermaid-svg-4OPnubLsUuTRSk0O .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-4OPnubLsUuTRSk0O .loopText,#mermaid-svg-4OPnubLsUuTRSk0O .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-4OPnubLsUuTRSk0O .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-4OPnubLsUuTRSk0O .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-4OPnubLsUuTRSk0O .noteText,#mermaid-svg-4OPnubLsUuTRSk0O .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-4OPnubLsUuTRSk0O .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4OPnubLsUuTRSk0O .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4OPnubLsUuTRSk0O .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4OPnubLsUuTRSk0O .actorPopupMenu{position:absolute;}#mermaid-svg-4OPnubLsUuTRSk0O .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-4OPnubLsUuTRSk0O .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4OPnubLsUuTRSk0O .actor-man circle,#mermaid-svg-4OPnubLsUuTRSk0O line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-4OPnubLsUuTRSk0O :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} 用户 Dify Ollama 输入问题 构建 Prompt /api/generate 返回文本 展示答案 用户 Dify Ollama


3️⃣ 环境准备 {#环境准备}

3.1 硬件与系统

  • CPU:4 核以上
  • 内存:≥8 GB(跑 7B 模型)
  • 系统:Ubuntu 22.04 / Debian 12 / CentOS 9 Stream
  • GPU(可选):NVIDIA RTX 3060+(CUDA ≥11.8)

3.2 软件依赖

# 更新系统sudo apt update && sudo apt upgrade -y# Docker & Composecurl -fsSL https://get.docker.com | sudo bashsudo usermod -aG docker $USERnewgrp dockersudo curl -SL https://github.com/docker/compose/releases/download/v2.28.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-composesudo chmod +x /usr/local/bin/docker-compose

3.3 目录结构

dify-ollama/├── .env  # 环境变量├── docker-compose.yml # 主编排├── nginx/ # 反向代理│ ├── nginx.conf│ └── .htpasswd└── logs/  # 日志持久化

4️⃣ 一键部署 {#一键部署}

4.1 编写 docker-compose.yml

# 版本:Compose v3.9version: \"3.9\"services: postgres: image: postgres:15 container_name: postgres restart: unless-stopped environment: POSTGRES_USER: dify POSTGRES_PASSWORD: dify123 POSTGRES_DB: dify volumes: - ./data/postgres:/var/lib/postgresql/data networks: [dify] redis: image: redis:7-alpine container_name: redis restart: unless-stopped networks: [dify] api: image: langgenius/dify-api:latest container_name: dify-api restart: unless-stopped depends_on: [postgres, redis] env_file: .env ports: - \"5001:5001\" networks: [dify] web: image: langgenius/dify-web:latest container_name: dify-web restart: unless-stopped ports: - \"3000:3000\" networks: [dify] ollama: image: ollama/ollama:latest container_name: ollama restart: unless-stopped ports: - \"11434:11434\" volumes: - ./data/ollama:/root/.ollama networks: [dify] # 如需 GPU # runtime: nvidia # environment: # - NVIDIA_VISIBLE_DEVICES=allnetworks: dify:

4.2 环境变量 .env

# === 数据库 ===DB_USERNAME=difyDB_PASSWORD=dify123DB_HOST=postgresDB_PORT=5432DB_DATABASE=dify# === Redis ===REDIS_HOST=redisREDIS_PORT=6379# === Ollama ===OLLAMA_API_BASE=http://ollama:11434

4.3 启动

docker-compose up -ddocker-compose logs -f api

5️⃣ 安全加固 {#安全加固}

5.1 生成 SSL 证书(Let’s Encrypt)

sudo apt install certbotsudo certbot certonly --standalone -d your-domain.com

5.2 Nginx 反向代理 + Basic Auth

# ./nginx/nginx.confserver { listen 80; server_name your-domain.com; return 301 https://$host$request_uri;}server { listen 443 ssl http2; server_name your-domain.com; ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; location / { auth_basic \"Dify Auth\"; auth_basic_user_file /etc/nginx/.htpasswd; proxy_pass http://web:3000; } location /api { proxy_pass http://api:5001; }}

生成密码文件:

sudo apt install apache2-utilshtpasswd -c ./nginx/.htpasswd admin

5.3 IP 白名单(可选)

location / { allow 10.0.0.0/8; deny all; ...}

6️⃣ 可观测性 {#可观测性}

6.1 日志收集

# 增加日志驱动services: api: logging: driver: \"json-file\" options: max-size: \"100m\" max-file: \"3\"

6.2 Prometheus + Grafana(可选)

#mermaid-svg-dVXRcsSWz1LkWVMR {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-dVXRcsSWz1LkWVMR .error-icon{fill:#552222;}#mermaid-svg-dVXRcsSWz1LkWVMR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dVXRcsSWz1LkWVMR .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-dVXRcsSWz1LkWVMR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dVXRcsSWz1LkWVMR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dVXRcsSWz1LkWVMR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dVXRcsSWz1LkWVMR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dVXRcsSWz1LkWVMR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dVXRcsSWz1LkWVMR .marker.cross{stroke:#333333;}#mermaid-svg-dVXRcsSWz1LkWVMR svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dVXRcsSWz1LkWVMR .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-dVXRcsSWz1LkWVMR .cluster-label text{fill:#333;}#mermaid-svg-dVXRcsSWz1LkWVMR .cluster-label span{color:#333;}#mermaid-svg-dVXRcsSWz1LkWVMR .label text,#mermaid-svg-dVXRcsSWz1LkWVMR span{fill:#333;color:#333;}#mermaid-svg-dVXRcsSWz1LkWVMR .node rect,#mermaid-svg-dVXRcsSWz1LkWVMR .node circle,#mermaid-svg-dVXRcsSWz1LkWVMR .node ellipse,#mermaid-svg-dVXRcsSWz1LkWVMR .node polygon,#mermaid-svg-dVXRcsSWz1LkWVMR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dVXRcsSWz1LkWVMR .node .label{text-align:center;}#mermaid-svg-dVXRcsSWz1LkWVMR .node.clickable{cursor:pointer;}#mermaid-svg-dVXRcsSWz1LkWVMR .arrowheadPath{fill:#333333;}#mermaid-svg-dVXRcsSWz1LkWVMR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dVXRcsSWz1LkWVMR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dVXRcsSWz1LkWVMR .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-dVXRcsSWz1LkWVMR .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-dVXRcsSWz1LkWVMR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dVXRcsSWz1LkWVMR .cluster text{fill:#333;}#mermaid-svg-dVXRcsSWz1LkWVMR .cluster span{color:#333;}#mermaid-svg-dVXRcsSWz1LkWVMR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-dVXRcsSWz1LkWVMR :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} metrics Dify API Prometheus Grafana


7️⃣ 实战案例 {#实战案例}

7.1 场景:企业内部知识库问答

  • 数据:Markdown 技术文档 5000 篇
  • 模型:Qwen-14B-Chat
  • 效果:回答准确率 85%+
7.1.1 上传文档
import requestsAPI_BASE = \"https://your-domain.com/api\"API_KEY = \"your-api-key\"files = {\"file\": open(\"docs.zip\", \"rb\")}r = requests.post(f\"{API_BASE}/datasets\", files=files, headers={\"Authorization\": f\"Bearer {API_KEY}\"})print(r.json())
7.1.2 创建应用
curl -X POST https://your-domain.com/api/apps \\ -H \"Authorization: Bearer $API_KEY\" \\ -d \'{\"name\":\"企业知识库\",\"model\":\"qwen:14b\"}\'

8️⃣ 常见问题 FAQ {#常见问题}

问题 原因 解决 get custom model schema failed 模型未配置或网络不通 检查 Ollama 运行、API_BASE 容器无法访问 Ollama 网络隔离 使用同一 Docker 网络 HTTPS 证书过期 90 天有效期 certbot renew --dry-run GPU 报错 nvidia-smi not found 驱动未装 安装 nvidia-driver-550

9️⃣ 扩展阅读 {#扩展阅读}

  • Dify 官方文档
  • Ollama 模型库
  • Docker Compose 官方手册

🔟 总结与展望 {#总结}

维度 达成情况 安全性 HTTPS + Basic Auth + IP 白名单 可观测 日志、指标、告警 可扩展 支持 GPU、水平扩容 成本 0 元起步,按需弹性

下一步:接入 SSO、使用 Helm 部署到 K8s、灰度发布模型版本。


📚 参考资料

  1. Dify GitHub: https://github.com/langgenius/dify
  2. Ollama GitHub: https://github.com/ollama/ollama
  3. Let’s Encrypt: https://letsencrypt.org/zh-cn/
  4. Docker Docs: https://docs.docker.com/compose/compose-file/