阿雷的工作流头像
星空组网实战:用 Python 部署一个远程 Ubuntu 状态页封面图

星空组网实战:用 Python 部署一个远程 Ubuntu 状态页

有一台 Ubuntu 主机,人在外面时想看它是否还在运行、磁盘还剩多少空间,最直接的练习就是做一张只读状态页,再通过星空组网地址访问它。本文从官网登录开始,带零基础读者完成组网确认、代码创建、Ubuntu 部署和跨设备验证。页面只展示时间、运行时长和根分区剩余空间,不需要安装第三方 Python 包。

1. 登录官网,确认两端已经入网

打开星空组网官网,进入登录页,选择账号登录。登录后先看管理平台首页的“组网状态”:这次操作时,两台设备都显示在线,页面提示组网成功。已有账号和设备的读者可直接进入下一步。
在这里插入图片描述
在这里插入图片描述

如果你还没有把电脑和 Ubuntu 加入同一个组网,先按官方设备与成员管理和 Linux 客户端说明完成安装与登录。官方文档将“成员账号”和设备关联:需要长期同时在线的设备,分别使用成员账号。本次演示沿用了已经在线的设备,没有新建成员账号。

2. 在 Ubuntu 上确认环境和组网 IP

登录 Ubuntu 终端,先执行:

cat /etc/os-release | head -n 3
python3 --version
ip -4 addr

我实际使用的是 Ubuntu 22.04 LTS、Python 3.10.12。ip -4 addr 输出中,StarVPN 接口的地址是 192.168.188.5/24。这里的 192.168.188.5 就是后面启动服务时要绑定的地址;你的设备可能不同。若找不到组网 IP,先检查客户端是否在线,不要直接用公网 IP 代替。

在这里插入图片描述

3. 创建状态页项目

在 Ubuntu 上建一个演示目录:

mkdir -p ~/starvpn-status-demo-20260925
cd ~/starvpn-status-demo-20260925
nano server.py

把下面的完整代码粘贴进 server.py,保存退出。它使用 Python 标准库读取 /proc/uptime、根分区磁盘信息和当前时间,再由内置 HTTP 服务返回页面。/proc/uptime 是 Linux 提供的数据文件,因此这份示例应在 Ubuntu 上运行。

"""A tiny read-only Ubuntu status page for a StarVPN demo."""

import argparse
import ipaddress
import shutil
from datetime import datetime
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from string import Template


PAGE = Template("""<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Ubuntu 状态卡片</title>
  <style>
    * { box-sizing: border-box; }
    body { margin: 0; min-height: 100vh; display: grid; place-items: center;
      background: #eef3f8; color: #172b4d; font: 16px/1.6 system-ui, sans-serif; }
    main { width: min(900px, calc(100% - 40px)); padding: 42px 0; }
    .eyebrow { color: #2771b8; font-size: 14px; font-weight: 700; letter-spacing: .08em; }
    h1 { margin: 6px 0; font-size: clamp(32px, 5vw, 48px); }
    .intro { margin: 0 0 30px; color: #58708d; }
    .cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
    article { min-height: 170px; padding: 24px; border: 1px solid #dce5f0;
      border-radius: 18px; background: #fff; box-shadow: 0 12px 30px #20436a0c; }
    article h2 { margin: 0 0 18px; color: #58708d; font-size: 15px; font-weight: 600; }
    strong { display: block; font-size: 25px; line-height: 1.3; }
    small { display: block; margin-top: 8px; color: #6d8097; }
    footer { margin-top: 22px; color: #6d8097; font-size: 14px; }
    @media (max-width: 700px) { .cards { grid-template-columns: 1fr; } article { min-height: 0; } }
  </style>
</head>
<body>
<main>
  <div class="eyebrow">STARVPN · 远程查看</div>
  <h1>Ubuntu 状态卡片</h1>
  <p class="intro">打开组网地址,即可查看这台主机的实时状态。</p>
  <section class="cards" aria-label="主机状态">
    <article><h2>当前时间</h2><strong>$now</strong><small>以 Ubuntu 主机时区为准</small></article>
    <article><h2>已运行</h2><strong>$uptime</strong><small>系统自上次启动以来</small></article>
    <article><h2>根分区剩余</h2><strong>$free_gib GiB</strong><small>总容量 $total_gib GiB</small></article>
  </section>
  <footer>刷新页面可获取最新数据 · 本页仅用于组网内演示</footer>
</main>
</body>
</html>
""")


def status_page() -> bytes:
    with open("/proc/uptime", encoding="ascii") as uptime_file:
        seconds = int(float(uptime_file.read().split()[0]))
    days, remainder = divmod(seconds, 86400)
    hours, minutes = divmod(remainder, 3600)
    minutes //= 60
    disk = shutil.disk_usage("/")
    page = PAGE.substitute(
        now=datetime.now().astimezone().strftime("%Y-%m-%d %H:%M:%S %z"),
        uptime=f"{days} 天 {hours} 小时 {minutes} 分钟",
        free_gib=f"{disk.free / 1024**3:.1f}",
        total_gib=f"{disk.total / 1024**3:.1f}",
    )
    return page.encode("utf-8")


class Handler(BaseHTTPRequestHandler):
    def do_GET(self) -> None:
        if self.path != "/":
            self.send_error(404, "Not Found")
            return
        try:
            content = status_page()
        except (OSError, ValueError) as exc:
            self.log_error("读取主机状态失败: %s", exc)
            self.send_error(500, "Internal Server Error")
            return
        self.send_response(200)
        self.send_header("Content-Type", "text/html; charset=utf-8")
        self.send_header("Cache-Control", "no-store")
        self.send_header("X-Content-Type-Options", "nosniff")
        self.send_header("Content-Length", str(len(content)))
        self.end_headers()
        self.wfile.write(content)


def main() -> None:
    parser = argparse.ArgumentParser(description="只在指定组网 IP 上展示 Ubuntu 状态")
    parser.add_argument("bind_ip", help="Ubuntu 客户端显示的组网 IP")
    parser.add_argument("port", type=int, help="监听端口,例如 18097")
    args = parser.parse_args()
    try:
        ipaddress.IPv4Address(args.bind_ip)
    except ipaddress.AddressValueError as exc:
        parser.error(f"无效的 IPv4 地址: {exc}")
    if not 1 <= args.port <= 65535:
        parser.error("端口必须在 1 到 65535 之间")
    try:
        with ThreadingHTTPServer((args.bind_ip, args.port), Handler) as server:
            print(f"状态页已启动:http://{args.bind_ip}:{args.port}/", flush=True)
            server.serve_forever()
    except OSError as exc:
        parser.exit(1, f"启动失败: {exc}n")
    except KeyboardInterrupt:
        print("n服务已停止")


if __name__ == "__main__":
    main()

代码只有一个页面 /,其他路径返回 404;读取状态失败时返回 500。页面数据每次请求时重新读取,刷新浏览器即可更新。服务只监听命令中指定的 IPv4 地址,示例里使用组网 IP;不要把它替换为 0.0.0.0。

4. 在 Ubuntu 上启动服务

确认端口 18097 没被占用,然后在项目目录执行:

cd ~/starvpn-status-demo-20260925
ss -ltnp | grep ':18097' || true
nohup python3 server.py 192.168.188.5 18097 >run.log 2>&1 &
echo $! >run.pid
ss -ltnp | grep ':18097'
curl -sS -o /dev/null -w 'HTTP %{http_code}n' http://192.168.188.5:18097/

nohup 让服务在关闭当前终端后继续运行,run.pid 记录本次进程号。我的终端中可以看到 192.168.188.5:18097 处于 LISTEN,本机 HTTP 请求返回 200。

在这里插入图片描述

如果端口已被占用,换一个未使用端口,并在后续访问地址中同步修改。启动失败时先看 cat run.log,常见原因是 IP 填错或端口被占用。

5. 从另一台设备访问并验证

让访问端电脑也登录同一星空组网,打开:

http://192.168.188.5:18097/

我在另一台已入网电脑的浏览器中看到了 Ubuntu 的当前时间、已运行时长和根分区剩余空间。地址栏是 Ubuntu 的组网 IP,说明这次访问用的是组网地址。下图是实际打开的页面,数据会随时间变化。

在这里插入图片描述

浏览器地址栏可能提示“不安全”,因为这个演示应用使用普通 HTTP,没有为网页配置 HTTPS。同一组网里能够访问这台主机的成员也可能看到页面,所以它只适合作为只读练习;不要在页面展示敏感数据,也不要为了访问它而开放公网端口。

如果页面打不开,按顺序检查:两端客户端是否在线、Ubuntu 的组网 IP 是否填对、ss -ltnp 是否显示服务监听、Ubuntu 本机 curl 是否能拿到 200。这些都正常后,再检查主机防火墙对组网接口的规则。

6. 演示结束后停止和清理

临时项目不需要一直运行。先在项目目录停止本次进程,再确认端口不再监听:

cd ~/starvpn-status-demo-20260925
ps -fp "$(cat run.pid)"
kill "$(cat run.pid)"
ss -ltnp | grep ':18097' || echo '演示服务已停止'

确认目录里只有本教程创建的文件后,可删除这个演示目录:

cd ~
rm -r ~/starvpn-status-demo-20260925

这次实操已按上述顺序停止并清理,星空组网账号和原有设备连接未删除。下次要复现时,重新放入 server.py 并启动即可。

小结

整个流程是:登录管理平台确认组网在线 → 在 Ubuntu 找到组网 IP → 编写只读状态页 → 绑定组网 IP 启动 → 从另一台设备打开验证。这个小项目的价值在于把“组网成功”落到一个可访问的服务上,同时避免了数据库、Docker 和额外依赖,适合作为第一次动手练习。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/iymei4986533030/article/details/166688858

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--