This commit is contained in:
parent
826e54c378
commit
e34897a136
|
|
@ -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
|
||||||
|
|
@ -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
150
app.py
|
|
@ -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 实例的用户数据
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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 "安装完成"
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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 "数据清理完成"
|
||||||
|
|
@ -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 "✅ [完成] 爬虫更新完成"
|
||||||
Loading…
Reference in New Issue