使用 Docker 搭建 miniflux 和 RSSHub,重建资讯订阅体系

date
Dec 7, 2019
note
slug
docker-miniflux-rsshub
type
Post
status
Published
tags
Docker
技术
RSS
summary
去年开始博主我大致确立了一个以 RSS 聚合为主,其它信源随缘查看的资讯订阅流程。其中一大需求是同步不同客户端的阅读记录,需要一个服务器端运行的订阅器。当时用的是 Tiny Tiny RSS 和它的 fever 插件,结合 tt-rss 安卓客户端和 Reeder 实现。
去年开始博主我大致确立了一个以 RSS 聚合为主,其它信源随缘查看的资讯订阅流程。其中一大需求是同步不同客户端的阅读记录,需要一个服务器端运行的订阅器。当时用的是 Tiny Tiny RSS 和它的 fever 插件,结合 tt-rss 安卓客户端和 Reeder 实现。
忙起来之后,碎片信息积攒得多了,有点超载。闲暇时间,也多消耗在读书类的 APP 和微信的公众号、看一看这类身边人分享的信息之中。加之手上服务器多了,这一套东西也无暇维护,不值得投入精力在反反复复的安装和修改配置上。最近这段时间也逐渐把手头的各个网站都迁移到 Docker,到了 tt-rss 这个站,想不清过去在这里魔改过什么,索性整个服务器重装了,这一套体系被我再次摧毁。
经过这样的建立、崩溃、重组、再崩溃的经历,RSS 这一工具在我的角度的使用场景也明确到了两个字:必读。RSS 满足了从关注的人、关注的领域开始发散的树形结构,与微信构筑的特有的以人的关系网为中心、不刻意的“口耳相传”这样的圈层结构相结合,形成我目前的资讯的主要来源。
RSS 这一块,进一步梳理之后可以得到以下需求:
  • 由于阅读不局限于一个地方,tt-rss 的强大功能反而更不适合,需尽可能地小巧精悍
  • 获得的信息不局限于日常所见,要穿过检查站,不能被 spy 和 filter,传输层加密是必要的
  • 不想在运维上花太多精力,希望安装配置有记录,方便后续的迁移和维护
对应的解决方案:
  1. miniflux:恰到好处的 Web 端阅读器,更新勤快,效率高,支持与各种服务对接。
  1. 域名 + HTTPS:满足加密的需求,Let's Encrypt 有免费的 HTTPS 证书
  1. Docker + Docker Compose:一次配置,一键部署
  1. RSSHub:消息源格式标准化,以便统一订阅

操作记录

基础环境:Ubuntu 18.04 + Docker CE + Docker Compose
此处假设读者对 Docker 和 Docker Compose 均有一定了解(能用它跑起想要的服务)。

1. 配置 Web 服务器

这里的关键是如何配置一个支持 HTTPS 的接入的“网关”,以 Nginx 作为网站的总入口。Nginx 有配套的 Docker 镜像,我们只需要拉取镜像,然后加载一些自定义配置即可。
Nginx 配置的核心在于 nginx.conf,这里我结合 Docker 镜像内部的配置,最后沉淀下来的配置目录结构如下:
site-env ├── nginx-config # Nginx 自定义配置 | ├── conf.d # 各个站点的配置文件 | ├── snippets # 代码片段 | ├── ssl # HTTPS 证书相关文件 | └── nginx.conf # 覆盖 Docker 容器初始的配置入口 ├── logs # 存放日志文件 └── docker-compose.yml # Docker Compose 配置文件
首先在 docker-compose.yml 配置一个 Nginx 服务:
version: "3.6" services: nginx: image: nginx:1.17-alpine container_name: docker-nginx restart: always ports: - "80:80" - "443:443" volumes: - ./logs:/var/log/nginx - ./nginx-config/nginx.conf:/etc/nginx/nginx.conf - ./nginx-config/conf.d:/etc/nginx/conf.d - ./nginx-config/snippets:/etc/nginx/snippets - /etc/nginx/ssl:/ssl - ./nginx-config/ssl/dhparam.pem:/ssl/dhparam.pem # - /var/www/websites:/wwwroot extra_hosts: - "localhost:127.0.0.1" healthcheck: test: ["CMD-SHELL", "wget -q --spider --proxy off http://localhost/get-health || exit 1"] interval: 5s retries: 12 logging: driver: "json-file" options: max-size: "100m"
几点关键:
  1. container_name 字段,这一字段指定生成的容器的名字为 docker-nginx,在证书签发的流程会用上。
  1. volumes 的 dhparam.pem,自定义 Diffie-Hellman 密钥交换参数,增强安全性
  1. /etc/nginx/ssl 指定宿主机证书保存位置,与容器内 /ssl 路径绑定,内部配置会在此查找证书
然后还需创建宿主机保存证书的目录:
$ mkdir /etc/nginx $ mkdir /etc/nginx/ssl
生成 dhparam.pem:
$ cd nginx-conf/ssl/ $ sh generate-dhparam.sh
稍等片刻生成完毕,拉取镜像、启动 Nginx 服务器。
$ docker-compose up -d
这一步涉及到的配置已整理到 GitHub 仓库 zgq354/docker-nginx-env,更多细节参考仓库的内容。

2. 域名和证书的签发

我这里使用的域名的 DNS 绑在 Cloudflare,用一键脚本 acme.sh 的 DNS 验证方式,签发支持泛域名 HTTPS 证书(假设这里域名为 domain.com)。
$ export CF_Key="***" $ export CF_Email="xxxxx@gmail.com" $ acme.sh --issue --dns dns_cf -d domain.com -d *.domain.com
待验证通过即可。

3. 安装证书

调用 acme.sh 的安装证书指令,把安装后的 reload-cmd 设为重启 Nginx 容器:
acme.sh --installcert -d example.com \ --key-file /etc/nginx/ssl/example.com.key \ --fullchain-file /etc/nginx/ssl/fullchain.cer \ --reloadcmd "docker restart docker-nginx"
到了这一步,所有的 HTTPS 服务需要的证书已经准备好了,容器里面的 /ssl/example.com.key 和 /ssl/fullchain.cer 与其一一对应。
在继续下一步之前,确认已在 DNS 添加需要的域名解析记录。

4. 进一步配置需要的服务

结合第一步的目录结构,按照 Nginx 的姿势,给出写下 server 配置,放在 nginx-conf/conf.d/ 之下。
关于配置的内容,这里需要额外引入 ssl_certificate 和 ssl_certificate_key,抽提出的其它参数放在了 snippets/ssl-params.conf,也需一并引入,参考:
server { listen 443 ssl http2; ssl_certificate /ssl/fullchain.cer; ssl_certificate_key /ssl/example.com.key; include snippets/ssl-params.conf; server_name rss.example.com; server_tokens off; access_log /var/log/nginx/wbrss-access.log; error_log /var/log/nginx/wbrss-error.log; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_pass http://service.weiborss:3000/; } }
按以上配置,在SSL Labs 的测试中可以获得 A+ 的评级。
Nginx 外的服务则可以通过在 docker-compose.yml 里面加入新的内容来配置。然后 docker-compose up -d 重新启动就会自动拉取镜像跑起来了。这里我配置了 miniflux, RSSHub, weibo-rss 三个站点,以及它们各自依赖的服务:
docker-compose.yml 参考:
version: "3.6" services: nginx: image: nginx:1.17-alpine container_name: docker-nginx restart: always ports: - "80:80" - "443:443" volumes: - ./logs:/var/log/nginx - ./nginx-config/nginx.conf:/etc/nginx/nginx.conf - ./nginx-config/conf.d:/etc/nginx/conf.d - ./nginx-config/snippets:/etc/nginx/snippets - /etc/nginx/ssl:/ssl - ./nginx-config/ssl/dhparam.pem:/ssl/dhparam.pem # - /var/www/websites:/wwwroot extra_hosts: - "localhost:127.0.0.1" healthcheck: test: ["CMD-SHELL", "wget -q --spider --proxy off http://localhost/get-health || exit 1"] interval: 5s retries: 12 logging: driver: "json-file" options: max-size: "100m" miniflux: image: miniflux/miniflux:latest expose: - 8080 depends_on: - db environment: - DATABASE_URL=postgres://miniflux:secret@db/miniflux?sslmode=disable - RUN_MIGRATIONS=1 - CREATE_ADMIN=1 - BASE_URL=https://rss.example.com/ - ADMIN_USERNAME=admin - ADMIN_PASSWORD=adminpassword db: image: postgres:latest environment: - POSTGRES_USER=miniflux - POSTGRES_PASSWORD=secret volumes: - miniflux-db:/var/lib/postgresql/data service.rsshub: image: diygod/rsshub restart: always environment: NODE_ENV: production CACHE_TYPE: redis REDIS_URL: 'redis://db.redis:6379/' PUPPETEER_WS_ENDPOINT: 'ws://service.browserless:3000' depends_on: - db.redis - service.browserless service.browserless: image: browserless/chrome restart: always service.weiborss: image: zgq354/weibo-rss restart: always db.redis: image: redis restart: always volumes: - redis-data:/data volumes: miniflux-db: redis-data:
nginx-conf/conf.d/rss.conf
# miniflux server { listen 443 ssl http2; ssl_certificate /ssl/fullchain.cer; ssl_certificate_key /ssl/example.com.key; include snippets/ssl-params.conf; server_name rss.example.com; server_tokens off; access_log /var/log/nginx/rss-access.log; error_log /var/log/nginx/rss-error.log; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_pass http://miniflux:8080/; } } # rsshub.example.com server { listen 443 ssl http2; ssl_certificate /ssl/fullchain.cer; ssl_certificate_key /ssl/example.com.key; include snippets/ssl-params.conf; server_name rsshub.example.com; server_tokens off; access_log /var/log/nginx/rsshub-access.log; error_log /var/log/nginx/rsshub-error.log; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_pass http://service.rsshub:1200/; } } # weibo-rss server { listen 443 ssl http2; ssl_certificate /ssl/fullchain.cer; ssl_certificate_key /ssl/example.com.key; include snippets/ssl-params.conf; server_name wbrss.example.com; server_tokens off; access_log /var/log/nginx/wbrss-access.log; error_log /var/log/nginx/wbrss-error.log; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_pass http://service.weiborss:3000/; } }
最终达成的效果:

总结

经过一年来的折腾,我之前总结的关于资讯订阅的姿势,大的方向没什么问题,需要调整的大都是一些细节。RSS Feed 是资讯源在数学角度最本质的抽象,但生活是具体的,我们更多时候是需要结合生活的节奏,调整其中的各种实现细节,实现我们需要的效果。

参考

  1. miniflux 配置文档
  1. RSSHub 部署文档
  1. 使用 Docker 搭建你自己的 RSS 服务(Miniflux) - 掘金
  1. 用 miniflux 自建 RSS 服务的指南 | Chinglish Small Talk

© zgq354 2014 - 2024 | CC BY-NC-SA 4.0 | RSS