全网热榜!一个网页看全网热搜的开源项目部署

msedge_3LGAlIiTQr.png

前言

在信息爆炸的时代,如何高效地聚合和浏览各大平台的热点信息,成为了许多人的诉求。“今日热榜”(DailyHot)是一个优秀的开源项目,它通过前后端分离的架构,抓取并展示了全网几十个平台的热搜榜单。前端项目为 DailyHot,后端 API 项目为 DailyHotApi

自托管(Self-hosting)这类项目有诸多好处:

  • 数据自主可控: API 服务由自己搭建,不受第三方服务关闭或限制的影响。
  • 无请求频率限制: 可以根据自己的需求自由调用 API。
  • 学习与实践: 部署过程本身就是一个极佳的服务器运维和问题排查的学习实践。
  • 高度可定制: 可以根据自己的喜好修改前端代码或扩展后端 API。

本篇文章将以客观、详实的笔触,完整记录一次在 Debian 系统的 VPS 上,利用 1Panel 和 Docker 部署“今日热榜”全栈项目的真实过程。这不仅是一份部署指南,更是一份详尽的排错实录,涵盖了从环境配置、内存问题到复杂的网络配置等一系列在实际部署中极易遇到的问题及其最终解决方案。

一、部署前准备

在开始之前,请确保您已具备以下条件:

  • 一台 VPS 服务器: 推荐至少 1GB 内存(2GB 或以上更佳,否则构建过程会遇到问题)。本文以 Debian 系统为例。
  • 两个域名(推荐):
    • 一个用于前端访问,例如 hot.yourdomain.com
    • 一个用于后端 API,例如 api.yourdomain.com。这是最终成功的关键,也是最佳实践。
  • 安装 1Panel 和 Docker: 1Panel 提供了友好的图形化界面,能极大简化服务器管理。Docker 则是我们部署应用的核心工具。

二、第一阶段:部署后端 API (DailyHotApi)

后端的部署过程相对直接,我们使用 Docker Compose 进行。

1. 创建项目目录与文件

通过 1Panel 的文件管理器或 SSH,在 /opt 目录下创建一个名为 DailyHotApi 的文件夹。接着,在该文件夹内创建一个名为 docker-compose.yml 的文件。

2. 编写 docker-compose.yml

将以下内容粘贴到文件中。我们直接使用作者预构建好的 Docker 镜像,这比在小内存 VPS 上本地构建要快得多。

version: "3.8"

services:
  DailyhotApi:
    image: imsyy/dailyhot-api:latest
    container_name: dailyhot-api
    ports:
      - "6688:6688"
    volumes:
      - ./logs:/app/logs
    environment:
      - PORT=6688
    user: "114514"
    restart: always

3. 启动后端服务

通过 SSH 进入项目目录,并启动容器。

cd /opt/DailyHotApi
docker-compose up -d

4. 后端部署排错与验证

  • 常见错误:镜像拉取超时

    • 现象: 在中国大陆等地区的服务器上,执行 docker-compose pullup 时,可能会因网络问题连接 Docker Hub 超时。
    • 原因: Docker Hub 服务器在国外,网络连接不稳定。
    • 解决方案: 配置国内的 Docker 镜像加速器。以阿里云为例,登录阿里云控制台,找到“容器镜像服务” -> “镜像加速器”,获取您的专属加速地址,并配置到服务器的 /etc/docker/daemon.json 文件中,然后重启 Docker 服务。
  • 验证后端是否成功

    • 执行 docker ps,应能看到名为 dailyhot-api 的容器正在运行 (Up)。
    • 在浏览器中直接访问 http://<您的服务器公网IP>:6688。如果看到 “服务已正常运行” 等提示,说明后端 API 已成功部署。

三、第二阶段:部署前端 (DailyHot) 及漫长的排错之旅

前端项目的部署过程远比后端复杂,因为它不提供 Docker 镜像,需要我们手动构建,并在过程中遇到了多个典型问题。

1. 准备前端构建环境

我们需要 Node.js 环境来编译前端代码。推荐使用 nvm (Node Version Manager) 来管理 Node.js 版本。

# 安装 Nginx (用于后续托管静态文件)
sudo apt update && sudo apt install nginx -y

# 安装 nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

# 使 nvm 命令立即生效
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

# 安装 Node.js (LTS长期支持版) 和 pnpm
nvm install --lts
npm install -g pnpm

2. 构建前端项目及排错

  1. 获取项目代码

    cd /opt
    git clone https://github.com/imsyy/DailyHot.git
    cd DailyHot
    
  2. 配置环境变量
    这是至关重要的一步,也是后续诸多问题的根源。先从模板创建一个配置文件:

    cp .env.example .env
    

    我们将在后续的最终解决方案中,再来正确地修改这个文件。

  3. 安装依赖
    pnpm install 会下载数百个依赖包,在网络不佳或服务器性能较低的情况下,这个过程可能会持续数分钟甚至更久,请耐心等待。

  4. 构建项目与内存问题排查
    执行构建命令 pnpm build 时,在小内存 VPS 上,几乎必然会遇到以下两个问题:

    • 错误一:Killed (Exit code 137)

      • 现象: 构建进程进行到一半突然被终止。
      • 原因: 这是 Linux 系统的 OOM (Out of Memory) Killer 机制在起作用。构建过程消耗的内存超过了服务器的物理内存上限,系统为了自保,强制杀死了最耗内存的进程。
      • 解决方案: 创建 SWAP(虚拟内存),将一部分硬盘空间当作内存使用。
        fallocate -l 2G /swapfile      # 创建一个2GB的交换文件
        chmod 600 /swapfile          # 设置权限
        mkswap /swapfile             # 格式化为swap
        swapon /swapfile             # 启用swap
        
    • 错误二:JavaScript heap out of memory (Exit code 134)

      • 现象: 即使有了 SWAP,构建依然失败,但错误信息变为 “JavaScript 堆内存溢出”。
      • 原因: 系统层面虽然有足够内存(物理内存+SWAP),但 Node.js 程序本身有一个默认的内存使用上限(堆内存大小)。构建过程超出了这个内部限制。
      • 解决方案: 在运行构建命令时,临时提高 Node.js 的内存上限。
        NODE_OPTIONS="--max-old-space-size=4096" pnpm build
        
        执行成功后,会在项目根目录生成一个 dist 文件夹,里面就是构建好的静态网站文件。

3. 网站部署与 Nginx 配置排错

  1. 在 1Panel 中创建网站

    • 进入 1Panel [网站] -> [创建网站] -> [静态网站]
    • 主域名填写 hot.yourdomain.com
  2. 部署网站文件
    1Panel 会自动创建一个网站根目录,例如 /opt/1panel/www/sites/hot.yourdomain.com/index。我们需要将 dist 文件夹的内容复制过去。

    cp -r /opt/DailyHot/dist/* /opt/1panel/www/sites/hot.yourdomain.com/index/
    
  3. 配置伪静态
    前端项目是单页面应用 (SPA),需要配置伪静态规则以防止刷新页面时出现 404。在 1Panel 网站配置的 [伪静态] 选项卡中,填入以下内容:

    location / {
      try_files $uri $uri/ /index.html;
    }
    
  4. Nginx 配置排错
    在上述过程中,我们遇到了两个由 1Panel 自身逻辑引发的严重问题:

    • 错误三:Nginx 容器无限重启

      • 现象: OpenResty 容器状态变为 Restarting
      • 原因: 在保存伪静态规则时,1Panel 可能只在主配置文件中加入了对伪静态文件的引用 (include ...),却没有成功创建那个伪静态文件本身,导致 Nginx 配置测试失败,进而无限重启。
      • 解决方案:
        1. 通过 docker ps -a 找到 OpenResty 容器的真实名称(例如 1Panel-openresty-rQh0)。
        2. 通过 docker inspect <容器真名> 找到 conf.d 目录在宿主机上的真实映射路径(例如 /opt/1panel/www/conf.d)。
        3. 手动删除那个损坏的配置文件:rm /opt/1panel/www/conf.d/hot.yourdomain.com.conf
        4. 重启容器:docker start <容器真名>
    • 错误四:1Panel 创建网站时报“服务内部错误”

      • 现象: 点击创建网站按钮后,1Panel 报错。
      • 原因: Nginx 日志显示 open() ".../log/access.log" failed。这是因为 1Panel 在测试 Nginx 配置时,网站所需的 log 目录尚未被创建。
      • 解决方案: 在 1Panel 创建网站之前,手动创建好它将要用到的日志目录。
        mkdir -p /opt/1panel/www/sites/hot.yourdomain.com/log
        

四、最终解决方案:前后端通信的最佳实践

在解决了所有部署和配置问题后,网站虽然能打开,但所有热榜数据都显示“加载失败”。这是整个部署过程中最核心、也是最容易出错的一环。

根本原因:混合内容 (Mixed Content) 与跨域资源共享 (CORS)

前端网站通过 HTTPS 访问,是安全的。而后端 API 如果通过 http://<IP>:6688 访问,则是不安全的。现代浏览器会出于安全策略,阻止一个 HTTPS 页面去请求不安全的 HTTP 资源。

最终、最正确、最专业的解决方案如下:

  1. 为后端 API 创建独立的 HTTPS 反向代理域名。

    • 在 1Panel 中,再创建一个网站,类型为 [反向代理],域名使用我们预留的 api.yourdomain.com
    • 为此域名申请并配置好 SSL 证书,确保它是 HTTPS 的。
    • 在反向代理配置中,目标 URL 填写 http://127.0.0.1:6688。由于 1Panel 的 Nginx 容器运行在 host 网络模式下,127.0.0.1 指向的就是宿主机本身,这是连接本机端口最稳妥的方式。
  2. 修改前端 .env 配置文件,指向这个新的、安全的 API 域名。

    • 通过 SSH 编辑前端项目下的 .env 文件:nano /opt/DailyHot/.env
    • 将其内容修改为:
      # 全局 API 地址
      VITE_GLOBAL_API="https://api.yourdomain.com"
      
      # ICP 备案号 (国外服务器可留空)
      VITE_ICP = ""
      
      # 全局目录
      VITE_DIR = "/"
      
  3. 最后一次重新构建并部署前端。

    # 1. 进入前端项目目录
    cd /opt/DailyHot
    
    # 2. 重新构建
    NODE_OPTIONS="--max-old-space-size=4096" pnpm build
    
    # 3. 清理旧文件并复制新文件
    rm -rf /opt/1panel/www/sites/hot.yourdomain.com/index/*
    cp -r /opt/DailyHot/dist/* /opt/1panel/www/sites/hot.yourdomain.com/index/
    
  4. 验证成功
    清理浏览器缓存,或使用无痕模式访问 https://hot.yourdomain.com。此时,前端页面和它请求的 API 都是安全的 HTTPS 连接,所有数据都将完美加载。

总结与展望

部署一个全栈开源项目,远不止是简单的 docker-compose up -d。从这个实战记录中,我们可以总结出几个关键的经验教训:

  • 内存是前端构建的硬性门槛: 对于小内存 VPS,SWAP 和 NODE_OPTIONS 几乎是必不可少的步骤。
  • 理解工具的局限性: 1Panel 虽好,但在某些边缘情况下也存在逻辑缺陷,需要我们具备通过命令行直接排错和修复的能力。
  • 网络是核心: 前后端分离项目的部署核心,在于解决它们之间的通信问题。直接暴露 IP 和端口的方案在生产环境中是不可取的。
  • 最佳实践是王道: 最终,通过为 API 创建独立的 HTTPS 反向代理域名,我们不仅解决了“混合内容”这一技术难题,也实现了一个架构清晰、安全且专业的部署方案。

这次漫长而曲折的部署过程,虽然充满了挑战,但每解决一个问题,都让我们对 Linux 运维、Docker 网络、Nginx 配置以及现代 Web 应用的部署模式有了更深刻的理解。希望这份详尽的记录,能为后来者铺平道路,让更多人享受到自托管的乐趣。