由于我有个内网的服务器,做开发或测试使用,一个个的暴露或者反代 HTTP 服务很不方便。于是就有了这样的想法 —— 动态反向代理。
环境
*请注意,在实验室环境中,内网 IP 和 VMID 所对应。比如 VMID 为 101,则内网 IP 是 192.168.0.101。
| 虚拟化环境 | Proxmox VE | ||
| 反向代理 | Nginx | ||
| 操作系统 | Debian11 | ||
| 虚拟化 | LXC | ||
| 内网网段 | 192.168.0.0/24 | 
| 服务名称 | VMID | 端口 | 
|---|---|---|
| Nginx 反向代理 | 123 | 443 | 
| Nextcloud | 101 | 80 | 
| Jupyter | 117 | 8080 | 
| CodeServer | 112 | 8080 | 
所有服务监听 IP 都为 0.0.0.0,防火墙均已关闭。
名词解释
| 名词 | 解释 | 
|---|---|
| name | 服务名称,在这里做区分作用。 | 
| vmid | 虚拟机在 Proxmox VE 中的唯一 ID。 | 
| port | 目标 IP 提供 HTTP 服务的端口,常见有 80, 8080。 | 
解决方案
将域名泛解析到 Nginx 的服务器,将所有请求交给 Nginx。
在 Nginx 的 server_name 中,使用正则表达式进行模糊匹配,匹配 name, vmid 与 port,并且过滤路由器等地址。
DNS
*.example -> your_nginx_server
安装 Nginx
不做过多说明。
apt install nginx-full -y
配置文件
仅需要编辑默认的 /etc/nginx/sites-available
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''   close;
}
server {
        listen 80 default_server;
        # 监听 IPv6
        listen [::]:80 default_server;
        listen 443 ssl http2;
        #listen 2083 ssl http2;
        # 这里改成你的证书(如有需要的话)。
        ssl_certificate    /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key    /etc/letsencrypt/live/example.com/privkey.pem;
        ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
        ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;
        add_header Strict-Transport-Security "max-age=31536000";
        error_page 497  https://$host$request_uri;
    
        # 这里是重点,使用正则来匹配 name, vmid 和 port。
        server_name ~^(?<name>\w+)\-(?<vmid>\w+)\-(?<port>\w+)\.example\.com example.com;
        # 如果 name 为空,则代表域名格式不合法。
        if ($name = "") {
                return 400 '没有输入服务名称,请检查域名格式。';
        }
        # 如果 vmid 为空,也就是没有填写被代理的机器。
        if ($vmid = "") {
                return 400 '格式中缺少被代理的 VMID。';
        }
        # 如果 port 为空,也就是没有指定要代理的端口。
        if ($port = "") {
                return 400 '格式中缺少端口。';
        }
        # 禁止一些 vmid,比如 路由器 等。
        if ($vmid = "1") {
                return 403;
        }
        if ($vmid = "2") {
                return 403;
        }
        if ($vmid = "254") {
                return 403;
        }
        # 禁止代理其自己。这里看上面的 环境 表格。
        if ($vmid = "123") {
                return 400 "无法代理自己。";
        }
        # 开始反向代理
        location / {
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header REMOTE-HOST $remote_addr;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection $connection_upgrade;
      
                # 这里替换成你的内网网段。
                proxy_pass http://192.168.0.$vmid:$port;
                proxy_read_timeout 90;
        }
             
}
应用更改
先检查配置文件有无错误。
nginx -t
重新加载 Nginx。
sudo systemctl reload nginx
测试
访问 Jupyter
https://www-117-8080.example.com 访问在 117 上的 8080 端口的 www 服务。
其他服务依次。

其他
还有个 Nginx 搭建流媒体服务器的

这就类似于AWS的开一堆服务那又臭又长的5级以上的域名了,不过这样确实挺方便的
没听懂QAQ
听懂的那一刻你就不再是少年了ヾ(´・ ・`。)ノ”
没听懂
这个 192.168.0.$vmid 的思路确实很清奇,但真的要做反向代理集群的话,这可能会累死(
不做反向代理集群,这个是方便测试环境用的