
1. 项目背景小张是公司的初级开发工程师工位旁边的测试老周天天催他你说的那个 Dify 智能客服啥时候能让我测小张看了看手边的电脑——一台 16G 内存的 Windows 笔记本心里有点虚“这个平台听说过有 Python、Node.js、PostgreSQL、Redis、Celery……光环境搭建就得花一周吧”这不是小张一个人的焦虑。Dify 虽然功能强大但其技术栈相当重后端 Flask Celery前端 Next.js依赖 PostgreSQL、Redis、向量数据库、沙箱服务、插件守护进程等 7 个子服务。如果从源码编译安装光是解决 Python 依赖冲突和 Node.js 版本兼容就能让新人崩溃。更别提那些微妙的配置项——SECRET_KEY 不能乱填API URL 要区分内部外部Redis 连接串的格式稍错就报Connection refused。好在小张的师傅指了指docker/目录试试docker compose up -d一条命令全搞定。小张将信将疑地敲下命令5 分钟后浏览器里出现了 Dify 的登录界面。他瞪大了眼睛“这就跑起来了那些 Python 虚拟机、Node 包管理器、数据库配置文件……我一个都没碰啊。”Docker Compose 部署是 Dify 的官方推荐方式也是新手最容易上手的入口。但能跑起来只是第一步。更重要的是你要理解docker-compose.yaml里到底定义了哪些服务每个服务是干什么的它们之间怎么通信。只有理解了这些你才能应对后续的知识库索引失败“Workflow 执行超时”模型调用 500等需要进去容器里排查的问题。本章的目标就是用一条命令让 Dify 跑起来再用一个小时让你看懂背后的服务拓扑。2. 项目设计小胖抱着一袋薯片盯着屏幕上滚动的 Docker log“大师神奇啊我就敲了docker compose up -d这一堆 Python、Node.js、数据库啥的都自动装好了这跟变魔术似的”大师“不是魔术是集装箱。Docker 把每个服务打包成一个独立的’集装箱’里面有自己的操作系统环境、依赖库、网络。你看到的docker-compose.yaml就是一张’装箱单’告诉 Docker 需要几个集装箱每个集装箱里装什么它们之间怎么连接。”小白翻看 docker 目录“但我看docker-compose.yaml里定义了十几个服务api、worker、web、db、redis、nginx、sandbox、plugin_daemon……这也太多了吧我只是跑一个聊天应用而已。”大师“这恰恰说明 Dify 是一个完整的应用平台不是一个简单的 API 包装。每个服务都有独立职责。最关键的是这四个api是大脑处理请求web是脸面前端界面db是记忆存数据redis是神经传消息给 Worker。其余的都是’能力插件’——Worker 负责后台苦力活Nginx 负责接待和引路Sandbox 负责安全执行代码Plugin Daemon 管理第三方插件。”小胖“等等api 和 worker 这两个容器里装的好像是同一个 docker 镜像”大师点头“观察力不错。api 和 worker 用的都是langgenius/dify-api这个镜像只是启动命令不一样。api 容器启动的是 Gunicorn 进程Web 服务器worker 容器启动的是 Celery worker 进程任务消费者。这就像你买了一台电脑——可以开机当服务器用也可以当办公电脑用硬件一样用法不同。”技术映射同一 Docker 镜像 不同启动命令 关注点分离Separate process concerns避免代码重复部署。小白“那环境变量呢我看docker/.env.example里有一大堆配置哪些是必须要改的”大师五类配置要关注密钥类SECRET_KEY必须改这是 Flask Session 加密用的用openssl rand -hex 32生成一个。地址类CONSOLE_API_URL控制台 API 地址和APP_API_URL应用 API 地址单机部署保持默认的http://localhost就行。数据库类DB_USERNAME、DB_PASSWORD、DB_HOST、DB_PORT、DB_DATABASE默认值就能跑。Redis 类REDIS_HOST、REDIS_PORT默认就行。存储类STORAGE_TYPE默认opendal本地文件系统你要用 S3 的话再改。技术映射环境变量Environment Variables是实现配置与代码分离的标准方式。小胖“那我部署完了下一步干啥”大师“三步走。第一步docker ps确认所有容器都是 Up 状态。第二步浏览器打开http://localhost注册账号。第三步创建一个 Chat App随便问个问题看它回不回你。如果这三步都通了恭喜你Dify 单机版部署成功。”小白“那如果其中有一步不通呢比如 web 容器启动了但页面打不开”大师“排查口诀先看容器状态docker ps -a再看容器日志docker logs 容器名最后检查端口占用netstat -an | findstr 80。80% 的问题出在端口冲突上——你电脑上的 IIS 或者别的软件可能占用了 80 端口。”小胖“明白了那我先去部署有问题再来找你”大师“等一下还有最后一个重要的概念你要带走——Dify 的容器之间是通过 Docker 的内部网络通信的。比如 web 容器调用 api 容器时用的地址是http://api:5001不是http://localhost:5001。这是因为 Docker Compose 会自动创建一个以服务名为域名的内部 DNS。如果你哪天想在外面用 curl 直接调 API那才用localhost。”小胖“原来如此那如果将来我想在生产环境部署是不是还得配置持久化存储不然容器重启数据就丢了”大师“问得好。docker-compose.yaml里已经为 PostgreSQL、Redis、向量数据库配置了 volumes——把容器内的数据目录映射到宿主机。你去看docker-compose.yaml里的volumes配置段就能看到每个有状态服务的持久化配置。这就是接下来中级篇要深入讲的。”技术映射Docker volumes 实现容器数据的持久化是无状态容器 有状态存储架构的基础。3. 项目实战环境准备依赖版本要求检查命令Docker Desktop24.0docker --versionDocker Composev2.20docker compose version可用内存≥ 8GB任务管理器查看可用磁盘≥ 20GBGet-PSDrive CWindows 特别注意确认 WSL2 已正确安装wsl --statusDocker Desktop 设置中开启 “Use WSL 2 based engine”如果 80 端口被占用net stop http停止 IIS或在.env中修改EXPOSE_NGINX_PORT分步实现步骤1获取部署配置目标拿到官方部署文件# 克隆 Dify 仓库如果还没做gitclone https://github.com/langgenius/dify.gitcddify/docker# 复制环境变量模板Copy-Item .env.example .env# 生成安全的 SECRET_KEYpython-cimport secrets; print(secrets.token_hex(32))|Tee-Object-VariablesecretKey# 手动将上面生成的密钥替换 .env 文件中的 SECRET_KEY常见坑Windows 下不要用记事本编辑.env文件可能导致编码问题。推荐使用 VS Code。步骤2启动服务目标一条命令拉起全部服务# 在 docker/ 目录下执行dockercompose up-d# 预期输出# [] Running 12/12# ✔ Network docker_default Created# ✔ Container docker-db-1 Started# ✔ Container docker-redis-1 Started# ✔ Container docker-weaviate-1 Started# ✔ Container docker-sandbox-1 Started# ✔ Container docker-web-1 Started# ✔ Container docker-api-1 Started# ✔ Container docker-worker-1 Started# ✔ Container docker-nginx-1 Started# ...常见坑如果某容器 Restarting 循环docker logs docker-api-1 --tail 50查看具体报错如果提示端口冲突修改.env中EXPOSE_NGINX_PORT8080改为其他端口如果拉取镜像慢配置 Docker 镜像加速器阿里云/中科大镜像源步骤3验证服务状态目标确认所有服务健康# 查看容器运行状态dockerps--formattable {{.Names}}\t{{.Status}}\t{{.Ports}}# 预期看到类似输出# NAMES STATUS PORTS# docker-nginx-1 Up 2 minutes 0.0.0.0:80-80/tcp# docker-api-1 Up 2 minutes 5001/tcp# docker-web-1 Up 2 minutes 3000/tcp# docker-worker-1 Up 2 minutes# docker-db-1 Up 2 minutes 5432/tcp# docker-redis-1 Up 2 minutes 6379/tcp# docker-sandbox-1 Up 2 minutes 8194/tcp# docker-weaviate-1 Up 2 minutes 8080/tcp# 检查 API 健康状态curlhttp://localhost/health# 预期输出{pid: xxx, status: ok, version: 1.14.0}# 查看各服务关键日志dockerlogs docker-api-1--tail5# 预期看到Booting worker with pid: 7排查技巧如果 API 返回 502 Bad Gateway通常是 api 容器还没启动完毕等待数据库连接等 1-2 分钟后重试。步骤4初始化 Dify目标完成首次注册和登录浏览器打开http://localhost如果改了端口就用http://localhost:8080首次访问会自动跳转到初始化页面填写邮箱adminexample.com密码至少 8 位包含大小写字母和数字确认密码点击设置进入工作空间如果出现初始化失败docker logs docker-api-1 | Select-String Error常见原因数据库迁移未完成等待 30 秒后刷新页面重试步骤5探索容器内的目录结构目标理解每个服务的文件布局# 进入 api 容器查看目录结构dockerexec-itdocker-api-1ls-la/app/api/# 预期看到# app.py -- 应用程序入口# app_factory.py -- 应用工厂# configs/ -- 配置目录# controllers/ -- 控制器# core/ -- 核心业务# extensions/ -- 扩展初始化# models/ -- 数据模型# services/ -- 服务层# tasks/ -- 异步任务# 进入 web 容器查看前端构建产物dockerexec-itdocker-web-1ls-la/app/# 查看 nginx 配置文件dockerexec-itdocker-nginx-1cat/etc/nginx/conf.d/default.conf# 观察 proxy_pass 指令如何将请求转发到 api 和 web关键发现api 容器的应用目录是/app/api/Gunicorn 的入口是app.pyweb 容器里是 Next.js 的standalone构建产物由自带的 Node.js server 提供服务nginx 根据请求路径前缀做路由/console/api/*→ api/api/*→ api其他 → web完整服务拓扑图/console/api/* /api/*/*浏览器 :80Nginxapi :5001web :3000PostgreSQL :5432Redis :6379Worker - CeleryPlugin DaemonSandbox :8194测试验证# 测试 1确认 Nginx 路由正确curl-s-o/dev/null-w%{http_code}http://localhost/# 预期200curl-s-o/dev/null-w%{http_code}http://localhost/health# 预期200返回 JSON# 测试 2通过 API 测试模型连通性需要先在控制台添加 Provider# 替换 YOUR_API_KEY 为你自己的 OpenAI keycurl-XPOST http://localhost/console/api/workspaces/current/models/test-model\-HContent-Type: application/json\-HCookie: 从浏览器复制你的 session cookie\-d{provider: openai, model: gpt-3.5-turbo, credentials: {openai_api_key: sk-xxx}}# 测试 3确认 Celery Worker 正常工作dockerlogs docker-worker-1--tail10# 预期看到celeryxxx ready.4. 项目总结优点与缺点维度优点缺点部署速度docker compose up -d一条命令5 分钟内完成首次拉取镜像耗时长约 3-5GB需稳定网络环境一致性容器化消除了在我机器上能跑的问题占用内存较多8GB不适合低配机器配置管理.env文件统一管理所有环境变量配置项多50新手容易遗漏服务可观测docker logs/docker stats直接查看状态没有内置监控面板需要自行搭建扩展性Worker 和 API 天然支持水平扩展单机部署不支持真正的高可用适用场景场景说明本地开发调试一键搭建完整的 Dify 环境不需要手动配置各项依赖团队 Demo 演示快速拉起一个全功能实例给客户或领导展示小规模生产团队内部使用 50 人单机足以支撑CI/CD 自动化测试在 CI 管道中用 Docker Compose 拉起环境跑集成测试学习研究通过探索容器内部结构深入理解 Dify 架构不适用场景生产环境高可用部署需要 K8s 或手动 multinode 部署极小资源 IoT 设备内存需 8GB注意事项数据持久化docker compose down不会删除 volumes但docker compose down -v会生产环境慎用-v参数密码安全.env文件不要提交到 Git.gitignore已默认排除请勿手动移除端口冲突80/443 端口是默认值如果宿主机已占用修改EXPOSE_NGINX_PORT和EXPOSE_NGINX_SSL_PORT内存限制Windows/macOS 的 Docker Desktop 默认内存限制可能是 2GB需手动调整到 8GB常见踩坑经验坑docker compose up -d后 Nginx 一直重启→ 根因.env文件编码格式错误UTF-8 BOM或 SECRET_KEY 包含特殊字符导致配置文件生成失败。解决用 VS Code 重新保存.env为 UTF-8无 BOM坑登录后页面一片空白→ 根因前端环境变量NEXT_PUBLIC_API_PREFIX与NEXT_PUBLIC_PUBLIC_API_PREFIX配置不同步。单机部署一般不触发此问题但自定义域名部署时要特别注意坑知识库上传文档后索引进度一直是 0→ 根因Celery Worker 与 Redis 的连接失败查看docker logs docker-worker-1典型错误Error 111 connecting to redis:6379。解决重启 Redis 容器后等待 10 秒再试思考题进阶题Dify 的 docker-compose.yaml 中为什么 api 和 worker 容器共用同一个镜像但 web 容器使用独立镜像如果让你来设计镜像策略你会怎么优化进阶题如果有一天你需要将 Dify 部署到一台无法访问外网的服务器上你会如何准备提示考虑离线镜像导出、私有 Registry、依赖预下载