
🎯 博主简介
CSDN 「新星创作者」 人工智能技术领域博主,码龄5年 ,累计发布
180+篇原创文章,博客总访问量24万+浏览!
🚀 持续更新AI前沿实战知识,专注于 AI 技术实战,多个专栏详细解析,包括:
- 🔥 生产级 RAG 系统--- 架构设计、多路召回、混合检索、生产优化
- 🤖 多 Agent 协作系统 --- Swarm 架构、任务编排、状态管理
- 🔌 MCP 协议 --- 协议规范与深度实现
- ⚡ OpenClaw超详细教程 --- AI 助手框架进阶应用
- ✨ Agent记忆系统 --- 深度解析Agent记忆系统
- ✨ Hermes Agent + Obsidian 打造第二大脑:14篇文章讲透第二大脑搭建!
同时也涉猎计算机视觉、Java 后端与 Spring 生态、Transformer 等深度学习技术。坚持从架构到代码、从原理到部署的实战风格。每篇文章配套代码与扩展资料,欢迎交流探讨。📱:
安逸Ai--- 每天一点AI:资料、笔记、工具、趋势,一起进步。
📑 目录
- [1. Docker 部署概述](#1. Docker 部署概述)
- [1.1 为什么选择 Docker](#1.1 为什么选择 Docker)
- [1.2 部署架构总览](#1.2 部署架构总览)
- [1.3 组件通信原理](#1.3 组件通信原理)
- [2. 完整部署流程](#2. 完整部署流程)
- [2.1 获取源码](#2.1 获取源码)
- [2.2 环境配置](#2.2 环境配置)
- [2.3 启动服务](#2.3 启动服务)
- [2.4 验证部署](#2.4 验证部署)
- [3. Obsidian 目录挂载](#3. Obsidian 目录挂载)
- [3.1 创建挂载目录](#3.1 创建挂载目录)
- [3.2 配置权限](#3.2 配置权限)
- [3.3 验证同步](#3.3 验证同步)
- [3.4 双向同步原理](#3.4 双向同步原理)
- [4. 生产环境配置](#4. 生产环境配置)
- [4.1 HTTPS 配置](#4.1 HTTPS 配置)
- [4.2 域名配置](#4.2 域名配置)
- [4.3 反向代理配置](#4.3 反向代理配置)
- [4.4 SSL 证书自动续期](#4.4 SSL 证书自动续期)
- [5. 运维管理](#5. 运维管理)
- [5.1 日志查看](#5.1 日志查看)
- [5.2 监控配置](#5.2 监控配置)
- [5.3 定时任务](#5.3 定时任务)
- [6. 扩容方案](#6. 扩容方案)
- [6.1 水平扩容](#6.1 水平扩容)
- [6.2 垂直扩容](#6.2 垂直扩容)
- [6.3 Kubernetes 部署方案](#6.3 Kubernetes 部署方案)
- [7. 故障排查](#7. 故障排查)
- [7.1 常见问题](#7.1 常见问题)
- [7.2 排查命令](#7.2 排查命令)
- [7.3 日志分析](#7.3 日志分析)
- [8. 迁移指南](#8. 迁移指南)
- [8.1 同服务器迁移](#8.1 同服务器迁移)
- [8.2 跨服务器迁移](#8.2 跨服务器迁移)
- [8.3 从开发环境到生产环境](#8.3 从开发环境到生产环境)
- [9. 监控与告警](#9. 监控与告警)
- [9.1 Prometheus + Grafana 监控](#9.1 Prometheus + Grafana 监控)
- [9.2 日志收集 ELK 方案](#9.2 日志收集 ELK 方案)
- [9.3 告警规则配置](#9.3 告警规则配置)
- [10. 常见问题 FAQ](#10. 常见问题 FAQ)
- [11. 总结](#11. 总结)
1. Docker 部署概述
1.1 为什么选择 Docker
Docker 是部署 Hermes Agent 的推荐方式,原因如下:
| 维度 | 传统部署 | Docker 部署 |
|---|---|---|
| 环境依赖 | 手动安装 Python、Node、PostgreSQL | 一键搞定 |
| 环境一致性 | "在我机器上能跑"问题 | 跨环境完全一致 |
| 版本管理 | 升级困难 | docker compose pull 即可 |
| 资源隔离 | 互相影响 | 独立容器,互不干扰 |
| 清理卸载 | 残留文件难以清除 | docker compose down 干净卸载 |
| 水平扩展 | 复杂 | Docker Swarm / K8s 原生支持 |
| 快速回滚 | 困难 | 版本标签,原生支持 |
┌─────────────────────────────────────────────────────────────────┐
│ Docker 部署优势图解 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 传统部署 Docker 部署 │
│ │
│ ┌─────────┐ ┌─────────┐ │
│ │ Python │ │ Container │ │
│ │ Node.js │ │ hermes-api │ │
│ │ Postgres│ =====> │ hermes-web │ │
│ │ Redis │ │ hermes-worker│ │
│ │ ... │ │ postgres │ │
│ └─────────┘ │ redis │ │
│ │ └──────────────┘ │
│ ▼ │ │
│ 环境冲突 环境隔离 │
│ 版本冲突 版本固定 │
│ 配置复杂 配置简单 │
│ │
└─────────────────────────────────────────────────────────────────┘
1.2 部署架构总览
┌────────────────────────────────────────────────────────────────────────────┐
│ Hermes Agent 部署架构 │
├────────────────────────────────────────────────────────────────────────────┤
│ │
│ 用户浏览器/应用 │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ 反向代理 (Nginx) │ ← HTTPS + 域名 │
│ │ 端口: 80/443 │ │
│ └──────────┬──────────┘ │
│ │ │
│ ┌──────────▼──────────┐ │
│ │ Docker Network │ │
│ │ hermes-network │ │
│ └──────────┬──────────┘ │
│ │ │
│ ┌───────────────────┼───────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Web │ │ API │ │ Worker │ │
│ │ :3000 │ │ :8080 │ │ (队列) │ │
│ └───────────┘ └─────┬─────┘ └───────────┘ │
│ │ │
│ ┌───────────────────┼───────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Postgres │ │ Redis │ │ Vault │ │
│ │ :5432 │ │ :6379 │ │ /data/* │ │
│ └───────────┘ └───────────┘ └───────────┘ │
│ │
│ 外部服务 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ OpenAI │ │ Anthropic │ │ Ollama │ │
│ │ API │ │ API │ │ (本地模型) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────────────┘
1.3 组件通信原理
┌─────────────────────────────────────────────────────────────────┐
│ 组件间通信流程图 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. 用户请求流程 │
│ │
│ 用户 ──▶ Nginx ──▶ Web (:3000) ──▶ API (:8080) │
│ │ │
│ ▼ │
│ ┌─────────┐ │
│ │ Worker │ │
│ │ (异步) │ │
│ └────┬────┘ │
│ │ │
│ 2. 数据写入流程 │
│ │
│ API ──▶ PostgreSQL (:5432) │
│ API ──▶ Redis (:6379) │
│ API ──▶ Vault (文件系统) │
│ API ──▶ LLM Provider (外部 API) │
│ │
│ 3. 任务队列流程 │
│ │
│ API ──入队──▶ Redis Queue ──出队──▶ Worker │
│ │ │
│ ▼ │
│ ┌─────────┐ │
│ │ 处理任务 │ │
│ │ (搜索索引│ │
│ │ 等) │ │
│ └─────────┘ │
│ │
│ 4. Web 与 API 通信 │
│ │
│ Web ──HTTP──▶ API (REST) │
│ Web ◀──SSE─── API (实时推送) │
│ │
└─────────────────────────────────────────────────────────────────┘
端口与协议对应表:
| 源容器 | 目标容器 | 目标端口 | 协议 | 用途 |
|---|---|---|---|---|
| web | api | 8080 | HTTP | REST API 调用 |
| api | postgres | 5432 | PostgreSQL | 数据读写 |
| api | redis | 6379 | Redis | 缓存/队列 |
| api | vault | - | 文件系统 | 文件读写 |
| api | llm | 外部 | HTTPS | LLM API 调用 |
| worker | postgres | 5432 | PostgreSQL | 数据读写 |
| worker | redis | 6379 | Redis | 队列消费 |
| worker | vault | - | 文件系统 | 文件读写 |
2. 完整部署流程
2.1 获取源码
bash
# 创建工作目录
mkdir -p ~/hermes-agent && cd ~/hermes-agent
# 克隆官方源码(如果有公开仓库)
git clone https://github.com/hermes-agent/hermes-agent.git .
# 或者手动创建 docker-compose.yml 和配置文件
# 参考 02 篇的完整配置
# 查看目录结构
ls -la
完整目录结构:
hermes-agent/
├── docker-compose.yml # 容器编排配置(核心)
├── docker-compose.override.yml # 开发环境覆盖配置
├── docker-compose.prod.yml # 生产环境配置
├── .env # 环境变量(不提交 Git)
├── .env.example # 环境变量模板
├── .env.production # 生产环境变量模板
├── .gitignore # Git 忽略配置
├── data/ # 数据目录
│ ├── vault/ # Obsidian Vault 挂载点
│ ├── postgres/ # PostgreSQL 数据持久化
│ ├── redis/ # Redis 数据持久化
│ └── backups/ # 备份目录
├── logs/ # 日志目录
├── scripts/ # 运维脚本
│ ├── backup.sh # 备份脚本
│ ├── restore.sh # 恢复脚本
│ ├── healthcheck.sh # 健康检查脚本
│ ├── migrate.sh # 数据迁移脚本
│ └── ssl-renew.sh # SSL 证书续期脚本
├── nginx/ # Nginx 配置
│ ├── nginx.conf # 主配置
│ └── conf.d/ # 额外配置
│ └── hermes.conf # Hermes 站点配置
├── prometheus/ # Prometheus 配置
│ └── prometheus.yml
├── grafana/ # Grafana 配置
│ └── provisioning/
├── README.md # 项目说明
├── CHANGELOG.md # 变更日志
└── LICENSE # 许可证
2.2 环境配置
复制环境变量模板:
bash
cp .env.example .env
编辑 .env 文件:
bash
nano .env # 或使用 VS Code: code .env
关键配置项详解:
bash
# ============================================
# 必须配置
# ============================================
# LLM API Key(从 OpenAI/Anthropic 获取)
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
# 或者使用 Anthropic
# ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxxxxx
# 安全密钥(生成随机字符串)
JWT_SECRET=your-jwt-secret-change-this-min-32-chars
API_SECRET_KEY=your-api-secret-change-this-min-32-chars
# Obsidian Vault 路径
OBSIDIAN_VAULT_PATH=/data/vault
# ============================================
# 可选配置(按需调整)
# ============================================
# LLM 提供商选择
LLM_PROVIDER=openai # openai | anthropic | ollama | azure
# 模型配置
OPENAI_MODEL=gpt-4o
OPENAI_API_BASE=https://api.openai.com/v1
OPENAI_MAX_TOKENS=4096
OPENAI_TEMPERATURE=0.7
# Anthropic 模型
ANTHROPIC_MODEL=claude-3-5-sonnet-20241022
ANTHROPIC_MAX_TOKENS=4096
# Ollama 本地模型(需要本地部署)
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_MODEL=llama3:70b
# Azure OpenAI(企业用户)
AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com
AZURE_OPENAI_KEY=your-azure-key
AZURE_OPENAI_DEPLOYMENT=gpt-4o
# ============================================
# 端口配置(一般不需要修改)
# ============================================
WEB_PORT=3000
API_PORT=8080
# ============================================
# 数据库配置(生产环境务必修改密码)
# ============================================
POSTGRES_DB=hermes
POSTGRES_USER=hermes
POSTGRES_PASSWORD=your-secure-db-password-here
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
POSTGRES_MAX_CONNECTIONS=200
# ============================================
# Redis 配置(生产环境务必修改密码)
# ============================================
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=your-secure-redis-password-here
REDIS_DB=0
# ============================================
# 同步配置
# ============================================
SYNC_INTERVAL=30 # 同步间隔(秒)
SYNC_STRATEGY=keep_both # 同步策略
ENABLE_FILE_LOCK=true
LOCK_TIMEOUT=30
# ============================================
# Worker 配置
# ============================================
WORKER_CONCURRENCY=5 # 并发 worker 数量
WORKER_QUEUE=hermes_tasks # 任务队列名称
WORKER_RETRY_ATTEMPTS=3 # 重试次数
WORKER_RETRY_DELAY=60 # 重试延迟(秒)
# ============================================
# 日志配置
# ============================================
LOG_LEVEL=info # debug | info | warn | error
LOG_FORMAT=json # json | text
LOG_MAX_SIZE=50m # 单个日志文件大小
LOG_MAX_FILES=5 # 保留日志文件数量
# ============================================
# 性能配置
# ============================================
DB_POOL_SIZE=20 # 数据库连接池大小
DB_POOL_TIMEOUT=30 # 连接池超时(秒)
CACHE_TTL=3600 # 缓存 TTL(秒)
# ============================================
# 安全配置
# ============================================
CORS_ORIGINS=http://localhost:3000,https://your-domain.com
RATE_LIMIT_ENABLED=true
RATE_LIMIT_REQUESTS_PER_MINUTE=60
# ============================================
# 备份配置
# ============================================
BACKUP_ENABLED=true
BACKUP_DIR=/data/backups
BACKUP_RETENTION_DAYS=30
# ============================================
# 国际化
# ============================================
LANGUAGE=zh-CN
TIMEZONE=Asia/Shanghai
生成安全密钥:
bash
# 方法1: OpenSSL(推荐)
# JWT 密钥(32 字节随机)
openssl rand -base64 32
# 输出示例: Xk9mPqR2sT3vUw5yBz7Jh4Nu6Lc8Df1Eg0OoP3aQbM=
# API 密钥(64 字节十六进制)
openssl rand -hex 32
# 输出示例: a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456
# 方法2: Python
python3 -c "import secrets; print('JWT_SECRET=' + secrets.token_urlsafe(32))"
python3 -c "import secrets; print('API_SECRET_KEY=' + secrets.token_hex(32))"
# 方法3: macOS
cat /dev/urandom | base64 | head -c 32 | pbcopy
2.3 启动服务
Step 1: 配置 Docker 镜像加速(国内用户)
bash
# 编辑 Docker 配置文件
sudo tee /etc/docker/daemon.json << 'EOF'
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com",
"https://mirror.baidubce.com",
"https://docker.nju.edu.cn"
],
"builder": {
"gc": {
"enabled": true,
"defaultKeepStorage": "20GB"
}
},
"experimental": false,
"features": {
"buildkit": true
}
}
EOF
# 重启 Docker
sudo systemctl restart docker
# 验证镜像加速
docker info | grep -A 5 "Registry Mirrors"
Step 2: 拉取镜像
bash
# 拉取所有镜像(可能需要几分钟)
docker compose pull
# 或者只拉取特定镜像
docker compose pull api worker web postgres redis
# 查看已拉取的镜像
docker images | grep hermes
Step 3: 创建数据目录
bash
# 创建数据目录
mkdir -p data/vault data/postgres data/redis data/backups logs
# 设置权限(Docker 容器内用户需要写入权限)
chmod -R 755 data/
chmod -R 755 logs/
# 如果遇到权限问题,尝试
sudo chown -R 1000:1000 data/
Step 4: 启动服务
bash
# 后台启动所有服务
docker compose up -d
# 如果需要重新创建容器
docker compose up -d --force-recreate
# 查看启动进度
docker compose logs -f
Step 5: 查看状态
bash
# 查看所有容器状态
docker compose ps
# 查看资源使用
docker stats
# 查看网络
docker network ls | grep hermes
预期输出:
$ docker compose ps
NAME STATUS PORTS HEALTH
hermes-web Up (healthy) 0.0.0.0:3000->3000/tcp healthy
hermes-api Up (healthy) 0.0.0.0:8080->8080/tcp healthy
hermes-worker Up 0.0.0.0:8080 -
hermes-postgres Up (healthy) 5432/tcp healthy
hermes-redis Up (healthy) 6379/tcp healthy
2.4 验证部署
健康检查 API:
bash
# API 健康检查
curl http://localhost:8080/health
# 预期输出
{
"status": "ok",
"version": "1.0.0",
"uptime": 1234,
"services": {
"database": "connected",
"redis": "connected",
"vault": "accessible"
}
}
# 详细状态检查
curl http://localhost:8080/health/detailed
# 预期输出
{
"status": "ok",
"components": {
"api": {"status": "healthy", "responseTime": "5ms"},
"web": {"status": "healthy", "responseTime": "10ms"},
"postgres": {"status": "healthy", "latency": "1ms"},
"redis": {"status": "healthy", "latency": "0ms"},
"vault": {"status": "healthy", "path": "/data/vault"}
},
"llm": {
"provider": "openai",
"status": "configured",
"daily_usage": {"prompt_tokens": 1500, "completion_tokens": 3200}
}
}
访问 Web UI:
浏览器打开: http://localhost:3000
首次访问时需要创建管理员账号:
json
{
"email": "admin@example.com",
"password": "YourSecurePassword123!",
"name": "Admin"
}
API 文档:
访问: http://localhost:8080/api/docs
Swagger UI 会自动加载,显示所有可用 API 端点。
WebSocket 连接测试:
bash
# 测试 WebSocket 连接
curl -i -N \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
http://localhost:8080/api/ws
3. Obsidian 目录挂载
3.1 创建挂载目录
bash
# 在宿主机创建 Obsidian Vault 目录
mkdir -p ~/Obsidian\ Vault
# 创建基础文件夹结构
cd ~/Obsidian\ Vault
mkdir -p "0_临时" "1_日志" "2_项目" "3_知识"
# 创建 .obsidian 配置目录
mkdir -p .obsidian
# 创建模板目录
mkdir -p templates
Obsidian Vault 完整结构:
Obsidian Vault/
├── 📁 0_临时/ # 收集箱,临时想法
│ ├── 📝 inbox.md # 收件箱入口
│ └── 📝 临时笔记/
├── 📁 1_日志/ # 每日日志
│ ├── 📁 每日/ # 每日笔记
│ ├── 📁 周报/ # 周报
│ └── 📁 月报/ # 月报
├── 📁 2_项目/ # 项目笔记
│ ├── 📁 进行中/ # 正在进行的项目
│ ├── 📁 已完成/ # 已完成的项目
│ └── 📁 已归档/ # 已归档的项目
├── 📁 3_知识/ # 知识沉淀
│ ├── 📁 技术/ # 技术文档
│ ├── 📁 产品/ # 产品知识
│ ├── 📁 运营/ # 运营知识
│ └── 📁 设计/ # 设计知识
├── 📁 4_归档/ # 归档资料
├── 📁 templates/ # 模板文件
│ ├── 📝 daily.md # 每日模板
│ ├── 📝 weekly.md # 周报模板
│ └── 📝 project.md # 项目模板
└── 📁 .obsidian/ # Obsidian 配置
├── 📝 workspace.json
└── 📝 community-plugins.json
3.2 配置权限
bash
# Docker 容器以特定用户运行,需要配置权限
# 查看容器运行用户
docker compose exec api id
# 输出示例: uid=1000(hermes) gid=1000(hermes) groups=1000(hermes)
# 设置 Vault 目录权限
sudo chown -R 1000:1000 ~/Obsidian\ Vault
# 设置读写权限
chmod -R 755 ~/Obsidian\ Vault
chmod -R 777 ~/Obsidian\ Vault/.obsidian
# 如果使用 WSL2 或 macOS
chmod -R 777 ~/Obsidian\ Vault
权限问题排查流程:
┌─────────────────────────────────────────────────────────────────┐
│ 权限问题排查流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. 检查目录所有者 │
│ ls -la ~/Obsidian\ Vault │
│ │
│ 2. 检查 Docker 容器用户 │
│ docker compose exec api id │
│ │
│ 3. 匹配权限 │
│ sudo chown -R 1000:1000 ~/Obsidian\ Vault │
│ │
│ 4. 测试写入 │
│ docker compose exec api touch /data/vault/test.md │
│ │
│ 5. 从容器内删除测试文件 │
│ docker compose exec api rm /data/vault/test.md │
│ │
└─────────────────────────────────────────────────────────────────┘
3.3 验证同步
bash
# 在宿主机 Vault 中创建测试文件
echo "# 测试文件" > ~/Obsidian\ Vault/test-sync.md
# 在容器内检查文件是否存在
docker compose exec api ls -la /data/vault/
# 应该看到 test-sync.md
# 通过 Hermes API 触发同步
curl -X POST http://localhost:8080/api/v1/sync/trigger \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_TOKEN"
# 预期输出
{
"status": "success",
"message": "Sync triggered",
"files_processed": 1,
"sync_id": "sync_20260426_123456"
}
# 检查 Obsidian 是否能检测到新文件
# 打开 Obsidian,应该能在 vault 中看到 test-sync.md
3.4 双向同步原理
┌─────────────────────────────────────────────────────────────────┐
│ 双向同步原理图 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Obsidian (本地) Hermes (服务器) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 用户编辑笔记 │ │ │ │
│ │ ↓ │ │ │ │
│ │ 文件系统变化 │ ──同步──▶ │ 接收变化 │ │
│ │ ↓ │ │ ↓ │ │
│ │ Hermes Watcher│ │ 写入 Vault │ │
│ │ │ │ ↓ │ │
│ │ ◀──通知── │ │ 更新索引 │ │
│ │ │ │ │ │
│ └───────────────┘ └─────────────────┘ │
│ ↑ │ │
│ │ │ │
│ └──◀─── Hermes 推送变化 ◀─────────────┘ │
│ │
│ 同步冲突处理: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 本地版本 服务器版本 合并策略 │ │
│ │ ───────── ───────── ───────── │ │
│ │ keep_both → A.md A.md │ │
│ │ A_conflict_20260426.md │ │
│ │ │ │
│ │ newest_wins → 比较时间戳 保留最新 │ │
│ │ │ │
│ │ manual → 标记冲突 需手动解决 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
4. 生产环境配置
4.1 HTTPS 配置
使用 Let's Encrypt 免费证书:
bash
# 安装 certbot
sudo apt update
sudo apt install certbot python3-certbot-nginx
# 停止 Nginx(如果正在运行)
sudo systemctl stop nginx
# 生成证书(standalone 模式)
sudo certbot certonly --standalone -d your-domain.com
# 或者 nginx 模式(需要 Nginx 已配置好)
sudo certbot --nginx -d your-domain.com
# 查看证书位置
sudo ls -la /etc/letsencrypt/live/your-domain.com/
# 证书包含:
# - fullchain.pem (证书链)
# - privkey.pem (私钥)
# - cert.pem (服务器证书)
4.2 域名配置
DNS 记录设置:
| 记录类型 | 主机名 | 值 | TTL |
|---|---|---|---|
| A | @ | 你的服务器 IP | 300 |
| A | www | 你的服务器 IP | 300 |
| A | api | 你的服务器 IP | 300 |
| CNAME | * | @ | 300 |
bash
# 验证 DNS 解析
dig your-domain.com
nslookup your-domain.com
# 测试 API 子域名
dig api.your-domain.com
4.3 反向代理配置
完整 Nginx 配置:
nginx
# /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
access_log /var/log/nginx/access.log main;
# 性能优化
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json application/javascript
application/xml application/xml+rss text/javascript application/x-javascript;
# 限制请求
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
# 上游服务器
upstream hermes_api {
least_conn;
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
keepalive 32;
}
upstream hermes_web {
server 127.0.0.1:3000;
keepalive 32;
}
# HTTP 服务器(重定向到 HTTPS)
server {
listen 80;
server_name your-domain.com api.your-domain.com;
# Let's Encrypt 验证
location /.well-known/acme-challenge/ {
root /var/www/certbot;
try_files $uri =404;
}
# 强制跳转到 HTTPS
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS 主站点
server {
listen 443 ssl http2;
server_name your-domain.com;
# SSL 证书
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
# SSL 安全配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# HSTS(可选,启用后浏览器会强制使用 HTTPS)
# add_header Strict-Transport-Security "max-age=63072000" always;
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 上传大小限制
client_max_body_size 100M;
client_body_timeout 300s;
proxy_read_timeout 300s;
# Web UI
location / {
proxy_pass http://hermes_web;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
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_cache_bypass $http_upgrade;
proxy_buffering off;
}
# API 请求
location /api/ {
proxy_pass http://hermes_api;
proxy_http_version 1.1;
proxy_set_header Host $host;
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;
# 限流
limit_req zone=api_limit burst=20 nodelay;
# 超时设置
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
# WebSocket 支持
location /ws/ {
proxy_pass http://hermes_api;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 86400;
}
# 静态资源(如果有)
location /static/ {
alias /var/www/hermes/static/;
expires 30d;
add_header Cache-Control "public, immutable";
}
# 健康检查(无需认证)
location /health {
proxy_pass http://hermes_api;
proxy_http_version 1.1;
proxy_set_header Host $host;
access_log off;
}
# 错误页面
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
# API 专用子域名
server {
listen 443 ssl http2;
server_name api.your-domain.com;
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://hermes_api;
proxy_http_version 1.1;
proxy_set_header Host $host;
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;
}
}
}
启用配置并测试:
bash
# 测试配置语法
sudo nginx -t
# 重载 Nginx
sudo systemctl reload nginx
# 查看 Nginx 状态
sudo systemctl status nginx
4.4 SSL 证书自动续期
创建续期脚本:
bash
cat > ~/Scripts/ssl-renew.sh << 'EOF'
#!/bin/bash
# 停止 Nginx
sudo systemctl stop nginx
# 续期证书
sudo certbot renew --standalone --pre-hook "sudo systemctl stop nginx" \
--post-hook "sudo systemctl start nginx" \
--deploy-hook "sudo systemctl reload nginx"
# 如果只想测试续期(不实际执行)
# sudo certbot renew --dry-run
# 查看证书过期时间
sudo certbot certificates
EOF
chmod +x ~/Scripts/ssl-renew.sh
添加到 crontab:
bash
# 编辑 crontab
crontab -e
# 添加以下行(每天凌晨 2 点检查续期)
0 2 * * * ~/Scripts/ssl-renew.sh >> ~/logs/ssl-renew.log 2>&1
5. 运维管理
5.1 日志查看
bash
# 实时查看所有日志
docker compose logs -f
# 查看特定服务日志
docker compose logs -f api
docker compose logs -f web
docker compose logs -f worker
docker compose logs -f postgres
docker compose logs -f redis
# 查看最近 100 行日志
docker compose logs --tail 100
# 保存日志到文件
docker compose logs > hermes_logs_$(date +%Y%m%d).txt
# 搜索日志中的错误
docker compose logs | grep -i error
docker compose logs | grep -i "failed\|timeout\|exception"
# 查看特定时间范围的日志
docker compose logs --since "2026-04-26T10:00:00"
docker compose logs --until "2026-04-26T12:00:00"
# 实时查看多个服务的日志
docker compose logs -f api worker
日志级别说明:
| 级别 | 含义 | 使用场景 |
|---|---|---|
| DEBUG | 详细调试信息 | 排查问题时 |
| INFO | 一般信息 | 正常运行日志 |
| WARN | 警告信息 | 需要关注但不紧急 |
| ERROR | 错误信息 | 需要立即处理 |
| FATAL | 致命错误 | 服务不可用 |
5.2 监控配置
Docker Stats 实时监控:
bash
# 查看资源使用(实时刷新)
docker stats
# 查看特定容器资源使用
docker stats hermes-api hermes-worker hermes-web
# 持续监控(每秒刷新)
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}" --no-stream
# 格式化输出示例
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}" --no-stream
Prometheus + Grafana 监控方案:
yaml
# docker-compose.yml 添加监控服务
services:
prometheus:
image: prom/prometheus:latest
container_name: hermes-prometheus
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--storage.tsdb.retention.time=30d'
networks:
- hermes-network
grafana:
image: grafana/grafana:latest
container_name: hermes-grafana
restart: unless-stopped
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=change-me-in-production
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- ./grafana/provisioning:/etc/grafana/provisioning
- grafana-data:/var/lib/grafana
depends_on:
- prometheus
networks:
- hermes-network
volumes:
prometheus-data:
grafana-data:
prometheus.yml 配置:
yaml
global:
scrape_interval: 15s
evaluation_interval: 15s
alerting:
alertmanagers:
- static_configs:
- targets: []
rule_files: []
scrape_configs:
# Prometheus 自身监控
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
# Hermes API 监控
- job_name: 'hermes-api'
static_configs:
- targets: ['api:8080']
metrics_path: '/metrics'
scrape_interval: 10s
# Hermes Web 监控
- job_name: 'hermes-web'
static_configs:
- targets: ['web:3000']
metrics_path: '/metrics'
# Docker 容器监控
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
# PostgreSQL 监控
- job_name: 'postgres'
static_configs:
- targets: ['postgres-exporter:9187']
# Redis 监控
- job_name: 'redis'
static_configs:
- targets: ['redis-exporter:9121']
5.3 定时任务
自动备份脚本:
bash
cat > ~/Scripts/hermes-backup.sh << 'EOF'
#!/bin/bash
# ============================================
# Hermes Agent 备份脚本
# ============================================
set -euo pipefail
# 配置
BACKUP_DIR=~/backup/hermes
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=30
LOG_FILE=~/logs/backup.log
# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# 创建备份目录
mkdir -p "$BACKUP_DIR"
mkdir -p ~/logs
log "========== 开始备份 =========="
# 1. 备份 Obsidian Vault (Git)
log "备份 Vault (Git)..."
cd ~/Obsidian\ Vault
git add . 2>/dev/null || true
git commit -m "Auto backup $DATE" 2>/dev/null || true
git push origin main 2>/dev/null || log "警告: Git 推送失败"
log "Vault 备份完成"
# 2. 备份 PostgreSQL
log "备份 PostgreSQL..."
docker compose exec -T postgres pg_dump -U "${POSTGRES_USER:-hermes}" "${POSTGRES_DB:-hermes}" > "$BACKUP_DIR/postgres_$DATE.sql"
gzip "$BACKUP_DIR/postgres_$DATE.sql"
log "PostgreSQL 备份完成: postgres_$DATE.sql.gz"
# 3. 备份 Redis
log "备份 Redis..."
docker compose exec redis redis-cli -a "${REDIS_PASSWORD:-}" SAVE 2>/dev/null
docker cp $(docker compose ps -q redis):/data/dump.rdb "$BACKUP_DIR/redis_$DATE.rdb" 2>/dev/null || true
log "Redis 备份完成"
# 4. 备份 Docker 卷
log "备份 Docker 卷..."
tar -czf "$BACKUP_DIR/vault_$DATE.tar.gz" -C ~/ hermes-agent/data/vault 2>/dev/null || true
log "Vault 归档备份完成"
# 5. 清理旧备份
log "清理超过 $RETENTION_DAYS 天的备份..."
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete 2>/dev/null || true
find "$BACKUP_DIR" -name "*.rdb" -mtime +$RETENTION_DAYS -delete 2>/dev/null || true
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete 2>/dev/null || true
log "旧备份清理完成"
# 6. 显示备份统计
log "========== 备份统计 =========="
log "备份目录: $BACKUP_DIR"
log "最新备份:"
ls -lh "$BACKUP_DIR" | tail -5
log "磁盘使用:"
du -sh "$BACKUP_DIR"
log "========== 备份完成 =========="
EOF
chmod +x ~/Scripts/hermes-backup.sh
# 添加到 crontab
(crontab -l 2>/dev/null; echo "0 3 * * * ~/Scripts/hermes-backup.sh >> ~/logs/backup.log 2>&1") | crontab -
# 手动执行测试
~/Scripts/hermes-backup.sh
自动更新脚本:
bash
cat > ~/Scripts/hermes-update.sh << 'EOF'
#!/bin/bash
set -euo pipefail
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
cd ~/hermes-agent
log "========== 开始更新 =========="
# 1. 备份当前状态
log "备份 docker-compose.yml..."
cp docker-compose.yml docker-compose.yml.backup
# 2. 拉取最新镜像
log "拉取最新镜像..."
docker compose pull
# 3. 重启服务
log "重启服务..."
docker compose up -d
# 4. 检查服务状态
sleep 10
docker compose ps
# 5. 检查健康状态
log "检查健康状态..."
curl -sf http://localhost:8080/health > /dev/null && log "API 健康检查通过" || log "警告: API 健康检查失败"
curl -sf http://localhost:3000/health > /dev/null && log "Web 健康检查通过" || log "警告: Web 健康检查失败"
log "========== 更新完成 =========="
EOF
chmod +x ~/Scripts/hermes-update.sh
# 每周日凌晨 4 点更新
(crontab -l 2>/dev/null; echo "0 4 * * 0 ~/Scripts/hermes-update.sh >> ~/logs/update.log 2>&1") | crontab -
6. 扩容方案
6.1 水平扩容
多副本部署架构:
┌─────────────────────────────────────────────────────────────────┐
│ 水平扩容架构图 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 用户请求 │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Nginx LB │ │
│ │ (负载均衡) │ │
│ └──────┬──────┘ │
│ │ │
│ ┌─────────────────┼─────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ API #1 │ │ API #2 │ │ API #3 │ │
│ │ :8080 │ │ :8081 │ │ :8082 │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ └─────────────────┴─────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Worker#1│ │Worker#2 │ │Worker#3 │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ PostgreSQL │ ← 主库 │
│ │ (读写分离) │ │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
docker-compose.yml 多副本配置:
yaml
services:
api:
deploy:
replicas: 3 # 3 个副本
ports:
- "8080-8082:8080" # 端口映射范围
worker:
deploy:
replicas: 3 # 3 个副本
Nginx 负载均衡配置:
nginx
upstream hermes_api {
least_conn; # 最少连接算法
server 127.0.0.1:8080 weight=1 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8081 weight=1 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8082 weight=1 max_fails=3 fail_timeout=30s;
keepalive 32;
}
6.2 垂直扩容
资源配置调整:
yaml
services:
api:
deploy:
resources:
limits:
memory: 4G # 增加到 4GB
cpus: '4' # 增加到 4 核
reservations:
memory: 2G
cpus: '2'
worker:
deploy:
resources:
limits:
memory: 2G
cpus: '2'
reservations:
memory: 1G
postgres:
deploy:
resources:
limits:
memory: 4G
cpus: '2'
command: >
postgres
-c max_connections=500
-c shared_buffers=1GB
-c effective_cache_size=2GB
-c maintenance_work_mem=256MB
-c work_mem=16MB
6.3 Kubernetes 部署方案
K8s 部署架构:
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes 架构图 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ K8s Cluster │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ Namespace: hermes │ │ │
│ │ │ │ │ │
│ │ │ ┌───────────┐ ┌───────────┐ │ │ │
│ │ │ │ Service │ │ Service │ │ │ │
│ │ │ │ web │ │ api │ │ │ │
│ │ │ └─────┬─────┘ └─────┬─────┘ │ │ │
│ │ │ │ │ │ │ │
│ │ │ ┌─────┴─────┐ ┌─────┴─────┐ │ │ │
│ │ │ │ Deployment│ │ Deployment│ │ │ │
│ │ │ │ replicas │ │ replicas │ │ │ │
│ │ │ │ = 3 │ │ = 3 │ │ │ │
│ │ │ └───────────┘ └───────────┘ │ │ │
│ │ │ │ │ │ │ │
│ │ │ └────────┬───────┘ │ │ │
│ │ │ │ │ │ │
│ │ │ ┌─────────────┴─────────────┐ │ │ │
│ │ │ │ Ingress │ │ │ │
│ │ │ │ (Nginx Controller) │ │ │ │
│ │ │ └───────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │
│ │ │ ConfigMap│ │ Secret │ │ PVC │ │ │
│ │ │ env │ │ api-key │ │ vault │ │ │
│ │ └───────────┘ └───────────┘ └───────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
K8s 部署清单:
yaml
# hermes-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: hermes-api
namespace: hermes
spec:
replicas: 3
selector:
matchLabels:
app: hermes-api
template:
metadata:
labels:
app: hermes-api
spec:
containers:
- name: api
image: hermes/hermes-api:latest
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: hermes-config
- secretRef:
name: hermes-secrets
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "2Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: vault
persistentVolumeClaim:
claimName: hermes-vault
---
apiVersion: v1
kind: Service
metadata:
name: hermes-api
namespace: hermes
spec:
selector:
app: hermes-api
ports:
- port: 8080
targetPort: 8080
type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hermes-worker
namespace: hermes
spec:
replicas: 3
selector:
matchLabels:
app: hermes-worker
template:
metadata:
labels:
app: hermes-worker
spec:
containers:
- name: worker
image: hermes/hermes-worker:latest
envFrom:
- configMapRef:
name: hermes-config
- secretRef:
name: hermes-secrets
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "1Gi"
cpu: "500m"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: hermes-vault
namespace: hermes
spec:
accessModes:
- ReadWriteMany
storageClassName: nfs
resources:
requests:
storage: 50Gi
7. 故障排查
7.1 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 容器启动后立即退出 | 配置错误/端口占用 | docker compose logs 查看原因 |
| 数据库连接失败 | 启动顺序问题 | 添加 depends_on + condition: service_healthy |
| API 返回 401 | Key 配置错误 | 检查 .env 配置 |
| 页面加载慢 | 资源不足 | 增加内存/CPU 限制 |
| 同步失败 | 权限问题 | chmod 777 Vault 目录 |
| WebSocket 断开 | Nginx 未配置 ws 支持 | 添加 proxy_set_header Upgrade |
| SSL 证书过期 | 未配置自动续期 | 设置 certbot cron 任务 |
7.2 排查命令
bash
# 查看所有容器状态
docker compose ps
# 查看容器详细信息
docker inspect hermes-api
# 查看容器进程
docker top hermes-api
# 进入容器调试
docker compose exec api /bin/sh
docker compose exec postgres psql -U hermes
# 测试网络连通性
docker compose exec api ping postgres
docker compose exec api nc -zv postgres 5432
# 查看资源限制
docker inspect hermes-api | grep -A 20 "HostConfig"
# 查看容器日志
docker compose logs --tail 100 hermes-api
# 查看网络
docker network inspect hermes-network
# 查看卷
docker volume ls
docker volume inspect hermes_vault-data
7.3 日志分析
常见错误代码:
| 错误代码 | 含义 | 排查方法 |
|---|---|---|
| 500 | 服务器内部错误 | 查看 API 日志 docker compose logs api |
| 502 | 网关错误 | 检查上游服务是否运行 |
| 503 | 服务不可用 | 检查容器健康状态 |
| 504 | 网关超时 | 增加 proxy_read_timeout |
| 401 | 未授权 | 检查 JWT/API Key 配置 |
| 403 | 禁止访问 | 检查 CORS 和权限配置 |
| 404 | 未找到 | 检查路由配置 |
| 429 | 请求过多 | 启用/调整限流配置 |
日志分析脚本:
bash
cat > ~/Scripts/analyze-logs.sh << 'EOF'
#!/bin/bash
echo "========== Hermes 日志分析 =========="
echo ""
# 错误统计
echo "错误统计:"
docker compose logs --tail=1000 2>&1 | grep -i error | wc -l
echo "个错误"
echo ""
# 错误类型分布
echo "错误类型分布:"
docker compose logs --tail=1000 2>&1 | grep -i error | sed 's/.*\(ERROR\|error\).*//' | sort | uniq -c | sort -rn | head -10
echo ""
# 最近 20 条错误
echo "最近 20 条错误:"
docker compose logs --tail=100 --since "10m" 2>&1 | grep -i error | tail -20
echo ""
# 警告统计
echo "警告统计:"
docker compose logs --tail=1000 2>&1 | grep -i warn | wc -l
echo "个警告"
echo ""
# 慢查询(PostgreSQL)
echo "PostgreSQL 慢查询:"
docker compose logs postgres --since "1h" 2>&1 | grep -i "slow\|duration" | tail -10
EOF
chmod +x ~/Scripts/analyze-logs.sh
8. 迁移指南
8.1 同服务器迁移
bash
# 1. 停止服务
cd ~/hermes-agent
docker compose down
# 2. 备份数据
cp -r data data.backup.$(date +%Y%m%d)
cp docker-compose.yml docker-compose.yml.backup
cp .env .env.backup
# 3. 迁移目录
mv ~/hermes-agent ~/hermes-agent-new
# 4. 启动服务
cd ~/hermes-agent-new
docker compose up -d
# 5. 验证
curl http://localhost:8080/health
8.2 跨服务器迁移
bash
# ===== 在旧服务器上执行 =====
# 1. 停止服务
cd ~/hermes-agent
docker compose down
# 2. 备份所有数据
docker compose exec postgres pg_dump -U hermes hermes > hermes_db.sql
tar -czf vault_backup.tar.gz ~/Obsidian\ Vault
tar -czf data_backup.tar.gz data/
# 3. 传输文件到新服务器
scp hermes_db.sql user@new-server:~/hermes-agent/
scp vault_backup.tar.gz user@new-server:~/hermes-agent/
scp data_backup.tar.gz user@new-server:~/hermes-agent/
scp .env user@new-server:~/hermes-agent/
# 4. 传输 docker-compose.yml
scp docker-compose.yml user@new-server:~/hermes-agent/
# ===== 在新服务器上执行 =====
# 1. 安装 Docker(如果未安装)
curl -fsSL https://get.docker.com | sh
# 2. 创建目录
mkdir -p ~/hermes-agent
cd ~/hermes-agent
# 3. 恢复数据
tar -xzf vault_backup.tar.gz
tar -xzf data_backup.tar.gz
mv hermes_db.sql data/postgres/
# 4. 启动服务
docker compose up -d
# 5. 恢复数据库
docker compose exec -T postgres psql -U hermes -d hermes < data/postgres/hermes_db.sql
# 6. 验证
curl http://localhost:8080/health
docker compose ps
8.3 从开发环境到生产环境
bash
# 1. 在生产服务器创建目录
mkdir -p ~/hermes-agent && cd ~/hermes-agent
# 2. 复制配置文件
scp user@dev-server:~/hermes-agent/docker-compose.prod.yml ~/hermes-agent/
scp user@dev-server:~/hermes-agent/.env.production ~/hermes-agent/.env
# 3. 编辑 .env,更新生产配置
nano .env
# - 更新 API Keys(使用生产密钥)
# - 更新域名配置
# - 更新数据库密码
# 4. 拉取生产镜像
docker compose -f docker-compose.prod.yml pull
# 5. 启动服务
docker compose -f docker-compose.prod.yml up -d
# 6. 配置 Nginx(生产域名)
sudo cp nginx/hermes.prod.conf /etc/nginx/sites-available/hermes
sudo ln -s /etc/nginx/sites-available/hermes /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
9. 监控与告警
9.1 Prometheus + Grafana 监控
Grafana 仪表盘配置:
json
{
"dashboard": {
"title": "Hermes Agent 监控",
"tags": ["hermes", "agent"],
"timezone": "Asia/Shanghai",
"panels": [
{
"title": "API 请求率",
"type": "graph",
"targets": [
{
"expr": "rate(http_requests_total{service=\"api\"}[5m])",
"legendFormat": "{{method}} {{path}}"
}
]
},
{
"title": "API 响应时间",
"type": "graph",
"targets": [
{
"expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket{service=\"api\"}[5m]))",
"legendFormat": "p95"
},
{
"expr": "histogram_quantile(0.99, rate(http_request_duration_seconds_bucket{service=\"api\"}[5m]))",
"legendFormat": "p99"
}
]
},
{
"title": "容器 CPU 使用率",
"type": "graph",
"targets": [
{
"expr": "rate(container_cpu_usage_seconds_total{name=~\"hermes.*\"}[5m]) * 100",
"legendFormat": "{{name}}"
}
]
},
{
"title": "容器内存使用",
"type": "graph",
"targets": [
{
"expr": "container_memory_usage_bytes{name=~\"hermes.*\"}",
"legendFormat": "{{name}}"
}
]
},
{
"title": "任务队列长度",
"type": "gauge",
"targets": [
{
"expr": "redis_queue_length{queue=\"hermes_tasks\"}",
"legendFormat": "待处理任务"
}
]
},
{
"title": "数据库连接数",
"type": "graph",
"targets": [
{
"expr": "pg_stat_database_numbackends{datname=\"hermes\"}",
"legendFormat": "活动连接"
},
{
"expr": "pg_stat_database_max_connections",
"legendFormat": "最大连接"
}
]
}
]
}
}
9.2 日志收集 ELK 方案
┌─────────────────────────────────────────────────────────────────┐
│ ELK 日志收集架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Docker │───▶│ Filebeat│───▶│Logstash │───▶│Elastic │ │
│ │ Logs │ │ │ │ │ │search │ │
│ └─────────┘ └─────────┘ └─────────┘ └────┬────┘ │
│ │ │
│ ▼ │
│ ┌─────────┐ │
│ │ Grafana │ │
│ │ Kibana │ │
│ └─────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
9.3 告警规则配置
yaml
# alertmanager.yml
global:
smtp_smarthost: 'smtp.gmail.com:587'
smtp_from: 'hermes-alerts@gmail.com'
smtp_auth_username: 'your-email@gmail.com'
smtp_auth_password: 'your-app-password'
route:
group_by: ['alertname']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: 'email'
receivers:
- name: 'email'
email_configs:
- to: 'your-email@gmail.com'
send_resolved: true
# prometheus/rules/hermes-alerts.yml
groups:
- name: hermes
rules:
# 服务宕机告警
- alert: HermesServiceDown
expr: up{job="hermes-api"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Hermes API 服务不可用"
description: "Hermes API 已停止运行超过 1 分钟"
# 内存使用过高
- alert: HermesHighMemory
expr: (container_memory_usage_bytes{name=~"hermes.*"} / container_spec_memory_limit_bytes{name=~"hermes.*"}) > 0.9
for: 5m
labels:
severity: warning
annotations:
summary: "Hermes 内存使用率过高"
description: "内存使用率超过 90%"
# CPU 使用过高
- alert: HermesHighCPU
expr: rate(container_cpu_usage_seconds_total{name=~"hermes.*"}[5m]) > 0.8
for: 10m
labels:
severity: warning
annotations:
summary: "Hermes CPU 使用率过高"
description: "CPU 使用率持续超过 80%"
# 磁盘空间不足
- alert: DiskSpaceLow
expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) < 0.1
for: 5m
labels:
severity: critical
annotations:
summary: "服务器磁盘空间不足"
description: "磁盘剩余空间少于 10%"
# API 错误率过高
- alert: HermesHighErrorRate
expr: rate(http_requests_total{service="api",status=~"5.."}[5m]) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "Hermes API 错误率过高"
description: "5xx 错误率超过 5%"
# 任务队列积压
- alert: TaskQueueBacklog
expr: redis_queue_length{queue="hermes_tasks"} > 100
for: 15m
labels:
severity: warning
annotations:
summary: "Hermes 任务队列积压"
description: "待处理任务超过 100 个"
10. 常见问题 FAQ
Q1: 如何完全重启所有服务?
bash
docker compose down
docker compose up -d
Q2: 如何查看容器内部进程?
bash
# 进入容器 bash
docker compose exec api /bin/bash
# 进入 PostgreSQL
docker compose exec postgres psql -U hermes -d hermes
# 进入 Redis
docker compose exec redis redis-cli -a $REDIS_PASSWORD
Q3: 如何修改已运行容器的配置?
bash
# 修改 .env 文件后,重启服务
docker compose up -d
# 如果需要重新创建容器
docker compose up -d --force-recreate
# 只重启特定服务
docker compose restart api
Q4: 如何更新到新版本?
bash
# 拉取最新镜像
docker compose pull
# 重启服务
docker compose up -d
# 如果有数据库迁移
docker compose run --rm api migrate
Q5: 如何完全卸载?
bash
# 停止服务
docker compose down
# 删除数据(谨慎!)
docker compose down -v
# 删除镜像
docker compose down --rmi all
# 删除残留文件和配置
rm -rf ~/hermes-agent
Q6: 容器间网络不通怎么办?
bash
# 检查网络
docker network inspect hermes-network
# 重新创建网络
docker network rm hermes-network
docker compose up -d
# 在容器内测试连通性
docker compose exec api ping postgres
docker compose exec api nc -zv postgres 5432
Q7: 如何限制外部访问?
yaml
# 只允许本地访问
services:
web:
ports:
- "127.0.0.1:3000:3000" # 只有本地能访问
Q8: 如何配置多个环境(开发/测试/生产)?
bash
# 开发环境
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
# 测试环境
docker compose -f docker-compose.yml -f docker-compose.test.yml up -d
# 生产环境
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Q9: 如何排查 PostgreSQL 连接问题?
bash
# 检查 PostgreSQL 状态
docker compose ps postgres
# 查看日志
docker compose logs postgres | tail -50
# 测试连接
docker compose exec api nc -zv postgres 5432
# 进入 PostgreSQL
docker compose exec postgres psql -U hermes -d hermes
# 执行 SQL 测试
SELECT version();
SELECT 1;
Q10: 如何配置 API 限流?
yaml
# docker-compose.yml
api:
environment:
- RATE_LIMIT_ENABLED=true
- RATE_LIMIT_REQUESTS_PER_MINUTE=60
- RATE_LIMIT_BURST=10
Q11: 如何备份和恢复 Obsidian Vault?
bash
# 备份
tar -czf vault_backup_$(date +%Y%m%d).tar.gz ~/Obsidian\ Vault
# 恢复
tar -xzf vault_backup_20260426.tar.gz -C ~/
# 或使用 Git
cd ~/Obsidian\ Vault
git pull origin main
Q12: 如何升级 PostgreSQL 版本?
bash
# 1. 备份数据
docker compose exec postgres pg_dump -U hermes hermes > backup.sql
# 2. 修改 docker-compose.yml 中的 PostgreSQL 版本
# postgres:
# image: postgres:16-alpine # 改为 postgres:17-alpine
# 3. 停止服务
docker compose down
# 4. 拉取新镜像
docker compose pull postgres
# 5. 启动服务
docker compose up -d
# 6. 验证数据
docker compose exec postgres psql -U hermes -d hermes -c "SELECT version();"
Q13: 如何排查 Worker 任务不执行的问题?
bash
# 查看 worker 日志
docker compose logs worker
# 检查队列状态
docker compose exec redis redis-cli -a $REDIS_PASSWORD LLEN hermes_tasks
# 查看所有 key
docker compose exec redis redis-cli -a $REDIS_PASSWORD KEYS "*"
# 手动触发任务
curl -X POST http://localhost:8080/api/v1/tasks/test
# 重启 worker
docker compose restart worker
Q14: 如何配置外部存储(如 NFS)?
yaml
# docker-compose.yml
volumes:
vault-data:
driver: local
driver_opts:
type: nfs
o: addr=192.168.1.100,rw
device: ":/data/hermes-vault"
Q15: 如何查看容器的实时资源使用?
bash
# 实时资源监控
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}" --no-stream
# 查看特定容器
docker stats hermes-api hermes-worker
# 每 5 秒刷新一次
docker stats --interval 5s
11. 总结
Docker 部署要点:
┌─────────────────────────────────────────────────────────────────┐
│ Docker 部署核心要点 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ✅ 使用 docker compose 管理所有服务 │
│ ✅ 配置 .env 管理敏感信息(不提交 Git) │
│ ✅ 使用 healthcheck 确保启动顺序 │
│ ✅ 配置资源限制防止 OOM │
│ ✅ 设置自动备份(Vault + 数据库) │
│ ✅ 生产环境必须使用 HTTPS │
│ ✅ 配置日志轮转,防止磁盘爆满 │
│ ✅ 使用监控和告警,及时发现和处理问题 │
│ ✅ 定期测试备份恢复流程 │
│ ✅ 记录部署配置和变更,便于排查问题 │
│ │
└─────────────────────────────────────────────────────────────────┘
推荐生产配置:
| 配置项 | 推荐值 | 说明 |
|---|---|---|
| CPU | 4+ 核 | API 和 Worker 需要 |
| 内存 | 16GB+ | PostgreSQL + Redis + 各服务 |
| 存储 | 100GB+ SSD | 数据库 + Vault |
| 网络 | 100Mbps+ | API 响应和文件同步 |
| 副本 | 2-3 个 | API 和 Worker 多副本 |
后续优化方向:
- 配置读写分离的 PostgreSQL 集群
- 使用 Redis Sentinel 或 Cluster 提高可用性
- 迁移到 Kubernetes 进行容器编排
- 配置 CDN 加速静态资源
- 实现灰度发布和回滚机制