Spaces:
Paused
Paused
File size: 8,085 Bytes
3e22808 c788633 c8abac0 3e22808 c788633 0a2e169 944926e 3e22808 c8abac0 f4851b0 c8abac0 3e22808 c848d83 3e22808 c860065 c848d83 c860065 a6f5f9d c848d83 14947a4 87572af c848d83 87572af c848d83 c788633 c848d83 d2ae842 3c4691b c848d83 3c4691b c848d83 3bd402b c848d83 c788633 3c4691b c848d83 7bd4402 e34cd33 c848d83 3c4691b c848d83 7bd4402 e34cd33 7bd4402 3c4691b c848d83 c788633 b62431c c788633 b62431c fdbaa4c b62431c fdbaa4c b62431c 489bf6e b62431c c788633 3c4691b c788633 c848d83 c788633 c848d83 3c4691b c848d83 3c4691b ec2586a 3c4691b c848d83 87572af | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | FROM python:3.11-slim
# 1. 安装 Node.js 及 Nginx 防盗门依赖组件
RUN apt-get update && apt-get install -y git curl nginx apache2-utils && \
curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
apt-get install -y nodejs && \
rm -rf /var/lib/apt/lists/*
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
WORKDIR /app
ENV UPDATE_CACHE_BUSTER="2026-06-11"
RUN git clone https://github.com/nousresearch/hermes-agent.git .
# 2. 编译 Web 界面并安装后端
RUN cd web && npm install && npm run build
RUN uv pip install --system -e ".[all]"
RUN uv pip install --system huggingface_hub
# =================================================================
# 核心环境变量与 CPU 环境优化
# =================================================================
ENV HOST=0.0.0.0
ENV PORT=7860
ENV HERMES_PORT=8000
ENV HERMES_GATEWAY_PLATFORM=feishu
ENV GATEWAY_ALLOW_ALL_USERS=true
# 强制禁用 GPU 探测,优化纯 CPU 环境下的进程调度与内存占用
ENV CUDA_VISIBLE_DEVICES="-1"
ENV OMP_NUM_THREADS=4
# =================================================================
# 3. 完美容灾、云端备份与防篡改启动脚本
# =================================================================
RUN cat <<'EOF' > /start.sh
#!/bin/bash
mkdir -p /root/.hermes
# --- 步骤 A: 开机自动从云端 Dataset 恢复数据 (带try-except终极防卡死补丁) ---
if [ -n "$HF_DATASET" ] && [ -n "$HF_TOKEN" ]; then
echo ">>> 探测到云端备份配置,正在尝试恢复数据..."
python3 -c "
import os, shutil
from huggingface_hub import snapshot_download
try:
snapshot_download(repo_id=\"$HF_DATASET\", repo_type=\"dataset\", local_dir=\"/root/.hermes\", token=\"$HF_TOKEN\")
print('>>> 云端数据同步成功。')
except Exception as e:
print('>>> [警告] 云端历史数据存在大小不匹配或损坏,已自动跳过并执行干净启动。')
"
else
echo ">>> 未完全配置 HF_DATASET 或 HF_TOKEN,跳过恢复。"
fi
# --- 步骤 B: 原有的防篡改与初始配置保底逻辑 ---
if [ ! -f /root/.hermes/config.yaml ]; then
echo ">>> 未发现本地配置,生成默认 Gemini 配置文件..."
cat << 'CONF' > /root/.hermes/config.yaml
provider: gemini
model: gemini-2.5-flash
auxiliary:
title_generation:
provider: gemini
model: gemini-2.5-flash
compression:
provider: gemini
model: gemini-2.5-flash
session_search:
provider: gemini
model: gemini-2.5-flash
CONF
fi
# --- 步骤 C: 定义优雅退出的 Trap 机制 (防止被杀时丢数据) ---
cleanup() {
echo ">>> 接收到终止信号 (SIGTERM)!准备冻结状态并执行紧急备份..."
service nginx stop
kill -SIGTERM $GATEWAY_PID $DASHBOARD_PID 2>/dev/null
wait $GATEWAY_PID $DASHBOARD_PID 2>/dev/null
if [ -n "$HF_DATASET" ] && [ -n "$HF_TOKEN" ]; then
echo ">>> 执行终极数据持久化 (隔离运行锁)..."
# 隔离可能导致 I/O 错误的锁文件
mv /root/.hermes/gateway.lock /tmp/gateway.lock.bak 2>/dev/null
mv /root/.hermes/gateway.log /tmp/gateway.log.bak 2>/dev/null
python3 -c "from huggingface_hub import HfApi; api=HfApi(); api.upload_folder(folder_path=\"/root/.hermes\", repo_id=\"$HF_DATASET\", repo_type=\"dataset\", ignore_patterns=[\"*.db-wal\", \"*.db-shm\", \"*.db-journal\", \"pairing*\", \"*.tmp\", \"*.mp4\", \"*.avi\", \"*.mov\", \"*.mkv\", \"*.wav\", \"*.mp3\", \"*.lock\"], commit_message=\"Emergency backup on exit\", token=\"$HF_TOKEN\")"
fi
echo ">>> 备份完成,容器安全退出。"
exit 0
}
trap cleanup SIGTERM SIGINT
# --- 步骤 D: 启动后台定时备份 (每15分钟 - 完美闭合版) ---
(while true; do
sleep 900
if [ -n "$HF_DATASET" ] && [ -n "$HF_TOKEN" ]; then
echo ">>> 触发每15分钟定时增量同步 (物理隔离锁文件)..."
# 1. 移走文件,避开扫描
mv /root/.hermes/gateway.lock /tmp/gateway.lock.bak 2>/dev/null
mv /root/.hermes/gateway.log /tmp/gateway.log.bak 2>/dev/null
# 2. 执行上传
python3 -c "from huggingface_hub import HfApi; api=HfApi(); api.upload_folder(folder_path=\"/root/.hermes\", repo_id=\"$HF_DATASET\", repo_type=\"dataset\", ignore_patterns=[\"*.db-wal\", \"*.db-shm\", \"*.db-journal\", \"pairing*\", \"*.tmp\", \"*.mp4\", \"*.avi\", \"*.mov\", \"*.mkv\", \"*.wav\", \"*.mp3\", \"*.lock\"], commit_message=\"Auto routine backup\", token=\"$HF_TOKEN\")"
# 3. 还原文件
mv /tmp/gateway.lock.bak /root/.hermes/gateway.lock 2>/dev/null
mv /tmp/gateway.log.bak /root/.hermes/gateway.log 2>/dev/null
fi
done) &
SYNC_PID=$!
# --- 步骤 E: 配置并启动 Nginx 防盗门 ---
echo ">>> 配置 Nginx 访问拦截策略..."
cat << 'NGINX' > /etc/nginx/sites-available/default
server {
listen 7860;
location / {
auth_basic "Restricted Access - Please Login";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://127.0.0.1:7861;
proxy_http_version 1.1;
# 维持 WebSocket 长连接
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 核心修复:彻底欺骗后端,伪装成本地请求,绕过所有 Host 和 Origin 跨域拦截
proxy_set_header Host 127.0.0.1;
proxy_set_header Origin "http://127.0.0.1:7861";
# 关闭缓冲,让 SSE 实时日志和打字机效果秒级透传
proxy_buffering off;
proxy_cache off;
chunked_transfer_encoding on;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 保持超长等待时间,防止大模型思考过久断开
proxy_read_timeout 3600;
proxy_send_timeout 3600;
}
}
NGINX
if [ -n "$WEB_USER" ] && [ -n "$WEB_PASS" ]; then
htpasswd -bc /etc/nginx/.htpasswd "$WEB_USER" "$WEB_PASS"
echo ">>> 已设置自定义 Web 登录密码。"
else
RANDOM_PASS=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 16)
htpasswd -bc /etc/nginx/.htpasswd admin "$RANDOM_PASS"
echo "================================================================="
echo "⚠️ 警告: 未检测到 WEB_USER 或 WEB_PASS 环境变量!"
echo "已触发安全保护机制,自动生成临时随机密码 (每次重启都会更换)。"
echo "默认账号: admin"
echo "随机密码: $RANDOM_PASS"
echo "================================================================="
fi
service nginx start
# --- 步骤 F: 启动核心业务进程 ---
echo ">>> 启动飞书 Gateway 守护进程..."
env PORT=8000 HERMES_PORT=8000 hermes gateway > /root/.hermes/gateway.log 2>&1 &
GATEWAY_PID=$!
echo ">>> 启动 Dashboard 面板 (绑定本地隐藏端口 7861)..."
hermes dashboard --port 7861 --host 127.0.0.1 --insecure &
DASHBOARD_PID=$!
# 双进程看守,任何一个崩溃即触发全面紧急备份退出
wait $DASHBOARD_PID $GATEWAY_PID
EOF
RUN chmod +x /start.sh
# 封装快捷一键手动备份口令(供未来飞书或Web终端直接调用)
# 封装快捷一键手动备份口令(供未来飞书或Web终端直接调用)
RUN echo '#!/bin/bash\nmv /root/.hermes/gateway.lock /tmp/gateway.lock.bak 2>/dev/null\nmv /root/.hermes/gateway.log /tmp/gateway.log.bak 2>/dev/null\npython3 -c "from huggingface_hub import HfApi; api=HfApi(); api.upload_folder(folder_path=\"/root/.hermes\", repo_id=\"$HF_DATASET\", repo_type=\"dataset\", ignore_patterns=[\"*.db-wal\", \"*.db-shm\", \"*.db-journal\", \"pairing*\", \"*.tmp\", \"*.mp4\", \"*.avi\", \"*.mov\", \"*.mkv\", \"*.wav\", \"*.mp3\", \"*.lock\"], commit_message=\"Manual backup via Feishu\", token=\"$HF_TOKEN\")"\nmv /tmp/gateway.lock.bak /root/.hermes/gateway.lock 2>/dev/null\nmv /tmp/gateway.log.bak /root/.hermes/gateway.log 2>/dev/null' > /usr/local/bin/force-backup && chmod +x /usr/local/bin/force-backup
EXPOSE 7860
CMD ["/start.sh"] |