使用 nginx 实现根据 header 进行静态资源的路由
使用 nginx 实现根据 header 进行静态资源的路由
背景
在开发过程中,希望针对静态资源进行动态切换,做一个灰度发布部署的功能,即区分主干环境与分支环境,根据请求的 header 中是否带有指定的字段(X-ENV-ID)来进行静态资源的路由。
实现
整体的架构图如下:
实现方式: 通过 nginx 本身的特性实现,先将 header 中的变量映射到对应的目录,在 location 中使用变量定义 root 目录,再通过 try_files .. @fallback 对分支环境中不存在的资源,转到默认目录(主干)上去寻找资源,如果还找不到,则返回404.
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
map $http_x_env_id $resource_directory {
default /usr/share/nginx/html/env0; # 默认静态文件资源目录
0 /usr/share/nginx/html/env0; # 默认静态文件资源目录,
1 /usr/share/nginx/html/env1; # 根据header值为value1转发到资源1的静态文件目录
2 /usr/share/nginx/html/env2; # 根据header值为value2转发到资源2的静态文件目录
}
server {
listen 80;
server_name localhost;
location / {
root $resource_directory; # 根据header值动态选择静态文件资源目录
try_files $uri $uri/ @fallback; # 尝试查找资源,如果找不到则跳转到 @fallback
}
location @fallback {
root /usr/share/nginx/html/env0/;
try_files $uri $uri/ =404; # 尝试查找资源,如果找不到返回404错误
}
}
}
案例:
- 环境要求:docker,本机 80 端口没被占用
- 运行: 执行 command.sh 脚本,打包 frontend 中的代码到 nginx 并执行,会启动 nginx 服务,监听 80 端口
主干&分支环境目录如下
├── env0
│ ├── index.html
│ └── static
│ ├── css
│ │ └── style.css
│ ├── img
│ │ └── image1.jpg
│ └── js
│ └── script.js
├── env1
│ ├── index.html
│ └── static
│ ├── css
│ │ └── style.css
│ ├── img
│ │ └── image1.jpg
│ └── js
│ └── script.js
├── env2
│ ├── index.html
│ └── static
│ ├── css
│ │ └── style.css
│ ├── img
│ └── js
其中, env0为主干环境,包括全量资源,env1为分支1环境,为全量的分支,env2为分支环境2,为部分的分支,因此当进入到 env1 环境时,访问的所有资源都是 env1 的资源,而当进入 env2 环境时, js 和 img 资源会使用主干环境的资源。
proxy_pass 的转发
如果静态资源的存储不是使用本地存储,而是通过另一个文件服务来访问,那么在 nginx 中需要使用 proxy_pass 进行转发,此时要实现同样的静态资源路由功能, 对应转发配置如下:
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
map $http_x_env_id $backend {
default http://127.0.0.1:8000; # 默认静态文件资源目录
0 http://127.0.0.1:8000; # 默认静态文件资源目录,
1 http://127.0.0.1:8001; # 根据header值为value1转发到资源1的静态文件目录
2 http://127.0.0.1:8002; # 根据header值为value2转发到资源2的静态文件目录
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass $backend;
proxy_intercept_errors on;
error_page 404 = @fallback;
}
location @fallback {
proxy_pass http://127.0.0.1:8000;
}
}
}