③ Docker 与 Compose:从零到可维护的服务编排(以 FreshRSS + RSSHub 为例)

目标:把 RSS 聚合(FreshRSS)与订阅源生产(RSSHub)用 Docker Compose v2 正确编排,配好 Nginx 反向代理 + HTTPS,解决“容器内 cron 不跑”“GitHub 下载失败”“Compose version: 警告”“内容提取失败”等一线问题。

说明:文中所有域名/IP 使用占位符,例如 rss.example.comSERVER_IP;请根据你的环境替换。默认以 admin 账号、家目录 /home/admin 操作(不在 /root)。


【实操教学】

一、准备与目录规范

# 1) 在 admin 家目录下为项目划一个独立空间
mkdir -p /home/admin/rss_stack/{freshrss_data,rsshub_redis}
# mkdir:创建目录
# -p:父目录不存在就一起创建;已存在也不报错

cd /home/admin/rss_stack
# cd:切换目录(change directory)

# 2) 确认 Docker 与 Compose v2 可用
docker -v                     # 看 Docker 版本
docker compose version        # Compose 子命令(注意是 `docker compose`,不是旧的 `docker-compose`)

说明

  • 我们把应用(Compose 文件)、持久化数据(freshrss_data/rsshub_redis/都放在 admin 家目录,便于备份与权限统一。

  • 你当前系统里 admin 已在 docker 组,所以下面命令默认不加 sudo


二、编写 docker-compose.yml(v2 写法,无

version:

nano docker-compose.yml
# nano:终端编辑器;粘贴下方配置,注意缩进用空格,不要 Tab

粘贴以下内容(可直接用,只需把域名改成你的):

services:
  freshrss:
    image: freshrss/freshrss:latest
    container_name: freshrss
    restart: unless-stopped                     # 异常退出自动重启
    environment:
      - TZ=Asia/Shanghai                        # 时区
      - CRON_MIN=*/20                           # 容器内 cron:每 20 分钟尝试刷新(若你用宿主机 cron,可忽略)
      # 可选:首管账号(首登向导也可以设置)
      # - FRESHRSS_INSTALL=1
      # - FRESHRSS_USER=admin
      # - FRESHRSS_PASSWORD=change-me
    volumes:
      - /home/admin/rss_stack/freshrss_data:/var/www/FreshRSS/data   # 数据持久化
    ports:
      - "127.0.0.1:8082:80"                     # 仅本机回环开放,外网走 Nginx 反代

  rsshub:
    image: diygod/rsshub:latest
    container_name: rsshub
    restart: unless-stopped
    environment:
      - NODE_ENV=production
      - TZ=Asia/Shanghai
      # 如需代理/防封,后期可加 HTTP(S)_PROXY、CACHE_TYPE、PUPPETEER_* 等变量
    depends_on:
      - redis
    ports:
      - "127.0.0.1:1200:1200"                   # RSSHub 默认 1200,仅本机回环开放
    # 如需挂载自定义路由/脚本,可加 volumes: 映射

  redis:
    image: redis:alpine
    container_name: rsshub_redis
    restart: unless-stopped
    command: ["redis-server","--appendonly","yes"]
    volumes:
      - /home/admin/rss_stack/rsshub_redis:/data

保存(Ctrl+O 回车)→ 退出(Ctrl+X)。

关键点

  • 没有 version: 顶级字段(Compose v2 的推荐写法)。老教程带 version: “3” 会触发“已弃用”的黄色警告。

  • 端口只绑定回环127.0.0.1:),后续统一用 Nginx/443 对外。

  • FreshRSS 的数据都在宿主机 freshrss_data/,升级/重建容器不丢数据。


三、启动与自检

docker compose up -d
# up:按 yml 启动/更新
# -d:后台运行

docker compose ps
# ps:查看状态与端口映射(应看到 127.0.0.1:8082->80、127.0.0.1:1200->1200)

# 只在服务器本机测:后端是否响应
curl -I http://127.0.0.1:8082     # -I:只取响应头;200/302 说明 FreshRSS 正常
curl -I http://127.0.0.1:1200     # RSSHub 返回 200/404 都不必紧张,表明端口通

四、Nginx/宝塔反向代理 + HTTPS(公网只开 443)

在面板里为 rss.example.com 新建站点(类型:静态),反向代理到 http://127.0.0.1:8082/,申请并强制开启 HTTPS

务必确保反代段包含这些头(防止样式/JS 混合内容与 URL 误判):

proxy_set_header Host              $host;
proxy_set_header X-Real-IP         $remote_addr;
proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host  $host;
proxy_set_header X-Forwarded-Port  $server_port;

说明

  • 我们没有在云防火墙放行 8082/1200,对外访问走 443 → Nginx → 127.0.0.1:端口,这就是“端口关了还能访问”的正确实践。

  • RSSHub 通常不需要对外暴露界面,用 FreshRSS 去拉它的路由即可。


五、FreshRSS 首次进入与“自动刷新”方案

  1. 浏览器打开 https://rss.example.com,完成向导(创建管理员、设站点参数)。

  2. 自动刷新订阅的两种做法(二选一):

A. 用 FreshRSS 容器内置的 cron(简单,但有时会失效)

我们在 compose 里设了 CRON_MIN=*/20,容器自己每 20 分钟跑一次。如果你发现并不触发,改用方案 B。

B. 用宿主机 crontab 从外部“驱动”容器(强烈推荐,稳定)

crontab -e
# 第一次会让你选编辑器,选 nano 即可

# 在打开的 crontab 里追加一行(每 20 分钟刷新一次,写日志)
*/20 * * * *  docker compose -f /home/admin/rss_stack/docker-compose.yml exec -T freshrss \
  php ./app/actualize_script.php -v >> /home/admin/rss_stack/actualize.log 2>&1
# 解释:
# */20:每 20 分钟触发
# -f:指定 compose 文件路径(即使不在项目目录也能执行)
# exec -T:在容器内执行命令(-T 禁用伪终端)
# php ./app/actualize_script.php -v:FreshRSS 官方刷新脚本(-v:verbose 输出)
# >> ... 2>&1:把标准输出和错误都追加到日志文件

你之前遇到“容器内 cron 不跑”,正是很多人踩过的坑。宿主机 cron 驱动是最稳的折中方案。


六、常见源“正文缺失”的处理(CSS 选择器)

个别站点(如某些问答/文章页)会只给摘要或结构复杂,FreshRSS 抓不到正文。

解决:用浏览器开发者工具(F12)定位正文容器的 CSS 选择器,在该订阅的“抓取规则/自定义提取”里填写。例如:

.QuestionRichText

经验要点

  • 尽量选 语义化、稳定的类名(如 .ArticleContent / .QuestionRichText),避免 .css-xxxxx 这类带 hash 的动态类。

  • 这一步非常“手艺活”,但一旦标定好,效果立竿见影。


七、RSSHub 返回 503/403 的情况

  • 很多站点会有 反爬限制,RSSHub 直连会被拦(返回 503/403/429)。

  • 可选缓解:

    • 拉长抓取间隔(FreshRSS 的刷新周期、RSSHub 的缓存策略);

    • 在 RSSHub 设置 HTTP 代理HTTP_PROXY/HTTPS_PROXY 环境变量);

    • 部分路由支持 puppeteer(需要额外资源与镜像参数);

    • 或者接受限制,不去硬凿高对抗站点。

  • 切记合法合规,尊重网站 Robots 与使用条款。


八、日常升级与排障

# 升级镜像并平滑重建
cd /home/admin/rss_stack
docker compose pull && docker compose up -d

# 看容器实时日志(Ctrl+C 退出)
docker compose logs -f freshrss
docker compose logs -f rsshub

# 验证端口只在回环监听(没有 0.0.0.0:8082 暴露)
ss -tulpen | grep -E '8082|1200'

【踩坑与解决方案归档】

  1. docker-compose vs docker compose

    • 旧命令 docker-compose(V1)已弃用;V2 用 docker compose 子命令。

    • 症状version: ‘3’ 警告、行为不一致。

    • 做法:移除顶级 version:,全部用 V2 语法。

  2. GitHub 直连失败(curl error 56/35/28)

    • 症状:服务器下载脚本超时或 TLS 错误。

    • 做法:在本地下载后通过面板文件管理上传,或换镜像源;不要卡在这一步。

  3. 容器内 cron 不工作

    • 症状:FreshRSS 不自动刷新,但手动 exec 刷新可用。

    • 做法:改用宿主机 crontab 调用 docker compose exec 驱动刷新。

  4. 页面“只见文字,无样式”

    • 症状:反代后的网页没有 CSS/JS。

    • 做法:在 Nginx 反代里加 6 行 proxy_set_header(尤其是 X-Forwarded-Proto $schemeHost $host),清缓存重载。

  5. FreshRSS 正文抓取失败

    • 症状:摘要正常、正文空。

    • 做法:用浏览器 F12 找语义化 CSS 选择器,填到该订阅的“自定义提取”里。


【深度解析与知识扩展】

  • 为什么把端口绑定到 127.0.0.1?

    回环绑定 = 只允许本机访问;外网必须经过 Nginx/443。安全边界清晰、证书/限流集中化、无需在云防火墙开放一堆高位端口。

  • 为什么建议用 admin 家目录组织项目?

    你后续要做备份、迁移、权限控制,统一放 /home/admin/<project> 最省心;挂载路径也更直观。

  • Compose v2 的心智模型

    一个 docker-compose.yml = 一组服务的 “声明式拓扑”docker compose up -d = 收敛到期望状态。删除/新增服务块后再次 up -d,系统会按差异重建或移除。

  • “以宿主机 cron 驱动容器任务” vs “容器内 cron”

    容器内 cron 需要 init/权限/时区等配合;一旦镜像或基础层改动容易“静默失效”。宿主机 cron 可观测、易审计,是生产运维常用做法。


【交付核对清单】

  • docker compose ps 显示 3 个容器健康(freshrss、rsshub、redis)

  • ss -tulpen 显示端口只在 127.0.0.1:8082 / 127.0.0.1:1200 监听

  • https://rss.example.com 正常打开(HSTS/HTTPS 强制)

  • FreshRSS 能自动刷新(无论容器内 cron 还是宿主机 cron)

  • 针对“难抓正文”的源,已配置 CSS 选择器

  • Uptime Kuma 已对 rss.example.com 增加 HTTP(s)–关键字 监控


发表回复

Your email address will not be published. Required fields are marked *.

*
*

Copyright © 2025 十两东日的随想. All Rights Reserved.

鄂ICP备2025140583号-1 | 鄂公网安备42011102005804号