尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

FTP上传与Docker部署:快速上手服务器项目发布

FTP上传与Docker部署:快速上手服务器项目发布 很多开发者在本地能把项目跑得飞快但一到“上线”这一步就开始卡壳代码怎么传到服务器服务器上的环境怎么和本地保持一致服务挂了怎么快速恢复这三个问题看起来简单却是新手从“会写代码”走向“会部署”的第一道坎。这篇文章要讲的就是一条非常朴素、但足够可靠的部署链路使用 FTP 将本地代码上传到服务器再通过 Docker 构建镜像并运行应用。Docker 解决的是运行环境标准化问题FTP 解决的是文件传输问题两者配合即使你还没有接触过 CI/CD、Git 自动化部署也能在半小时内把一个小项目完整地跑在云服务器上。先说一个判断对于个人项目、中小型团队或还没有接入自动化的场景Docker FTP 不是最优雅的方案却是最容易理解、最不容易出错的方案。理解了这条链路之后再迁移到 Git Webhook 或流水线部署只需要替换“代码传输”这一段其余思路完全通用。读完这篇文章你应该能自己动手完成一次完整部署并知道遇到问题时先查哪里。1. 这篇文章真正要解决的问题先聊一个最常见的现象本地开发一切正常一到服务器就“跑不起来”。你可能会遇到这些情况本地 Python 版本是 3.10服务器上是 3.6语法不兼容启动直接报错。本地装了某个 Redis 版本服务器上装的是另一个版本数据格式出问题。代码依赖很多服务器上缺了这个包、少了那个库逐个安装很痛苦。换一台服务器所有环境又要从头配一遍。这些问题本质上是“环境不一致”造成的。Docker 的解决思路是把应用和它依赖的运行环境一起打包成镜像镜像跑到哪里环境就是什么样。这样本地能跑服务器上也一定能跑前提是代码和依赖完整传到了服务器上。而“代码怎么传到服务器”就是 FTP 要解决的事。FTP 的优点是简单直观安装一个客户端输入服务器 IP、用户名、密码就能把文件拖上去。它不需要你会 Git不需要理解分支和远程仓库也不需要先在服务器上初始化一个仓库目录。你只需要把本地项目文件夹完整复制到服务器某个目录下就算是完成了“代码上传”。所以这篇文章针对的读者非常清晰刚买了一台云服务器还不知道怎么把项目跑起来的开发者。公司或团队还没有搭建 CI/CD部署方式仍然是“手动打包上传”的开发者。需要快速给客户或同事演示 demo希望用最小成本达到“服务器上能访问”的开发者。要明确一个边界FTP 默认是明文传输生产环境更推荐使用 SFTP、SCP 或自动化流水线。这一点会在后面的最佳实践部分详细讲但这不妨碍我们用 FTP 作为理解部署链路的起点。2. 基础概念与核心原理2.1 FTP 是什么FTPFile Transfer Protocol文件传输协议是应用层协议用于在客户端和服务器之间传输文件。它诞生得很早但至今仍在大量场景中使用。从技术原理看FTP 使用两个连接控制连接默认使用 21 端口负责传递命令比如登录、切换目录、删除文件。数据连接负责实际传输文件内容。数据连接的建立方式又分为主动模式Active Mode和被动模式Passive Mode。主动模式下客户端向服务器的 21 端口发起控制连接服务器主动向客户端开放的数据端口发起数据连接。被动模式下服务器开放一个随机端口并告知客户端客户端主动连接这个端口进行数据传输。现代网络中客户端大多位于 NAT 或防火墙之后主动模式常常连不通因此被动模式是更常用的选择。你在 FTP 客户端里看到的“被动模式”选项原因就在这里。2.2 SFTP 和 FTP 的区别容易混淆的是 SFTPSSH File Transfer Protocol和 FTPSFTP over SSL/TLS。下面用表格做一个简单对比对比项FTPFTPSSFTP是否加密否明文传输是基于 SSL/TLS是基于 SSH默认端口212122认证方式用户名密码用户名密码/证书用户名密码/密钥是否适合生产环境不建议可以更推荐很多人把 SFTP 当成“加密版 FTP”但从实现上看SFTP 实际上是 SSH 协议的文件传输子协议和 FTP 协议没有直接继承关系。2.3 Docker 核心概念Docker 是一个容器化平台它让你可以把应用和依赖打包进一个标准化的“镜像”中再通过“容器”运行这个镜像。三个核心概念镜像Image一个只读的模板包含运行应用所需的代码、运行时、系统工具、依赖库和配置。你可以把镜像理解成“安装包”或“快照”。容器Container镜像的运行实例。同一个镜像可以启动多个容器容器之间相互隔离但共享宿主机的操作系统内核。Dockerfile用于构建镜像的文本文件按行描述从基础镜像开始需要安装什么、复制什么、暴露什么端口、启动什么命令。2.4 Docker 和虚拟机的区别初学者最容易混淆的是 Docker 和虚拟机VM。虚拟机通过 Hypervisor 虚拟出完整的硬件环境每个虚拟机都有自己的操作系统资源开销大启动速度以分钟计。容器则直接运行在宿主机内核之上没有独立的 OS只包含应用和依赖启动速度以秒计资源开销小很多。但这并不意味着容器一定比虚拟机好。虚拟机提供更强的隔离性适合运行不受信任的负载而容器更适合微服务、CI/CD、应用部署等场景。日常项目部署Docker 是性价比很高的选择。2.5 整体流程预览这条部署链路由三大部分组成在本地准备项目和 Dockerfile。使用 FTP 把项目上传到服务器指定目录。在服务器上使用 Docker 构建镜像、启动容器并验证服务可访问。理解这条链路的关键点在于Docker 管“运行”FTP 管“传输”。两边各有分工缺一不可。3. 环境准备与前置条件开始操作前先确认本地和服务器两边的环境。3.1 本地环境你只需要满足以下条件可以正常编写和运行项目代码的操作系统Windows、macOS、Linux 均可。一个 FTP 客户端推荐 FileZilla跨平台、免费、功能完整。也可以使用 WinSCPWindows 平台。本地项目代码以及项目所需的依赖清单文件。这里不限定具体项目类型。文章后续示例使用 Python Flask 写一个最小 Web 应用如果你用的是 Node.js、Java Spring Boot 或其他语言核心流程完全一致区别只在于 Dockerfile 中的基础镜像和启动命令。3.2 服务器环境你需要一台可以联网的服务器推荐 Linux 系统常见的选择是 Ubuntu Server 或 CentOS Stream。需要确认以下几点有 SSH 登录权限能够使用 root 用户或拥有 sudo 权限的普通用户操作。开放了 22 端口供 SSH 连接。开放了 21 端口供 FTP 连接。提前放行应用端口例如本文示例中的 8000 端口。服务器上安装了 Docker 和 Docker Compose。版本方面请以实际操作时的最新稳定版为准不要盲目复制过时的安装命令。Docker 官方文档和 Docker Hub 会给出当前推荐的安装方式。3.3 FTP 服务端的选择Linux 服务器上常用的 FTP 服务软件是 vsftpdVery Secure FTP Daemon。它的名字里有“Secure”但请注意vsftpd 默认仍然可能以明文方式传输数据真正的安全部署需要配合 TLS 或改用 SFTP。如果你只是想快速跑通流程可以先使用 vsftpd如果希望从一开始就避免明文传输也可以跳过 FTP直接使用 SFTP。SFTP 不需要额外安装 FTP 服务端只要服务器开启了 SSH就可以使用支持 SFTP 的客户端连接。这个建议先记在这里后面还会提到。4. 部署流程全览整个部署流程可以拆成六个步骤在服务器上安装 FTP 服务端并启动。在本地安装 FTP 客户端并连接服务器。通过 FTP 将本地项目代码上传到服务器指定目录。在服务器上确认代码文件和目录结构完整。在服务器上编写或确认 Dockerfile。使用 Docker 构建镜像、启动容器并验证访问。为什么要按这个顺序因为需要先解决“文件怎么到服务器”的问题才能开始“怎么在服务器上运行”的部分。把 FTP 连接先跑通后续每次改代码只需要增量上传部署成本会低很多。从工程效率的角度看如果你需要频繁改代码每次都用 FTP 全量上传整个项目目录是很浪费时间的。更聪明的做法是首次完整上传之后只上传有改动的文件或者把项目打成压缩包上传后在服务器上解压。这一点在后面会展开。5. 使用 FTP 将本地代码上传到服务器5.1 在服务器上安装并配置 vsftpd以 Ubuntu 系统为例安装 vsftpd 的基本命令如下sudo apt update sudo apt install -y vsftpd安装完成后建议先备份默认配置再修改sudo cp /etc/vsftpd.conf /etc/vsftpd.conf.bak sudo vim /etc/vsftpd.conf一个最小可用配置可以参考# /etc/vsftpd.conf 关键配置 anonymous_enableNO local_enableYES write_enableYES local_umask022 dirmessage_enableYES xferlog_enableYES connect_from_port_20YES pasv_enableYES pasv_min_port30000 pasv_max_port31000 chroot_local_userYES说明几点anonymous_enableNO禁止匿名登录只允许系统用户登录。local_enableYES允许本地用户登录。write_enableYES允许写入文件否则客户端只能读取不能上传。pasv_min_port和pasv_max_port被动模式端口范围。客户端采用被动模式连接时会使用这个范围内的端口传输数据。云服务器安全组和本机防火墙需要放行这些端口。修改完后重启服务sudo systemctl restart vsftpd sudo systemctl status vsftpd如果显示active (running)说明服务已经启动。这里真正容易踩坑的地方是很多用户配置了 vsftpd却在 FTP 客户端里一直“连接超时”或“列目录失败”。原因通常不是 vsftpd 没启动而是云服务器安全组没有放行 21 端口和被动模式端口范围。务必检查安全组的入站规则。5.2 在本地客户端中连接 FTP以 FileZilla 为例打开后在上方输入主机服务器公网 IP用户名Linux 系统用户例如ubuntu或ftpuser密码该用户的密码端口21连接后左侧是本地目录右侧是服务器目录。把项目文件夹拖到右侧目标目录即可。如果你使用的是命令行也可以手动操作# 以 ftp 命令连接服务器不推荐生产环境使用明文密码仅用于快速验证 ftp 服务器IP登录后使用命令# 切换为二进制传输模式防止文件被修改 binary # 上传单个文件 put localfile.txt /home/user/project/localfile.txt # 切换本地目录 lcd /path/to/local/project # 切换远程目录 cd /home/user/project # 批量上传当前本地目录下所有文件 mput *使用完执行bye退出。5.3 传代码时最容易踩的坑第一传输模式必须区分。FTP 有 ASCII 模式和二进制模式。ASCII 模式只适合纯文本文件并且在传输过程中会根据系统自动转换换行符。图片、压缩包、可执行文件、SQL 备份等文件必须使用二进制模式否则文件会损坏。FileZilla 默认选择“自动”模式会自动判断文件类型。但为了避免意外建议在传输大量文件时直接手动切换为“二进制”模式。第二尽量传压缩包而不是无数小文件。如果一个项目里有成千上万个小文件比如node_modules或.git目录用 FTP 逐个传会很慢中间断一次还要重来。更稳妥的做法是本地打包成project.tar.gz或project.zip上传压缩包然后在服务器上解压。# 本地打包以 Linux/macOS 为例 tar -czvf project.tar.gz --excludenode_modules --exclude.git . # 服务器上解压 mkdir -p /home/user/project tar -xzvf project.tar.gz -C /home/user/project第三注意是否上传了多余文件。node_modules、.git、__pycache__、venv这类目录不需要上传。它们要么体积巨大要么可以在服务器上通过依赖清单重新生成。建议在本地做好排除或直接在服务器上执行依赖安装命令。第四目录权限要正确。上传完成后检查服务器上项目目录的权限。某些 Web 应用需要写权限如果目录所有者不对应用可能会报“Permission denied”。通过chown和chmod调整即可。6. 使用 Dockerfile 构建镜像并运行服务代码上传到服务器后接下来就进入 Docker 的部分。6.1 编写项目代码和 Dockerfile先准备一个最小 Flask 应用。在本地创建项目目录my-web-app/ ├── app.py ├── requirements.txt └── Dockerfileapp.py# 文件路径my-web-app/app.py from flask import Flask app Flask(__name__) app.route(/) def index(): return Docker FTP 部署成功 if __name__ __main__: app.run(host0.0.0.0, port8000)requirements.txtflask3.0.3Dockerfile# 文件路径my-web-app/Dockerfile FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8000 CMD [python, app.py]对 Dockerfile 做一些解释FROM python:3.11-slim使用官方 Python 3.11 精简镜像作为基础镜像。slim版本体积更小减少镜像拉取和存储成本。WORKDIR /app设置容器内的工作目录后续命令都会在这个目录下执行。COPY requirements.txt .先复制依赖清单文件。这样做的原因是 Docker 构建有缓存机制只要requirements.txt没变后续RUN pip install就能直接使用缓存构建速度会快很多。RUN pip install --no-cache-dir -r requirements.txt安装依赖。COPY . .把项目源码复制进镜像。EXPOSE 8000声明容器内应用监听的端口方便阅读镜像信息和后续端口映射。CMD [python, app.py]容器启动时执行的命令。如果你的项目是 Node.js可以把基础镜像换成node:20-slim安装依赖用npm install启动命令调整为npm start。核心逻辑不变。6.2 构建 Docker 镜像在服务器项目目录下执行docker build -t my-web-app:1.0.0 .-t是给镜像打标签格式通常是名称:版本。命令最后的.表示构建上下文是当前目录。构建过程中Docker 会按 Dockerfile 的每一行指令生成一个层。如果某一步失败会停止构建并输出错误信息。最常见的失败原因是requirements.txt中有无法下载的依赖包或者基础镜像拉取失败。如果拉取镜像较慢可以考虑在 Docker 配置中设置镜像加速器这里不展开讲。构建成功后可以查看镜像列表docker images输出中应该能看到my-web-app和版本号1.0.0。6.3 启动容器使用docker run启动容器docker run -d --name my-web-app -p 8000:8000 my-web-app:1.0.0参数说明-d后台运行。--name my-web-app给容器命名方便后续管理。-p 8000:8000把服务器的 8000 端口映射到容器的 8000 端口。前面是宿主机端口后面是容器内端口。这一步非常关键如果没有映射端口外部请求无法访问到容器内的应用。启动后用下面的命令确认容器状态docker ps如果状态是Up说明容器正常运行。6.4 验证服务在服务器本地验证curl http://localhost:8000如果返回Docker FTP 部署成功说明服务已经正常启动。再从本地浏览器访问http://服务器IP:8000能打开页面就说明整个部署链路已经跑通。6.5 使用 docker-compose 管理服务当项目只有单个容器时docker run够用。但一旦你加入了数据库、Redis、Nginx 等服务命令会变得很长参数也很难维护。这时推荐使用 Docker Compose。在项目根目录创建docker-compose.yml# 文件路径my-web-app/docker-compose.yml services: app: build: . image: my-web-app:1.0.0 ports: - 8000:8000 restart: unless-stopped启动命令docker-compose up -drestart: unless-stopped表示容器异常退出后会自动重启除非你手动停止它。这个配置对生产环境很重要可以减少服务长时间不可用的问题。6.6 查看日志与停止容器查看容器日志docker logs -f my-web-app-f表示持续跟踪输出。如果容器启动后立即退出日志里的错误信息能帮助你定位问题。停止容器docker stop my-web-app删除容器docker rm my-web-app如果你需要更新代码常规操作流程是用 FTP 把新代码上传到服务器。重新构建镜像。删除旧容器并启动新容器。这里真正容易踩坑的地方是直接重新构建镜像后旧的容器还占用着 8000 端口。如果容器名或端口冲突docker run会失败。所以更新时最好先停掉旧容器或者使用 compose 的docker-compose up -d --build自动完成“检测到镜像更新则重新创建容器”的流程。7. 常见问题与排查方法联合使用 FTP 和 Docker 时常见问题往往集中在这几个环节。下面用表格列出典型现象、可能原因和排查思路。问题现象可能原因排查方式解决方案Docker Desktop 启动失败提示 virtualization support not detected未开启 BIOS 虚拟化或未安装/启用 WSL2检查 BIOS 设置运行systeminfo查看 Hyper-V 要求或在 PowerShell 执行wsl --status重启进入 BIOS 开启虚拟化Windows 平台安装并升级 WSL2FTP 连接超时无法连接服务器21 端口未放行或服务未启动查看安全组入站规则检查服务器systemctl status vsftpd放行 21 端口启动 FTP 服务FTP 可以登录但列目录失败或无响应被动模式端口未放行或防火墙拦截登录时切换主动/被动模式测试查看安全组放行pasv_min_port到pasv_max_port范围端口FTP 上传文件后应用启动报错传输模式错误导致文件损坏或依赖目录未上传完整解压/查看服务器文件对比本地文件大小和内容用二进制模式传输或先传压缩包再在服务器解压Docker 构建成功但容器立刻退出应用启动命令或依赖有问题docker logs 容器名查看错误日志根据日志修复 Dockerfile 或依赖浏览器无法访问服务但服务器本机 curl 正常安全组未放行应用端口或端口映射错误检查安全组入站规则确认docker ps端口映射放行 8000 端口检查-p 8000:8000映射数据库写入数据后容器删除数据丢失未挂载数据卷数据存储在容器可写层查看 Dockerfile 和运行参数是否有 volume使用-v或 compose volumes 持久化数据这里给一个通用的排查顺序建议看服务状态systemctl status vsftpd、docker ps、docker logs。看端口是否监听ss -lntp或netstat -lntp。看防火墙和安全组系统防火墙、云平台安全组。看文件权限项目目录所有者、目录可写权限。看错误日志而不是猜原因日志永远是最直接的线索。8. 最佳实践与工程建议8.1 安全方面尽早从 FTP 切换到 SFTPFTP 在传输过程中默认是明文用户名、密码、文件内容理论上都有被截获的风险。如果服务器有公网 IP强烈建议尽快切换到 SFTP。使用 SFTP 的好处是不需要额外安装服务端软件Linux 服务器开启 SSH 即可。传输过程加密认证信息不会被明文暴露。使用同一个 SSH 端口减少额外的防火墙配置。FileZilla 也支持 SFTP连接时把协议从 FTP 改为 SFTP端口改为 22 即可。如果你已经通过 FTP 理解了“本地文件传到服务器目录”这个过程切到 SFTP 几乎是无缝的。如果出于兼容原因必须使用 FTP 服务建议配置 vsftpd 的 TLS 支持避免明文传输明文密码。生产环境中不要把 FTP 端口暴露给公网可以考虑限制 IP 来源或用防火墙规则限制访问。8.2 Docker 运行安全不要在容器中使用 root 用户运行应用。Dockerfile 中可以使用USER指令创建并切换到普通用户。# 在 Dockerfile 中使用普通用户运行应用 RUN useradd -m appuser USER appuser这条建议对于公网服务尤其重要。一旦应用存在漏洞以普通用户身份运行能降低容器被攻破后对宿主机的威胁。另外镜像中的依赖和基础镜像需要定期更新。旧版本的基础镜像可能包含已知漏洞定期docker pull或重新构建可以降低风险。8.3 使用数据卷持久化数据容器是临时的容器删除后写入容器可写层的数据也会丢失。对于 MySQL、PostgreSQL、Redis、上传文件等需要持久化的数据必须使用 Docker 数据卷或挂载宿主机目录。# 示例挂载宿主机目录到容器 docker run -d \ --name my-db \ -v /data/mysql:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORDyourpassword \ mysql:8.0在docker-compose.yml中也可以使用volumes声明。8.4 日志与监控Docker 容器默认把 stdout 作为日志输出入口使用docker logs就能看到。生产环境中建议把日志接入集中式日志平台比如 ELK、Loki或者至少把日志采集到宿主机目录方便后续排查。如果服务挂了restart: unless-stopped可以自动拉起容器但它无法解决“新容器依然无法启动”的问题。更完整的做法是配合健康检查让 Docker 或编排平台感知应用真实状态。docker-compose.yml中可以配置healthcheckDockerfile 中也支持HEALTHCHECK指令。8.5 从手动部署走向自动化当部署次数变多手动上传和命令操作会消耗大量时间且容易出错。推荐的演进路径是当前阶段FTP 上传代码 Docker 手动构建启动。适合学习和简单项目。下一阶段用 Git 管理代码在服务器上拉取代码配 Webhook 自动部署。再进一步使用 GitHub Actions、GitLab CI 或其他流水线工具在 Push 或打 Tag 时自动构建镜像并发布到镜像仓库服务器拉取新镜像并滚动更新。从第一步到第二步你只需要把“FTP 上传”换成“服务器上 git pull”从第二步到第三步是把“手动执行 Docker 命令”换成“流水线自动执行”。核心链路始终是代码 - 构建 - 运行理解好这个思想工具怎么换都不慌。8.6 目录和镜像命名习惯给镜像打标签时建议带上明确的版本号而不是永远用latest。部署时记录当前运行的镜像版本出现问题就能精确回滚到上一个版本。# 构建时打版本标签 docker build -t my-web-app:1.0.0 . docker build -t my-web-app:1.0.1 . # 启动指定版本 docker run -d --name my-web-app -p 8000:8000 my-web-app:1.0.0回滚时只需要重新用旧版本镜像启动容器即可。如果所有镜像都叫latest回滚时很难知道旧版本到底对应哪一次构建。9. 总结与后续学习方向这篇文章讲了一条完整的部署链路在本地编写项目代码通过 FTP 把代码上传到服务器再用 Dockerfile 构建镜像、启动容器最后通过端口映射让外部可以访问服务。其中关键的知识点可以总结为三块第一FTP 解决的是“文件传输”问题需要注意传输模式、端口放行和目录权限对公网服务器更推荐使用 SFTP 替代 FTP。第二Docker 解决的是“运行环境”问题Dockerfile 写清楚依赖和启动命令后本地能跑的服务在服务器上同样能跑环境和版本差异被镜像隔离掉了。第三部署不是一次性的工作。更新代码、重启容器、回滚版本、持久化数据、查看日志这些才是部署真正日常的部分。先把这套基础操作练熟再逐步向自动化流水线迁移会顺畅很多。下一步你可以自己动手做三件小事用一个简单的 Flask 或 Node 项目完整走一遍“FTP 上传 Docker 构建 容器启动 浏览器访问”的流程不要跳过任何步骤。把项目迁移到 Git 管理在服务器上用git pull代替 FTP 上传感受代码更新方式的变化。尝试给项目加上 MySQL 或 Redis 容器学习数据卷的用法为正式生产环境做准备。到这一步你已经从“能在本地跑代码”进阶到了“能把服务部署到服务器上让别人访问”。接下来真正拉开差距的是在这套基础上持续做安全性加固、性能优化和流程自动化。建议收藏这篇教程等你自己动手操作时再对照着排查一遍常见问题。
返回列表