2026/4/6 3:55:20
网站建设
项目流程
网站对服务器要求,哪些网站收录排名好,百度投放广告流程,h5邀请函制作欢迎来到我的博客#xff0c;代码的世界里#xff0c;每一行都是一个故事#x1f38f;#xff1a;你只管努力#xff0c;剩下的交给时间 #x1f3e0; #xff1a;小破站 从零搭建 Dify AI 平台#xff1a;一次跌宕起伏的部署之旅前言服务器环境Dify 架构解析各容器的职…欢迎来到我的博客代码的世界里每一行都是一个故事你只管努力剩下的交给时间 小破站从零搭建 Dify AI 平台一次跌宕起伏的部署之旅前言服务器环境Dify 架构解析各容器的职责1. dify-nginx流量指挥官2. dify-api dify-worker业务大脑3. Redis三重身份4. PostgreSQL数据仓库5. Weaviate向量大脑6. plugin-daemon扩展引擎7. sandbox ssrf_proxy安全卫士目录结构设计部署过程踩坑实录坑1镜像版本不存在坑2路径硬编码问题坑3网络配置的大坑坑4端口 80 冲突坑5Nginx 配置错误坑6plugin-daemon 的连环坑问题1容器反复重启问题2端口配置混乱问题3环境变量名错误问题4认证失败401坑7数据库未初始化坑8日志文件爆满策略1限制容器日志策略2Nginx 日志轮转策略3定时清理 Docker完整配置文件docker-compose.yml核心部分.env记得改密钥启动流程详细步骤NPM 反向代理配置Details 标签页SSL 标签页Advanced 标签页请求流程全景图故障排查1. 容器不停重启2. 页面访问 5023. Plugin 相关 400 错误4. 数据库相关错误性能优化建议1. PostgreSQL 调优16GB 内存2. Redis 优化3. API Worker 调优监控与维护日志查看技巧备份策略磁盘空间监控安全加固1. 修改默认端口2. 防火墙规则3. 定期更新成果展示总结与经验1. 充分理解架构2. 环境变量是魔鬼细节3. 日志是最好的老师4. 官方文档可能过时5. 40GB 磁盘真的够呛6. 备份是必须的写在最后本文记录了在 CentOS Stream 9 服务器上使用 Docker Compose 部署 Dify 的完整过程包括踩过的所有坑和解决方案。前言最近公司想搭建一套私有化的 AI 应用平台选来选去最终选了 Dify。官方文档看起来很简单docker-compose up -d一把梭但实际部署时却遇到了一堆问题。这篇文章不会告诉你五分钟快速部署的美梦而是分享真实的部署过程和那些让人抓狂的错误。服务器环境系统CentOS Stream 9香港节点配置16 核 CPU 16GB 内存 20Mbps 带宽硬盘40GB 系统盘没错就这么小Docker29.0.1Docker Composev2.40.3Dify 架构解析在开始部署前先理解 Dify 是怎么工作的。它不是一个单体应用而是由多个容器协同工作各容器的职责1. dify-nginx流量指挥官这是 Dify 内部的反向代理负责把请求分发到正确的服务/→ dify-web前端页面/api→ dify-apiAPI 接口/console/api→ dify-api控制台 API/v1→ dify-api应用 API关键点它不直接暴露到外网而是通过 Nginx Proxy ManagerNPM访问。这样做的好处是 SSL 证书、域名都由 NPM 统一管理。2. dify-api dify-worker业务大脑这两个容器用的是同一个镜像langgenius/dify-api但通过环境变量MODE区分角色dify-apiMODEapi处理 HTTP 请求负责用户交互dify-workerMODEworker处理异步任务比如训练模型、处理文件它们通过 Redis 的消息队列通信3. Redis三重身份Redis 在 Dify 中扮演三个角色缓存Session、用户权限等热数据消息队列Celery 任务队列DB 0、1、2分布式锁防止并发操作冲突配置中使用了不同的 DB 隔离REDIS_DB:0# Session 存储CELERY_BROKER_URL:redis://:密码redis:6379/1# 任务队列CELERY_RESULT_BACKEND:redis://:密码redis:6379/2# 结果存储4. PostgreSQL数据仓库Dify 用了两个数据库dify主业务数据用户、应用、对话历史等dify_plugin插件系统专用数据库这个设计很巧妙插件系统完全隔离不会影响主业务。5. Weaviate向量大脑这是个向量数据库负责存储文档的 Embedding向量表示。当你问 AI 问题时流程是这样的6. plugin-daemon扩展引擎这是 Dify 0.4 版本引入的插件系统守护进程。它做两件事管理插件安装、卸载、版本控制运行插件提供隔离的 Python 运行环境它有两个端口5002HTTP API给 dify-api 调用5003远程调试端口gnet 协议7. sandbox ssrf_proxy安全卫士sandbox代码沙箱用户可以在工作流中运行 Python 代码但被严格隔离ssrf_proxySSRF 防护代理防止代码访问内网敏感服务目录结构设计部署前先规划好目录这能避免很多麻烦/acowbo/docker-compose/dify/ ├── docker-compose.yml# 核心编排文件├── .env# 环境变量包含密钥├── nginx/# Nginx 配置│ ├── nginx.conf# 主配置│ ├── proxy.conf# 代理设置│ └── conf.d/ │ └── dify.conf# 路由规则├── volumes/# 持久化数据│ ├── app/storage/# 用户上传文件│ ├── postgres/data/# 数据库数据│ ├── redis/data/# Redis 持久化│ ├── weaviate/data/# 向量数据│ ├── plugin-daemon/# 插件文件│ ├── sandbox/dependencies/# 沙箱依赖│ └── nginx/logs/# 访问日志├── cleanup-docker.sh# 清理脚本├── logrotate-dify# 日志轮转└── init.sh# 初始化脚本部署过程踩坑实录坑1镜像版本不存在官方文档可能会过时。我一开始配置的是image:langgenius/dify-api:1.10.0启动时报错Error response from daemon: manifest for langgenius/dify-api:1.10.0 not found去 Docker Hub 一看根本没有 1.10.0 这个 tag解决方案用latest标签或者去 Docker Hub 确认版本。image:langgenius/dify-api:latest坑2路径硬编码问题我参考的文档里路径是/opt/dify/dify-build但我实际部署在/acowbo/docker-compose/dify。结果logrotate-dify文件里写死了路径/opt/dify/dify-build/volumes/nginx/logs/*.log{# 配置...}导致日志轮转找不到文件。经验教训docker-compose.yml 里尽量用相对路径./volumes系统级配置文件要手动改成实际路径部署前用grep -r 你的路径全局检查坑3网络配置的大坑我想让 Dify 和 Nginx Proxy ManagerNPM共享网络acowbo_network一开始这样配置networks:acowbo_network:name:acowbo_networkdriver:bridgeipam:config:-subnet:172.20.0.0/16启动时警告network with name acowbo_network exists but was not created for project dify原因是 NPM 已经创建了这个网络Dify 不能重复创建。正确做法networks:acowbo_network:external:true# 使用已存在的外部网络坑4端口 80 冲突启动时报错Bind for 0.0.0.0:80 failed: port is already allocated因为 NPM 已经占用了 80 端口其实 Dify 的 Nginx 根本不需要暴露端口通过容器名访问就行。解决方案nginx:# ports: # 注释掉不暴露端口# - 80:80NPM 中配置反向代理Domain: dify.acowbo.com Forward to: http://dify-nginx:80 # 用容器名流程图坑5Nginx 配置错误我把map指令放在了proxy.conf里# proxy.conf错误位置 map $http_upgrade $connection_upgrade { default upgrade; close; }启动报错nginx: [emerg] map directive is not allowed heremap指令只能放在http块而proxy.conf是被include到server块的。正确做法# nginx.conf http { # map 指令必须在这里 map $http_upgrade $connection_upgrade { default upgrade; close; } include /etc/nginx/proxy.conf; include /etc/nginx/conf.d/*.conf; }坑6plugin-daemon 的连环坑这是最折磨人的部分花了几个小时调试。问题1容器反复重启日志显示[PANIC]Invalid configuration: plugin remote installing host is empty缺少环境变量。查官方文档和源码后发现需要配置environment:SERVER_KEY:${SECRET_KEY}# 认证密钥SERVER_PORT:5002# HTTP API 端口PLUGIN_REMOTE_INSTALLING_HOST:0.0.0.0# 调试服务地址PLUGIN_REMOTE_INSTALLING_PORT:5003# 调试端口PLUGIN_WORKING_PATH:/app/storage/cwd# 工作目录# 数据库配置DB_DATABASE:dify_plugin# 注意不是 dify问题2端口配置混乱plugin-daemon 有两个服务GIN HTTP 服务端口 5002给 dify-api 调用的 REST APIgnet 远程调试服务端口 5003供开发者远程调试插件我一开始把两个端口搞混了导致 API 连接失败。关键配置# plugin-daemonenvironment:SERVER_PORT:5002# GIN HTTP 端口PLUGIN_REMOTE_INSTALLING_PORT:5003# gnet 调试端口# dify-apienvironment:PLUGIN_DAEMON_URL:http://plugin-daemon:5002# 连接 HTTP 端口日志验证[gnet] Launching gnet with 8 event-loops, listening on: tcp://0.0.0.0:5003 [GIN] 200 | GET /health/check # HTTP 服务在 5002问题3环境变量名错误这个最坑我配置了PLUGIN_DAEMON_ENDPOINT但 Dify 代码里认的是PLUGIN_DAEMON_URL测试dockerexecdify-api python -c\from configs import dify_config; print(dify_config.PLUGIN_DAEMON_ENDPOINT)# 报错AttributeError: DifyConfig object has no attribute PLUGIN_DAEMON_ENDPOINT# Did you mean: PLUGIN_DAEMON_TIMEOUT?正确的环境变量# .env 文件PLUGIN_DAEMON_URLhttp://plugin-daemon:5002# 不是 ENDPOINTPLUGIN_DAEMON_KEY你的密钥问题4认证失败401配置都对了但请求返回 401 Unauthorized。测试发现认证方式不对# 错误Bearer Tokencurl-HAuthorization: Bearer 密钥http://plugin-daemon:5002/plugin/xxx# 401 Unauthorized# 正确X-Api-Keycurl-HX-Api-Key: 密钥http://plugin-daemon:5002/plugin/xxx# 200 OK查看源码确认了认证方式# api/core/plugin/impl/base.pyprepared_headers[X-Api-Key]dify_config.PLUGIN_DAEMON_KEY坑7数据库未初始化第一次访问页面所有/console/api/接口都返回 400sqlalchemy.exc.ProgrammingError: relation dify_setups does not exist原因是数据库表还没创建。解决方案# 运行数据库迁移dockerexecdify-api flask db upgrade# 重启服务docker compose restart api worker然后访问https://你的域名/install完成初始化创建管理员账号。坑8日志文件爆满40GB 系统盘很容易被日志撑爆必须做日志管理。策略1限制容器日志services:api:logging:driver:json-fileoptions:max-size:50m# 单文件最大 50MBmax-file:2# 保留 2 个文件compress:true# 压缩旧日志策略2Nginx 日志轮转# /etc/logrotate.d/dify/acowbo/docker-compose/dify/volumes/nginx/logs/*.log{daily# 每天轮转rotate7# 保留 7 天maxsize 100M# 超过 100M 强制轮转compress# 压缩delaycompress# 延迟一天压缩missingok# 文件不存在不报错notifempty# 空文件不轮转postrotate dockerexecdify-nginx nginx -s reopen endscript}部署sudocplogrotate-dify /etc/logrotate.d/difysudochmod644/etc/logrotate.d/difysudologrotate-d /etc/logrotate.d/dify# 测试策略3定时清理 Docker# cleanup-docker.shdocker system prune -f --volumes --filteruntil168h# 删除 7 天前的数据docker image prune -a -f --filteruntil168h# crontab03* *0/acowbo/docker-compose/dify/cleanup-docker.sh完整配置文件docker-compose.yml核心部分services:api:image:langgenius/dify-api:latestcontainer_name:dify-apirestart:alwaysenvironment:TZ:Asia/Hong_Kong# 时区MODE:apiSECRET_KEY:${SECRET_KEY}# 数据库DB_USERNAME:${DB_USERNAME:-postgres}DB_PASSWORD:${DB_PASSWORD}DB_HOST:postgresDB_PORT:5432DB_DATABASE:dify# RedisREDIS_HOST:redisREDIS_PORT:6379REDIS_PASSWORD:${REDIS_PASSWORD}# 插件配置关键PLUGIN_ENABLED:truePLUGIN_DAEMON_URL:http://plugin-daemon:5002# 注意是 URL 不是 ENDPOINTPLUGIN_DAEMON_KEY:${SECRET_KEY}volumes:-./volumes/app/storage:/app/api/storagenetworks:-acowbo_networkdepends_on:-postgres-redisplugin-daemon:image:langgenius/dify-plugin-daemon:0.4.1-localcontainer_name:dify-plugin-daemonrestart:alwaysenvironment:TZ:Asia/Hong_Kong# 服务配置SERVER_PORT:5002# HTTP APISERVER_KEY:${SECRET_KEY}# 调试配置PLUGIN_REMOTE_INSTALLING_HOST:0.0.0.0PLUGIN_REMOTE_INSTALLING_PORT:5003# gnet 调试端口PLUGIN_WORKING_PATH:/app/storage/cwd# 数据库独立库DB_USERNAME:${DB_USERNAME:-postgres}DB_PASSWORD:${DB_PASSWORD}DB_HOST:postgresDB_PORT:5432DB_DATABASE:dify_plugin# 注意不是 dify# RedisREDIS_HOST:redisREDIS_PORT:6379REDIS_PASSWORD:${REDIS_PASSWORD}volumes:-./volumes/plugin-daemon:/app/storagenetworks:-acowbo_networkdepends_on:-postgresnetworks:acowbo_network:external:true# 与 NPM 共享网络.env记得改密钥# # 密钥配置必须修改# SECRET_KEY生成随机密钥DB_PASSWORD生成随机密码REDIS_PASSWORD生成随机密码WEAVIATE_API_KEY生成随机密钥# # 域名配置# CONSOLE_WEB_URLhttps://your_domain.comCONSOLE_API_URLhttps://your_domain.comAPP_WEB_URLhttps://your_domain.com# # 插件配置关键# PLUGIN_ENABLEDtruePLUGIN_DAEMON_URLhttp://plugin-daemon:5002# 不是 ENDPOINTPLUGIN_DAEMON_KEY${SECRET_KEY}# # 数据库性能优化16GB 内存# POSTGRES_SHARED_BUFFERS2GBPOSTGRES_EFFECTIVE_CACHE_SIZE8GBREDIS_MAXMEMORY512mb生成随机密钥# SECRET_KEY64 字符openssl rand -base6448# 密码32 字符openssl rand -base6424启动流程详细步骤# 1. 创建目录并上传文件mkdir-p /acowbo/docker-compose/difycd/acowbo/docker-compose/dify# 上传 docker-compose.yml, .env, nginx/ 等文件# 2. 生成密钥并修改 .envvi.env# 修改所有密钥和域名# 3. 创建数据目录./init.sh# 或手动创建 volumes/ 下的目录# 4. 确保 Docker 网络存在如果用外部网络docker network create acowbo_network||true# 5. 启动服务docker compose up -d# 6. 查看状态docker composeps# 7. 初始化数据库第一次部署dockerexecdify-api flask db upgrade# 8. 重启确保配置生效docker compose restart api worker# 9. 访问初始化页面https://你的域名/installNPM 反向代理配置Details 标签页Domain Names: dify.acowbo.com Scheme: http Forward Hostname/IP: dify-nginx Forward Port: 80 ☑ Cache Assets ☑ Block Common Exploits ☑ Websockets Support ← 必须勾选SSL 标签页SSL Certificate: Request a new SSL Certificate ☑ Force SSL ☑ HTTP/2 Support ☑ HSTS Enabled Email: youremail.com ☑ I Agree to the Lets Encrypt Terms of ServiceAdvanced 标签页# AI 响应可能较慢增加超时 proxy_read_timeout 600s; proxy_connect_timeout 600s; proxy_send_timeout 600s; # 支持文件上传 client_max_body_size 100M; # 传递真实 IP proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme;请求流程全景图故障排查1. 容器不停重启# 查看日志docker logs 容器名 --tail100# 常见原因# - 环境变量缺失# - 数据库连接失败# - 端口被占用# - 配置文件语法错误2. 页面访问 502# 检查容器状态docker composeps# 检查网络连通性dockerexecdify-nginxcurlhttp://api:5001/health dockerexecdify-nginxcurlhttp://web:3000/# 检查 NPM 到 dify-nginxdockerexecnpm容器名curlhttp://dify-nginx/health3. Plugin 相关 400 错误这是最常见的错误检查清单# 1. 确认环境变量名正确dockerexecdify-apienv|grepPLUGIN# 应该看到# PLUGIN_DAEMON_URLhttp://plugin-daemon:5002 不是 ENDPOINT# PLUGIN_DAEMON_KEY...# 2. 测试连接dockerexecdify-apicurl-HX-Api-Key: 你的密钥\http://plugin-daemon:5002/health/check# 3. 查看 plugin-daemon 日志docker logs dify-plugin-daemon --tail50# 4. 确认数据库存在dockerexecdify-postgres psql -U postgres -l|grepplugin4. 数据库相关错误# relation xxx does not exist# → 运行迁移dockerexecdify-api flask db upgrade# password authentication failed# → 检查 .env 中的 DB_PASSWORD# could not connect to server# → 检查 postgres 容器是否启动docker composepspostgres性能优化建议1. PostgreSQL 调优16GB 内存# .envPOSTGRES_SHARED_BUFFERS2GB# 共享缓冲区POSTGRES_EFFECTIVE_CACHE_SIZE8GB# 系统缓存POSTGRES_WORK_MEM16MB# 排序/JOIN 内存POSTGRES_MAINTENANCE_WORK_MEM512MB# 维护操作内存POSTGRES_MAX_CONNECTIONS200# 最大连接数2. Redis 优化REDIS_MAXMEMORY512mb# 最大内存REDIS_MAXMEMORY_POLICYallkeys-lru# 淘汰策略3. API Worker 调优# Celery 并发CELERY_WORKER_CLASSgevent# 使用协程CELERY_MAX_WORKERS8# 根据 CPU 核心数监控与维护日志查看技巧# 实时查看所有容器日志docker compose logs -f# 只看特定服务docker compose logs -f api worker# 查看最近 100 行docker logs dify-api --tail100# 查看最近 5 分钟docker logs dify-api --since 5m# 过滤错误docker logs dify-api21|grep-i error备份策略#!/bin/bash# backup.shBACKUP_DIR/backup/dify-$(date%Y%m%d)mkdir-p$BACKUP_DIR# 备份数据库dockerexecdify-postgres pg_dump -U postgres dify$BACKUP_DIR/dify.sql dockerexecdify-postgres pg_dump -U postgres dify_plugin$BACKUP_DIR/dify_plugin.sql# 备份向量数据dockerexecdify-weaviatecurl-X POST http://localhost:8080/v1/backups/filesystem\-HContent-Type: application/json\-d{id:backup-$(date%Y%m%d)}# 备份上传文件tar-czf$BACKUP_DIR/storage.tar.gz volumes/app/storage/# 删除 7 天前的备份find/backup -namedify-*-mtime 7 -execrm-rf{}\;定时任务# crontab -e02* * * /acowbo/docker-compose/dify/backup.sh磁盘空间监控40GB 系统盘必须时刻盯着# 查看磁盘使用df-h# 查看 Docker 占用docker systemdf# 查看各目录大小du-sh volumes/*# 查看日志大小du-sh volumes/nginx/logs/du-sh /var/lib/docker/containers/*/告警脚本#!/bin/bash# disk-alert.shUSAGE$(df/|tail-1|awk{print$5}|seds/%//)if[$USAGE-gt80];thenecho磁盘使用率:$USAGE% - 告警|mail -sDify 磁盘告警adminexample.com# 清理 Dockerdocker system prune -ffi安全加固1. 修改默认端口# NPM 管理端口别用 81ports:-10811:81# 用随机高端口2. 防火墙规则# 只开放必要端口firewall-cmd --permanent --add-port80/tcp firewall-cmd --permanent --add-port443/tcp firewall-cmd --permanent --add-port10811/tcp# NPM 管理firewall-cmd --reload# 限制 NPM 管理端口访问firewall-cmd --permanent --add-rich-rule rule familyipv4 source address你的IP port port10811 protocoltcp accept3. 定期更新# 拉取最新镜像docker compose pull# 重启服务docker compose up -d# 清理旧镜像docker image prune -a -f --filteruntil168h成果展示部署完成后的界面访问地址https://your_domain.com功能验证清单✅ 用户注册/登录✅ 创建应用✅ 上传文档训练✅ 对话测试✅ 插件安装✅ Workflow 编排✅ API 调用总结与经验这次部署从下午 1 点折腾到晚上 9 点踩了无数坑。总结几点经验1. 充分理解架构不要一上来就docker-compose up先搞清楚每个容器的作用和依赖关系。Dify 的微服务架构并不复杂但容器间的交互需要弄明白。2. 环境变量是魔鬼细节变量名要精确PLUGIN_DAEMON_URLvsPLUGIN_DAEMON_ENDPOINT密钥要一致API 的 KEY 和 plugin-daemon 的 SERVER_KEY别忘了非标准端口plugin-daemon 的 5002 和 50033. 日志是最好的老师遇到问题别慌看日志docker logs 容器名 --tail100--follow90% 的问题日志里都有答案。4. 官方文档可能过时尤其是 plugin-daemon 这种新功能文档更新不及时。遇到问题要看官方 GitHub Issues查看源码Python/Golang用docker exec进容器探索5. 40GB 磁盘真的够呛建议挂载对象存储OSS存用户文件日志必须轮转定期清理 Docker 缓存6. 备份是必须的数据库没备份睡觉都不踏实。至少做到每天自动备份数据库每周全量备份备份文件异地存储写在最后Dify 是个很棒的 AI 应用平台但部署确实不算开箱即用。希望这篇文章能帮到想要自建的朋友少踩点坑。如果你也遇到了部署问题欢迎交流讨论。记住日志是你最好的朋友耐心是你最大的武器。参考资料Dify 官方文档Dify GitHubDocker Compose 文档