因项目需要,在生产环境中部署了一套 dify,这里记录一下关键要点。本文适用于 v1.0.0 及以后的版本。
部署方式
部署原则是能省则省,所以还无需涉及高可用。前提条件只需要一台具有公网 IP 的云主机。
使用 Docker 部署,好处是管理起来更方便,不会污染云主机原来的环境。坏处当然是调整代码不方便,但这有其它方法解决。
准备部署描述文件
Dify 的组件非常多,在前文《架构解析与私有化部署》中我用的是 v0.15.3 这个版本,它只有 5 个组件。
-
• api -
• worker -
• web -
• sandbox -
• ssrf_proxy
现在 v1.0.0+ 它多了个
-
• plugin_daemon
组件多的系统,用 Docker Compose 来管理是最合适的。它需要两个文件
-
• .env
-
• compose.yaml
.env
这里面要改的有域名、数据库、Redis。
域名需要两个,我把它们命名为
-
• ai.example.com (转发到 http://api:5001) -
• aistudio.example.com (转发到 http://web:3000)
为什么要两个?因为 Dify 有一个用 Python Flask 框架开发的 API 服务,还有一个用 Next.js 框架开发的 Web 服务。两个服务都要对外开放。
数据库和 Redis 在本机直接安装的,没有跑在 Docker 里。容器里的服务需要通过 host.docker.internal
[1] 才能访问到。
以上就是配置 .env
的要点。
compose.yaml
需要对源代码里的 docker-compose.yaml
作一些裁剪,只保留 7 个服务组件(加向量数据库 weaviate)。
注意仍然使用隔离的 bridge 网络,不要使用 host 网络。
常用部署命令
docker compose up -d # 启动
docker compose down # 停止
docker compose ps # 查看容器
docker compose top # 查看容器内进程
docker compose logs # 查看日志
启动后的服务如下
IMAGE COMMAND SERVICE CREATED STATUS PORTS
langgenius/dify-api:1.1.2 "/bin/bash /entrypoi…" api 11 hours ago Up 11 hours 0.0.0.0:5001->5001/tcp, :::5001->5001/tcp
langgenius/dify-plugin-daemon:0.0.6-local "/bin/bash -c /app/e…" plugin_daemon 11 hours ago Up 11 hours 0.0.0.0:5003->5003/tcp, :::5003->5003/tcp
langgenius/dify-sandbox:0.2.10 "/main" sandbox 11 hours ago Up 11 hours (healthy)
ubuntu/squid:latest "sh -c 'cp /docker-e…" ssrf_proxy 11 hours ago Up 11 hours 3128/tcp
semitechnologies/weaviate:1.19.0 "/bin/weaviate --hos…" weaviate 11 hours ago Up 11 hours
langgenius/dify-web:1.1.2 "/bin/sh ./entrypoin…" web 11 hours ago Up 11 hours 0.0.0.0:3001->3000/tcp, :::3001->3000/tcp
langgenius/dify-api:1.1.2 "/bin/bash /entrypoi…" worker 11 hours ago Up 11 hours 5001/tcp
检查进程和日志没有问题之后,访问 https://aistudio.example.com 就能看到 Dify 首页了?。
技术内幕
简要讲下 v1.0.0+ 版本引入的最大变化:插件系统。
引入插件系统无疑让项目变得更加复杂。本来我想坚持用 v0.15.x 的,因为所有的模型和工具代码都在一个 repo 里,好改啊。
但是新版的特性实在太吸引人,尤其那个工作流里的 Agent 节点是刚需。
We've introduced a new Agent node in Workflow
为什么 Agent 节点是刚需?
因为我们想要自治智能体(Autonomous Agent),也就是按照“自然语言—>自主决策—>选择工具—>完成任务”这个路径去执行。没错这就是 Manus 的方式。不需要把决策逻辑是硬编码在流程中。
扯远了,回到主题,插件系统的复杂度在哪里?
-
1. 原来都是进程内的调用,现在全变成 RPC 了。跟插件相关的,有模型、工具等等。什么?!我调用一次模型,要做一次 RPC;我调用一次工具,也要做一次 RPC。 -
2. 安装一个插件,就要多启动一个后台进程。比如我这里安装了两个插件:

后台就多了两个进程
$ docker top plugin_daemon-1
UID PID PPID C STIME TTY TIME CMD
root 1768263 1768168 0 11:56 ? 00:00:00 /bin/bash -c /app/entrypoint.sh
root 1768560 1768263 0 11:56 ? 00:02:25 /app/main
root 1768889 1768560 0 11:56 ? 00:00:10 /app/storage/cwd/langgenius/agent-0.0.9@f16916b704a20067317dbe9030f62aa28f0832021a3ef6d4ce699504074c5e13/.venv/bin/python -m main
root 1768983 1768560 0 11:56 ? 00:00:11 /app/storage/cwd/langgenius/tongyi-0.0.13@5a4c38f32498ab259b96833608a6a90c0b29671ecf14af272fbc7cde97e813d1/.venv/bin/python -m main
这个插件系统设计[2]得有点精妙,它解决了两个问题:
-
1. 如何在保持核心代码不臃肿的情况下,方便支持更多的自定义模型和工具。 -
2. 如何保证插件的质量不会影响主进程的可用性。
采用多进程 RPC 这种解决方案,是不是参考了 vscode 经典的插件系统设计呢?
毕竟大部分 Dify 定制开发,要改的也就是工具,现在终于可以不动主进程来定制垂直行业大模型应用了。主进程可以一直跟上游保持最新版,享用新增加的特性和 BUG 修复。
最后剩下的问题,就是开发时如何调试插件了。我将在下一篇文章介绍 Dify 插件的最佳开发实践。