This commit is contained in:
mshe 2026-06-15 15:14:14 +08:00
parent 826e54c378
commit e34897a136
14 changed files with 687 additions and 207 deletions

24
Dockerfile Normal file
View File

@ -0,0 +1,24 @@
FROM vnc-base
ENV DEBIAN_FRONTEND=noninteractive \
VNC_PASSWORD=123456
# 安装 Snap 版 Chromium
RUN snap install chromium \
&& ln -s /snap/bin/chromium /usr/local/bin/chromium-browser
RUN git clone https://github.com/novnc/noVNC.git /opt/novnc \
&& git clone https://github.com/novnc/websockify.git /opt/novnc/utils/websockify
RUN mkdir -p /root/.vnc && echo "$VNC_PASSWORD" | vncpasswd -f > /root/.vnc/passwd && chmod 600 /root/.vnc/passwd
EXPOSE 6080 9222
CMD Xvfb :1 -screen 0 1280x1024x24 & \
export DISPLAY=:1 && \
fluxbox & \
vncserver :1 -geometry 1280x1024 -depth 24 -localhost no && \
/opt/novnc/utils/novnc_proxy --vnc localhost:5901 --listen 6080 & \
chromium-browser --no-sandbox --disable-gpu --remote-debugging-port=9222 --remote-debugging-address=0.0.0.0 --window-size=1280,1024 && \
tail -f /dev/null

14
Dockerfile_base Normal file
View File

@ -0,0 +1,14 @@
# FROM ubuntu:22.04
FROM harbor.hk.crabapples.cn:22580/library/ubuntu:24.04
ENV DEBIAN_FRONTEND=noninteractive \
VNC_PASSWORD=123456
RUN sed -i 's@//.*archive.ubuntu.com@//mirrors.aliyun.com@g' /etc/apt/sources.list.d/ubuntu.sources &&\
sed -i 's@//security.ubuntu.com@//mirrors.aliyun.com@g' /etc/apt/sources.list.d/ubuntu.sources &&\
sed -i 's@//ports.ubuntu.com@//mirrors.aliyun.com@g' /etc/apt/sources.list.d/ubuntu.sources
RUN apt-get update
RUN apt-get install -y wget curl gnupg x11vnc xvfb fluxbox git python3 snapd && rm -rf /var/lib/apt/lists/*

150
app.py
View File

@ -58,7 +58,7 @@ async def start_xvnc_server(instance: int = 1, password: str = "123456") -> str:
try: try:
# 调用 browser-vnc.sh 脚本 # 调用 browser-vnc.sh 脚本
process = await asyncio.create_subprocess_exec( process = await asyncio.create_subprocess_exec(
f"{SHELL_DIR}/browser-vnc.sh", f"{SHELL_DIR}/start-vnc.sh",
"start", "start",
str(instance), str(instance),
password, password,
@ -139,154 +139,6 @@ async def stop_xvnc_server(instance: int = 1, keep_data: bool = False) -> str:
data["message"] = str(e) data["message"] = str(e)
return json.dumps(data, ensure_ascii=False) return json.dumps(data, ensure_ascii=False)
@mcp.tool()
async def stop_all_xvnc_servers(keep_data: bool = False) -> str:
"""停止所有 VNC 实例
Args:
keep_data: 是否保留用户数据默认 False删除所有数据
Returns:
操作结果
"""
data = {
"status": 200,
"action": "stop_all",
"keep_data": keep_data
}
try:
# 选择命令stop-all 或 stop-all-keep-data
cmd = "stop-all-keep-data" if keep_data else "stop-all"
process = await asyncio.create_subprocess_exec(
f"{SHELL_DIR}/browser-vnc.sh",
cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
stdout, stderr = await process.communicate()
output = stdout.decode()
if process.returncode == 0:
data["message"] = output
else:
data["status"] = 500
data["message"] = stderr.decode() or output or "Unknown error"
return json.dumps(data, ensure_ascii=False)
except Exception as e:
data["status"] = 500
data["message"] = str(e)
return json.dumps(data, ensure_ascii=False)
@mcp.tool()
async def list_xvnc_servers() -> str:
"""列出所有运行中的 VNC 实例"""
data = {
"status": 200,
"action": "list",
"instances": []
}
try:
process = await asyncio.create_subprocess_exec(
f"{SHELL_DIR}/browser-vnc.sh",
"status",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
stdout, stderr = await process.communicate()
output = stdout.decode()
# 解析输出,提取运行中的实例
for line in output.split('\n'):
if "✅ Running" in line:
# 提取实例编号,例如 "Instance 1: ✅ Running"
import re
match = re.search(r'Instance (\d+):', line)
if match:
instance = int(match.group(1))
data["instances"].append({
"instance": instance,
"vnc_port": 5900 + (instance - 1),
"display": 98 + instance,
"vnc_url": f"{VNC_SERVER_HOST}/vnc_lite.html?path=?token=user{instance}"
})
data["count"] = len(data["instances"])
data["raw_output"] = output
return json.dumps(data, ensure_ascii=False)
except Exception as e:
data["status"] = 500
data["message"] = str(e)
return json.dumps(data, ensure_ascii=False)
@mcp.tool()
async def get_vnc_connection_info(instance: int = 1) -> str:
"""获取指定实例的连接信息
Args:
instance: 实例编号 (1-10)
Returns:
包含连接信息的 JSON 字符串
"""
data = {
"instance": instance,
"status": 200
}
try:
# 检查实例是否在运行
process = await asyncio.create_subprocess_exec(
f"{SHELL_DIR}/browser-vnc.sh",
"status",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
stdout, _ = await process.communicate()
output = stdout.decode()
# 检查实例状态
instance_pattern = f"Instance {instance}:"
is_running = False
for line in output.split('\n'):
if instance_pattern in line:
if "✅ Running" in line:
is_running = True
break
public_ip = subprocess.getoutput("hostname -I | awk '{print $1}'").strip()
vnc_port = 5900 + (instance - 1)
display_num = 98 + instance
data["is_running"] = is_running
data["vnc_url"] = f"{VNC_SERVER_HOST}/vnc_lite.html?path=?token=user{instance}"
data["vnc_port"] = vnc_port
data["display"] = display_num
data["public_ip"] = public_ip
if not is_running:
data["warning"] = "Instance is not running, start it with start_xvnc_server"
return json.dumps(data, ensure_ascii=False)
except Exception as e:
data["status"] = 500
data["message"] = str(e)
return json.dumps(data, ensure_ascii=False)
@mcp.tool() @mcp.tool()
async def cleanup_xvnc_data(instance: Optional[int] = None) -> str: async def cleanup_xvnc_data(instance: Optional[int] = None) -> str:
"""清理 VNC 实例的用户数据 """清理 VNC 实例的用户数据

77
bin/start-vnc.sh Executable file
View File

@ -0,0 +1,77 @@
#!/bin/bash
# 浏览器 VNC 守护进程
set -euo pipefail
TARGET_URL="https://www.baidu.com"
VNC_PWD=${1:-"123456"} # VNC 密码
VNC_PORT=${2:-5900} # VNC 端口
DISPLAY_NUM=${3:-99} # 显示编号
DATA_DIR=${4:-"/tmp/chrome-profile-${DISPLAY_NUM}"} # 用户数据目录
PID_FILE="/tmp/chrome-browser-${DISPLAY_NUM}.pid" # 进程 ID 文件
LOG_FILE="/tmp/chrome-browser-${DISPLAY_NUM}.log" # 日志文件
USER_COUNT="${5:-10}"
GREEN='\033[0;32m'; RED='\033[0;31m'; NC='\033[0m'
log() { echo -e "${GREEN}[$(date '+%H:%M:%S')]${NC} $*"; }
err() { echo -e "${RED}[$(date '+%H:%M:%S')]${NC} $*" >&2; }
# 检查是否已在运行
if [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE") 2>/dev/null; then
err "实例 :${DISPLAY_NUM} 已在运行中"
exit 1
fi
cleanup() {
log "正在清理实例 :${DISPLAY_NUM}..."
pkill -P $$ 2>/dev/null || true
pkill -f "Xvfb :${DISPLAY_NUM}" 2>/dev/null || true
pkill -f "x11vnc.*${DISPLAY_NUM}" 2>/dev/null || true
rm -f "$PID_FILE" 2>/dev/null || true
}
trap cleanup EXIT INT TERM
# 记录当前进程 ID
echo $$ > "$PID_FILE"
log "正在启动 Xvfb :${DISPLAY_NUM}"
Xvfb :${DISPLAY_NUM} -screen 0 1280x1024x24 -nolisten tcp &
sleep 2
export DISPLAY=:${DISPLAY_NUM}
log "正在启动 Chromium (数据目录: ${DATA_DIR})"
mkdir -p "$DATA_DIR"
# 查找 Chromium 可执行文件
CHROME_BIN=$(ls $HOME/.cache/ms-playwright/chromium-*/chrome-linux/chrome 2>/dev/null | head -1)
if [ -z "$CHROME_BIN" ]; then
CHROME_BIN=$(which chromium-browser 2>/dev/null || which google-chrome-stable 2>/dev/null || true)
fi
[ -z "$CHROME_BIN" ] && { err "未找到 Chromium"; exit 1; }
# 启动 Chromium非 headless 模式,可见界面)
"$CHROME_BIN" \
--no-sandbox \
--disable-gpu \
--disable-dev-shm-usage \
--user-data-dir="${DATA_DIR}" \
--window-size=1280,1024 \
"${TARGET_URL}" &
CHROME_PID=$!
log "正在启动 x11vnc端口: ${VNC_PORT}..."
x11vnc -display :${DISPLAY_NUM} -forever -shared -passwd ${VNC_PWD} -rfbport ${VNC_PORT} -bg &
# 获取服务器公网 IP
PUBLIC_HOST=$(curl -sf ifconfig.me 2>/dev/null || hostname -I | awk '{print $1}')
log "=========================================="
log "✅ 浏览器已启动 (仅 VNC无 CDP)"
log "🖥️ VNC 地址: ${PUBLIC_HOST}:${VNC_PORT}"
log "🔑 VNC 密码: ${VNC_PWD}"
log "📁 数据目录: ${DATA_DIR}"
log "🛑 停止命令: ./browser-vnc.sh stop ${DISPLAY_NUM}"
log "=========================================="
# 等待 Chromium 进程结束
wait $CHROME_PID 2>/dev/null || true

137
bin/vnc_manager.sh Normal file
View File

@ -0,0 +1,137 @@
#!/bin/bash
# 浏览器 VNC 管理器
DAEMON_SCRIPT="$(dirname $0)/start-browser-vnc.sh"
BASE_VNC_PORT=5900
BASE_DATA_DIR="/tmp/chrome-profiles" # 用户数据目录
start() {
local instance=${1:-1}
local vnc_pwd=${2} # 密码必须传入,不再有默认值
local display_num=$((98 + instance))
local vnc_port=$((BASE_VNC_PORT + instance - 1))
local data_dir="${BASE_DATA_DIR}/instance-${instance}"
# 检查密码是否提供
if [ -z "$vnc_pwd" ]; then
echo "❌ 错误: 启动实例时必须提供密码"
echo "用法: $0 start <实例编号> <密码>"
return 1
fi
if [ -f "/tmp/chrome-browser-${display_num}.pid" ] && kill -0 $(cat "/tmp/chrome-browser-${display_num}.pid") 2>/dev/null; then
echo "实例 ${instance} 已在运行中"
return 1
fi
echo "正在启动实例 ${instance} (显示编号 :${display_num}, VNC 端口: ${vnc_port})..."
echo "数据目录: ${data_dir}"
# 确保数据目录存在
mkdir -p "$data_dir"
nohup "$DAEMON_SCRIPT" "$vnc_pwd" "$vnc_port" "$display_num" "$data_dir" >> "/tmp/chrome-browser-${display_num}.log" 2>&1 &
sleep 3
local public_ip=$(hostname -I | awk '{print $1}')
echo "✅ 实例 ${instance} 已启动"
echo " VNC: ${public_ip}:${vnc_port}"
echo " 密码: ${vnc_pwd}"
echo " 数据: ${data_dir} (停止时将删除)"
}
stop() {
local instance=${1:-1}
local display_num=$((98 + instance))
local pid_file="/tmp/chrome-browser-${display_num}.pid"
local data_dir="${BASE_DATA_DIR}/instance-${instance}"
if [ -f "$pid_file" ]; then
local pid=$(cat "$pid_file")
echo "正在停止实例 ${instance} (PID: $pid)..."
# 先发送 TERM 信号,让浏览器优雅关闭
kill -TERM $pid 2>/dev/null
sleep 3
# 如果还在运行,强制杀死
if kill -0 $pid 2>/dev/null; then
echo "正在强制终止..."
kill -9 $pid 2>/dev/null
fi
# 删除 PID 文件
rm -f "$pid_file"
# 删除用户数据目录
if [ -d "$data_dir" ]; then
echo "正在删除用户数据: ${data_dir}"
rm -rf "$data_dir"
fi
echo "✅ 实例 ${instance} 已停止"
else
echo "实例 ${instance} 未运行,正在清理数据..."
# 即使进程不在运行,也清理数据目录
if [ -d "$data_dir" ]; then
rm -rf "$data_dir"
echo "✅ 数据已清理: ${data_dir}"
fi
fi
# 额外清理:杀掉可能残留的 Xvfb 和 x11vnc 进程
pkill -f "Xvfb :${display_num}" 2>/dev/null
pkill -f "x11vnc.*${display_num}" 2>/dev/null
# 清理临时文件
rm -f "/tmp/.X${display_num}-lock" 2>/dev/null
rm -f "/tmp/.X11-unix/X${display_num}" 2>/dev/null
}
cleanup_data() {
local instance=${1:-1}
local data_dir="${BASE_DATA_DIR}/instance-${instance}"
# 先停止实例
stop $instance
# 如果还有残留数据,强制删除
if [ -d "$data_dir" ]; then
echo "正在强制清理数据: ${data_dir}"
rm -rf "$data_dir"
fi
echo "✅ 实例 ${instance} 的数据已清理"
}
case "${1:-}" in
start)
if [ $# -lt 3 ]; then
echo "❌ 错误: start 命令需要实例编号和密码"
echo "用法: $0 start <实例编号> <密码>"
echo "示例: $0 start 1 mypassword"
exit 1
fi
start "$2" "$3"
;;
stop)
stop "${2:-1}"
;;
cleanup)
cleanup_data "${2:-1}"
;;
*)
echo "用法: $0 {start|stop|cleanup} [参数]"
echo ""
echo "命令说明:"
echo " start <编号> <密码> - 启动实例 (必须提供密码)"
echo " stop [编号] - 停止实例并删除用户数据 (默认: 1)"
echo " cleanup [编号] - 强制删除指定实例的用户数据 (默认: 1)"
echo ""
echo "使用示例:"
echo " $0 start 1 mypass # 启动实例 1密码 mypass"
echo " $0 start 2 123456 # 启动实例 2密码 123456"
echo " $0 stop 1 # 停止实例 1 并删除其数据"
exit 1
;;
esac

46
bin_bak/auto_install.sh Normal file
View File

@ -0,0 +1,46 @@
#!/bin/bash
#wget -O- -q --show-progress https://git.nps.crabapples.cn/crabapples/learn-spider/raw/branch/main/bin/auto_install.sh | bash
# wget -q --show-progress https://git.nps.crabapples.cn/crabapples/learn-spider/raw/branch/main/bin/close_vnc.sh && chmod +x close_vnc.sh
SECOND=5
GIT_DOMAIN=https://git.nps.crabapples.cn
echo "清理旧脚本..."
rm -rf ./install.sh ./start_vnc_server.sh ./close_vnc_server.sh ./start_spider.sh ./update.sh ./uninstall.sh
echo "开始下载..."
echo "下载安装脚本..."
wget -q --show-progress $GIT_DOMAIN/crabapples/learn-spider/raw/branch/main/bin/install.sh && chmod +x install.sh
echo "下载启动VNC脚本..."
wget -q --show-progress $GIT_DOMAIN/crabapples/learn-spider/raw/branch/main/bin/start_vnc_server.sh && chmod +x start_vnc_server.sh
echo "下载关闭VNC脚本..."
wget -q --show-progress $GIT_DOMAIN/crabapples/learn-spider/raw/branch/main/bin/close_vnc_server.sh && chmod +x close_vnc_server.sh
echo "下载启动爬虫脚本..."
wget -q --show-progress $GIT_DOMAIN/crabapples/learn-spider/raw/branch/main/bin/start_spider.sh && chmod +x start_spider.sh
echo "下载更新脚本..."
wget -q --show-progress $GIT_DOMAIN/crabapples/learn-spider/raw/branch/main/bin/update.sh && chmod +x update.sh
echo "下载卸载脚本..."
wget -q --show-progress $GIT_DOMAIN/crabapples/learn-spider/raw/branch/main/bin/uninstall.sh && chmod +x uninstall.sh
for i in $(seq 1 $SECOND); do
clear
echo "脚本下载完成$(($SECOND-$i))S后开始自动安装...."
sleep 1
done
USER_COUNT=10
# 公共的工作目录
PUBLIC_DIR="/shared"
# git仓库加速地址
GIT_NO_VNC="$GIT_DOMAIN/crabapples/noVNC.git"
GIT_WEBSOCKIFY="$GIT_DOMAIN/crabapples/websockify.git"
GIT_PY_SPIDER="$GIT_DOMAIN/crabapples/learn-spider.git"
./install.sh $USER_COUNT $PUBLIC_DIR $GIT_NO_VNC $GIT_WEBSOCKIFY $GIT_PY_SPIDER
echo "安装完成"

View File

@ -1,6 +1,5 @@
#!/bin/bash #!/bin/bash
# Browser VNC Manager # 浏览器 VNC 管理器
DAEMON_SCRIPT="$(dirname $0)/start-browser-vnc.sh" DAEMON_SCRIPT="$(dirname $0)/start-browser-vnc.sh"
BASE_VNC_PORT=5900 BASE_VNC_PORT=5900
@ -14,12 +13,12 @@ start() {
local data_dir="${BASE_DATA_DIR}/instance-${instance}" local data_dir="${BASE_DATA_DIR}/instance-${instance}"
if [ -f "/tmp/chrome-browser-${display_num}.pid" ] && kill -0 $(cat "/tmp/chrome-browser-${display_num}.pid") 2>/dev/null; then if [ -f "/tmp/chrome-browser-${display_num}.pid" ] && kill -0 $(cat "/tmp/chrome-browser-${display_num}.pid") 2>/dev/null; then
echo "Instance ${instance} already running" echo "实例 ${instance} 已在运行中"
return 1 return 1
fi fi
echo "Starting instance ${instance} (display :${display_num}, VNC port: ${vnc_port})..." echo "正在启动实例 ${instance} (显示编号 :${display_num}, VNC 端口: ${vnc_port})..."
echo "Data directory: ${data_dir}" echo "数据目录: ${data_dir}"
# 确保数据目录存在 # 确保数据目录存在
mkdir -p "$data_dir" mkdir -p "$data_dir"
@ -28,10 +27,10 @@ start() {
sleep 3 sleep 3
local public_ip=$(hostname -I | awk '{print $1}') local public_ip=$(hostname -I | awk '{print $1}')
echo "Instance ${instance} started" echo "实例 ${instance} 已启动"
echo " VNC: ${public_ip}:${vnc_port}" echo " VNC: ${public_ip}:${vnc_port}"
echo " Password: ${vnc_pwd}" echo " 密码: ${vnc_pwd}"
echo " Data: ${data_dir} (will be deleted on stop)" echo " 数据: ${data_dir} (停止时将删除)"
} }
stop() { stop() {
@ -43,7 +42,7 @@ stop() {
if [ -f "$pid_file" ]; then if [ -f "$pid_file" ]; then
local pid=$(cat "$pid_file") local pid=$(cat "$pid_file")
echo "Stopping instance ${instance} (PID: $pid)..." echo "正在停止实例 ${instance} (PID: $pid)..."
# 先发送 TERM 信号,让浏览器优雅关闭 # 先发送 TERM 信号,让浏览器优雅关闭
kill -TERM $pid 2>/dev/null kill -TERM $pid 2>/dev/null
@ -51,7 +50,7 @@ stop() {
# 如果还在运行,强制杀死 # 如果还在运行,强制杀死
if kill -0 $pid 2>/dev/null; then if kill -0 $pid 2>/dev/null; then
echo "Force killing..." echo "正在强制终止..."
kill -9 $pid 2>/dev/null kill -9 $pid 2>/dev/null
fi fi
@ -60,17 +59,17 @@ stop() {
# 删除用户数据目录 # 删除用户数据目录
if [ "$delete_data" = "true" ] && [ -d "$data_dir" ]; then if [ "$delete_data" = "true" ] && [ -d "$data_dir" ]; then
echo "Deleting user data: ${data_dir}" echo "正在删除用户数据: ${data_dir}"
rm -rf "$data_dir" rm -rf "$data_dir"
fi fi
echo "Instance ${instance} stopped" echo "实例 ${instance} 已停止"
else else
echo "Instance ${instance} not running, cleaning up data..." echo "实例 ${instance} 未运行,正在清理数据..."
# 即使进程不在运行,也清理数据目录 # 即使进程不在运行,也清理数据目录
if [ -d "$data_dir" ] && [ "$delete_data" = "true" ]; then if [ -d "$data_dir" ] && [ "$delete_data" = "true" ]; then
rm -rf "$data_dir" rm -rf "$data_dir"
echo "Data cleaned: ${data_dir}" echo "数据已清理: ${data_dir}"
fi fi
fi fi
@ -84,7 +83,7 @@ stop() {
} }
status() { status() {
echo "=== Browser Instances (VNC only) ===" echo "=== 浏览器实例状态 (仅 VNC) ==="
for i in {1..10}; do for i in {1..10}; do
local display_num=$((98 + i)) local display_num=$((98 + i))
local pid_file="/tmp/chrome-browser-${display_num}.pid" local pid_file="/tmp/chrome-browser-${display_num}.pid"
@ -92,19 +91,19 @@ status() {
if [ -f "$pid_file" ] && kill -0 $(cat "$pid_file") 2>/dev/null; then if [ -f "$pid_file" ] && kill -0 $(cat "$pid_file") 2>/dev/null; then
local pid=$(cat "$pid_file") local pid=$(cat "$pid_file")
echo "Instance ${i}: ✅ Running (VNC: $((5900 + i - 1)), PID: ${pid}, Data: ${data_dir})" echo "实例 ${i}: ✅ 运行中 (VNC 端口: $((5900 + i - 1)), PID: ${pid}, 数据: ${data_dir})"
else else
echo "Instance ${i}: ❌ Stopped" echo "实例 ${i}: ❌ 已停止"
fi fi
done done
} }
stop_all() { stop_all() {
echo "Stopping all instances..." echo "正在停止所有实例..."
for i in {1..10}; do for i in {1..10}; do
stop $i "${1:-true}" stop $i "${1:-true}"
done done
echo "✅ All instances stopped" echo "✅ 所有实例已停止"
} }
cleanup_data() { cleanup_data() {
@ -116,23 +115,23 @@ cleanup_data() {
# 如果还有残留数据,强制删除 # 如果还有残留数据,强制删除
if [ -d "$data_dir" ]; then if [ -d "$data_dir" ]; then
echo "Force cleaning data: ${data_dir}" echo "正在强制清理数据: ${data_dir}"
rm -rf "$data_dir" rm -rf "$data_dir"
fi fi
echo "Data for instance ${instance} cleaned" echo "实例 ${instance} 的数据已清理"
} }
cleanup_all_data() { cleanup_all_data() {
echo "Cleaning all instance data..." echo "正在清理所有实例数据..."
for i in {1..10}; do for i in {1..10}; do
local data_dir="${BASE_DATA_DIR}/instance-${i}" local data_dir="${BASE_DATA_DIR}/instance-${i}"
if [ -d "$data_dir" ]; then if [ -d "$data_dir" ]; then
rm -rf "$data_dir" rm -rf "$data_dir"
echo " Removed: ${data_dir}" echo " 已删除: ${data_dir}"
fi fi
done done
echo "✅ All data cleaned" echo "✅ 所有数据已清理"
} }
case "${1:-}" in case "${1:-}" in
@ -161,25 +160,25 @@ case "${1:-}" in
cleanup_all_data cleanup_all_data
;; ;;
*) *)
echo "Usage: $0 {start|stop|stop-keep-data|stop-all|stop-all-keep-data|status|cleanup|cleanup-all} [instance_num] [password]" echo "用法: $0 {start|stop|stop-keep-data|stop-all|stop-all-keep-data|status|cleanup|cleanup-all} [实例编号] [密码]"
echo "" echo ""
echo "Commands:" echo "命令说明:"
echo " start [num] [pwd] - Start instance (default: 1, 123456)" echo " start [编号] [密码] - 启动实例 (默认: 1, 123456)"
echo " stop [num] - Stop and DELETE user data" echo " stop [编号] - 停止并删除用户数据"
echo " stop-keep-data [num] - Stop but KEEP user data" echo " stop-keep-data [编号] - 停止但保留用户数据"
echo " stop-all - Stop all and DELETE all user data" echo " stop-all - 停止所有实例并删除所有数据"
echo " stop-all-keep-data - Stop all but KEEP user data" echo " stop-all-keep-data - 停止所有实例但保留所有数据"
echo " status - Show all instances status" echo " status - 显示所有实例状态"
echo " cleanup [num] - Force delete user data for instance" echo " cleanup [编号] - 强制删除指定实例的用户数据"
echo " cleanup-all - Force delete all user data" echo " cleanup-all - 强制删除所有实例的用户数据"
echo "" echo ""
echo "Examples:" echo "使用示例:"
echo " $0 start 1 # Start instance 1 (VNC:5900, password:123456)" echo " $0 start 1 # 启动实例 1 (VNC:5900, 密码:123456)"
echo " $0 start 2 mypass # Start instance 2 (VNC:5901, password:mypass)" echo " $0 start 2 mypass # 启动实例 2 (VNC:5901, 密码:mypass)"
echo " $0 stop 1 # Stop instance 1 and DELETE its data" echo " $0 stop 1 # 停止实例 1 并删除其数据"
echo " $0 stop-keep-data 1 # Stop instance 1 but KEEP its data" echo " $0 stop-keep-data 1 # 停止实例 1 但保留其数据"
echo " $0 status # Show all instances" echo " $0 status # 显示所有实例状态"
echo " $0 cleanup-all # Delete all instance data" echo " $0 cleanup-all # 删除所有实例数据"
exit 1 exit 1
;; ;;
esac esac

244
bin_bak/install.sh Normal file
View File

@ -0,0 +1,244 @@
#!/bin/bash
# ==================== 配置变量 ====================
# 需要创建的用户数量
USER_COUNT=${1:-10}
# 公共的工作目录
PUBLIC_DIR="${2:-/shared}"
# git仓库地址
GIT_NO_VNC="${3:-https://github.com/novnc/noVNC.git}"
GIT_WEBSOCKIFY="${4:-https://github.com/novnc/websockify.git}"
GIT_PY_SPIDER="${5:-https://git.nps.crabapples.cn/crabapples/learn-spider.git}"
echo "USER_COUNT: $USER_COUNT"
echo "PUBLIC_DIR: $PUBLIC_DIR"
echo "GIT_NO_VNC: $GIT_NO_VNC"
echo "GIT_WEBSOCKIFY: $GIT_WEBSOCKIFY"
echo "GIT_PY_SPIDER: $GIT_PY_SPIDER"
echo ""
sleep 3
# ==================== 函数定义 ====================
# 1. 预安装软件
pre_install(){
echo "当前进度:01.预安装软件"
sudo apt update
sudo apt install -y xfce4 xfce4-goodies
sudo apt install -y tigervnc-standalone-server tigervnc-common
sudo apt install -y git python3 openssl
echo "✅ [完成] 预安装软件"
echo ""
}
# 2. 开放防火墙
open_firewall(){
echo "当前进度:02.开放防火墙"
sudo ufw allow 6080/tcp
echo "✅ [完成] 开放防火墙端口 6080"
echo ""
}
# 3. 批量创建用户
batch_create_user(){
echo "当前进度:03.创建用户"
for i in $(seq 1 $USER_COUNT); do
username="user$i"
sudo useradd -m -s /bin/bash "$username"
echo "$username:$username" | sudo chpasswd
echo " ✓ 用户 $username 创建完成,密码: $username"
done
echo "✅ [完成] 共创建 $USER_COUNT 个用户"
echo ""
}
# 4. 批量设置VNC密码
batch_set_vncpdw(){
echo "当前进度:04.设置VNC密码"
for i in $(seq 1 $USER_COUNT); do
username="user$i"
sudo su - "$username" -c "mkdir -p ~/.vnc && echo '$username' | vncpasswd -f > ~/.vnc/passwd && chmod 600 ~/.vnc/passwd"
echo " ✓ 正在设置 $username 的VNC密码"
done
echo "✅ [完成] 所有VNC密码设置完成"
echo ""
}
# 5. 创建 VNC 启动配置
create_xstartup(){
echo "当前进度:05.创建VNC启动配置"
for i in $(seq 1 ${USER_COUNT}); do
username="user$i"
sudo bash -c "cat > /home/$username/.vnc/xstartup << 'EOF'
#!/bin/sh
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
startxfce4 &
wait
EOF"
sudo chmod +x "/home/$username/.vnc/xstartup"
echo " ✓ 已创建 $username 的 xstartup"
done
echo "✅ [完成] 所有用户的 VNC 启动配置创建完成"
echo ""
}
# 6. 创建公共目录
cerate_public_dir(){
echo "当前进度:06.创建公共工作目录"
sudo mkdir -p "$PUBLIC_DIR"
sudo chmod 777 "$PUBLIC_DIR"
sudo chmod +t "$PUBLIC_DIR"
sudo chown root:root "$PUBLIC_DIR"
echo " 公共文件夹已创建: $PUBLIC_DIR"
echo " 权限: $(ls -ld $PUBLIC_DIR)"
echo "✅ [完成] 公共工作目录创建完成"
echo ""
}
# 7. 在每个用户home目录创建软连接
create_shortcut_for_users(){
echo "当前进度:07.创建公共工作目录软连接"
for i in $(seq 1 $USER_COUNT); do
username="user$i"
sudo su - "$username" -c "ln -sf $PUBLIC_DIR ~/workspace"
echo " ✓ 正在创建 $username 的快捷方式"
done
echo "✅ [完成] 所有用户的软连接创建完成"
echo ""
}
# 8. 准备novnc文件
install_vnc_server(){
echo "当前进度:08.下载NoVNC源码"
cd $PUBLIC_DIR
sudo git clone $GIT_NO_VNC
sudo git clone $GIT_WEBSOCKIFY
echo "✅ [完成] NoVNC 源码下载完成"
echo ""
}
# 9. 生成ssl证书
create_ssl_cert(){
echo "当前进度:09.生成SSL证书,有效期:10年"
cd $PUBLIC_DIR/noVNC
sudo openssl req -new -x509 -days 3650 -nodes \
-out self.pem \
-keyout self.pem \
-subj "/C=CN/ST=Beijing/L=Beijing/O=Test/CN=localhost"
echo "✅ [完成] SSL证书生成完成"
echo ""
}
# 10. 预安装爬虫
pre_install_spider(){
echo "当前进度:10.下载爬虫源码"
cd $PUBLIC_DIR
if [ -d "learn-spider" ]; then
cd learn-spider
sudo git pull
else
sudo git clone $GIT_PY_SPIDER
cd learn-spider
fi
sudo chmod +x ./pre_install_spider.sh
sudo chmod +x ./start_spider.sh
sudo ./pre_install_spider.sh
echo "✅ [完成] 爬虫环境安装完成"
echo ""
}
# 11. 创建 token.conf 配置文件
create_token_conf(){
echo "当前进度:11.创建token.conf配置文件"
sudo tee "$PUBLIC_DIR/websockify/token.conf" > /dev/null << EOF
# noVNC Token Configuration
$(for i in $(seq 1 $USER_COUNT); do echo "user$i: 127.0.0.1:$((5900 + i))"; done)
EOF
sudo chmod 644 "$PUBLIC_DIR/websockify/token.conf"
echo " token.conf 内容预览:"
sudo cat "$PUBLIC_DIR/websockify/token.conf" | head -5
echo "✅ [完成] token.conf 已创建"
echo ""
}
# 12. 创建novnc服务文件
create_novnc_service(){
echo "当前进度:12.创建novnc.service服务配置文件"
WEB_ROOT="$PUBLIC_DIR/noVNC"
WORKSPACE="$PUBLIC_DIR/websockify"
TOKEN_FILE="$WORKSPACE/token.conf"
CERT_PATH="$WEB_ROOT/self.pem"
SERVICE_FILE="/etc/systemd/system/novnc.service"
sudo tee "$SERVICE_FILE" > /dev/null << EOF
[Unit]
Description=noVNC WebSocket Proxy
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=$WORKSPACE
ExecStart=$WORKSPACE/run \
--web $WEB_ROOT \
--target-config $TOKEN_FILE \
--cert $CERT_PATH \
6080
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl stop novnc 2>/dev/null
sudo systemctl enable novnc.service --now
echo " 服务状态检查:"
sudo systemctl status novnc --no-pager | head -5
echo "✅ [完成] noVNC 服务已创建并启动"
echo " 查看日志: sudo journalctl -u novnc -f"
echo " 访问地址: https://$(hostname -I | awk '{print $1}'):6080/vnc.html"
echo ""
}
# 13. 显示部署总结
show_summary(){
echo "==================== 部署总结 ===================="
echo "✅ 用户数量: $USER_COUNT"
echo "✅ 公共目录: $PUBLIC_DIR"
echo "✅ VNC 端口范围: 5901-$((5900 + $USER_COUNT))"
echo "✅ noVNC 访问地址: https://$(hostname -I | awk '{print $1}'):6080/vnc.html"
echo "✅ Token 配置: user1-user$USER_COUNT 使用对应密码"
echo ""
echo "==================== 部署完成 ===================="
}
# ==================== 主执行流程 ====================
main(){
echo ""
echo "========== 开始部署 VNC 环境 =========="
echo "开始时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
pre_install
open_firewall
batch_create_user
batch_set_vncpdw
create_xstartup
cerate_public_dir
create_shortcut_for_users
install_vnc_server
create_ssl_cert
pre_install_spider
create_token_conf
create_novnc_service
show_summary
echo "结束时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
}
main

View File

@ -1,15 +1,15 @@
#!/bin/bash #!/bin/bash
# Browser VNC Daemon # 浏览器 VNC 守护进程
set -euo pipefail set -euo pipefail
TARGET_URL="https://www.baidu.com" TARGET_URL="https://www.baidu.com"
VNC_PWD=${1:-"123456"} VNC_PWD=${1:-"123456"} # VNC 密码
VNC_PORT=${2:-5900} VNC_PORT=${2:-5900} # VNC 端口
DISPLAY_NUM=${3:-99} DISPLAY_NUM=${3:-99} # 显示编号
DATA_DIR=${4:-"/tmp/chrome-profile-${DISPLAY_NUM}"} # 用户数据目录 DATA_DIR=${4:-"/tmp/chrome-profile-${DISPLAY_NUM}"} # 用户数据目录
PID_FILE="/tmp/chrome-browser-${DISPLAY_NUM}.pid" PID_FILE="/tmp/chrome-browser-${DISPLAY_NUM}.pid" # 进程 ID 文件
LOG_FILE="/tmp/chrome-browser-${DISPLAY_NUM}.log" LOG_FILE="/tmp/chrome-browser-${DISPLAY_NUM}.log" # 日志文件
GREEN='\033[0;32m'; RED='\033[0;31m'; NC='\033[0m' GREEN='\033[0;32m'; RED='\033[0;31m'; NC='\033[0m'
log() { echo -e "${GREEN}[$(date '+%H:%M:%S')]${NC} $*"; } log() { echo -e "${GREEN}[$(date '+%H:%M:%S')]${NC} $*"; }
@ -17,12 +17,12 @@ err() { echo -e "${RED}[$(date '+%H:%M:%S')]${NC} $*" >&2; }
# 检查是否已在运行 # 检查是否已在运行
if [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE") 2>/dev/null; then if [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE") 2>/dev/null; then
err "Instance :${DISPLAY_NUM} already running" err "实例 :${DISPLAY_NUM} 已在运行中"
exit 1 exit 1
fi fi
cleanup() { cleanup() {
log "Cleaning up instance :${DISPLAY_NUM}..." log "正在清理实例 :${DISPLAY_NUM}..."
pkill -P $$ 2>/dev/null || true pkill -P $$ 2>/dev/null || true
pkill -f "Xvfb :${DISPLAY_NUM}" 2>/dev/null || true pkill -f "Xvfb :${DISPLAY_NUM}" 2>/dev/null || true
pkill -f "x11vnc.*${DISPLAY_NUM}" 2>/dev/null || true pkill -f "x11vnc.*${DISPLAY_NUM}" 2>/dev/null || true
@ -30,21 +30,25 @@ cleanup() {
} }
trap cleanup EXIT INT TERM trap cleanup EXIT INT TERM
# 记录当前进程 ID
echo $$ > "$PID_FILE" echo $$ > "$PID_FILE"
log "Starting Xvfb :${DISPLAY_NUM}" log "正在启动 Xvfb :${DISPLAY_NUM}"
Xvfb :${DISPLAY_NUM} -screen 0 1280x1024x24 -nolisten tcp & Xvfb :${DISPLAY_NUM} -screen 0 1280x1024x24 -nolisten tcp &
sleep 2 sleep 2
export DISPLAY=:${DISPLAY_NUM} export DISPLAY=:${DISPLAY_NUM}
log "Starting Chromium (data dir: ${DATA_DIR})" log "正在启动 Chromium (数据目录: ${DATA_DIR})"
mkdir -p "$DATA_DIR" mkdir -p "$DATA_DIR"
# 查找 Chromium 可执行文件
CHROME_BIN=$(ls $HOME/.cache/ms-playwright/chromium-*/chrome-linux/chrome 2>/dev/null | head -1) CHROME_BIN=$(ls $HOME/.cache/ms-playwright/chromium-*/chrome-linux/chrome 2>/dev/null | head -1)
if [ -z "$CHROME_BIN" ]; then if [ -z "$CHROME_BIN" ]; then
CHROME_BIN=$(which chromium-browser 2>/dev/null || which google-chrome-stable 2>/dev/null || true) CHROME_BIN=$(which chromium-browser 2>/dev/null || which google-chrome-stable 2>/dev/null || true)
fi fi
[ -z "$CHROME_BIN" ] && { err "Chromium not found"; exit 1; } [ -z "$CHROME_BIN" ] && { err "未找到 Chromium"; exit 1; }
# 启动 Chromium非 headless 模式,可见界面)
"$CHROME_BIN" \ "$CHROME_BIN" \
--no-sandbox \ --no-sandbox \
--disable-gpu \ --disable-gpu \
@ -55,16 +59,18 @@ fi
CHROME_PID=$! CHROME_PID=$!
log "Starting x11vnc on port ${VNC_PORT}..." log "正在启动 x11vnc端口: ${VNC_PORT}..."
x11vnc -display :${DISPLAY_NUM} -forever -shared -passwd ${VNC_PWD} -rfbport ${VNC_PORT} -bg & x11vnc -display :${DISPLAY_NUM} -forever -shared -passwd ${VNC_PWD} -rfbport ${VNC_PORT} -bg &
# 获取服务器公网 IP
PUBLIC_HOST=$(curl -sf ifconfig.me 2>/dev/null || hostname -I | awk '{print $1}') PUBLIC_HOST=$(curl -sf ifconfig.me 2>/dev/null || hostname -I | awk '{print $1}')
log "==========================================" log "=========================================="
log "✅ Browser started (VNC only, no CDP)" log "✅ 浏览器已启动 (仅 VNC CDP)"
log "🖥️ VNC: ${PUBLIC_HOST}:${VNC_PORT}" log "🖥️ VNC 地址: ${PUBLIC_HOST}:${VNC_PORT}"
log "🔑 Password: ${VNC_PWD}" log "🔑 VNC 密码: ${VNC_PWD}"
log "📁 Data: ${DATA_DIR}" log "📁 数据目录: ${DATA_DIR}"
log "🛑 Stop: ./browser-vnc.sh stop ${DISPLAY_NUM}" log "🛑 停止命令: ./browser-vnc.sh stop ${DISPLAY_NUM}"
log "==========================================" log "=========================================="
# 等待 Chromium 进程结束
wait $CHROME_PID 2>/dev/null || true wait $CHROME_PID 2>/dev/null || true

24
bin_bak/start_spider.sh Normal file
View File

@ -0,0 +1,24 @@
#!/bin/bash
spider_code=$1
index=$2
PUBLIC_DIR="${2:-/shared}"
export DISPLAY=:$index
echo "当前显示桌面:$DISPLAY"
start_spider(){
echo "正在启动爬虫$spider_code"
if [ "$spider_code" = "01" ]; then
spider_name="china_net.py"
elif [ "$spider_code" = "02" ]; then
spider_name="mail_qq.py"
else
echo "爬虫名称错误"
exit -1
fi
echo "爬虫名称:$spider_name"
.venv/bin/python3.12 "spider/$spider_name"
}
echo "启动爬虫"
cd $PUBLIC_DIR/learn-spider
echo "$PUBLIC_DIR/learn-spider"
start_spider

40
bin_bak/uninstall.sh Normal file
View File

@ -0,0 +1,40 @@
#!/bin/bash
USER_COUNT="${1:-10}"
PUBLIC_DIR="${2:-/shared}"
SECOND="${3:-5}"
batch_kill_process(){
echo "正在结束进程"
for i in $(seq 1 $USER_COUNT); do
username="user$i"
# 杀死用户所有进程
sudo pkill -u "$username" 2>/dev/null
done
echo "进程结束完成"
}
batch_delete_user(){
for i in $(seq 1 $USER_COUNT); do
username="user$i"
# 杀死用户所有进程
sudo userdel -r "$username" 2>/dev/null
if [ $? -eq 0 ]; then
echo "已删除用户: $username"
else
echo "用户 $username 不存在,跳过"
fi
done
echo "批量删除完成!"
}
for i in $(seq 1 $SECOND); do
clear
echo "$(($SECOND-$i))S后开始数据清理...."
sleep 1
done
batch_kill_process
sleep 5
batch_delete_user
sudo rm -rf $PUBLIC_DIR
echo "数据清理完成"

17
bin_bak/update.sh Normal file
View File

@ -0,0 +1,17 @@
#!/bin/bash
PUBLIC_DIR="${1:-/shared}"
GIT_PY_SPIDER="${2:-https://git.nps.crabapples.cn}"
echo "开始更新爬虫"
cd $PUBLIC_DIR
if [ -d "learn-spider" ]; then
cd learn-spider
sudo git pull
else
sudo git clone $GIT_PY_SPIDER
cd learn-spider
fi
sudo chmod +x ./pre_install_spider.sh ./start_spider.sh
sudo ./pre_install_spider.sh
echo "✅ [完成] 爬虫更新完成"