Ubuntu 22.04 企业级 AI 应用部署实战:从 Dify 升级到 Dify-Plus 的深度避坑指南

最近在帮几个团队做内部 AI 应用平台的升级,从开源的 Dify 迁移到功能更完善的企业级增强版 Dify-Plus。本以为是个简单的 Docker Compose 替换,结果在实际的 Ubuntu 22.04 生产环境里,遇到了不少预料之外的“坑”。端口冲突、容器缓存、依赖版本这些老问题,在新的组合下又有了新花样。这篇文章就是把我踩过的坑、验证过的解决方案,以及一套可复用的排查清单,完整地分享出来。如果你也正计划升级,或者需要在已有服务的服务器上部署新的 AI 应用,这篇指南应该能帮你省下不少折腾的时间。

1. 部署前的深度环境审视与规划

很多部署失败,其实在敲下第一条命令之前就埋下了种子。直接照搬官方或社区的 docker-compose up -d,在干净的测试环境可能没问题,但在已经运行着其他服务(比如旧版 Dify、Nginx、PostgreSQL 等)的生产服务器上,几乎必然会撞墙。部署 Dify-Plus 企业版,第一步不是动手安装,而是做一次彻底的环境“侦察”。

核心侦察点一:端口占用全景扫描 Dify-Plus 默认会使用多个端口,你需要一张清晰的“端口地图”。最直接的方法是使用 netstatss 命令。

# 查看所有监听中的端口
sudo netstat -tulpn | grep LISTEN
# 或者使用更现代的 ss 命令
sudo ss -tulpn

光看列表还不够,你需要重点关注 Dify-Plus 的默认端口以及你环境中可能冲突的端口。我整理了一个关键端口对照表,部署前务必逐一核对:

服务组件 默认容器端口 默认映射到宿主机的端口 常见冲突服务
Nginx (Web前端) 80 80 (HTTP) 现有 Nginx、Apache、其他 Web 服务
Nginx (SSL) 443 443 (HTTPS) 同上,或已配置 SSL 的其他服务
后端 API 服务 5001 5001 自定义后端服务、开发调试服务
管理中心前端 8080 8081 Jenkins、其他管理面板、备用 Web 端口
PostgreSQL 5432 5432 现有 PostgreSQL 数据库实例
Redis 6379 6379 现有 Redis 缓存实例

注意:上表中“默认映射到宿主机的端口”指的是 Dify-Plus 项目 .env.example 文件中的默认配置。冲突不仅发生在完全相同的端口号上。例如,如果你的旧版 Dify 将 Nginx 映射到了宿主机的 8080 端口,而 Dify-Plus 的管理中心映射到了 8081,虽然端口号不同,但如果它们都试图绑定宿主机的同一个 IP 地址(如 0.0.0.0),且旧容器未正确停止,也可能导致奇怪的错误。

核心侦察点二:现有 Docker 资产盘点 如果服务器上曾经运行过 Dify 或其他类似应用,残留的容器、镜像、卷和网络都可能成为新部署的绊脚石。

# 1. 查看所有运行中和已停止的容器,寻找旧版 Dify
docker ps -a | grep -i dify

# 2. 查看所有 Docker 卷,识别可能存储了旧数据的卷
docker volume ls

# 3. 查看 Docker 网络,特别是自定义网络
docker network ls

这一步的目的是做到心中有数。如果发现名为 dify-appdify-nginx 的容器,或者 dify_postgres_data 这样的卷,你就知道在启动新服务前需要先清理它们。

核心侦察点三:资源与权限预检

  • 磁盘空间:AI 应用涉及模型文件,磁盘消耗不小。检查 /opt 或你计划安装目录的可用空间:df -h /opt
  • 用户权限:确保当前用户有权限执行 docker 命令(通常在 docker 用户组),并且对目标安装目录有读写权限。
  • 依赖版本:虽然 Docker 隔离了大部分环境,但 Docker 和 Docker Compose 版本仍需满足最低要求。Ubuntu 22.04 默认仓库的版本可能较旧,建议通过官方渠道安装。

2. 分步部署与关键配置详解

环境侦察完毕,我们就可以开始动手了。但这里的“动手”不是一键脚本,而是带着理解去操作每一个步骤,并在关键节点做好配置。

2.1 项目获取与目录结构解析

首先,选择一个合适的部署目录。/opt 是存放可选应用软件的标准位置,比较合适。

sudo mkdir -p /opt/dify-plus
sudo chown -R $USER:$USER /opt/dify-plus
cd /opt/dify-plus

接下来克隆项目。这里有个细节:Dify-Plus 的 Docker 部署文件在 docker 子目录下。明确目录结构有助于后续理解配置路径。

git clone https://github.com/YFGaia/dify-plus.git .
# 克隆后,当前目录(/opt/dify-plus)下就有 docker-compose.dify-plus.yaml 等文件
# Docker 相关的环境配置和文件都在 ./docker/ 目录里
cd docker

进入 docker 目录,你会看到核心的 docker-compose.dify-plus.yaml.env.example 文件。.env 文件是控制部署行为的“总开关”,我们需要基于示例文件创建自己的配置。

cp .env.example .env
vim .env

打开 .env 文件,你会看到一系列配置项。对于首次部署,我们最需要关注的是 端口映射数据持久化 部分。

2.2 端口冲突的根治性解决方案

端口冲突是最高频的问题。解决方案不是简单改一个数字,而是系统性地规划和修改。

策略一:修改 .env 文件(推荐) 这是最清晰、最易于管理的方式。在 .env 文件中,找到以下关键配置行并进行修改:

# Docker Compose Service Expose Host Port Configurations
# ------------------------------
EXPOSE_NGINX_PORT=8383
EXPOSE_NGINX_SSL_PORT=8384
EXPOSE_API_PORT=5001
EXPOSE_WEB_PORT=8081

假设你的服务器 80 和 443 端口已被其他 Nginx 占用,5001 端口也被占用,你可以这样修改:

EXPOSE_NGINX_PORT=8888          # 将Web前端HTTP访问端口改为8888
EXPOSE_NGINX_SSL_PORT=8443      # 将HTTPS端口改为8443
EXPOSE_API_PORT=5002            # 将后端API端口改为5002
EXPOSE_WEB_PORT=8088            # 将管理后台端口改为8088

策略二:直接修改 docker-compose 文件(适用于复杂映射) 如果 .env 文件的变量没有覆盖你需要的所有端口映射(比如数据库端口),你可以直接编辑 docker-compose.dify-plus.yaml。找到类似 ports: 的配置段:

services:
  nginx:
    ...
    ports:
      - "${EXPOSE_NGINX_PORT}:80"
      - "${EXPOSE_NGINX_SSL_PORT}:443"
  postgres:
    ...
    ports:
      - "5432:5432" # 如果宿主机5432端口已占用,需要修改前面的宿主机端口,如 "5433:5432"

重要提示:修改端口后,访问应用的 URL 也需要相应改变。例如,将 EXPOSE_NGINX_PORT 改为 8888,那么初始化安装的访问地址就是 http://你的服务器IP:8888/install

策略三:彻底停止并移除冲突服务 如果冲突端口来自旧版 Dify 或其他你确定可以停止的服务,最干净的做法是先清理它们。

# 定位并停止旧版 Dify 容器(假设容器名包含 dify)
docker stop $(docker ps -a | grep dify | awk '{print $1}')
# 移除这些已停止的容器
docker rm $(docker ps -a | grep dify | awk '{print $1}')
# 如果旧服务是用 Docker Compose 启动的,进入其项目目录执行
# docker-compose down

2.3 启动服务与初始化流程

配置好端口,就可以启动了。建议第一次启动时先不加 -d 参数,在前台观察日志,便于第一时间发现问题。

# 在 /opt/dify-plus/docker 目录下执行
docker-compose -f docker-compose.dify-plus.yaml up

如果看到所有容器都成功启动,没有报错退出,再用 Ctrl+C 停止,然后以后台模式重新启动。

docker-compose -f docker-compose.dify-plus.yaml up -d

启动后,用 docker-compose ps 检查所有服务状态是否为 Up

接下来是初始化,这里有一个顺序问题

  1. 首先访问 Dify 应用初始化页面:打开浏览器,访问 http://<你的服务器IP>:<你设置的EXPOSE_NGINX_PORT>/install。按照页面提示,创建第一个超级管理员账号。这个账号是用于 Dify AI 工作台本身的。
  2. 然后访问管理中心初始化页面:完成上一步后,访问 http://<你的服务器IP>:<你设置的EXPOSE_WEB_PORT>/#/init。你会看到一个登录页面,请使用刚刚在第一步创建的 Dify 超级管理员账号和密码进行登录。登录后,完成管理中心的初始化配置。

易错点提醒:很多朋友会在这里卡住,试图用一个新的邮箱去注册管理中心。务必理解,Dify-Plus 的“管理中心”是一个独立的前端模块,但其身份认证依赖于底层的 Dify 核心。因此,管理中心的第一个登录账号,必须是已在 Dify 核心中存在的超级管理员。

3. 高频故障排查清单与实战修复

即使按照上述步骤操作,依然可能会遇到问题。下面是我总结的一个从表象到根源的排查清单,你可以像查字典一样对照使用。

问题现象:容器启动后立即退出 (Exited)

  • 检查1:端口冲突。使用 sudo ss -tulpn | grep :端口号 命令,检查你配置的宿主机端口是否真的被占用。
  • 检查2:.env 文件格式错误。确保 .env 文件中没有多余的空格(尤其是等号后面),并且使用 LF 换行符而非 CRLF。可以用 cat -A .env 检查。
  • 检查3:镜像拉取失败。查看容器日志:docker-compose logs <服务名>,如 docker-compose logs nginx。常见于网络问题,可以尝试更换 Docker 镜像源。

问题现象:能访问页面,但初始化失败或登录后白屏/报错

  • 检查1:容器间网络通信。确保所有容器在同一个 Docker 默认网络或自定义网络中,并且 docker-compose.yaml 中服务间的依赖(depends_on)和主机名(hostname)配置正确。可以进入一个容器内部,尝试 ping 另一个服务名。
    docker exec -it dify-plus-docker-nginx-1 sh
    ping api # 尝试ping compose文件中定义的api服务名
    
  • 检查2:数据库初始化。查看 PostgreSQL 容器的日志,看表结构是否成功创建。有时需要手动进入数据库容器,检查 dify 数据库是否存在。
    docker exec -it dify-plus-docker-postgres-1 bash
    psql -U postgres -d dify -c "\dt"
    
  • 检查3:浏览器缓存与 Cookie。这是一个非常常见但容易被忽略的点。在浏览器开发者工具 (F12) 的“应用”或“存储”选项卡中,清除当前站点的所有 Cookie 和本地存储数据,然后硬刷新 (Ctrl+F5) 页面。

问题现象:修改配置后重启,变更不生效

  • 检查1:Docker 缓存。Docker Compose 会缓存构建的镜像和旧的容器配置。最彻底的解决方法是先停止并删除容器、卷(注意备份重要数据),再重新构建。
    docker-compose -f docker-compose.dify-plus.yaml down -v
    docker-compose -f docker-compose.dify-plus.yaml up -d --build
    
    -v 参数会删除匿名卷,谨慎使用。如果只想删除容器保留数据卷,则去掉 -v
  • 检查2:环境变量是否生效。确保修改的是正确的 .env 文件,并且位于 docker-compose 命令执行的同一目录下。可以通过 docker-compose config 命令来验证最终生效的配置。

问题现象:性能缓慢或内存不足

  • 检查1:宿主机资源。使用 htopfree -h 查看 CPU 和内存使用情况。Dify-Plus 运行多个服务,尤其是处理 AI 任务时,需要一定资源。
  • 检查2:Docker 资源限制。检查是否对 Docker 容器设置了过低的资源限制。可以在 docker-compose.yaml 中为服务添加资源限制:
    services:
      api:
        ...
        deploy:
          resources:
            limits:
              cpus: '2.0'
              memory: 4G
    

4. 生产环境进阶考量与优化建议

当服务能稳定运行后,我们可以从“能用”向“好用”和“稳定”迈进。以下是一些针对生产环境的优化思路。

数据持久化与备份策略 默认的 Docker Compose 文件已经将 PostgreSQL 数据、Redis 数据和上传的文件目录映射到了宿主机的卷或路径。你需要确认这些映射是符合你预期的,并且备份方案覆盖了这些数据。

  • 数据库:定期使用 pg_dump 命令备份 dify 数据库。
  • 上传文件:位于 storage 目录下的用户上传文件,需要纳入你的文件备份体系。
  • 镜像版本固化:在 .env 文件中,可以指定具体的镜像版本号(如 dify-plus-api:latest 改为 dify-plus-api:v1.0.0),避免因自动拉取最新镜像而引入不兼容的变更。

网络与安全配置

  • 使用 HTTPS:生产环境必须启用 HTTPS。你可以修改 Nginx 配置,挂入自己的 SSL 证书和私钥,或者使用 Let‘s Encrypt 自动签发。这涉及到修改 docker-compose.yaml 中 Nginx 服务的配置和卷映射。
  • 防火墙设置:在宿主机防火墙(如 UFW)中,只开放必要的端口(如你自定义的 8888, 8443, 5002, 8088),而不是默认的全部 Docker 端口范围。
  • 反向代理:更常见的做法是,将 Dify-Plus 的 Nginx 端口(如 8888)不直接暴露给公网,而是通过宿主机上一个主 Nginx 或 Traefik 作为反向代理,统一管理域名、SSL 和访问入口。

监控与日志收集

  • 日志轮转:Docker 容器日志默认不限制大小,长期运行可能撑满磁盘。可以在 /etc/docker/daemon.json 中配置全局的日志驱动和大小限制。
  • 基础监控:使用 docker stats 命令可以实时查看容器资源消耗。对于长期监控,可以考虑集成 Prometheus 和 Grafana,许多服务(如 Redis, PostgreSQL)都暴露了 Prometheus 指标。

升级与版本管理 Dify-Plus 项目处于活跃开发中。升级前,务必:

  1. 仔细阅读 Release Notes 和更新说明。
  2. 完整备份数据库和存储文件
  3. 在测试环境先行验证。
  4. 遵循项目推荐的升级步骤,通常包括拉取新代码、更新镜像、执行数据库迁移命令等。

部署这件事,细节决定成败。尤其是在一个已有负载的服务器上引入新的复杂服务,更像是一次精密的“外科手术”,需要清晰的预案和对环境的绝对掌控。希望这份融合了实战踩坑经验的指南,能让你在部署 Dify-Plus 的道路上更加顺畅。

更多推荐