Devlog#04|RoadSign

发布于:01/16/2025 12:53:54

RoadSign aka 路碑,是 Solar Network 的一个核心组件,负责 Web 托管相关的业务。你所看到的官网就是由路碑 Serve 的。

如果你有心留意一下服务器传来的 Header,里面有一个 Server 字段。你就会看到 Caddy, RoadSign

这里的 Caddy 是 Go 写的一个非常棒的开源方向代理,个人认为比 Nginx 好,配置少,自带 SSL,连管理面板都不用了。

而 RoadSign 就是我们的本文的主题了,其实它已经出来很久了。之前的官网也有留他的版面。但是因为一系列原因,文档和维护没有做的很完善。不过它还是有在我们的生产服务器良好运转,也为我提供了很多 DX 上面的改善,因此来聊一聊它背后的实现和故事。

起步

RoadSign 立项于 2023.11.16,光看年份距今已经有两年的岁月了。最开始,它的目标是成为一个良好的反向代理,直接替代 Nginx 和 Caddy。你如果看到它的 README 就可以发现 Benchmark 都是跟 Nginx 对标的。它真的很快,不愧是 Go 写的。

但是后来,在一些领域就发现力不从心。包括在处理 HTTPS 的 SSL 这块不是很好。目前的架构只允许它一个实力绑定一个证书,对于一个 IP 要多个网站 / 域名的使用场景很不适合。所以我知难而退,专业的事情留给专业的人做,自己一个人肯定是和专门一个公司 / 组织做反向代理的比不上。

所以目前的 RoadSign 就成为一个推荐运行在反向代理之后,帮助你 Serve 和更新你的 Web 应用的 HTTP 服务器。

使用

RoadSign 即使放弃成为 Caddy, Nginx 这样的反向代理。但是它内部还是保留了一部分路由的功能,而且功能很强大。你甚至可以根据查询参数(URL 问号后的)分到不同的 Destination(目的地,跟 Nginx 的 Upstream 类似概念)。

关于为什么可以根据查询参数分流,主要是 Inspired by YetAnotherReverseProxy aka YARP. 微软的一个 .NET 的反向代理库,也可以独立运行。在一次重构中,RoadSign 曾经使用过 .NET 来实现

同时,由于被 Caddy 的配置文件简洁性所吸引,RoadSign 的配置也是尽量简洁,以下是一个 Region 的配置文件,用来托管一个 SPA 静态文件网页。

id = "solian"

[[locations]]
id = "solian"
hosts = ["sn.solsynth.dev"]
paths = ["/"]
[[locations.destinations]]
id = "solian-web"
uri = "files:///workdir/solian?fallback=index.html&index=index.html"

虽然看起来还是稍微臃肿,但是,这是在我们不开发自己独有的配置文件格式之前的最小实现了,总体上来看还是比 Nginx 简洁吧。

来使用 RoadSign,你需要首先部署它。考虑到 RoadSign 目前仅支持绑定一个证书,并且只支持 HTTP/1.1 协议,我们强烈建议你在一个真正的反向代理后使用它,关于反向代理,我们推荐 Caddy。

你可以使用我们自己用的 docker-compose.yaml 来部署它

services:
  roadsign:
    image: xsheep2010/roadsign:delta
    restart: unless-stopped
    ports:
      - 8000:8000
      - 81:81
    volumes:
      - "/srv/roadsign/config:/config"
      - "/srv/roadsign/logs:/logs"
      - "/srv/roadsign/workdir:/workdir"
      - "/srv/roadsign/settings.toml:/settings.toml"

小故事:你知道为什么镜像名字后面的 Tag 是 delta 吗?我们还有一个预选版本叫做 sigma 使用 Rust 实现的 (discontinued)。还有另一个预选版本是 omega 使用 .NET 实现的 (discontinued, removed)。

其中 /config 目录存放着所有 Region 的配置,我们可以稍后上传。/logs 是所有日志。/workdir 是资源文件。/settings.toml 是实例配置文件。以上所有的目录绑定除了实例配置文件都可以自行修改路径名称。

id = "central"

[debug]
print_routes = false

[sideload]
ports = [":81"]
secured_ports = []
trusted_proxies = ["localhost"]

[hypertext]
ports = [":8000"]
secured_ports = []
force_https = false

# [[hypertext.certificate]]
# key = "./certs/privkey.pem"
# pem = "./certs/fullchain.pem"

[hypertext.limitation]
max_body_size = 549_755_813_888 # 512 GiB
max_qps = -1

[logging]
access = "/logs/access.log"
warden_apps = "/logs/warden"

[paths]
configs = "/config"

[telemetry]
request_logging = true
capture_traces = true

[performance]
low_memory = false
traces_limit = 256
prefork = false

[security]
credential = "<credential>"

你需要在实例配置文件里面预先制定好所有需要用的端口,这样未来才能在 Region 配置中获得到对应端口的路由。

Sideload API 是用来与 RoadSign CLI 交互的。建议使用加密模式,或是自在内网使用。

基础的 Region 配置上文介绍过,此处不在赘述。

至此,你的 RoadSign 已经 Up & Running 了。但是没有内容,此时去访问监听的 8000 端口会看到 404,接下来我们来为其添加点内容。

RoadSign CLI

尽管你可以手动在服务器管理 RoadSign,但是都用了 RD,不尝一尝它的特色功能还是不行的。

既然你是做 Web 开发的,电脑上有个 NodeJS 也是当然的吧,除非你是喜欢用 Rust WASM 写网页的异类就当我没说。

使用 npm 来安装 RoadSign CLI

npm install -g roadsign-cli

之后你就可以来求救查看可用命令了

rdcli --help

在正式操作之前,我们需要让 RoadSign CLI 连接到我们的服务器 Sideload API。

使用以下命令来建立连接,其中的 <credential> 是你配置文件中的密钥。<label> 是你本地制定的名称,未来用来表明是与这个服务器进行操作。

rdcli login <label> <credential>

之后就可以来看看服务器的状态了。

必须是 Up & Running 啊

介绍两个使用次数最多的命令 syncdeploy

sync 是用来同步 Region 配置文件的。而 deploy 是用来部署更新 Destination 的。具体用法可以查看不存在的文档。实践用途可以让我们接着看下去。

.roadsignrc

你可以在项目中配置一个 .roadsignrc 用来高速 RoadSign CLI 在 Deploy 和 Sync 的过程中的参数,这样就不用每次都手动输入了。

{
  "sync": {
    "region": "solian",
    "configPath": "roadsign.toml"
  },
  "deployments": [
    {
      "region": "solian",
      "site": "solian-web",
      "path": "build/web"
    }
  ]
}

以上是一个 .roadsignrc 的配置,你可以用来参考。其中 Sync 部份的 configPath 是你本地 Region 的配置文件,会同步到云端的对应 Region。

接下来就让我们 Sync & Deploy 吧~

rdcli sync prod
rdcli deploy prod

对了,你仔细看这个截图就能发现我们的 Solian 网页版也是它提供的,不错吧~

Server-side Rendering

说到现在,RoadSign 除了快捷的更新远端文件,好像相比别的 HTTP 服务器也没差多少嘛。这你可就错了,我们 RD 还 Bundle 了一个进程管理呢!用它你就可以轻松的实现服务器渲染的 App 部署,例如 Nuxtjs 和 Nextjs 也只用一行命令就可以部署。

在你的 Region 设置中添加这块

[[applications]]
id = "capital-app"
workdir = "/workdir/capital"
command = ["node", "standalone/server.js"]
environment = ["HOSTNAME=0.0.0.0"]

这样在下次 Deploy 的时候,RD 就会自动帮你启动你的 App

Tips: RoadSign 镜像中 Bundle 了 NodeJS 和 npm 的最新版本,如果你的 App 需要别的运行环境,你可以手动 Attach 进入内部的 Shell 手动安装或者自行构建一个新的镜像。


我自己很喜欢 RoadSign 这个项目,甚至跟 Solar Network 相比,我觉得 RD 甚至更好。

希望你也跟我一样喜欢这个项目,未来我还会给它加入新功能,并且在官网重新加回它专属的介绍页。但愿有一天能在我自己的服务器之外见到它在 Server 标头中的身影。

Thanks for you reading!