本文不是入门教程是一次生产环境 FTP 故障的完整排查实录适合有运维基础的开发者。一、问题现象能爬取 CSDN 文章但图片上传环节报错FTP 连接失败。工具类日志仅输出“FTP 连接失败”无任何细节本地测试 FTP 正常Docker 容器内上传失败多次修改密码仍报错最终定位到用户权限问题二、按排查顺序拆解所有坑与解决步骤坑 1阿里云迁移遗留的pasv_address写死公网 IP现象能连接 FTP 控制端口2121但上传文件时报错。根因原阿里云服务器配置中pasv_address59.223.113.141写死了旧公网 IP。被动模式数据通道会强制指向该 IP而 Docker 容器内网无法访问公网 IP导致数据通道超时失败。解决步骤bash# 1. 编辑 vsftpd 配置文件 sudo nano /etc/vsftpd.conf # 2. 注释掉固定公网 IP 配置 # pasv_address59.223.113.141 # 3. 重启 FTP 服务 sudo systemctl restart vsftpd坑 2Ubuntu 防火墙未开放 FTP 被动端口段现象控制端口2121连通但文件上传仍失败。根因FTP 被动模式需要开放pasv_min_port~pasv_max_port段的端口。配置中是 40000-50000但防火墙只开放了 30000-31000导致数据通道被拦截。解决步骤bash# 1. 查看防火墙状态 sudo ufw status # 2. 开放被动端口段 sudo ufw allow 40000:50000/tcp # 3. 重载防火墙规则 sudo ufw reload # 4. 验证端口是否开放 sudo ufw status | grep 40000坑 3FTP 用户使用/usr/sbin/nologin导致登录被拦截现象容器内测试 FTP 登录输入正确密码仍返回530 Login incorrect。根因vsftpd 默认安全策略禁止 shell 为/usr/sbin/nologin的用户登录 FTP即使密码正确也会被拦截且返回的 530 错误码会误导人以为是密码错误。解决步骤bash# 1. 查看用户 shell 配置 cat /etc/passwd | grep ftpuser # 输出ftpuser:x:1001:1001::/home/ftpuser:/usr/sbin/nologin # 2. 修改用户 shell 为可登录类型 sudo usermod -s /bin/bash ftpuser # 3. 重启 FTP 服务 sudo systemctl restart vsftpd # 4. 容器内重新测试登录 ftp 192.168.18.100 2121 # 输入密码后返回 230 Login successful坑 4工具类异常日志过于简陋无法定位问题现象报错仅显示“FTP 连接失败”无法区分是连接、登录还是权限问题。根因工具类未对连接、登录、被动模式配置的每一步进行日志记录也未打印 FTP 响应码和错误信息排查全靠盲猜。解决步骤java// 重构 FtpClientUtil 工具类为每一步添加详细日志 public boolean uploadFile(String path, InputStream inputStream) { try { log.info(FTP 连接尝试host{}, port{}, host, port); boolean connectSuccess ftpClient.connect(host, port); log.info(FTP 连接结果{}, connectSuccess ? 成功 : 失败); log.info(FTP 登录尝试username{}, username); boolean loginSuccess ftpClient.login(username, password); log.info(FTP 登录响应码{}, ftpClient.getReplyCode()); if (!loginSuccess) { log.error(FTP 登录失败响应码{}, ftpClient.getReplyCode()); return false; } // 设置被动模式 ftpClient.enterLocalPassiveMode(); log.info(FTP 已切换到被动模式); // ... 上传逻辑 } catch (Exception e) { log.error(FTP 上传异常响应码{}, ftpClient.getReplyCode(), e); return false; } }三、完整排查流程以后遇到同类问题直接用验证网络连通性在容器内执行ftp 192.168.18.100 2121确认控制端口是否连通检查被动模式配置确认 vsftpd 配置中pasv_min_port和pasv_max_port存在确认pasv_address未写死旧公网 IP确认防火墙已开放被动端口段验证用户登录权限检查用户 shell 配置避免使用/usr/sbin/nologin确认用户密码正确可通过passwd ftpuser重置验证优化工具类日志确保能打印每一步操作和 FTP 响应码最终验证重新触发文件上传确认图片能正常写入 FTP 并在前端展示四、可复用的 vsftpd 推荐配置内外网兼容inilistenYES listen_port2121 # 被动模式设置兼容内外网 pasv_enableYES pasv_min_port40000 pasv_max_port50000 # 注释掉固定公网IP让vsftpd自动适配客户端IP # pasv_address59.223.113.141 # 网络连接设置 pasv_promiscuousYES port_promiscuousYES listen_ipv6NO # 基本安全设置 anonymous_enableNO local_enableYES write_enableYES allow_writeable_chrootYES五、踩坑总结与避坑指南坑点正确做法迁移时pasv_address写死公网 IP注释掉该配置让 vsftpd 自动适配防火墙未开放被动端口段检查pasv_min_port和pasv_max_port确保防火墙已放行用户 shell 为/usr/sbin/nologinFTP 用户应使用/bin/bash或专门的 FTP shell工具类日志过于简陋每一步连接、登录、被动模式配置都打印响应码六、最后迁移类问题优先检查配置硬编码。FTP 问题必须拆分排查先验证控制端口再检查被动端口最后验证用户权限不要一上来就怀疑代码。日志是排查的核心工具类必须打印每一步操作和 FTP 响应码。今天的坑从阿里云迁移到hp-server从被动模式到防火墙从 nologin 到日志重构——每个坑都踩得明明白白。 系列导航【人生底稿 01】农村少年1995–2005【技术底稿】0137岁老码农用4台机器搭了套个人DevOps平台【产品底稿01】37 岁 Java 老码农用 Java 搭了个 AI 写作助手把自己 14 年技术文章全喂给了 AI