内容导航

2026/5/28

个人云服务与本地 Debian 服务器网络框架

记录一套通用的个人云网络框架:云服务器作为公网入口,本地 Debian 承担应用服务,通过 FRP 和 Nginx 安全接入。文中只保留可复用的网络搭建和新增服务模板,不包含真实域名、IP、账号、密钥和业务数据。

服务器与部署 DebianNginxFRPDocker内网穿透安全
本文目录

这篇只记录网络框架,不记录具体业务账号、真实域名、公网 IP、密钥、topic、设备 ID 或截图。目标是保留一套可复制的个人云服务接入模板:云服务器负责公网入口,本地 Debian 负责运行应用。

架构目标

用户浏览器 / 手机 App / 小程序 / 自动化客户端
  -> DNS
  -> 云服务器 Nginx 80/443
  -> 云服务器 127.0.0.1:<remote-port>
  -> frps
  -> 本地 Debian frpc
  -> 本地 127.0.0.1:<local-port>
  -> Docker / systemd 应用服务

核心原则:

  • 云服务器只开放公网入口和必要基础端口。
  • 应用服务不直接暴露公网,只通过 Nginx 的域名入口访问。
  • 本地 Debian 上的业务服务尽量只监听 127.0.0.1
  • FRP 只开放受控端口段,例如 9001-9099
  • 所有密钥、token、密码、真实域名和 IP 只保存在服务器本地配置中。

端口规划

类型示例端口说明
云 Nginx80, 443统一公网入口
FRP 控制端口7000frpc 连接 frps
FRP Dashboard7500仅监听 127.0.0.1
本地服务映射9001-9099云端 Nginx 反代到这些端口
本地应用端口3000, 5678, 8000仅本地监听

安全组建议:

公网开放:
TCP 80
TCP 443

限制来源:
TCP 22
TCP 7000 <限制为本地服务器地址>

不直接公网开放:
TCP 7500
TCP 9001-9099 <若国内未完成icp备案,可以临时开放一个端口,临时使用>
本地应用真实端口

通常 9001-9099 是云服务器本机反代端口。它们可以在云服务器上监听,但不应在云厂商安全组里对公网开放。

一、云服务器安装基础组件

sudo apt update
sudo apt install -y nginx curl unzip git rsync ufw ca-certificates

创建统一目录:

sudo mkdir -p /etc/frp
sudo mkdir -p /var/www/letsencrypt
sudo mkdir -p /var/log/nginx

二、云服务器部署 frps

下载并安装 frps 后,创建配置:

先生成 FRP 认证 token 和 dashboard 密码。只在服务器本地执行,不要把输出写入公开仓库。

FRP_TOKEN="$(openssl rand -hex 32)"
DASHBOARD_USER="frpadmin"  #--------------------------------------------------自己设定
DASHBOARD_PASSWORD="$(openssl rand -base64 24 | tr -d '\n')"

echo "FRP_TOKEN=${FRP_TOKEN}"
echo "DASHBOARD_USER=${DASHBOARD_USER}"
echo "DASHBOARD_PASSWORD=${DASHBOARD_PASSWORD}"

如果想长期保存,建议写入 root 只读文件:

sudo install -d -m 700 /root/.secrets
sudo tee /root/.secrets/frp.env > /dev/null <<EOF
FRP_TOKEN=${FRP_TOKEN}
DASHBOARD_USER=${DASHBOARD_USER}
DASHBOARD_PASSWORD=${DASHBOARD_PASSWORD}
EOF
sudo chmod 600 /root/.secrets/frp.env

模板:

. /root/.secrets/frp.env

sudo tee /etc/frp/frps.toml > /dev/null <<EOF
bindPort = 7000

auth.method = "token"
auth.token = "${FRP_TOKEN}"

allowPorts = [
  { start = 9001, end = 9099 }
]

webServer.addr = "127.0.0.1"
webServer.port = 7500
webServer.user = "${DASHBOARD_USER}"
webServer.password = "${DASHBOARD_PASSWORD}"
EOF
sudo chmod 600 /etc/frp/frps.toml

创建 systemd 服务:

sudo tee /etc/systemd/system/frps.service > /dev/null <<'EOF'
[Unit]
Description=FRP Server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/frps -c /etc/frp/frps.toml
Restart=always
RestartSec=3
LimitNOFILE=1048576

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now frps
sudo systemctl status frps --no-pager

验证:

ss -lntp | grep -E '7000|7500'
sudo journalctl -u frps -n 80 --no-pager

三、本地 Debian 部署 frpc

本地 Debian 安装 frpc 后,创建配置:

sudo mkdir -p /etc/frp
sudo nano /etc/frp/frpc.toml

基础模板:

serverAddr = "<CLOUD_SERVER_IP>"
serverPort = 7000

auth.method = "token"
auth.token = "<FRP_TOKEN>"

创建 systemd:

sudo tee /etc/systemd/system/frpc.service > /dev/null <<'EOF'
[Unit]
Description=FRP Client
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/frpc -c /etc/frp/frpc.toml
Restart=always
RestartSec=3
LimitNOFILE=1048576

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now frpc
sudo systemctl status frpc --no-pager

四、灰云模式签发 Let’s Encrypt 证书

这里采用“DNS 灰云 + Let’s Encrypt + 源站 Nginx”的方式。灰云表示 DNS 只解析到云服务器公网 IP,不经过 CDN 代理。这样证书验证和 TLS 握手都直接发生在浏览器和云服务器之间,链路最容易排查。

DNS 先按下面方式配置:

Type: A
Name: @
Content: <CLOUD_SERVER_IP>
Proxy: DNS only

Type: A
Name: app1
Content: <CLOUD_SERVER_IP>
Proxy: DNS only

安装证书工具:

sudo apt update
sudo apt install -y certbot python3-certbot-nginx

准备 ACME 验证目录:

sudo install -d -m 755 /var/www/letsencrypt/.well-known/acme-challenge
echo ok | sudo tee /var/www/letsencrypt/.well-known/acme-challenge/test

先配置一个只处理 HTTP 验证和跳转的 Nginx 文件:

sudo tee /etc/nginx/conf.d/00-acme-http.conf > /dev/null <<'EOF'
server {
    listen 80;
    server_name _;

    location ^~ /.well-known/acme-challenge/ {
        root /var/www/letsencrypt;
        default_type text/plain;
        try_files $uri =404;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}
EOF

sudo nginx -t
sudo systemctl reload nginx

本机验证:

curl http://127.0.0.1/.well-known/acme-challenge/test -H "Host: <ROOT_DOMAIN>"
curl http://127.0.0.1/.well-known/acme-challenge/test -H "Host: app1.<ROOT_DOMAIN>"

公网验证:

curl http://<ROOT_DOMAIN>/.well-known/acme-challenge/test
curl http://app1.<ROOT_DOMAIN>/.well-known/acme-challenge/test

确认都返回 ok 后签发证书:

sudo certbot certonly --webroot \
  -w /var/www/letsencrypt \
  -d <ROOT_DOMAIN> \
  -d www.<ROOT_DOMAIN> \
  -d app1.<ROOT_DOMAIN>

如果后续新增子域名,再单独扩展证书:

sudo certbot certonly --webroot \
  -w /var/www/letsencrypt \
  --expand \
  -d <ROOT_DOMAIN> \
  -d www.<ROOT_DOMAIN> \
  -d app1.<ROOT_DOMAIN> \
  -d app2.<ROOT_DOMAIN>

证书路径通常是:

/etc/letsencrypt/live/<ROOT_DOMAIN>/fullchain.pem
/etc/letsencrypt/live/<ROOT_DOMAIN>/privkey.pem

验证证书:

openssl s_client -connect 127.0.0.1:443 -servername app1.<ROOT_DOMAIN> </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates

自动续期一般由 certbot timer 处理,检查:

systemctl list-timers | grep certbot
sudo certbot renew --dry-run

五、云服务器 Nginx 入口配置

Nginx 的职责是把公网 HTTPS 域名转发到云服务器本机的 FRP 端口,例如:

https://app1.<ROOT_DOMAIN>
  -> Nginx 443
  -> http://127.0.0.1:9001
  -> frps/frpc
  -> 本地 Debian 127.0.0.1:<local-port>

先创建 WebSocket 支持片段:

sudo tee /etc/nginx/conf.d/00-proxy-common.conf > /dev/null <<'EOF'
map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}
EOF

创建第一个应用入口:

sudo tee /etc/nginx/conf.d/app1.conf > /dev/null <<'EOF'
server {
    listen 443 ssl http2;
    server_name app1.<ROOT_DOMAIN>;

    ssl_certificate /etc/letsencrypt/live/<ROOT_DOMAIN>/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/<ROOT_DOMAIN>/privkey.pem;

    client_max_body_size 100m;

    location / {
        proxy_pass http://127.0.0.1:9001;
        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_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}
EOF

上面的 <ROOT_DOMAIN> 不会被 shell 自动替换。如果想直接生成真实配置,可以用变量:

ROOT_DOMAIN="example.com"
SUBDOMAIN="app1"
REMOTE_PORT="9001"

sudo tee "/etc/nginx/conf.d/${SUBDOMAIN}.conf" > /dev/null <<EOF
server {
    listen 443 ssl http2;
    server_name ${SUBDOMAIN}.${ROOT_DOMAIN};

    ssl_certificate /etc/letsencrypt/live/${ROOT_DOMAIN}/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/${ROOT_DOMAIN}/privkey.pem;

    client_max_body_size 100m;

    location / {
        proxy_pass http://127.0.0.1:${REMOTE_PORT};
        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_set_header X-Forwarded-Proto \$scheme;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}
EOF

检查并重载:

sudo nginx -t
sudo systemctl reload nginx
curl -k -I https://127.0.0.1 -H "Host: app1.<ROOT_DOMAIN>"

六、新增服务标准流程

假设要新增一个服务:

服务名:app1
本地端口:127.0.0.1:3000
云端 FRP 端口:9001
访问域名:app1.example.com

1. 本地服务只监听本机

Docker Compose 示例:

services:
  app1:
    image: <APP_IMAGE>
    container_name: app1
    restart: unless-stopped
    ports:
      - "127.0.0.1:3000:3000"
    environment:
      TZ: Asia/Shanghai

systemd 服务示例:

[Unit]
Description=App1
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
WorkingDirectory=/opt/apps/app1
ExecStart=/usr/bin/env bash -lc 'exec <START_COMMAND>'
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

本地验证:

curl -I http://127.0.0.1:3000

2. 本地 frpc 增加端口映射

编辑:

sudo nano /etc/frp/frpc.toml

追加:

[[proxies]]
name = "app1-9001"
type = "tcp"
localIP = "127.0.0.1"
localPort = 3000
remotePort = 9001

重启:

sudo systemctl restart frpc
sudo journalctl -u frpc -n 80 --no-pager

3. 云服务器确认远端端口

ss -lntp | grep ':9001'
curl -I http://127.0.0.1:9001

如果没有监听,检查:

sudo journalctl -u frps -n 80 --no-pager
grep -n "allowPorts" /etc/frp/frps.toml

4. 云服务器 Nginx 增加子域名

创建:

sudo nano /etc/nginx/conf.d/app1.conf

普通 HTTP 应用模板:

server {
    listen 443 ssl http2;
    server_name app1.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    client_max_body_size 100m;

    location / {
        proxy_pass http://127.0.0.1:9001;
        proxy_http_version 1.1;
        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_read_timeout 300s;
    }
}

需要 WebSocket 的应用使用:

server {
    listen 443 ssl http2;
    server_name app1.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    client_max_body_size 100m;

    location / {
        proxy_pass http://127.0.0.1:9001;
        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_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

重载:

sudo nginx -t
sudo systemctl reload nginx

5. DNS 增加记录

在 DNS 服务商添加:

Type: A
Name: app1
Content: <CLOUD_SERVER_IP>
Proxy: 按需开启

如果使用代理 CDN,先确认源站 HTTPS 正常,再开启代理。

6. 访问验证

云服务器本机:

curl -I http://127.0.0.1:9001
curl -k -I https://127.0.0.1 -H "Host: app1.example.com"

公网:

curl -I https://app1.example.com

七、新增服务编号建议

可以按端口段管理:

9001-9019  Web 应用
9020-9039  API 服务
9040-9059  自动化工具
9060-9079  文件与同步服务
9080-9099  临时测试服务

维护一张本地表即可:

服务名 | 本地端口 | 云端端口 | 域名 | 部署目录 | 是否需要 WebSocket | 是否加入日报

八、安全检查清单

新增服务后执行:

sudo nginx -t
sudo systemctl status frps --no-pager
sudo systemctl status frpc --no-pager
ss -lntup

检查项:

  • 云安全组只开放 80/443 给公网。
  • 9001-9099 不在云安全组公网开放。
  • FRP dashboard 只监听 127.0.0.1:7500
  • 本地服务只监听 127.0.0.1:<local-port>
  • Nginx 配置不包含真实密码、token、cookie。
  • 项目 .env 不进入 Git。
  • 新服务加入健康检查和日报白名单。

九、故障排查顺序

访问失败时按链路从内到外检查:

# 本地 Debian
curl -I http://127.0.0.1:<local-port>
sudo systemctl status frpc --no-pager
sudo journalctl -u frpc -n 80 --no-pager

# 云服务器
ss -lntp | grep ':<remote-port>'
curl -I http://127.0.0.1:<remote-port>
sudo nginx -t
sudo systemctl status nginx --no-pager
sudo journalctl -u frps -n 80 --no-pager

# 公网
curl -I https://<subdomain>

常见判断:

本地端口不通:应用没启动或只监听了错误地址
云端 remotePort 不监听:frpc 未连接或 frps allowPorts 未放行
云端 remotePort 通但域名 502:Nginx proxy_pass 或服务端口错误
域名证书错误:证书路径、DNS、CDN SSL 模式或 SNI 配置问题
公网超时:安全组、防火墙、DNS 或 CDN 代理状态问题

十、总结

这套框架把公网入口和应用运行分开:

云服务器:DNS 入口、Nginx、TLS、frps、安全边界
本地 Debian:Docker、systemd、业务数据、应用计算

后续新增服务只需要重复:

本地启动服务
frpc 增加映射
云 Nginx 增加子域名
DNS 指向云服务器
安全组保持只开放 80/443
加入监控与工作笔记

这样可以在不暴露本地网络和真实业务端口的前提下,持续扩展个人云服务。

评论

Giscus 评论尚未配置。填写 GitHub Discussions 的仓库和分类 ID 后,这里会显示评论区。