FTP上传代码到Docker部署:完整闭环流程与排错指南
2026/8/31 20:20:32 网站建设 项目流程

当业务需要把本地开发好的代码部署到服务器上,同时又要用 Docker 统一管理运行环境时,很多同学会卡在中间那个环节:代码怎么安全高效地传到服务器?用 Git 拉取当然可以,但很多时候服务器只有临时文件需要更新,或者团队还没完全走通 CI/CD 流程,FTP 仍然是最直接、最常用的传输方式。这篇文章就来完整梳理一遍从 FTP 上传代码到 Docker 部署的闭环流程,包含环境准备、服务器 FTP 配置、本地文件上传、Dockerfile 编写、镜像构建和容器运行的全部步骤,以及高频报错的排查思路。

文章适合这么几类读者:一是有 Linux 基础、想系统落地 Docker 部署的开发者;二是公司服务器已经装了 Docker,但部署流程还是手工操作的后端同学;三是刚接触云服务器、想把本地项目跑起来的学生和转行工程师。全文以实际可运行为标准,尽量少讲空洞概念,多给可以直接复制的命令和配置。

1. 背景与核心概念

1.1 为什么需要 FTP 上传代码

FTP(File Transfer Protocol,文件传输协议)是 TCP/IP 协议族中的一员,主要作用是在客户端和服务器之间传输文件。它诞生得很早,但直到今天依然是很多运维场景里的基础工具。我们平时开发项目,代码先写在本地电脑上,经过调试、测试、确认功能正常后,需要把代码文件放进服务器的指定目录里,才能让服务器上的服务加载新代码。

在 Docker 部署流程中,FTP 上传这个动作通常发生在 Docker 构建之前。也就是说,代码先传到服务器的一个工作目录,然后在服务器上执行 docker build 通过 Dockerfile 把代码打包成镜像,最后 docker run 启动容器对外提供服务。整个流程可以概括为:

本地开发 -> FTP 上传到服务器工作目录 -> docker build 构建镜像 -> docker run 运行容器 -> 访问服务

有些人会问,服务器上直接装 Git,再 git pull 拉代码不就可以了吗?当然可以。Git 适合代码仓库管理规范、团队协作频繁的场景。但 FTP 的优点是简单直接,不需要配置仓库权限、不需要处理 SSH key,尤其适合以下场景:

  • 临时更新某个配置文件或者静态资源;
  • 服务器上跑的是非 Git 管理的脚本或工具;
  • 本地开发环境与服务器网络隔离,只能开放 21 端口;
  • 需要把编译好的产物(dist、jar、war)直接传上去,而不是源码。

1.2 Docker 在整个流程里的作用

Docker 是一种操作系统级虚拟化技术,它把应用以及应用依赖的运行环境一起打包成镜像。镜像相当于一个只读模板,容器则是这个模板运行起来的实例。

为什么要用 Docker?因为传统部署方式里,代码上传到服务器后,还要手动装 JDK、Node.js、Nginx、MySQL 客户端等各种依赖,每次都容易因为版本不一致导致“在我电脑上是好的,在服务器上就挂了”的问题。Docker 把依赖固化到镜像里,任何一台安装了 Docker 的服务器都能用同一种方式运行同一套服务,可移植性很强。

在实际部署流程中,Docker 承担的是“最后一个环节的标准化”。FTP 负责把代码搬到服务器,Docker 负责把代码变成可运行的服务。两者分工明确,组合起来就是一套足够轻量又完整的部署方案。

1.3 本文涉及的整体流程规划

为了让读者对整个操作过程有全局概念,这里先列出一份流程清单,后面的章节会逐一展开。

阶段操作内容工具/技术
环境准备配置本地客户端和云服务器基础环境Windows / Mac / Linux,云服务器
Docker 安装在服务器上安装 Docker 运行环境Docker Engine
FTP 服务搭建在服务器上安装并配置 FTP 服务vsftpd(Linux)
本地文件上传连接 FTP,将本地代码目录上传到服务器FileZilla 或 ftp 命令
编写 Dockerfile定义镜像构建规则,把代码和运行环境打包Dockerfile
构建并运行容器构建镜像、启动容器、验证服务可访问docker build / docker run
维护与排错日志查看、容器状态检查、常见问题修复docker logs / docker ps 等

2. 环境准备与版本说明

2.1 本地环境准备

本教程不限定本地操作系统。无论你用的是 Windows 10/11、macOS 还是 Linux,FTP 客户端和 Docker 构建命令都可以正常工作。唯一的区别在于 Windows 上如果要用 Docker Desktop,需要开启系统虚拟化支持;macOS 上则直接安装 Docker Desktop 即可。

本地环境需要准备以下软件:

  • FTP 客户端:推荐 FileZilla,跨平台且免费开源,也可以直接用命令行 ftp 工具,Windows 自带,Linux 和 macOS 也默认包含。
  • 一个准备部署的项目目录:可以是任意语言编写的 Web 项目,本文示例以 Node.js 和 Python 两个常用镜像为例,方便读者对应自己的项目调整。
  • 代码编辑器:VS Code 或其他任意编辑器,用于编写 Dockerfile。

2.2 服务器环境准备

服务器推荐使用 Linux 发行版,比如 Ubuntu 20.04/22.04 或 CentOS 7/8,这样可以获得最广泛的 Docker 和 FTP 软件包支持。如果手上的服务器是 Windows Server,那么 FTP 和 Docker 的安装方式会有区别,本文会额外给出一个简单的参考说明。

服务器的运行配置建议:

  • 最低 1 核 2G 内存,Docker 构建过程中对内存有一定要求,太小容易触发 OOM。
  • 磁盘至少留出 10G 可用空间,因为镜像、容器日志和代码本身都会占用空间。
  • 开放安全组端口:SSH 22、FTP 21、FTP 数据端口 20,以及后续 Web 服务要使用的端口,比如 8080 或 80。

需要注意,端口开放需要在两个层面完成:云服务商控制台的安全组规则和服务器系统防火墙规则。很多场景下,FTP 连不上是因为只配了系统防火墙,但云控制台安全组没有放行,或者反过来。

2.3 版本与软件版本说明

本文涉及的软件版本,读者需要根据自己实际的服务器操作系统和本地电脑情况调整。以下列出的是当前常见环境中比较稳定的选择:

软件示例版本说明
Ubuntu20.04 / 22.04本文命令基于 apt 包管理器
CentOS7 / 8命令略有差异,会用 yum 说明
Docker Engine20.10 及以上推荐使用 Docker 官方源安装
vsftpd3.0.xLinux 下最常见的 FTP 服务端
FileZilla3.6x.xWindows/macOS 免费客户端

版本这个东西必须提醒一句:不要迷信最新版,稳定优先。Docker 的 API 一直在演化,但 docker build、docker run 这类基础命令非常稳定,不会因为版本小升级而出现不兼容。vsftpd 的配置语法也多年没有大变化。

2.4 项目结构示例

为了后面演示 Dockerfile 的写法,这里准备一个最简单的目录结构。假设我们的项目放在本地 D 盘的my-web-app目录下,内容如下:

my-web-app/ ├── src/ │ └── index.js ├── package.json └── README.md

如果是一个 Python 项目,则可能是:

my-python-app/ ├── app.py ├── requirements.txt └── README.md

Dockerfile 需要放在项目根目录下,这样 docker build 才能通过上下文把整个项目目录打包给 Docker 引擎使用。后面实战章节会详细写这两种项目的 Dockerfile。

3. 服务器 Docker 安装与基础配置

3.1 为什么先在服务器上安装 Docker

Docker 不是 CentOS 或 Ubuntu 系统自带的组件,需要单独安装。安装 Docker 之后,服务器才有能力执行 docker build、docker run 等命令。这一步是整个部署流程的地基。

很多第一次部署的同学会直接在本地用 Docker Desktop 构建好镜像,然后想办法把镜像文件传到服务器。这种方式是可行的,镜像可以导出为 tar 包再导入,但流程更繁琐,而且镜像文件往往很大,不适合跨网络传输。更常见的做法是在服务器上直接构建,因为 Dockerfile 是文本文件,代码上传完毕后一切都在服务器本地完成。

所以,FTP 上传代码到服务器之后,下一步就是在服务器上执行 Docker 相关命令。为了保证这个流程顺畅,先要把 Docker 装好。

3.2 在 Ubuntu 上安装 Docker

如果服务器使用 Ubuntu,推荐用官方安装脚本:

sudo apt update sudo apt install -y apt-transport-https ca-certificates curl software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" sudo apt update sudo apt install -y docker-ce

安装完成后,先确认 Docker 运行状态:

sudo systemctl status docker sudo docker version

如果systemctl status docker显示 active (running),说明 Docker 服务已经在运行。为了避免每一条 docker 命令都加 sudo,可以把当前用户加入 docker 用户组:

sudo usermod -aG docker $USER

执行完后需要重新登录服务器,用户组权限才会生效。

这里解释一下为什么要加用户组:Docker 守护进程默认以 root 权限运行,普通用户执行 docker 命令会提示权限不足。把用户加入 docker 组,相当于授予了该用户操作 Docker 的权限。这只适合信任的运维用户,生产环境中需要谨慎管理权限。

3.3 在 CentOS 上安装 Docker

如果服务器是 CentOS 7 或 8,安装命令如下:

sudo yum install -y yum-utils device-mapper-persistent-data lvm2 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install -y docker-ce sudo systemctl start docker sudo systemctl enable docker

CentOS 8 需要注意,如果系统自带 Podman 或容器相关软件,可能与 docker-ce 冲突。一般服务器是干净的,安装问题不大。systemctl enable docker的作用是设置开机自启,这样服务器重启后 Docker 自动运行。

3.4 配置 Docker 镜像加速器

在国内服务器上安装好 Docker 后,建议配置镜像加速器,否则从 Docker Hub 拉取镜像时速度会非常慢,甚至超时。

Docker 的镜像源配置文件位于/etc/docker/daemon.json,默认不存在,需要手动创建:

sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"] } EOF

配置完成后重启 Docker:

sudo systemctl daemon-reload sudo systemctl restart docker

镜像加速器的选择需要根据当前网络环境调整,不同时间、不同地区的可用性不一样。配置完成后,可以执行一条docker pull nginx:alpine测试拉取速度,如果能顺利拉完就说明加速生效。

4. 服务器 FTP 服务搭建与配置

4.1 FTP 服务选型:vsftpd

Linux 下最常用的 FTP 服务端软件是 vsftpd(Very Secure FTP Daemon)。它的优点是稳定、轻量、安全性较高,Ubuntu 和 CentOS 官方软件源里都包含。

安装 vsftpd 的命令:

Ubuntu:

sudo apt install -y vsftpd

CentOS:

sudo yum install -y vsftpd

安装完成后,先查看配置文件位置。vsftpd 的主配置文件是/etc/vsftpd.conf。Ubuntu 默认配置可以直接用,但需要做几处关键调整,下面会详细说明。

4.2 配置 FTP 用户与目录

出于安全考虑,不建议使用 root 账号直接登录 FTP。推荐创建一个专门的系统用户,并把它的家目录设置为项目代码存放的目录。

创建用户并指定家目录:

sudo useradd -m -d /home/deploy deploy sudo passwd deploy

useradd参数说明:

  • -m:自动创建家目录;
  • -d /home/deploy:指定家目录路径;
  • deploy:用户名,这里可以按自己的习惯取,比如 webuser、ftpuser。

创建完用户后,在用户家目录下创建一个www目录,专门用来接收本地代码:

sudo mkdir -p /home/deploy/www sudo chown -R deploy:deploy /home/deploy/www

把目录所有者改成 deploy 用户,这样 FTP 上传文件时才有写权限。

4.3 修改 vsftpd 配置

编辑/etc/vsftpd.conf

sudo vim /etc/vsftpd.conf

需要确认或修改以下几个关键配置项:

# 允许本地用户登录 local_enable=YES # 允许文件写入 write_enable=YES # 本地用户的 umask 值,保持默认即可 local_umask=022 # 启用被动模式 pasv_enable=YES pasv_min_port=30000 pasv_max_port=31000 # 限定用户只能访问自己的家目录 chroot_local_user=YES # 允许写入被 chroot 限定的目录 allow_writeable_chroot=YES

这里解释一下几个容易出问题的配置:

chroot_local_user=YES会让用户登录后只能访问自己的家目录,这个安全限制很好,但如果家目录本身不允许写入,FTP 上传时会报 550 Permission denied。所以同时设置allow_writeable_chroot=YES,允许用户在家目录里有写权限。

被动模式端口范围pasv_min_portpasv_max_port非常重要。FTP 有两种连接模式:主动模式(Active)和被动模式(Passive)。默认情况下,客户端软件通常使用被动模式,服务器会开启一个随机端口用于数据传输。如果服务器防火墙没有放行这个端口段,就会卡在列目录或上传环节。建议固定一个端口段,比如 30000 到 31000,然后在防火墙和安全组中一并放行。

修改完配置后重启 vsftpd:

sudo systemctl restart vsftpd sudo systemctl enable vsftpd

4.4 防火墙放行 FTP 端口

如果是 CentOS 且启用了 firewalld,需要执行:

sudo firewall-cmd --permanent --add-port=21/tcp sudo firewall-cmd --permanent --add-port=20/tcp sudo firewall-cmd --permanent --add-port=30000-31000/tcp sudo firewall-cmd --reload

如果是 Ubuntu 且启用了 ufw,则执行:

sudo ufw allow 21/tcp sudo ufw allow 20/tcp sudo ufw allow 30000:31000/tcp sudo ufw reload

还需要提醒的是:云服务器一般还有一个安全组控制台。不同云厂商的安全组入口不同,但逻辑一样,必须确认 TCP 21、20 和 30000-31000 端口在安全组入方向规则中放行。很多 FTP 连接问题,最后查下来都是安全组没配好。

4.5 Windows Server 上的 FTP 搭建说明

如果你的服务器是 Windows Server,可以通过 IIS 的角色功能添加 FTP 服务。操作路径一般是:服务器管理器 -> 添加角色和功能 -> 勾选 Web 服务器(IIS)-> 勾选 FTP 服务。配置流程不再单独展开,Windows 图形化界面比较直观。但需要注意,云服务器安全组同样要放行 21 端口和被动模式端口段,否则无法连接。

5. 本地通过 FTP 上传代码

5.1 使用 FileZilla 客户端上传

FileZilla 是跨平台的 FTP 客户端,界面简单,支持拖拽上传和下载。打开 FileZilla,在顶部填写:

  • 主机:服务器公网 IP,比如123.123.123.123
  • 用户名:前面创建的deploy
  • 密码:对应用户的密码
  • 端口:21

点击快速连接后,右侧显示服务器目录,左侧显示本地目录。进入本地项目的根目录,选择所有文件,拖拽到右侧服务器的/home/deploy/www目录中。

上传完成后,可以右键点击远程目录里的文件,选择“查看/编辑”确认内容是否完整。需要注意,FileZilla 默认会显示隐藏文件,如果项目里有.dockerignore.env这类以点开头的文件,务必确保它们被上传了。

5.2 使用命令行 ftp 上传

有些情况下服务器上只有命令行环境,或者你更习惯用终端操作。本地命令行也能完成 FTP 上传。

首先连接服务器:

ftp 123.123.123.123

输入用户名和密码后,进入 FTP 交互模式。常用命令有:

命令作用
ls查看远程目录
cd /home/deploy/www切换远程目录
lcd D:\my-web-app切换本地目录
put index.js上传单个文件
mput *.js上传多个匹配文件
passive切换被动模式
bye退出 FTP

一段典型的上传过程如下:

ftp> passive Passive mode on. ftp> cd /home/deploy/www ftp> lcd D:\my-web-app ftp> mput *

命令行 ftp 功能相对基础,不支持断点续传,如果文件很大且网络不稳定,可能上传到一半失败。对于大文件或整个目录的传输,建议还是用 FileZilla,它有断点续传和并发上传能力。

5.3 上传后检查文件完整性

代码上传到服务器后,先不要急着构建 Docker 镜像。建议在服务器上先确认文件结构和内容:

ls -la /home/deploy/www

再看下关键文件是否存在:

find /home/deploy/www -type f | wc -l

这个命令会统计文件数量,可以跟本地对比,避免漏传文件。如果需要确认某个文件内容是否完整,可以用cathead查看:

head -20 /home/deploy/www/package.json

确认无误后,就可以进入 Docker 部署环节。需要说明,FTP 是明文传输协议,在公网环境下文件内容有被截获的风险。如果是公司内部测试环境,这个方案完全够用;如果涉及密码、密钥等敏感信息,生产环境建议切换到 SFTP 或者 scp 命令,后面最佳实践章节会再展开。

6. Docker 部署实战:构建镜像并启动容器

6.1 编写 Dockerfile

代码已经在服务器的/home/deploy/www目录下,接下来要在这个目录里编写 Dockerfile。

先看一个 Node.js 项目的 Dockerfile 示例。假设项目用 Express 框架,入口文件是src/index.js

文件路径:/home/deploy/www/Dockerfile

# 基础镜像:Node.js 20 的 Alpine 版本,体积更小 FROM node:20-alpine # 设置工作目录 WORKDIR /app # 先复制 package.json 和 package-lock.json,充分利用 Docker 缓存 COPY package*.json ./ # 安装项目依赖 RUN npm install --registry=https://registry.npmmirror.com # 复制项目源码 COPY . . # 暴露应用端口 EXPOSE 3000 # 容器启动命令 CMD ["node", "src/index.js"]

逐行解释关键点:

FROM node:20-alpine指定基础镜像。alpine 是精简版 Linux,体积小,拉取速度快,生产环境很常用。

WORKDIR /app是在容器内部创建一个工作目录,后面的操作都基于这个目录执行。

COPY package*.json ./先把依赖清单复制进去。这一步单独提出来,是为了利用 Docker 的层缓存机制:只要 package.json 没变,后面重新构建时就不会重复执行 npm install。

RUN npm install在构建阶段安装依赖。这里加了 npm 镜像源,可以提升安装速度。

EXPOSE 3000声明容器对外提供服务的端口,它只是声明,真正发布端口还需要在 docker run 时用-p参数映射。

再来看一个 Python Flask 项目的 Dockerfile:

# 基础镜像 FROM python:3.11-slim # 设置工作目录 WORKDIR /app # 复制依赖清单并安装 COPY requirements.txt ./ RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple # 复制源码 COPY . . # 暴露端口 EXPOSE 5000 # 启动命令 CMD ["python", "app.py"]

编写 Dockerfile 的核心思路都一样:先复制依赖清单,安装依赖,再复制源码,最后声明端口和启动命令。不同的语言只是基础镜像和包管理器不同。

6.2 编写 .dockerignore

如果项目里有node_modules.git__pycache__这类不需要进入镜像的目录,建议在项目根目录创建.dockerignore文件:

node_modules .git .gitignore *.log .env __pycache__ dist

.dockerignore的作用是减小构建上下文体积,加快构建速度,同时避免把本地依赖目录或敏感文件打包进镜像。因为 docker build 会把上下文目录整个打包发送给 Docker 引擎,如果不排除node_modules,一个动辄几百 MB 的目录会被白白发送过去。

6.3 构建 Docker 镜像

一切准备就绪,在服务器上进入项目目录并执行构建命令:

cd /home/deploy/www docker build -t my-web-app:v1.0 .

命令说明:

  • -t my-web-app:v1.0给镜像打标签,my-web-app是镜像名,v1.0是版本号;
  • 末尾的.表示构建上下文是当前目录。

构建过程中,Docker 会逐层执行 Dockerfile 里的指令,终端会输出每一步的日志。第一次构建耗时较长,因为要拉取基础镜像并安装依赖。看到类似下面的输出就说明构建成功:

Successfully built 6f9a4d2f1b3a Successfully tagged my-web-app:v1.0

可以用docker images确认镜像列表:

docker images

输出中应该能看到my-web-app和标签v1.0

6.4 运行 Docker 容器

镜像构建完成后,用docker run启动容器:

docker run -d --name my-web-app -p 3000:3000 my-web-app:v1.0

参数说明:

  • -d:后台运行容器;
  • --name my-web-app:给容器命名,方便后续管理;
  • -p 3000:3000:把服务器的 3000 端口映射到容器内的 3000 端口。注意,如果服务器防火墙和安全组没有放行 3000 端口,外部仍然无法访问;
  • 最后的my-web-app:v1.0是镜像名。

运行完成后,查看容器状态:

docker ps

状态是Up就说明容器正常在运行。查看日志:

docker logs -f my-web-app

如果端口没有冲突、依赖安装正确,日志里应该会出现服务启动成功的提示,比如 Node.js 项目的Server running at http://0.0.0.0:3000

最后用浏览器访问:

http://服务器公网IP:3000

看到页面内容,整个部署流程就走通了。

6.5 停止与重新部署

后续代码更新后,只需要重新上传代码,然后执行以下几个命令:

# 停止旧容器 docker stop my-web-app # 删除旧容器 docker rm my-web-app # 删除旧镜像(可选) docker rmi my-web-app:v1.0 # 重新构建镜像 docker build -t my-web-app:v1.0 . # 重新启动容器 docker run -d --name my-web-app -p 3000:3000 my-web-app:v1.0

这一套操作就是最基础的“重新构建 + 重新部署”流程。如果项目代码更新频繁,可以结合 Docker Compose 或 CI/CD 工具进一步优化。下面简单说一下用 Docker Compose 的做法。

6.6 使用 Docker Compose 管理部署

当容器配置比较复杂(比如需要环境变量、数据卷挂载、多个服务联动),使用 docker run 会变得很长。这时候可以在项目根目录创建docker-compose.yml

version: "3.8" services: web: build: . image: my-web-app:v1.0 container_name: my-web-app ports: - "3000:3000" environment: - NODE_ENV=production restart: always

然后通过 Docker Compose 管理和启动:

docker compose up -d

查看服务状态:

docker compose ps

停止服务:

docker compose down

restart: always的意思是容器异常退出后会自动重启,这对线上服务的稳定性很重要。使用 Compose 之后,重新部署只需要:

docker compose up -d --build

--build参数会在启动前重新构建镜像,省去了手动 stop、rm、build、run 这一连串操作。

7. 常见问题与排查思路

在实际部署过程中,FTP 连接失败、Docker 构建报错、容器启动失败这几个问题出现频率最高。下面把常见场景整理成表格,并给出排查思路。

问题现象常见原因解决思路
FileZilla 连接超时服务器防火墙或安全组未放行 21 端口检查安全组和 firewall/ufw 规则
服务器返回 530 Login incorrect用户名或密码错误;用户未创建成功确认 deploy 用户存在,密码正确
上传文件时卡住或提示 500被动模式端口段未放行放行 pasv_min_port 到 pasv_max_port 端口段
上传文件提示 550 Permission denied目标目录无写权限chown 目录所有者,检查目录权限
docker build 报 network timeout 错误镜像源或依赖源网络不稳定配置 Docker 镜像加速器,npm/pip 换镜像源
docker ps 看不到容器docker run失败后容器已退出docker ps -a查看所有容器,再用docker logs查日志
容器启动后立即退出启动命令有问题或端口被占用查看日志,检查 CMD 命令和端口占用
访问服务超时安全组未放行业务端口在云控制台放行对应端口,比如 3000

下面挑几个重点问题单独展开。

7.1 Docker Desktop 启动报虚拟化错误

有同学习惯在本地用 Docker Desktop 做验证,结果启动时报:

Virtualization support was detected but is disabled

或者:

Docker Desktop failed to start because virtualization support wasn't detected

这个问题的根因是 Windows 系统没有开启虚拟化功能。需要到 BIOS 中开启 Intel VT-x 或 AMD-V 虚拟化技术,然后在 Windows 功能里启用“适用于 Linux 的 Windows 子系统”和“虚拟机平台”。开启后重启电脑,Docker Desktop 一般就能正常启动。

如果确认 BIOS 已经开启虚拟化但 Docker Desktop 仍然报错,可以在管理员 PowerShell 中执行:

bcdedit /set hypervisorlaunchtype auto

然后重启电脑。这一步是让 Windows Hypervisor 启动,Docker Desktop 依赖它运行 Linux 虚拟机。

7.2 FileZilla 能连接但列不出目录

这个问题非常常见。登录成功,但远程目录列表一直在刷新或者超时。原因几乎都是被动模式的数据端口没有放行。vsftpd 配置中已经设置了pasv_min_port=30000pasv_max_port=31000,那么服务器防火墙和安全组必须放行 30000 到 31000 的 TCP 端口。

另外,FileZilla 默认使用被动模式,如果服务器没有在配置中启用被动模式,或者被动端口与防火墙冲突,就会出现这种现象。

排查时可以这样做:先用命令检查 vsftpd 配置是否生效:

sudo grep -E "pasv|listen" /etc/vsftpd.conf

再确认防火墙状态,Ubuntu 下:

sudo ufw status

CentOS 下:

sudo firewall-cmd --list-ports

最后到云服务商控制台,确认安全组入方向是否有 30000-31000 的规则。

7.3 docker build 卡在下载依赖

如果构建过程中 npm install 或 pip install 卡住,通常不是命令的问题,而是网络问题。解决方案是给包管理器配置国内镜像源。

npm 在 Dockerfile 中指定镜像源:

RUN npm install --registry=https://registry.npmmirror.com

pip 在 Dockerfile 中指定镜像源:

RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

也可以把 Docker 的基础镜像拉取改为使用镜像加速器,这在前面的章节已经介绍过。构建成功后网络问题一般不会影响容器运行。

7.4 容器启动后立即退出

docker ps看不到容器,但docker ps -a能看到一个 Exited 状态的容器。原因是容器内的主进程执行完就退出,或者启动命令报错。

排查步骤:

docker ps -a docker logs <容器名>

日志中如果能看到报错堆栈,按报错原因针对性修改。比如 Node.js 项目找不到入口文件,检查 Dockerfile 中的CMD ["node", "src/index.js"]路径是否正确;Python 项目找不到模块,检查requirements.txt是否完整。

另外有一种情况:开发环境依赖安装成功,但容器启动时仍然报模块找不到,常见原因是 .dockerignore 把依赖目录排除后,Dockerfile 又忘了COPY依赖清单。确认 Dockerfile 中先 COPY 依赖清单再安装依赖的顺序是否正确。

8. 最佳实践与工程建议

8.1 生产环境尽量使用 SFTP/SCP 替代 FTP

FTP 协议有一个致命短板:包括用户名、密码、文件内容在内的所有数据都是明文传输。只要数据经过公网,就可能被中间人截获。如果在生产环境中传输敏感代码或配置,建议改用 SFTP(SSH File Transfer Protocol)或 scp 命令。

SFTP 与 FTP 的命令和操作逻辑相似,但底层走的是 SSH 协议,数据加密传输。FileZilla 同样支持 SFTP,只需要在主机栏填写:

sftp://服务器IP

端口改为 22。如果已经配置好 SSH 密钥,也可以在 FileZilla 里加载私钥文件,实现免密登录。

8.2 不要在 FTP 目录下存放敏感配置

很多项目根目录有.env文件,里面包含数据库连接串、密码、API Key 等敏感信息。通过 FTP 上传代码时,如果 .env 文件被意外传到了服务器,再被其他人拿到,后果很严重。

建议在本地就把敏感配置与代码分离,或者使用.dockerignore.env排除在镜像之外。生产环境的配置可以通过 Docker 的环境变量参数注入:

docker run -d --name my-web-app -p 3000:3000 \ -e DB_PASSWORD=your_password \ my-web-app:v1.0

或者使用 Docker Compose 的 environment 配置项。这样做的好处是:敏感信息不会被打包进镜像,不会因为镜像分发导致泄露。

8.3 镜像版本管理

不要只使用latest标签。latest 表示最新,但部署时无法确定当前服务器上跑的是哪一次构建的镜像,回滚时也难以定位。

建议每次构建都打上明确的版本号:

docker build -t my-web-app:v1.0 . docker build -t my-web-app:v1.1 .

如果需要回滚旧版本,只要用旧镜像重新运行容器即可:

docker run -d --name my-web-app -p 3000:3000 my-web-app:v1.0

如果镜像存放在私有仓库中,可以同时设置多个标签,比如v1.0latest都指向同一个镜像,方便统一管理。

8.4 数据卷挂载与日志持久化

对于需要持久化的数据(比如上传的图片、数据库文件、日志),不要存储在容器内部。容器的文件系统是临时的,容器删除后数据就没了。推荐使用 Docker 数据卷将宿主机目录挂载到容器内部:

docker run -d --name my-web-app \ -p 3000:3000 \ -v /home/deploy/data:/app/data \ my-web-app:v1.0

这样容器内部/app/data目录的数据会直接写到服务器/home/deploy/data目录,即使容器删除重建,数据也不会丢失。

日志同理,可以通过--log-opt参数限制日志大小:

docker run -d --name my-web-app \ --log-opt max-size=10m \ --log-opt max-file=3 \ my-web-app:v1.0

防止容器长时间运行导致日志文件膨胀,占满服务器磁盘。

8.5 使用 Docker Compose 或 CI/CD 优化部署流程

手工执行docker stopdocker rmdocker builddocker run这一系列命令,偶尔操作一两次没问题,但频繁部署时容易出错。现阶段推荐的做法:

  • 团队规模小、部署频率低:用 Docker Compose 管理容器生命周期;
  • 团队协作频繁、部署频率高:把 FTP 上传和 Docker 构建接入 CI/CD 流水线,比如 Jenkins、GitLab CI、GitHub Actions。代码推送到仓库后自动触发构建,在服务器上自动拉取最新代码并重建容器,不再需要人工上传。

无论采用哪种方式,都建议保持 Dockerfile 的幂等性:同一份代码、同一个 Dockerfile,在任何时间构建出的镜像行为一致。这是可维护性的基础。

8.6 定期更新基础镜像

基础镜像里的软件包可能存在安全漏洞,建议定期执行:

docker pull node:20-alpine docker pull python:3.11-slim

然后重新构建服务镜像。镜像更新前先在测试环境验证,再把新镜像部署到生产。没有把握的情况下,不要贸然升级基础镜像的大版本,比如从 Node 18 跳到 Node 20,因为某些依赖可能不兼容。

9. 总结与下一步方向

到这里,一条完整的部署链路已经跑通了:本地代码通过 FTP 上传到服务器,服务器上的 vsftpd 服务接收文件,随后在服务器上通过 Dockerfile 构建镜像,最后用 docker run 或 Docker Compose 启动容器对外提供服务。

整篇文章的关键技术点可以归纳为三个方面。第一是 FTP 服务的搭建和配置,重点在于用户权限、被动模式端口、防火墙和安全组的配合,能够解决绝大多数连接类问题。第二是 Dockerfile 的编写逻辑,依赖清单先行、源码后置、合理使用 .dockerignore 是控制构建时间和镜像体积的关键。第三是容器的生命周期管理,从构建、启动、日志查看到重新部署,每一条命令都有明确的职责。

下一步建议按自己的技术基础选择学习路线。如果对 Docker 还比较陌生,可以先系统学习镜像和容器的核心概念,理解分层存储机制;如果 Docker 基础已经没问题,可以尝试把 FTP 手动上传替换成 Git 仓库拉取,再配合 Docker Compose 做多容器编排;如果已经能够熟练管理单个服务的部署,可以考虑把整体流程迁移到 CI/CD 工具中,实现代码提交后自动构建、自动部署。每一步都在解决上一个环节中出现的重复劳动问题。

最后给一个部署前检查小清单:服务器 SSH 能连通、FTP 用户和目录权限配好、防火墙和安全组端口都放行、代码目录结构完整、Docker 服务正常运行、Dockerfile 里的启动命令和实际入口文件一致。这些都没有问题的话,一次成功的部署基本就是顺手的事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询