在本地开发环境配置线上域名代理

0 阅读4分钟

需求背景

目前接手的一个项目,在开发初期并没有进行配置统一管理,线上域名散布在项目文件中各处。

这种做法虽然开发初期看似方便,但对后期维护造成了很大影响:

  • 不便于本地调试:需要频繁手动替换线上地址为本地地址;
  • 易出错:开发完成后推送代码前需要手动替换为线上域名,极易漏改;
  • 不易协作:不同开发者使用不同端口或后端地址,需反复修改代码。

因为是后续接手而不是从头开发,对于先前文件并不熟悉,因此也不好擅自重构。

因此,为了降低开发环境的维护成本,提高对接效率,并尽量不干扰已有项目结构,采用了本地反向代理 + hosts 映射的方式,模拟线上环境,统一域名入口,便于开发调试。

实现目标

对线上域名进行代理。将域名从线上环境代理到本地开发环境。

可以支持 WebSocket。

在无需进行替换的前提下,实现稳定的本地开发体验。

前置条件

  • 安装 openresty 一个基于 nginx 和 lua 的 web 平台。
    • 也可以直接安装 nginx。两者配置基本相同。
  • 下载 mkcert ,用于生成 SSL 证书。
  • 一个文本编辑器。

配置本地域名

需要修改本地 hosts 文件,将线上域名指向更改为指向本地 127.0.0.1 地址,以便本地使用 openresty 转发请求。

# 修改 hosts 文件
127.0.0.1 custom.domain.local
# 刷新 dns 配置 使得自定义本地域名生效
ipconfig /flushdns

# 尝试 ping 通
ping custom.domain.local

# 可 ping 通代表设置成功
正在 Ping custom.domain.local [127.0.0.1] 具有 32 字节的数据:
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128

设置 nginx 配置

server {
    # 监听 80 端口
    listen 80;
    # 监听域名
    server_name custom.domain.local;
    # 设置对应服务
    location / {
        # 设置本地 8080 端口服务
        proxy_pass  http://127.0.0.1:8080;
    }
}

如果无法访问,需要检查是否设置了网络代理。

在网络代理处设置本地自定义域名不经过代理即可。

配置证书

可以使用 mkcert 这个工具生成 SSL 证书。

mkcert -install          # 生成本地根证书
mkcert app.custom.local  # 生成 app.custom.local 域名证书
mkcert api.custom.local  # 生成 api.custom.local 域名证书

运行以上命令后,将生成对应的证书文件。

为了方便统一管理,建议将这些证书放在一个文件夹内。

设置 SSL

server {
    listen       80;
    listen       443 ssl;
    server_name  custom.domain.local;
    ssl_certificate     证书路径;
    ssl_certificate_key 证书私钥路径;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;
    location / {
        # 设置本地 8080 端口服务
        proxy_pass  http://127.0.0.1:8080;
    }
}

配置 WSS 代理

WSS 协议是 WS 协议的 SSL 版本,可以将两者的关系看做 HTTPS 和 HTTP 。

location /ws {
    access_by_lua_block {
        ngx.log(ngx.INFO, "WebSocket 请求 xx.xx.xx/ws 来自 ", ngx.var.remote_addr)
    }
    proxy_pass http://xx.xx.xx.xx:xxxx/ws;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
}

配置以上内容,当访问 wss://xx.xx.xx/ws 时就会自动转发到本地。

为什么代理 http:// 而不是 ws://

这是因为 ws 协议在初始阶段使用 http 协议发起的

首先客户端发起一个 http 请求用来告诉服务器要将这个连接升级为 ws 协议

服务器若返回特定响应,则连接升级为 ws 协议。

这就是为什么要设置以下内容的原因。

这几项配置用于告诉 nginx 如何处理 WS 连接。

proxy_http_version 1.1;                   # 支持长连接
proxy_set_header Upgrade $http_upgrade;   # 开启协议升级
proxy_set_header Connection "upgrade";    # 明确说明是协议升级
proxy_set_header Host $host;              # 可选,用于保持原始域名

拆分配置文件

为了后续维护和方便调整,这里将配置拆分为单独文件。

nginx.conf 文件中的 http 块末尾引入其他配置。

# nginx.conf
http {
    include ./conf/custom/*.conf; # 导入 custom 目录下的所有配置
}

这里使用通配符 * 而不是单独指名,是因为这样后续可以在文件夹内自由增加配置文件,而不需要每次增删都去修改配置文件。减少了心智负担。

下面是配置文件全貌:

# custom.local.conf
# app.custom.local -> http://localhost:3000
server {
    listen       80;
    listen       443 ssl;
    server_name  app.custom.local;

    ssl_certificate     ./conf/certs/app.custom.local.pem;
    ssl_certificate_key ./conf/certs/app.custom.local-key.pem;

    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;

    location / {
        access_by_lua_block {
            ngx.log(ngx.INFO, "访问 app.custom.local: ", ngx.var.request_uri, " from ", ngx.var.remote_addr)
        }
        add_header Cache-Control no-store;
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

# api.custom.local -> http://localhost:8080
# wss://api.custom.local/ws -> ws://localhost:3002/ws
server {
    listen       80;
    listen       443 ssl;
    server_name  api.custom.local;

    # 替换为你自己生成或申请的证书路径(使用 mkcert 或其他工具)
    ssl_certificate     ./conf/certs/api.custom.local.pem;
    ssl_certificate_key ./conf/certs/api.custom.local-key.pem;

    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;

    location / {
        access_by_lua_block {
            ngx.log(ngx.INFO, "访问 api.custom.local: ", ngx.var.request_uri, " from ", ngx.var.remote_addr)
        }
        proxy_pass http://localhost:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location /ws {
        access_by_lua_block {
            ngx.log(ngx.INFO, "WebSocket 请求 api.custom.local/ws 来自 ", ngx.var.remote_addr)
        }
        proxy_pass http://localhost:3002/ws;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}

问题排查

无法访问域名

  • 未修改 hosts
    • 修改 hosts 将域名指向本地 127.0.0.1 即可。
  • 修改 hosts 未生效
    • ipconfig /flushdns 刷新 DNS 。
  • 检查是否使用了网络代理。
    • 关闭代理或增加规则,确保远程地址是本地 127.0.0.1 的 80 或 443 端口。

image.png

image.png