hermes-agent / Dockerfile
shuiseliuli's picture
Update Dockerfile
fdbaa4c verified
Raw
History Blame Contribute Delete
8.09 kB
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"]