2026/5/28
个人云服务与本地 Debian 服务器网络框架
记录一套通用的个人云网络框架:云服务器作为公网入口,本地 Debian 承担应用服务,通过 FRP 和 Nginx 安全接入。文中只保留可复用的网络搭建和新增服务模板,不包含真实域名、IP、账号、密钥和业务数据。
本文目录
这篇只记录网络框架,不记录具体业务账号、真实域名、公网 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 只保存在服务器本地配置中。
端口规划
| 类型 | 示例端口 | 说明 |
|---|---|---|
| 云 Nginx | 80, 443 | 统一公网入口 |
| FRP 控制端口 | 7000 | frpc 连接 frps |
| FRP Dashboard | 7500 | 仅监听 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 后,这里会显示评论区。