由于不堪忍受 ucloud 香港机房 2Mbps 的小水管,蹲了几天搬瓦工的粉丝惊喜,蹲到了美西 DC6 机房,2c2g 的特价机。
$99.90 一年的高品质线路,加上这么多年(第一次用好像是 2015 年)都没垮,应该是靠谱的服务商。果断剁手,准备搬家。
由于主机性能还行,先布了个 n8n,可以跑一些自己喜欢的定时任务以及 webhook 节点——主要是因为黑群晖跑 n8n 太卡了,本地网络编辑能用出九十年代上网的顿挫感。最新的 n8n 社区版镜像包含两个内容,一个是 n8n 本体,一个是叫 Traefik
的组件,作为类似 Nginx 的反代。整个 n8n 直接跑官方的 docker-compose 就可以顺利使用。
而对于部署 FarBox(FB) 的困难我是有心理准备的,两年前在 UCloud 做第一次部署就零零碎碎花了一年多时间才顺利运转。为了给下次迁移 FB(如果还有的话)打下基础,赶紧从头记录下来,也给选择 FB 作为静态博客框架的人们留个路标。
主机情况
纯裸 VPS,2 vCPU, 2GB memory, 2.5G 网口,机房在 US-LA,GIA 线路
Ubuntu 22.04
域名托管和 DNS 都在 Cloudflare(CF)
第一个困难很快遇到:Ubuntu 22.04 自带 Python 3.10,pip 版本也不再支持 python 2.7。而当年 FB 的开发者 Hepo 选择了 Python 2.7 作为开发语言,众所周知 Python3 在升级时发生了重大破坏性升级,导致与 Python2 语法不兼容,组件完全无法使用。所以如果要使用官方的安装方法,就一定要造出一个 Python2 环境以便执行官方脚本中的 xserver
小工具。在历经 Pipenv 因为依赖问题安装出错,最后 pipenv 因为版本问题无法造出 Python2 环境等一系列死循环依赖后,重新思考:如果使用 NAS 部署,NAS 上的 python 版本更加多样,作者是如何解决的?
回到作者后续更新的文档 如何部署到 NAS 上 里找到用 docker run 来部署的裸脚本,大概明白了 xserver 脚本的功能。
#!/bin/bash
docker run -d \
-p 7788:80 -p 443:443 -p 80:80 \
-v /home/run/$name$/configs:/mt/web/configs \
-v /data/log/$name$:/mt/web/log \
-v /data/$name$:/mt/web/data \
-v /data/$name$_ssdb:/mt/ssdb/data \
-v /static/$name$:/mt/web/static \
-v /log/docker:/mt/docker/log \
hepochen/farbox_bucket:latest
按照部署 NAS 的描述:
FarBox 的运行,需要一些配置文件,也就是最终 Docker 容器内要读取的 /mt/web/configs 路径,这里的内容可以从 https://github.com/hepochen/FarBox/tree/master/farbox_bucket/deploy/run/configs 获得,也可以直接下载 https://doc.farbox.org/_attachments/2021-04-21/configs.zip 解压处理。
下载好 configs 文件,解压到 /home/run/farbox/configs
这个 docker 映射的本地路径(不然会无法正常启动容器)。执行上面的 bash 脚本,即可使用。
按照往常的习惯,装好 Nginx,准备在做反代分离 n8n 和 FB,发现 80 端口与 Traefik 冲突。修改 Traefik 监听,让出 80 端口,在 CF 配好 DNS 和 TXT,FB 即可正常访问。
但是一方面用着 Nginx 和 Traefik 两套代理有些代码洁癖上的膈应,另一方面 80 端口二者只能其一使用,另一套注定要用域名+端口号伴其一生有些落寞。更重要的是,如果用 apt install nginx
装的 Nginx 试图通过反代 proxy_pass
给 Traefik,再跳转 n8n,结果竟然是不成立!猜测是 Traefik 直接监听了底层 socket,并且其主动服务发现直接连接了下游服务。因此 Nginx 与 Traefik 处于并列状态,没有形成路由的前后关系。于是决定二者留下一个,改动另外一个的入口,把所有的下游服务集中起来管理。Nginx 用起来已经非常熟悉但配置上略显陈旧,而 Traefik 一方面看起来很新鲜,另一方面其自带 Let'sencrypt 好像能剩下未来续证书的烦恼——虽然如果使用 Cloudflare CDN 其实已经把它做完了。
于是卸掉了 Nginx,把 80 端口交给 Traefik。Traefik 的官网有简单的配置方法,docker 模式下,修改 docker-compose.yml 配置即可,把 FB 的挂在配置写进去。
farbox:
image: hepochen/farbox_bucket:latest
restart: always
ports:
- "7788:80"
- "127.0.0.1:8443:443"
- "127.0.0.1:8080:80"
labels:
- traefik.enable=true
- traefik.http.routers.farbox.rule=Host(`test.wellwellsleep.com`)
- traefik.http.routers.farbox.tls=true
- traefik.http.routers.farbox.entrypoints=web,websecure
- traefik.http.routers.farbox.tls.certresolver=mytlschallenge
- traefik.http.middlewares.farbox.headers.SSLRedirect=true
- traefik.http.middlewares.farbox.headers.STSSeconds=315360000
- traefik.http.middlewares.farbox.headers.browserXSSFilter=true
- traefik.http.middlewares.farbox.headers.contentTypeNosniff=true
- traefik.http.middlewares.farbox.headers.forceSTSHeader=true
# - traefik.http.middlewares.farbox.headers.SSLHost=${DOMAIN_NAME}
- traefik.http.middlewares.farbox.headers.STSIncludeSubdomains=true
- traefik.http.middlewares.farbox.headers.STSPreload=true
- traefik.http.routers.farbox.middlewares=farbox@docker
volumes:
- /home/run/farbox/configs:/mt/web/configs
- /data/log/farbox:/mt/web/log
- /data/farbox:/mt/web/data
- /data/farbox_ssdb:/mt/ssdb/data
- /static/farbox:/mt/web/static
- /log/docker:/mt/docker/log
所有的服务使用 127.0.0.1:[ports]
即可,避免了大量的端口暴露,这一点在之前的反向代理配置中一直没有注意,这次也补上了。
基本的迁移就此结束。接下来要把 farbox_client
接上,数据和模板还原,SES 测试通过,就可以正式上线啦。
2023-10-21 更新
之前还是低估了 FB 的坑。由于 FB 的镜像内实际带有 Nginx,但配置不透明,因此按照上述方式去部署 FB 时,FB 内部的类 SaaS 管理会出现奇奇怪怪的 bug。比如:
- 在
/admin
管理员界面,如果使用超管私钥登录,无法访问超管页独有的Buckets
和Domains
等页面,会提示 404 - 如果使用普通用户私钥登录,则在使用
绑定域名到 Bucket
时,无法出现需要设置在 DNS 服务商的 TXT 验证值,填入任何值都会提示「no-login」,也就是页面跳转后没带上鉴权信息
反复排查端口和翻查 Traefik 的 配置手册 后,偶然的机会下,发现使用直接 IP 访问加端口号的方式,即不使用 HTTPS 的话,就可以正常使用相关功能。这样一来似乎问题就明确了:FB 的某些页面跳转可能没有兼容 HTTPS 方式,使得 Traefik 在自动全路径 HTTPS 化后,部分链接出现死链或无法继承 session 信息。
最终的 docker-compose.yml 部署文件如下(可以看到注释掉的配置里,即便我尽量控制 Traefik 放弃自动 HTTPS,但实际效果仍然是所有在 Traefik 注册的域名都自动加上了 HTTPS):
version: "3.7"
services:
traefik:
image: "traefik"
restart: always
command:
- "--api=true"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--accessLog"
- "--entrypoints.web.address=:80"
#- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
#- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.fb7788.address=:7788"
- "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
- "--certificatesresolvers.mytlschallenge.acme.email=${SSL_EMAIL}"
- "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
- "8888:8080"
volumes:
- traefik_data:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock:ro
farbox:
image: hepochen/farbox_bucket:latest
restart: always
ports:
- "7788:80" # 7788 端口需要公开,用于 farbox_client 的客户端同步
- "8443:443"
- "8080:80"
labels:
- traefik.enable=true
- traefik.http.routers.farbox.rule=Host(`test.wellwellsleep.com`) || Host(`tech.wellwellsleep.com`) # 租户域名在这里添加
#- traefik.http.routers.farbox.tls=true
- traefik.http.routers.farbox.entrypoints=web,websecure,fb7788
- traefik.http.routers.farbox.tls.certresolver=mytlschallenge
- traefik.http.services.farbox.loadbalancer.server.port=80
- traefik.http.middlewares.farbox.headers.SSLRedirect=true
- traefik.http.middlewares.farbox.headers.STSSeconds=315360000
- traefik.http.middlewares.farbox.headers.browserXSSFilter=true
- traefik.http.middlewares.farbox.headers.contentTypeNosniff=true
- traefik.http.middlewares.farbox.headers.forceSTSHeader=true
- traefik.http.middlewares.farbox.headers.SSLHost=${DOMAIN_NAME}
- traefik.http.middlewares.farbox.headers.STSIncludeSubdomains=true
- traefik.http.middlewares.farbox.headers.STSPreload=true
- traefik.http.routers.farbox.middlewares=farbox@docker
volumes:
- /home/run/farbox/configs:/mt/web/configs
- /data/log/farbox:/mt/web/log
- /data/farbox:/mt/web/data
- /data/farbox_ssdb:/mt/ssdb/data
- /static/farbox:/mt/web/static
- /log/docker:/mt/docker/log
volumes:
traefik_data:
external: true
当要在 FB 中增加租户的时候,使用 IP+端口号(本例为 8080)
的方式强制使用 HTTP 设置即可。
话说我都忘了如何增加租户,步骤是:
1. 使用超管账户邀请
2. 使用/__register
(两个下划线)路径输入邀请码后设好私钥登录
2. 到租户的管理页绑定 new-domain
3. (照目前的配置)去 docker-compose.yml 里 farbox 的服务部分,增加 Host(new-domain
) 的路由规则,
最后想想,其实 CF 的 DNS 做 CDN 的时候已经套了 HTTPS,本质上在主机的网关内完全可以使用 HTTP 访问。只是由于 n8n 在部署后强制要求 HTTPS,所以引入了 Traefik,结果 FB 又不完全兼容……
测试 ses
23333
again test
traefik sts false test
is http ok?
@wellsleep actually no
new ses test with defined referrer policy
back to https+referrer
remove 127.0.0.1
does it work now with same host and referer?
it will not work with https link because no referrer and host fields
@sad 啊?丢评论?!
https://github.com/hepochen/FarBox/blob/daeda4f5080467f1ddf4b60424b8562f914756bd/farbox_bucket/server/comments/notification.py#L66
此函数内对 client_referrer 和 request.referrer 做比较,相同或发现为空则丢弃。
因此修改 traefik,增加 header 的字段。
1. http 可看到明确的 host 和 referer 值
2. https 无法解析 Host 和 referer,增加 X-Forword-Host 疑似解决
2023-11-07a.png
2023-11-07b.png
https for all traffic
localhost for service discover
@wellsleep success