使用 NGINX 实现动静态混合的路径 Overlay

 

最近部署 MediaWiki 时遇到一个需求,即用 NGINX 实现类似 Overlay FS 的特性。 具体地,由于我使用了 Git 版本的 MediaWiki,而又需要在目录中添加一些自定义的文件(如 logo、favicon.ico、各种 verification 文件等)。为了不污染 Git,并在升级时减少麻烦,因此我想把这些文件独立于源码放置。

抽象一下问题,NGINX 原来需要从 /srv/foo/orig 提供服务,现在需要它除了搜索原来的路径,还要额外从 /srv/foo/overlay 尝试搜索文件(注意与 Overlay FS 不同的是,优先使用原来的路径)。并且由于 MediaWiki 依赖 PHP,因此还需要能够同时处理动态与静态文件。

经过我和坏人的一些讨论,以及数次摸索,获得了以下的配置:

root /srv/foo/orig;

location @overlay {
  root /srv/foo/overlay;
  try_files $uri $uri/ @orig;
}

location @orig {
  rewrite ^/([^?]*)(?:\?(.*))? /index.php?title=$1&$2 last;
}

location / {
  index index.php;
  try_files $uri $uri/ @overlay;
}

在这些配置下,NGINX 在遇到形如 /baz 的路径时,会顺次进行如下尝试:

  1. 匹配 /:尝试 /srv/foo/orig/baz/srv/foo/orig/baz/
  2. 转向 @overlay:尝试 /srv/foo/overlay/baz/srv/foo/orig/baz/
  3. 转向 @orig:重写为 /index.php 的 query string,并由 FastCGI 处理请求。此部分适用于在两个位置都没有匹配到静态文件的情况,可以进行任何兜底操作,比如交由动态程序处理、重定向(rewrite),或者返回错误(error_page)。注意此部分不能合并到 @overlay 中,因为两者的 root 不同。

上面的 @overlay@orig 是 NGINX 的 named location,官方文档描述如下:

The “@” prefix defines a named location. Such a location is not used for a regular request processing, but instead used for request redirection. They cannot be nested, and cannot contain nested locations.

使用 named location 的一大优点是,进行跳转不影响实际的 URL,避免了各种复杂的匹配处理。注意 @overlay 中必须使用 root 切换目录,因为 named location 中不能使用 alias。此外,由于某些我还没有搞清的机制(与 NGINX 的 context 有关),配置中必须级联 try_files 来进行链式查找,而不能写成 try_files $uri $uri/ @overlay @orig

如果只有静态文件,那么只需要保留 @overlay 即可。如果想要优先搜索 overlay 目录(与 Overlay FS 的用法相同),则需要另一种写法:

root /srv/foo/orig;

location @overlay {
  root /srv/foo/overlay;
  try_files $uri $uri/ @orig;
}

location @orig {
  try_files $uri $uri/ @orig_dyn;
}

location @orig_dyn {
  rewrite ^/([^?]*)(?:\?(.*))? /index.php?title=$1&$2 last;
}

location / {
  index index.php;
  try_files @overlay;
}

由于 root 相同,这里的 @orig_dyn 是可以直接合并到 @orig 中的。