ZeroTier虚拟局域网内搭建DNS服务器
一、搭建本地 DNS 服务器(适合多设备共享)
如果需要让局域网内所有设备(电脑、手机、智能家居等)都能使用自定义域名,推荐搭建本地 DNS 服务器,统一管理域名解析。
推荐工具:dnsmasq
(轻量易用,适合 Linux 服务器)
步骤 1:配置 dnsmasq
解析虚拟局域网内的域名
-
安装 dnsmasq(以 Ubuntu 为例):
sudo apt update && sudo apt install dnsmasq -y
-
配置自定义域名解析:编辑配置文件:
sudo nano /etc/dnsmasq.conf
添加以下内容(根据你的虚拟局域网节点信息修改)
-
# 监听虚拟局域网的 IP(确保 dnsmasq 能被虚拟局域网内的其他节点访问) listen-address=127.0.0.1,10.144.0.10 # 10.144.0.10 是 dnsmasq 所在服务器的虚拟 IP# 允许所有设备查询(虚拟局域网内信任环境下可用) interface=ztxxxxxx # 替换为 ZeroTier 在服务器上的虚拟网卡名称(可通过 `ip addr` 查看,如 zt0) bind-interfaces # 仅绑定到指定网卡,增强安全性# 自定义域名映射(虚拟局域网内的节点) address=/nas.vlan/10.144.0.20 # 例如:nas.vlan → 节点1的虚拟 IP address=/server.vlan/10.144.0.30 # 例如:server.vlan → 节点2的虚拟 IP address=/printer.vlan/10.144.0.40 # 例如:printer.vlan → 节点3的虚拟 IP# 可选:设置上游 DNS 服务器(用于解析虚拟局域网外的公网域名,如 www.baidu.com) server=8.8.8.8 # 谷歌 DNS server=223.5.5.5 # 阿里云 DNS
- 关键配置说明:
listen-address
必须包含dnsmasq
服务器的虚拟 IP,否则其他节点无法访问该 DNS 服务。interface
指定 ZeroTier 的虚拟网卡(如zt0
),确保 DNS 服务仅在虚拟局域网内提供。address
规则中的域名可自定义(如.vlan
后缀),方便区分虚拟局域网内资源。
-
重启 dnsmasq 生效:
sudo systemctl restart dnsmasq
可能存在的问题:dnsmasq
服务启动失败,原因是 端口 53 被占用(failed to create listening socket for port 53: Address already in use
)。端口 53 是 DNS 服务的默认端口,可能系统中已有其他程序(如 systemd-resolved
)占用了该端口。以下是解决步骤:
步骤 1:查找占用端口 53 的进程
执行命令,查看哪个进程在使用端口 53:
sudo netstat -tulpn | grep :53
或者用 lsof
命令(如果没安装,先执行 sudo apt install lsof
):
sudo lsof -i :53
步骤 2:停止占用端口的服务
假设是 systemd-resolved
占用(Ubuntu 常见情况),执行以下命令停止并禁用该服务:
sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved
如果是其他服务(如 bind9
等),则执行 sudo systemctl stop 服务名
和 sudo systemctl disable 服务名
来停止禁用。
步骤 3:重启 dnsmasq
服务
sudo systemctl restart dnsmasq
步骤 4:验证 dnsmasq
是否正常运行
执行命令查看 dnsmasq
服务状态:
sudo systemctl status dnsmasq
如果显示 active (running)
,则说明 dnsmasq
已成功启动。
步骤 2:让虚拟局域网内的所有节点使用该 dnsmasq
服务器作为 DNS
需要为虚拟局域网内的每台节点配置 DNS 服务器地址为 dnsmasq
所在服务器的虚拟 IP(即 10.144.0.10
),确保节点的 DNS 请求会被发送到 dnsmasq
处理。
方法 1:在 ZeroTier 控制器中统一配置(推荐,批量生效)
通过 ZeroTier 私有控制器(即你搭建的 http://ip:3443
管理界面)为虚拟局域网内的所有节点分配 DNS 服务器,无需逐个节点手动设置:
- 登录 ZeroTier 控制器管理界面,进入你的虚拟局域网(如网络 ID 为
8056c2e21cxxxxxx
)。 - 进入 “设置” 或 “网络配置” 页面,找到 DNS 配置 选项(通常在 “Advanced” 或 “DHCP” 部分):
- 设置 DNS 服务器 为
10.144.0.10
(dnsmasq
服务器的虚拟 IP)。 - 可选:设置 DNS 搜索域(如
vlan
,这样访问nas
即可自动补全为nas.vlan
)。
- 设置 DNS 服务器 为
- 保存配置后,虚拟局域网内的节点会自动获取该 DNS 服务器(可能需要重启节点的 ZeroTier 服务生效)。
方法 2:手动为单个节点配置 DNS(适合少量节点)
如果无法通过控制器统一配置,可在每个节点上手动指定 DNS 服务器为 10.144.0.10
:
-
Linux 节点:编辑网络配置文件(以 Ubuntu 为例):
sudo nano /etc/systemd/resolved.conf
修改
DNS
字段:[Resolve] DNS=10.144.0.10 # dnsmasq 服务器的虚拟 IP
重启解析服务:
sudo systemctl restart systemd-resolved
-
Windows 节点:进入 “网络连接”→ 找到 ZeroTier 虚拟网卡 → 右键 “属性”→ 双击 “Internet 协议版本 4 (TCP/IPv4)”→ 设置 “首选 DNS 服务器” 为
10.144.0.10
。 -
macOS 节点:进入 “系统设置”→“网络”→ 选择 ZeroTier 虚拟网卡 →“详细信息”→“DNS”→ 点击 “+” 添加
10.144.0.10
。
步骤 3:测试虚拟局域网内的域名访问
在虚拟局域网内的任意节点上,通过以下方式验证域名解析是否生效:
-
使用
ping
或nslookup
测试:# 测试自定义域名解析 ping nas.vlan # 应能 ping 通 10.144.0.20 nslookup server.vlan # 应返回 10.144.0.30# 测试公网域名解析(验证上游 DNS 是否生效) nslookup www.baidu.com # 应返回百度的公网 IP
-
通过浏览器或应用访问:若节点上运行着 Web 服务(如
nas.vlan
的 80 端口),在浏览器中输入http://nas.vlan
应能访问到对应服务。
注意事项
执行后,防火墙会允许虚拟局域网内的其他节点通过 zt0
网卡访问这台服务器的 53 端口(dnsmasq
的 DNS 服务端口)。
如果需要同时允许 TCP 协议(某些 DNS 场景可能用到 TCP,如长查询),可以再补充一条命令:
sudo ufw allow in on zt0 to any port 53 proto tcp
- 防火墙设置:确保
dnsmasq
所在服务器的53/udp
端口(DNS 服务端口)在虚拟局域网中放行(可通过ufw allow in on zt0 to any port 53/udp
配置)。 -
sudo ufw allow in on zt0 to any port 53 proto udp
zt0
替换为你的 ZeroTier 虚拟网卡名称。port 53 proto udp
表示允许 53 端口的 UDP 协议(DNS 服务默认用 UDP 协议)。- 域名冲突:避免自定义域名与公网域名重复(如不要用
baidu.vlan
),防止解析混乱。 - 节点 IP 固定:若虚拟局域网内的节点 IP 是动态分配的,建议在 ZeroTier 控制器中为节点设置 “固定 IP”(Static IP),避免 IP 变化导致域名映射失效。
可能存在的问题:DNS设置了10.11.12.35,但是依然显示
服务器: 192.168.0.1
Address: 192.168.0.1
出现这种情况,说明 Windows 节点实际使用的 DNS 服务器仍为路由器的 192.168.0.1
,而非你设置的 10.11.12.35
(dnsmasq
服务器的虚拟 IP)。问题根源在于 DNS 配置未真正生效,可能是 ZeroTier 虚拟网卡的 DNS 设置被其他网络优先级覆盖,或配置步骤有误。以下是具体解决方法:
尝试通过 PowerShell 命令 来调整网络适配器优先级,步骤如下:
步骤 1:查看网卡名称和当前优先级
以管理员身份打开 PowerShell(右键点击开始菜单,选择 “Windows PowerShell(管理员)”),执行以下命令,列出所有网络适配器及其优先级(InterfaceMetric
数值越小,优先级越高):
Get-NetIPInterface | Where-Object {$_.InterfaceAlias -like "*"} | Select-Object InterfaceAlias, InterfaceMetric
执行后会看到类似输出(找到 ZeroTier 虚拟网卡、以太网、WLAN 对应的条目):
InterfaceAlias InterfaceMetric
-------------- ---------------
以太网 25
WLAN 35
ZeroTier Virtual Port #2 45 # 假设 ZeroTier 网卡当前优先级较低
步骤 2:降低其他网卡的优先级(提升 ZeroTier 优先级)
假设 以太网
的 InterfaceMetric
是 25
,WLAN
是 35
,而 ZeroTier 是 45
。我们需要提高其他网卡的数值(如设为 50
)。
-
提高其他网卡的
InterfaceMetric
(以以太网
为例): -
Set-NetIPInterface -InterfaceAlias "以太网" -InterfaceMetric 50
对
WLAN
等其他网卡重复此操作,确保它们的InterfaceMetric
大于45
。
步骤 3:刷新 DNS 缓存并测试
-
刷新 Windows DNS 缓存:
ipconfig /flushdns
-
再次测试
nslookup
,强制使用 ZeroTier 网卡的 DNS:nslookup server.symboland 10.11.12.35
- 如果返回
server.symboland
对应的 IP,说明dnsmasq
解析正常,只是 Windows 优先级配置问题。 - 如果仍返回错误,需检查
dnsmasq
服务器是否运行正常,以及域名server.symboland
是否在dnsmasq
中正确配置。
- 如果返回
步骤 4:最终验证
执行普通 nslookup
,确认 Windows 自动选择 ZeroTier 的 DNS:
nslookup server.symboland
如果输出的「服务器」是 10.11.12.35
,且解析到正确 IP,说明优先级调整成功。
通过以上配置,ZeroTier 虚拟局域网内的所有节点即可通过自定义域名互相访问,无需记忆复杂的虚拟 IP。
扩展:在宝塔面板中部署 DNS 域名注册系统
1. 准备环境
1.1 确保 dnsmasq 正常运行
在终端中检查:
bash
systemctl status dnsmasq # 如果未安装,使用以下命令安装 # apt update && apt install dnsmasq -y
1.2 检查 dnsmasq 配置
确保 /etc/dnsmasq.conf
包含:
bash
# 加载自定义配置目录 conf-dir=/etc/dnsmasq.d/,*.conf # 本地域名后缀 local=/symboland/ domain=symboland
2. 宝塔面板部署步骤
2.1 创建网站
-
登录宝塔面板
-
点击「网站」→「添加站点」
-
填写站点信息:
-
域名:
nas.symboland
-
根目录:
/www/wwwroot/nas.symboland
-
PHP版本:选择纯静态(因为我们将使用Python)
-
其他保持默认
-
2.2 上传文件
在 /www/wwwroot/nas.symboland
目录下创建以下文件:
文件结构:
text
/www/wwwroot/nas.symboland/ ├── app.py # Flask 应用 ├── config.py # 配置文件 ├── requirements.txt # Python依赖 ├── static/ │ └── index.html # 前端页面 ├── logs/ # 日志目录 └── dnsmasq/ # DNS配置目录(软链接)
2.2.1 创建 Flask 应用 (app.py
)
python
#!/usr/bin/env python3 """ DNS 域名注册系统 - 宝塔面板版本 """from flask import Flask, request, jsonify, send_file import subprocess import os import re from pathlib import Path import logging from datetime import datetimeapp = Flask(__name__, static_folder='static', static_url_path='')# 配置 - 宝塔面板适配 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) DNSMASQ_CONF_DIR = "/etc/dnsmasq.d" CUSTOM_DNS_FILE = os.path.join(DNSMASQ_CONF_DIR, "custom_domains.conf") DOMAIN_SUFFIX = "symboland" LOG_FILE = os.path.join(BASE_DIR, "logs", "dns_register.log")# 创建必要的目录 os.makedirs(os.path.join(BASE_DIR, "logs"), exist_ok=True)# 设置日志 logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(levelname)s - %(message)s',handlers=[logging.FileHandler(LOG_FILE),logging.StreamHandler()] )def validate_domain(domain):"""验证域名格式"""if not domain or len(domain) < 2 or len(domain) > 63:return False# 只允许字母、数字和连字符if not re.match(r'^[a-zA-Z0-9-]+$', domain):return False# 不能以连字符开头或结尾if domain.startswith('-') or domain.endswith('-'):return Falsereturn Truedef get_client_ip():"""获取客户端真实IP - 适配宝塔Nginx"""if request.headers.get('X-Forwarded-For'):ip = request.headers.get('X-Forwarded-For').split(',')[0]elif request.headers.get('X-Real-IP'):ip = request.headers.get('X-Real-IP')else:ip = request.remote_addrreturn ipdef read_existing_domains():"""读取已存在的域名配置"""domains = {}if os.path.exists(CUSTOM_DNS_FILE):with open(CUSTOM_DNS_FILE, 'r', encoding='utf-8') as f:for line in f:line = line.strip()if line and not line.startswith('#'):# 解析 dnsmasq 配置格式if line.startswith('address=/'):parts = line.split('/')if len(parts) >= 4:domain = parts[2]ip = parts[3]domains[domain] = ipreturn domainsdef write_domain_config(domain, ip):"""写入域名配置到dnsmasq配置文件"""try:# 确保配置目录存在Path(DNSMASQ_CONF_DIR).mkdir(parents=True, exist_ok=True)# 读取现有配置existing_domains = read_existing_domains()# 更新或添加新域名full_domain = f"{domain}.{DOMAIN_SUFFIX}"existing_domains[full_domain] = ip# 写入新配置with open(CUSTOM_DNS_FILE, 'w', encoding='utf-8') as f:f.write("# 自动生成的域名配置 - 请勿手动修改\n")f.write(f"# 生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")f.write(f"# 总记录数: {len(existing_domains)}\n\n")for dom, dom_ip in existing_domains.items():f.write(f"address=/{dom}/{dom_ip}\n")logging.info(f"域名配置已更新: {full_domain} -> {ip}")return Trueexcept Exception as e:logging.error(f"写入配置文件失败: {e}")return Falsedef reload_dnsmasq():"""重新加载dnsmasq配置"""try:result = subprocess.run(['systemctl', 'reload', 'dnsmasq'],capture_output=True,text=True,timeout=10)if result.returncode == 0:logging.info("dnsmasq 重新加载成功")return Trueelse:logging.error(f"dnsmasq 重新加载失败: {result.stderr}")return Falseexcept subprocess.TimeoutExpired:logging.error("dnsmasq 重新加载超时")return Falseexcept Exception as e:logging.error(f"重新加载dnsmasq时发生错误: {e}")return False@app.route('/') def index():"""主页面"""return send_file('static/index.html')@app.route('/api/get-ip') def get_ip():"""获取客户端IP API"""client_ip = get_client_ip()return jsonify({'ip': client_ip})@app.route('/api/register-domain', methods=['POST']) def register_domain():"""注册域名API"""try:data = request.get_json()if not data:return jsonify({'success': False,'message': '无效的请求数据'}), 400domain = data.get('domain', '').strip().lower()client_ip = get_client_ip()# 验证域名if not validate_domain(domain):return jsonify({'success': False,'message': '域名格式无效。只能使用字母、数字和连字符,长度2-63个字符'}), 400# 检查域名是否已存在existing_domains = read_existing_domains()full_domain = f"{domain}.{DOMAIN_SUFFIX}"if full_domain in existing_domains:existing_ip = existing_domains[full_domain]if existing_ip == client_ip:return jsonify({'success': True,'message': f'域名 {full_domain} 已指向您的IP'})else:return jsonify({'success': False,'message': f'域名 {full_domain} 已被IP {existing_ip} 使用'}), 400# 写入配置if not write_domain_config(domain, client_ip):return jsonify({'success': False,'message': '配置文件更新失败'}), 500# 重新加载dnsmasqif not reload_dnsmasq():return jsonify({'success': False,'message': 'DNS服务重载失败,请联系管理员'}), 500# 记录注册日志logging.info(f"域名注册成功: {full_domain} -> {client_ip}")return jsonify({'success': True,'message': f'域名 {full_domain} 注册成功!现在可以通过该域名访问您的设备。','domain': full_domain,'ip': client_ip})except Exception as e:logging.error(f"域名注册过程出错: {e}")return jsonify({'success': False,'message': '服务器内部错误,请稍后重试'}), 500@app.route('/api/domains') def list_domains():"""列出所有已注册的域名"""domains = read_existing_domains()return jsonify({'success': True,'domains': domains,'count': len(domains)})@app.route('/api/status') def system_status():"""系统状态检查"""try:# 检查dnsmasq服务状态dnsmasq_status = subprocess.run(['systemctl', 'is-active', 'dnsmasq'],capture_output=True,text=True)domains_count = len(read_existing_domains())return jsonify({'success': True,'dnsmasq_status': dnsmasq_status.stdout.strip(),'domains_count': domains_count,'config_file': CUSTOM_DNS_FILE,'config_exists': os.path.exists(CUSTOM_DNS_FILE)})except Exception as e:return jsonify({'success': False,'error': str(e)}), 500if __name__ == '__main__':app.run(host='0.0.0.0', port=5000, debug=False)
2.2.2 创建前端页面 (static/index.html
)
html
<!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>NAS 域名注册系统 - 宝塔面板版</title><style>* {margin: 0;padding: 0;box-sizing: border-box;}body {font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);min-height: 100vh;display: flex;justify-content: center;align-items: center;padding: 20px;}.container {background: white;border-radius: 15px;box-shadow: 0 20px 40px rgba(0,0,0,0.1);padding: 40px;width: 100%;max-width: 500px;}.header {text-align: center;margin-bottom: 30px;}.header h1 {color: #333;margin-bottom: 10px;font-size: 28px;}.header p {color: #666;font-size: 16px;}.form-group {margin-bottom: 20px;}label {display: block;margin-bottom: 8px;color: #333;font-weight: 500;}input[type="text"] {width: 100%;padding: 12px 15px;border: 2px solid #e1e5e9;border-radius: 8px;font-size: 16px;transition: all 0.3s ease;}input[type="text"]:focus {outline: none;border-color: #667eea;box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);}.domain-suffix {color: #667eea;font-weight: 600;}.btn {width: 100%;padding: 14px;background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);color: white;border: none;border-radius: 8px;font-size: 16px;font-weight: 600;cursor: pointer;transition: all 0.3s ease;}.btn:hover {transform: translateY(-2px);box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);}.btn:active {transform: translateY(0);}.message {margin-top: 20px;padding: 12px;border-radius: 8px;text-align: center;display: none;}.success {background: #d4edda;color: #155724;border: 1px solid #c3e6cb;}.error {background: #f8d7da;color: #721c24;border: 1px solid #f5c6cb;}.info {background: #cce7ff;color: #004085;border: 1px solid #b3d7ff;}.current-ip {background: #f8f9fa;padding: 15px;border-radius: 8px;margin-bottom: 20px;text-align: center;border: 1px solid #e9ecef;}.ip-address {font-weight: 600;color: #667eea;font-size: 18px;}.system-info {background: #fff3cd;padding: 10px 15px;border-radius: 8px;margin-bottom: 20px;border: 1px solid #ffeaa7;font-size: 14px;color: #856404;}</style> </head> <body><div class="container"><div class="header"><h1>🚀 域名注册系统</h1><p>宝塔面板版 - 为您的设备注册专属域名</p></div><div class="system-info">💡 系统提示:请确保您的设备与DNS服务器在同一局域网内</div><div class="current-ip"><div>您的当前 IP 地址:</div><div class="ip-address" id="currentIp">检测中...</div></div><form id="domainForm"><div class="form-group"><label for="domainName">请输入域名:</label><input type="text" id="domainName" placeholder="例如:mydevice" required><div style="margin-top: 5px; color: #666; font-size: 14px;">完整域名:<span id="fullDomain" class="domain-suffix">输入域名.symboland</span></div></div><button type="submit" class="btn">注册域名</button></form><div id="message" class="message"></div><div style="margin-top: 20px; text-align: center;"><button type="button" class="btn" οnclick="checkSystemStatus()" style="background: #6c757d; margin-bottom: 10px;">检查系统状态</button><button type="button" class="btn" οnclick="listDomains()" style="background: #17a2b8;">查看已注册域名</button></div></div><script>// 获取客户端IP地址async function getClientIP() {try {const response = await fetch('/api/get-ip');const data = await response.json();document.getElementById('currentIp').textContent = data.ip;return data.ip;} catch (error) {document.getElementById('currentIp').textContent = '获取失败';return null;}}// 更新完整域名显示function updateFullDomain() {const domainName = document.getElementById('domainName').value;const fullDomainElement = document.getElementById('fullDomain');if (domainName) {fullDomainElement.textContent = domainName + '.symboland';} else {fullDomainElement.textContent = '输入域名.symboland';}}// 显示消息function showMessage(message, type) {const messageElement = document.getElementById('message');messageElement.textContent = message;messageElement.className = 'message ' + type;messageElement.style.display = 'block';// 自动隐藏成功消息if (type === 'success') {setTimeout(() => {messageElement.style.display = 'none';}, 5000);}}// 检查系统状态async function checkSystemStatus() {try {const response = await fetch('/api/status');const data = await response.json();if (data.success) {showMessage(`系统状态正常 | DNS服务: ${data.dnsmasq_status} | 已注册域名: ${data.domains_count}个`, 'info');} else {showMessage('系统状态检查失败', 'error');}} catch (error) {showMessage('系统状态检查失败', 'error');}}// 列出已注册域名async function listDomains() {try {const response = await fetch('/api/domains');const data = await response.json();if (data.success) {const domains = data.domains;if (Object.keys(domains).length === 0) {showMessage('暂无已注册域名', 'info');} else {let domainsList = '已注册域名:\n';for (const [domain, ip] of Object.entries(domains)) {domainsList += `${domain} -> ${ip}\n`;}showMessage(domainsList, 'info');}} else {showMessage('获取域名列表失败', 'error');}} catch (error) {showMessage('获取域名列表失败', 'error');}}// 处理表单提交document.getElementById('domainForm').addEventListener('submit', async function(e) {e.preventDefault();const domainName = document.getElementById('domainName').value.trim();if (!domainName) {showMessage('请输入域名', 'error');return;}// 简单的域名格式验证if (!/^[a-zA-Z0-9-]+$/.test(domainName)) {showMessage('域名只能包含字母、数字和连字符', 'error');return;}if (domainName.length < 2 || domainName.length > 63) {showMessage('域名长度必须在2-63个字符之间', 'error');return;}try {const response = await fetch('/api/register-domain', {method: 'POST',headers: {'Content-Type': 'application/json',},body: JSON.stringify({domain: domainName})});const data = await response.json();if (data.success) {showMessage(`✅ ${data.message}`, 'success');document.getElementById('domainName').value = '';updateFullDomain();} else {showMessage(`❌ ${data.message}`, 'error');}} catch (error) {showMessage('网络错误,请检查连接', 'error');}});// 初始化document.getElementById('domainName').addEventListener('input', updateFullDomain);getClientIP();// 页面加载时检查系统状态setTimeout(checkSystemStatus, 1000);</script> </body> </html>
2.2.3 创建依赖文件 (requirements.txt
)
txt
Flask==2.3.3 gunicorn==21.2.0
2.3 配置 Python 项目
-
在宝塔面板中安装 Python 项目管理器
-
进入「软件商店」
-
搜索「Python 项目管理器」并安装
-
-
添加 Python 项目
-
进入「软件商店」→「Python 项目管理器」
-
点击「添加项目」
-
配置如下:
-
项目名称:
dns-register
-
路径:
/www/wwwroot/nas.symboland
-
Python 版本:选择 3.7+
-
框架:
Flask
-
启动方式:
gunicorn
-
启动文件:
app.py
-
运行用户:
root
(需要修改DNS配置,所以需要root权限) -
端口:
5001
(避免冲突)
-
-
2.4 配置网站反向代理
-
进入「网站」→ 选择
nas.symboland
站点 -
点击「设置」→「反向代理」
-
添加反向代理:
-
代理名称:
python_app
-
目标URL:
http://127.0.0.1:5001
-
2.5 设置权限
在终端中执行:
bash
# 确保应用目录权限 chown -R www:www /www/wwwroot/nas.symboland chmod -R 755 /www/wwwroot/nas.symboland# 创建日志文件并设置权限 touch /www/wwwroot/nas.symboland/logs/dns_register.log chown www:www /www/wwwroot/nas.symboland/logs/dns_register.log chmod 644 /www/wwwroot/nas.symboland/logs/dns_register.log
2.6 重启服务
bash
# 重启 dnsmasq systemctl restart dnsmasq# 在宝塔Python项目管理器中重启项目
3. 验证部署
-
访问系统:在浏览器中打开
http://nas.symboland
-
测试功能:
-
系统应该显示你的IP地址
-
尝试注册一个测试域名
-
检查系统状态
-
-
验证DNS:
bash
nslookup 你注册的域名.symboland ping 你注册的域名.symboland
4. 故障排除
如果遇到问题,检查以下位置:
-
日志文件:
-
应用日志:
/www/wwwroot/nas.symboland/logs/dns_register.log
-
Python项目日志:在宝塔Python项目管理器中查看
-
Nginx日志:在宝塔网站设置中查看
-
-
常见问题:
-
权限问题:确保Python项目以root用户运行
-
端口冲突:检查5001端口是否被占用
-
DNS缓存:重启dnsmasq或清除DNS缓存
-
-
手动测试API:
bash
curl http://127.0.0.1:5001/api/status
这样就完成了在宝塔面板中的部署。系统现在可以通过 http://nas.symboland
访问,用户可以在网页上注册自己的域名了。