侧边栏壁纸
博主头像
MicroMatrix 博主等级

明月别枝惊鹊,清风半夜鸣蝉

  • 累计撰写 135 篇文章
  • 累计创建 41 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

使用 Docker 部署 3x-ui,并结合 Nginx、Cloudflare 小黄云优化访问

David
2023-12-13 / 0 评论 / 0 点赞 / 1274 阅读 / 0 字

前言

由于工作环境的需要,我们通常会用到代理服务。过去我使用过 x-ui 来搭建面板,但是原项目维护不算活跃,后来出现了不少变种版本。这里我使用的是 3x-ui,并记录一次完整的 Docker 部署、Nginx 反向代理、SSL 证书、Cloudflare 小黄云、订阅链接优化以及 Reality 节点配置经验。

本文目标:

1. 使用 Docker 部署 3x-ui,尽量不影响服务器其他服务
2. 使用 Nginx 反代 3x-ui 面板、订阅服务和 WebSocket 节点
3. 通过 Cloudflare 小黄云代理 VLESS + WS + TLS 节点
4. 让 3x-ui 面板生成的订阅链接可以直接给用户使用
5. Reality 节点单独使用灰云域名直连,不经过 Cloudflare 小黄云

本文使用的示例信息如下,实际部署时请替换成自己的:

面板域名 / CDN 域名:cdn.example.com
Reality 域名:reality.example.com
3x-ui 面板端口:2096
3x-ui 订阅端口:8443
VLESS + WS 入站端口:2083
VLESS + Reality 入站端口:32069
WebSocket 路径:/american
订阅路径:/v2ray-sub/

准备工作

需要准备:

1. VPS 一台
2. 域名一个
3. 域名已经解析到 VPS
4. 服务器系统建议使用 Debian / Ubuntu
5. 已经开放必要端口,例如 80、443、Reality 入站端口等

示例 DNS 规划:

cdn.example.com       开启 Cloudflare 小黄云
reality.example.com   关闭 Cloudflare 小黄云,DNS only

其中:

cdn.example.com

用于:

3x-ui 面板访问
3x-ui 订阅链接
VLESS + WS + TLS 节点

而:

reality.example.com

用于:

VLESS + TCP + Reality 节点

Reality 节点不建议走 Cloudflare 小黄云,应该使用灰云域名直连 Xray 入站端口。


安装 Docker 和 Docker Compose

下面以 Debian / Ubuntu 为例。

1. 更新系统

sudo apt update
sudo apt upgrade -y

2. 安装依赖

sudo apt install -y \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

3. 添加 Docker 官方 GPG 密钥

sudo install -m 0755 -d /etc/apt/keyrings

curl -fsSL https://download.docker.com/linux/debian/gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

sudo chmod a+r /etc/apt/keyrings/docker.gpg

如果你使用的是 Ubuntu,把上面的 Docker 源地址换成 Ubuntu 对应地址。

4. 添加 Docker 软件源

Debian 示例:

echo \
  "deb [arch=$(dpkg --print-architecture) \
  signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

更新软件源:

sudo apt update

5. 安装 Docker

sudo apt install -y \
    docker-ce \
    docker-ce-cli \
    containerd.io \
    docker-buildx-plugin \
    docker-compose-plugin

查看 Docker 状态:

systemctl status docker

使用 Docker 部署 3x-ui

创建目录:

mkdir -p ~/x-ui
cd ~/x-ui

创建 docker-compose.yml

vim docker-compose.yml

写入以下内容:

services:
  xui:
    image: ghcr.io/mhsanaei/3x-ui:latest
    container_name: xui
    volumes:
      - ./db/:/etc/x-ui/
      - ./cert/:/root/cert/
    environment:
      - XRAY_VMESS_AEAD_FORCED=false
    restart: unless-stopped
    network_mode: host
    cap_drop:
      - ALL
    cap_add:
      - NET_ADMIN

启动容器:

docker compose up -d

查看容器:

docker ps

查看日志:

docker logs -f xui

进入 3x-ui 管理脚本:

docker exec -it xui x-ui

常用命令:

# 停止并删除容器
docker compose down

# 更新镜像
docker pull ghcr.io/mhsanaei/3x-ui:latest

# 重启容器
docker restart xui

端口规划

建议先规划清楚端口,避免后面冲突。

本文示例端口如下:

Nginx 对外 HTTP:80
Nginx 对外 HTTPS:443
3x-ui 面板端口:2096
3x-ui 订阅端口:8443
VLESS + WS 入站端口:2083
VLESS + Reality 入站端口:32069

重点注意:

80 和 443 给 Nginx 使用
3x-ui 面板端口不要设置成 80 或 443
3x-ui 订阅端口不要设置成 443
Xray 入站端口不要和 Nginx、面板、订阅端口冲突

如果把 3x-ui 的面板端口或订阅端口改成 443,而 443 已经被 Nginx 使用,3x-ui 就会启动失败。

可以查看端口占用:

ss -lntp | grep -E ':80|:443|:2096|:8443|:2083|:32069'

安装 Nginx 和 Certbot

安装 Nginx:

sudo apt update
sudo apt install -y nginx

安装 Certbot:

sudo apt install -y snapd
sudo systemctl restart snapd

sudo snap install core
sudo snap refresh core
sudo snap install --classic certbot

sudo ln -s /snap/bin/certbot /usr/bin/certbot

创建 Nginx 配置文件:

touch /etc/nginx/conf.d/xui.conf

先写一个最简单的 HTTP 配置用于申请证书:

server {
    listen 80;
    listen [::]:80;
    server_name cdn.example.com;

    location / {
        proxy_pass http://127.0.0.1:2096;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
    }
}

检查配置:

nginx -t

申请证书:

certbot --nginx --agree-tos --no-eff-email --email [email protected] -d cdn.example.com

证书申请成功后,Certbot 通常会自动修改 Nginx 配置。后面我们会给出完整的最终配置。

测试续期:

certbot renew --dry-run

查看证书:

certbot certificates

删除证书:

certbot delete --cert-name cdn.example.com

Cloudflare 小黄云配置说明

Cloudflare 小黄云适合代理:

HTTP
HTTPS
WebSocket

所以适合:

VLESS + WS + TLS
VMess + WS + TLS
Trojan + WS + TLS

不适合直接代理:

VLESS + TCP + Reality
VLESS + TCP
普通 TCP 转发
UDP 流量

因此推荐:

cdn.example.com       小黄云,给 WS + TLS 使用
reality.example.com   灰云,给 Reality 直连使用

3x-ui 面板配置

面板端口

3x-ui 面板端口建议使用:

2096

不要设置为:

80
443

因为 80 和 443 应该交给 Nginx 使用。

外部用户访问:

https://cdn.example.com

Nginx 内部转发到:

https://127.0.0.1:2096

3x-ui 订阅服务配置

为了让 3x-ui 面板生成的订阅链接可以直接给用户使用,推荐这样配置订阅服务。

在 3x-ui 面板中找到订阅设置,示例配置如下:

监听域名:留空
监听端口:8443
URI 路径:/v2ray-sub/
反向代理 URI:https://cdn.example.com/v2ray-sub/

说明:

监听域名留空:
表示监听所有地址,方便 Nginx 通过 127.0.0.1 访问。

监听端口 8443:
这是 3x-ui 订阅服务真实监听端口,不要设置成 443。

URI 路径 /v2ray-sub/:
这是订阅服务内部路径,必须以 / 开头并以 / 结尾。

反向代理 URI:
这是对外展示给用户使用的订阅地址前缀。

配置完成后,3x-ui 面板生成的订阅链接应该类似:

https://cdn.example.com/v2ray-sub/xxxxxxxxxxxx

而不是:

https://cdn.example.com:8443/v2ray-sub/xxxxxxxxxxxx

如果反向代理 URI 只填写:

/v2ray-sub/

有些版本的 3x-ui 可能只显示相对路径:

/v2ray-sub/xxxxxxxxxxxx

如果希望用户复制出来就是完整链接,建议填写完整公网前缀:

https://cdn.example.com/v2ray-sub/

VLESS + WS + TLS 入站配置

Cloudflare 小黄云节点建议使用:

VLESS + WebSocket + TLS

但是这里要区分:

Xray 真实监听配置
客户端对外连接配置

Xray 真实入站可以配置为:

协议:VLESS
监听端口:2083
传输:ws
路径:/american
TLS:关闭
Reality:关闭
Flow:留空

为什么 Xray 入站 TLS 关闭?

因为 TLS 由 Nginx 在 443 上处理,Nginx 再把请求以普通 WebSocket 的方式转发到:

http://127.0.0.1:2083

客户端对外连接应该是:

地址:cdn.example.com
端口:443
TLS:开启
SNI:cdn.example.com
Host:cdn.example.com
传输:ws
路径:/american

也就是说:

客户端连接 cdn.example.com:443
Nginx 转发到 127.0.0.1:2083

不要让客户端直接使用:

cdn.example.com:2083

除非你不走 Cloudflare 小黄云。


让订阅内容里的节点也使用 443 + TLS

订阅链接本身正确,并不代表订阅内容里的节点参数一定正确。

例如订阅链接可能已经是:

https://cdn.example.com/v2ray-sub/xxxxxxxxxxxx

但是订阅内容里的节点可能还是:

vless://[email protected]:2083?security=none&type=ws&path=%2Famerican

这对 Cloudflare 小黄云场景是不合适的。

CDN 节点应该生成类似:

vless://[email protected]:443?security=tls&type=ws&host=cdn.example.com&sni=cdn.example.com&path=%2Famerican

因此在 3x-ui 的入站配置中,如果你的版本提供类似以下选项:

外部代理
External Proxy
外部端口
分享链接配置
订阅节点外部参数

建议将 CDN 入站的对外参数设置为:

地址:cdn.example.com
端口:443
TLS:开启
SNI:cdn.example.com
Host:cdn.example.com
Path:/american

注意:

真实监听端口仍然是 2083
对外分享端口是 443

如果你的 3x-ui 版本没有类似的外部代理设置,那么订阅链接可以反代成功,但订阅内容可能仍会按真实入站端口生成。这种情况下需要手动调整节点、升级 3x-ui,或者使用订阅转换工具处理端口和 TLS 参数。


Nginx 完整配置示例

下面是一份完整 Nginx 配置。

请将:

cdn.example.com

替换成自己的域名。

请将证书路径替换成自己 Certbot 实际生成的路径。

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name cdn.example.com;

    ssl_certificate /etc/letsencrypt/live/cdn.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/cdn.example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # VLESS + WS 节点入口
    # 客户端访问:wss://cdn.example.com/american
    # Nginx 转发到:127.0.0.1:2083
    location = /american {
        proxy_pass http://127.0.0.1:2083;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        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_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }

    # 3x-ui 订阅反代入口
    # 3x-ui 订阅服务真实监听端口:8443
    # 用户实际订阅地址:https://cdn.example.com/v2ray-sub/xxxxxxxxxxxx
    location /v2ray-sub/ {
        proxy_pass https://127.0.0.1:8443;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-Port 443;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_set_header Range $http_range;
        proxy_set_header If-Range $http_if_range;

        proxy_redirect off;
        proxy_ssl_verify off;
    }

    # 3x-ui 面板入口
    # 3x-ui 面板真实监听端口:2096
    location / {
        proxy_pass https://127.0.0.1:2096;

        proxy_http_version 1.1;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-Port 443;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_redirect off;
        proxy_ssl_verify off;
    }
}

server {
    listen 80;
    listen [::]:80;
    server_name cdn.example.com;

    return 301 https://$host$request_uri;
}

保存后检查配置:

nginx -t

没有报错后重载 Nginx:

systemctl reload nginx

测试 WebSocket 是否正常

可以使用下面的命令测试 WebSocket 握手:

curl --http1.1 -i -N \
  -H "Connection: Upgrade" \
  -H "Upgrade: websocket" \
  -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jQ==" \
  -H "Sec-WebSocket-Version: 13" \
  https://cdn.example.com/american

如果配置正常,会返回类似:

HTTP/1.1 101 Switching Protocols

这说明:

Cloudflare -> Nginx -> Xray WS 入站

链路已经打通。

如果直接使用普通 curl 访问 WebSocket 地址,可能会返回:

400 Bad Request

这不一定是错误,因为 WebSocket 需要 Upgrade 握手头。


测试订阅链接

订阅链接应该类似:

https://cdn.example.com/v2ray-sub/xxxxxxxxxxxx

可以用 curl 测试:

curl -k https://cdn.example.com/v2ray-sub/xxxxxxxxxxxx

如果返回的是 base64 内容,可以解码查看:

curl -k https://cdn.example.com/v2ray-sub/xxxxxxxxxxxx | base64 -d

对于 Cloudflare CDN 节点,订阅内容里应该是:

地址:cdn.example.com
端口:443
security:tls
type:ws
path:/american
host:cdn.example.com
sni:cdn.example.com

如果看到:

端口:2083
security:none

说明订阅 URL 本身已经反代成功,但是节点对外参数还没有设置好。此时需要回到 3x-ui 入站配置里设置外部代理或分享参数。


Reality 节点配置

Reality 节点不建议走 Cloudflare 小黄云。

建议单独使用一个灰云域名,例如:

reality.example.com

Cloudflare DNS 添加:

类型:A
名称:reality
内容:VPS 公网 IP
代理状态:DNS only

也就是关闭小黄云。

Reality 入站示例:

协议:VLESS
传输:TCP
安全:Reality
监听端口:32069
SNI:按自己的 Reality 配置填写
Flow:按自己的客户端配置保持一致

客户端连接:

reality.example.com:32069

Reality 不需要 Nginx 反代,也不要写 Nginx 的 location。它应该由客户端直接连接到 Xray 的 Reality 入站端口。

如果使用 UFW,需要放行端口:

ufw allow 32069/tcp

检查端口监听:

ss -lntp | grep 32069

开启 BBR 加速

创建配置文件:

sudo nano /etc/sysctl.d/99-bbr.conf

写入:

net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr

保存后执行:

sudo sysctl --system

或者:

sudo sysctl -p /etc/sysctl.d/99-bbr.conf

查看是否生效:

sysctl net.ipv4.tcp_congestion_control

如果输出:

net.ipv4.tcp_congestion_control = bbr

说明已经开启。


Certbot 常用命令

查看证书:

certbot certificates

手动测试续期:

certbot renew --dry-run

为指定域名申请证书:

certbot --nginx -d example.com -d www.example.com

删除证书:

certbot delete --cert-name example.com

常见问题排查

1. 3x-ui 面板或订阅服务无法启动

优先检查端口冲突:

ss -lntp | grep -E ':80|:443|:2096|:8443|:2083|:32069'

如果 3x-ui 订阅端口被设置成了 443,而 Nginx 已经占用了 443,3x-ui 会启动失败。

如果面板已经无法打开,可以直接修改数据库。

进入 3x-ui 目录:

cd ~/x-ui

查看数据库表:

sqlite3 ./db/x-ui.db ".tables"

查看端口相关设置:

sqlite3 ./db/x-ui.db "select id, key, value from settings where lower(key) like '%port%' or lower(key) like '%sub%' or value like '%443%';"

如果看到:

subPort|443

可以改回:

sqlite3 ./db/x-ui.db "update settings set value='8443' where key='subPort';"

然后重启容器:

docker restart xui

2. WebSocket 返回 101,但客户端无法代理

如果测试返回:

HTTP/1.1 101 Switching Protocols

说明 Nginx 和 Xray 的 WebSocket 链路是通的。

此时重点检查客户端节点参数:

端口是否是 443
TLS 是否开启
SNI 是否是 cdn.example.com
Host 是否是 cdn.example.com
Path 是否是 /american
UUID 是否一致
Flow 是否留空

如果客户端只把端口从 2083 改成 443,但是 TLS 仍然关闭,也无法正常使用。

正确的 CDN 节点应该是:

端口:443
security:tls
type:ws
path:/american
host:cdn.example.com
sni:cdn.example.com

3. 订阅链接正确,但节点不可用

订阅链接正确只代表客户端能获取订阅内容,不代表节点参数一定正确。

可以解码订阅内容:

curl -k https://cdn.example.com/v2ray-sub/xxxxxxxxxxxx | base64 -d

如果 CDN 节点仍然是:

cdn.example.com:2083
security=none

那客户端会按普通 WS 访问 2083,通常无法通过 Cloudflare 小黄云正常使用。

应当让订阅内容生成:

cdn.example.com:443
security=tls

4. Reality 节点无法连接

Reality 节点应检查:

域名是否是灰云 DNS only
端口是否放行
Xray 是否监听对应端口
客户端 UUID、Reality 公钥、shortId、SNI 是否一致

检查监听:

ss -lntp | grep 32069

Reality 不走 Nginx,也不走 Cloudflare 小黄云。


最终推荐架构

最终推荐结构如下:

VLESS + WS + TLS:
客户端 -> Cloudflare 小黄云 -> Nginx 443 -> Xray WS 入站 2083

3x-ui 面板:
浏览器 -> Cloudflare 小黄云 -> Nginx 443 -> 3x-ui 面板 2096

3x-ui 订阅:
客户端 -> Cloudflare 小黄云 -> Nginx 443 -> 3x-ui 订阅服务 8443

VLESS + TCP + Reality:
客户端 -> 灰云域名 -> Xray Reality 入站 32069

这种方式可以避免端口冲突,也方便让用户直接使用 3x-ui 面板生成的订阅链接:

https://cdn.example.com/v2ray-sub/xxxxxxxxxxxx

同时,Reality 节点也不会被 Cloudflare 小黄云影响。

0

评论区