你是否曾想过拥有一个运行在云端、拥有干净IP 地址、并且能够利用 Google 强大计算资源的私人浏览器?也许是为了测试网页在不同环境下的表现,或是安全地访问某些网络,又或是仅仅为了“白嫖”一下 Google 的网络带宽。

今天,我们将通过 Google Colab、VNC 和 FRP(一个强大的内网穿透工具),一步步教你如何将一个免费的 Colab 实例,打造成一个可以通过公网随时访问的远程桌面浏览器。

核心技术栈

  • Google Colab: 提供免费的 Linux 计算环境和强大的网络。
  • Xvfb + Fluxbox: 创建一个虚拟的图形桌面环境。
  • x11vnc + noVNC: 将虚拟桌面通过 VNC 协议暴露,并允许通过浏览器直接访问。
  • FRP (Fast Reverse Proxy): 它负责在你的公网服务器和 Colab 实例之间建立一个安全的隧道,让我们能从任何地方访问 Colab 内部的 VNC 服务。也可以使用其他内网穿透方案,比如 cloudflare tunnel。

准备工作

在开始之前,请确保你拥有以下资源:

  1. 一个 Google 账号: 用于访问和运行 Google Colab。
  2. 一台拥有公网 IP 的服务器 (VPS): 用作 FRP 服务端。这台服务器不需要很高的配置,最低配的云服务器即可。
  3. 基本的 Linux 操作知识: 知道如何通过 SSH 连接到你的服务器并执行命令。

第一步:配置你的公网服务器 (frps)

首先,我们需要在你的公网 VPS 上安装并运行 FRP 的服务端 (frps)。

  1. 下载并解压 FRP:
    通过 SSH 登录你的服务器,执行以下命令下载并解压 FRP。你可以从 FRP Releases 页面 找到最新的版本。

    # 注意替换为最新的版本号
    wget https://github.com/fatedier/frp/releases/download/v0.64.0/frp_0.64.0_linux_amd64.tar.gz
    tar -zxvf frp_0.64.0_linux_amd64.tar.gz
    cd frp_0.64.0_linux_amd64
  2. 配置 frps.ini:
    编辑服务端配置文件 frps.ini

    [common]
    bind_port = 7000
    # 设置一个安全的认证 token,Colab 端的配置需要与此保持一致
    token = YOUR_FRP_TOKEN 

    强烈建议YOUR_FRP_TOKEN 替换为一个复杂且唯一的字符串,以确保隧道的安全。

  3. 启动 frps 服务:
    使用 nohup 命令让 frps 在后台持续运行:

    nohup ./frps -c ./frps.ini &
  4. 配置服务器防火墙:
    这是非常关键的一步!请确保你的服务器防火墙(或云服务商的安全组)已经放行了两个端口:

    • FRP 通信端口:7000 (TCP)
    • 远程访问端口:10123 (TCP),这个端口是后面 Colab 连接成功后,我们用来访问浏览器的端口。

第二步:在 Colab 中搭建远程环境

现在,服务端的准备工作已经就绪。打开 Google Colab,创建一个新的 Notebook,然后按顺序粘贴并执行以下代码单元格。

单元格 1: 安装通用依赖

这个单元格负责安装所有必要的软件包,比如浏览器、虚拟桌面等。在一次 Colab 会话中,您只需要运行它一次。

# @title 1. 安装通用依赖 (一次性运行)
!apt-get update
!apt-get install -y xvfb x11vnc fluxbox novnc websockify fonts-wqy-zenhei
print("🔄 正在刷新字体缓存...")
!fc-cache -fv
# 安装Google Chrome
!wget -q https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
!dpkg -i google-chrome-stable_current_amd64.deb
# 尝试修复因安装Chrome可能产生的依赖问题
!apt-get install -f -y

print("\n✅ 所有通用依赖安装完成!")

代码解析:

  • xvfb, fluxbox, x11vnc, novnc: 搭建虚拟桌面和 VNC 服务。
  • fonts-wqy-zenhei: 安装中文字体,防止网页出现乱码(豆腐块)。
  • google-chrome-stable: 我们要远程使用的主角——Chrome 浏览器。

单元格 2: 配置并下载 FRP

这个单元格专门负责处理frp的配置。您需要在这里填写您的服务器信息。它会下载frp并根据您的设置生成配置文件。

# @title 2. 配置并下载 FRP (配置完成后运行一次)

import os

# --- FRP 配置 (请务必修改!) ---
FRP_SERVER_IP   = "YOUR_SERVER_PUBLIC_IP"  # 替换成你公网服务器的IP地址
FRP_TOKEN       = "YOUR_FRP_TOKEN" # 必须和服务端 frps.ini 中的 token 一致
REMOTE_PORT     = 10123            # 你希望通过哪个公网端口访问,确保服务器防火墙已放行
# --------------------------------

# --- 内部服务端口配置 (通常无需修改) ---
NOVNC_LOCAL_PORT = 6080 # noVNC 在Colab内部监听的端口
# --------------------------------

print("🚀 正在下载并配置 frp 客户端...")
# 注意:frpc客户端版本最好与你的frps服务端版本一致或兼容
# 这里使用一个稳定的版本 v0.64.0 作为示例
FRP_VERSION = "0.64.0"
!wget -q "https://github.com/fatedier/frp/releases/download/v{FRP_VERSION}/frp_{FRP_VERSION}_linux_amd64.tar.gz" -O frp.tar.gz
!tar -zxvf frp.tar.gz
FRP_DIR = f"frp_{FRP_VERSION}_linux_amd64"

# 动态创建 frpc.ini 配置文件
frpc_config = f"""
[common]
server_addr = {FRP_SERVER_IP}
server_port = 7000
token = {FRP_TOKEN}
log_file = ./frpc.log
log_level = info

[colab_vnc]
type = tcp
local_ip = 127.0.0.1
local_port = {NOVNC_LOCAL_PORT}
remote_port = {REMOTE_PORT}
"""

with open(f'{FRP_DIR}/frpc.ini', 'w') as f:
    f.write(frpc_config)

print("✅ frp客户端配置完成。")
print(f"✅ 您现在可以运行下一个单元格来启动所有服务了。")
重要提示: 运行前,请务必将 YOUR_SERVER_PUBLIC_IPYOUR_FRP_TOKEN 替换为您自己的真实信息。REMOTE_PORT 必须与您在服务器防火墙上放行的端口一致。

单元格 3: 启动所有服务

这个单元格负责启动浏览器、VNC和frp客户端。您可以反复运行此单元格来启动服务(前提是您需要先运行中止单元格)。

# @title 3. 启动所有服务 (可重复运行)
import os
import subprocess
import time

# 检查FRP是否已配置,避免用户跳过上一步
if 'FRP_DIR' not in globals():
    print("❌错误:请先运行上面的 '配置并下载 FRP' 单元格!")
else:
    # 全局进程列表,用于后续中止
    running_processes = []

    print("🚀 正在启动后台服务...")
    try:
        # 启动 Xvfb (虚拟屏幕)
        os.environ['DISPLAY'] = ':1'
        xvfb_process = subprocess.Popen(["Xvfb", ":1", "-screen", "0", "1600x900x24"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        running_processes.append(xvfb_process)
        print(f"✅ 虚拟屏幕 (Xvfb) 已启动,PID: {xvfb_process.pid}")

        # 启动 Fluxbox (窗口管理器)
        fluxbox_process = subprocess.Popen(["fluxbox"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        running_processes.append(fluxbox_process)
        print(f"✅ 窗口管理器 (Fluxbox) 已启动,PID: {fluxbox_process.pid}")

        # 启动 VNC 服务 (内部端口固定为5900)
        vnc_process = subprocess.Popen(["x11vnc", "-display", ":1", "-nopw", "-forever", "-rfbport", "5900"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        running_processes.append(vnc_process)
        print(f"✅ VNC 服务 (x11vnc) 已启动,PID: {vnc_process.pid}")

        # 启动 noVNC Web服务器 (内部端口由 NOVNC_LOCAL_PORT 变量决定)
        novnc_process = subprocess.Popen(["websockify", "-D", str(NOVNC_LOCAL_PORT), "localhost:5900", "--web", "/usr/share/novnc/"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        running_processes.append(novnc_process)
        print(f"✅ noVNC Web服务器 已启动。")

        # 启动 Chrome 浏览器
        chrome_process = subprocess.Popen(["google-chrome", "--no-sandbox", "--window-size=1500,800"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        running_processes.append(chrome_process)
        print(f"✅ Google Chrome 已启动,PID: {chrome_process.pid}")

        print("⏳ 等待服务稳定...")
        time.sleep(5)

        # 最后,启动frp客户端进行穿透
        frpc_process = subprocess.Popen([f"./{FRP_DIR}/frpc", "-c", f"./{FRP_DIR}/frpc.ini"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        running_processes.append(frpc_process)
        print(f"✅ frp 客户端已启动,PID: {frpc_process.pid}")

        print("\n\n🎉 远程浏览器应该准备就绪了!🎉")
        print("👇👇👇 在你的浏览器中打开下面的地址:")
        access_url = f"http://{FRP_SERVER_IP}:{REMOTE_PORT}"
        print(access_url)
        print("\n如果无法访问,请检查:")
        print("1. 公网服务器IP、token和端口是否正确。")
        print("2. 服务器防火墙是否已放行 7000 和", REMOTE_PORT, "端口。")
        print("3. frps 服务是否在服务器上正常运行。")

    except Exception as e:
        print(f"❌ 启动过程中发生错误: {e}")
        # 如果出错,尝试清理已启动的进程
        for p in running_processes:
            try: p.terminate()
            except: pass
代码解析: 此单元格按顺序启动了所有后台服务,并通过 subprocess.Popen 将它们置于后台运行。最后,它会启动 frpc 客户端,连接到你的公网服务器,打通访问隧道。成功后,它会打印出最终的访问地址。


3. 启动所有服务 (无FRP穿透 - 修改版)

# @title 3. 启动所有服务 (无FRP穿透 - 修改版)
import os
import subprocess
import time

# --- 可配置参数 ---
# noVNC Web服务器将在此端口上监听。你需要将此端口通过你自己的方式暴露到公网。
NOVNC_LOCAL_PORT = 6080
# ------------------

# 全局进程列表,用于后续中止
running_processes = []

print("🚀 正在启动后台服务...")
try:
    # 启动 Xvfb (虚拟屏幕)
    os.environ['DISPLAY'] = ':1'
    xvfb_process = subprocess.Popen(["Xvfb", ":1", "-screen", "0", "1600x900x24"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    running_processes.append(xvfb_process)
    print(f"✅ 虚拟屏幕 (Xvfb) 已启动,PID: {xvfb_process.pid}")

    # 启动 Fluxbox (窗口管理器)
    fluxbox_process = subprocess.Popen(["fluxbox"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    running_processes.append(fluxbox_process)
    print(f"✅ 窗口管理器 (Fluxbox) 已启动,PID: {fluxbox_process.pid}")

    # 启动 VNC 服务 (内部端口固定为5900)
    vnc_process = subprocess.Popen(["x11vnc", "-display", ":1", "-nopw", "-forever", "-rfbport", "5900"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    running_processes.append(vnc_process)
    print(f"✅ VNC 服务 (x11vnc) 已启动,PID: {vnc_process.pid}")

    # 启动 noVNC Web服务器 (内部端口由 NOVNC_LOCAL_PORT 变量决定)
    # 它将 VNC 的 5900 端口桥接到 Web 的 NOVNC_LOCAL_PORT 端口
    novnc_process = subprocess.Popen(["websockify", "-D", str(NOVNC_LOCAL_PORT), "localhost:5900", "--web", "/usr/share/novnc/"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    running_processes.append(novnc_process)
    print(f"✅ noVNC Web服务器 已启动,正在监听本地端口: {NOVNC_LOCAL_PORT}")

    # 启动 Chrome 浏览器
    chrome_process = subprocess.Popen(["google-chrome", "--no-sandbox", "--window-size=1500,800"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    running_processes.append(chrome_process)
    print(f"✅ Google Chrome 已启动,PID: {chrome_process.pid}")

    print("⏳ 等待服务稳定...")
    time.sleep(5)

    print("\n\n🎉 所有本地服务已成功启动!🎉")
    print("✅ noVNC 服务正在此 Colab 实例的以下端口上监听:")
    print(f"   --> 本地端口: {NOVNC_LOCAL_PORT}")
    print("\n下一步操作:")
    print("您现在需要使用您自己的内网穿透工具 (如 frp, nps, Cloudflare Tunnel, a-la-carte 等) 将此端口暴露到公网。")
    print(f"   例如,将此 Colab 实例的 localhost:{NOVNC_LOCAL_PORT} 映射到 [您的公网服务器IP]:[您希望的公网端口]")
    print("映射成功后,通过浏览器访问 `http://[您的公网服务器IP]:[您希望的公网端口]` 即可连接到远程桌面。")
    print("\n提示: 所有已启动的进程句柄保存在 `running_processes` 列表中,需要时可手动终止。")

except FileNotFoundError as e:
    print(f"❌ 启动过程中发生错误: 命令未找到 -> {e.filename}")
    print("这通常意味着某些必要的软件包没有被正确安装。请运行前面的安装单元格。")
    # 如果出错,尝试清理已启动的进程
    for p in running_processes:
        try: p.terminate()
        except: pass
except Exception as e:
    print(f"❌ 启动过程中发生错误: {e}")
    # 如果出错,尝试清理已启动的进程
    for p in running_processes:
        try: p.terminate()
        except: pass


单元格 4: 中止所有服务

当您使用完毕后,运行此单元格可以干净地关闭所有后台服务,释放资源。

# @title 4. 中止所有服务
if 'running_processes' in globals() and running_processes:
    print("⏳ 正在中止所有由脚本启动的进程...")
    for p in running_processes:
        try:
            pid = p.pid
            p.terminate() # 发送 SIGTERM 信号
            print(f"✅ 已向 PID {pid} 发送终止信号。")
        except Exception as e:
            print(f"⚠️ 终止进程 {pid} 时出错: {e}")

    # 清空列表
    globals()['running_processes'] = []
    print("\n✅ 所有记录的进程已终止。")
else:
    print("ℹ️ 没有找到正在运行的进程记录。")

# 强制清理,确保没有残留进程
print("\n🔍 正在进行强制清理...")
!pkill -f "Xvfb"
!pkill -f "fluxbox"
!pkill -f "x11vnc"
!pkill -f "websockify"
!pkill -f "google-chrome"
!pkill -f "frpc"
print("✅ 强制清理完成。")
代码解析: 首先尝试优雅地终止(terminate)由启动脚本记录的所有进程。随后,通过 pkill -f 进行强制清理,确保没有残留进程占用 Colab 资源。

结语

恭喜!通过以上步骤,你已经成功地将一个 Colab 实例变成了功能齐全的远程浏览器。现在,你可以通过 http://你的公网IP:10123 这个地址,在任何地方访问它了。

这个工具不仅有趣,而且在很多场景下非常实用:

  • 网页自动化测试: 结合 Selenium 等工具,在云端进行大规模的网页自动化操作。
  • 网络环境隔离: 使用一个干净的 IP 源访问网络,保护你本地环境的隐私和安全。
  • 访问特定资源: 轻松访问一些对地理位置或网络环境有特殊要求的网站。

当然,请记住 Colab 的使用限制(如最长运行时间、禁止挖矿等),合理、合规地使用这个强大的免费资源。希望这篇指南能为你打开一扇新的大门!

添加新评论