docs: 添加自动部署方案到 README
功能:
- Gitea Webhook + 更新脚本方案
- 自动拉取代码、构建镜像、重启容器
- 健康检查确认更新成功
作者: 小白 🐶
This commit is contained in:
100
README.md
100
README.md
@@ -631,6 +631,106 @@ volumes:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 自动部署 🔄
|
||||||
|
|
||||||
|
> Gitea 仓库提交后自动更新 Docker 容器
|
||||||
|
|
||||||
|
### 方案对比
|
||||||
|
|
||||||
|
| 方案 | 复杂度 | 说明 |
|
||||||
|
|------|--------|------|
|
||||||
|
| **方案 1: Webhook + 脚本** | ⭐ 简单 | 推荐,实现快、依赖少 |
|
||||||
|
| **方案 2: Gitea Actions** | ⭐⭐⭐ 中等 | 功能强大,需要配置 Runner |
|
||||||
|
| **方案 3: Watchtower** | ⭐⭐ 简单 | 自动监控镜像更新 |
|
||||||
|
|
||||||
|
### 推荐方案:Webhook + 脚本
|
||||||
|
|
||||||
|
#### 架构流程
|
||||||
|
|
||||||
|
```
|
||||||
|
开发者推送代码 → Gitea 触发 Webhook → 更新脚本执行
|
||||||
|
→ 拉取代码 → 构建镜像 → 重启容器 → 健康检查
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 1. 更新脚本
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# /www/wwwroot/pit-router/auto-update.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
cd /www/wwwroot/pit-router
|
||||||
|
|
||||||
|
# 拉取最新代码
|
||||||
|
git fetch origin && git reset --hard origin/main
|
||||||
|
|
||||||
|
# 重建并启动
|
||||||
|
docker-compose down
|
||||||
|
docker-compose build --no-cache
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
sleep 10
|
||||||
|
curl -sf http://localhost:1999/health && echo "✅ 更新成功"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Webhook 服务
|
||||||
|
|
||||||
|
```python
|
||||||
|
# /www/wwwroot/pit-router/webhook-server.py
|
||||||
|
|
||||||
|
from flask import Flask, request, jsonify
|
||||||
|
import hmac, hashlib, subprocess
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
SECRET = 'your-webhook-secret'
|
||||||
|
|
||||||
|
@app.route('/webhook/pit-router', methods=['POST'])
|
||||||
|
def handle():
|
||||||
|
# 验证签名
|
||||||
|
sig = request.headers.get('X-Gitea-Signature', '')
|
||||||
|
expected = 'sha256=' + hmac.new(SECRET.encode(), request.data, hashlib.sha256).hexdigest()
|
||||||
|
if not hmac.compare_digest(sig, expected):
|
||||||
|
return {'error': 'Invalid signature'}, 401
|
||||||
|
|
||||||
|
# 执行更新
|
||||||
|
result = subprocess.run(['/www/wwwroot/pit-router/auto-update.sh'], capture_output=True)
|
||||||
|
return {'status': 'success' if result.returncode == 0 else 'error'}
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='0.0.0.0', port=5001)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. 配置 Gitea Webhook
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "http://localhost:3000/api/v1/repos/yunxiafei/pit-router/hooks" \
|
||||||
|
-H "Authorization: token $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"type": "gitea",
|
||||||
|
"config": {
|
||||||
|
"url": "http://localhost:5001/webhook/pit-router",
|
||||||
|
"content_type": "json",
|
||||||
|
"secret": "your-webhook-secret"
|
||||||
|
},
|
||||||
|
"events": ["push"],
|
||||||
|
"active": true
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 实施清单
|
||||||
|
|
||||||
|
- [ ] 创建 `auto-update.sh` 脚本
|
||||||
|
- [ ] 创建 `webhook-server.py` 服务
|
||||||
|
- [ ] 配置 systemd 服务
|
||||||
|
- [ ] 配置 Gitea Webhook
|
||||||
|
- [ ] 测试自动更新
|
||||||
|
|
||||||
|
**预计时间**:30 分钟
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 相关项目
|
## 相关项目
|
||||||
|
|
||||||
- [PIT Channel 插件](http://1.14.58.157:3000/yunxiafei/PIT_Channel) - OpenClaw Channel 插件(智队中枢客户端)
|
- [PIT Channel 插件](http://1.14.58.157:3000/yunxiafei/PIT_Channel) - OpenClaw Channel 插件(智队中枢客户端)
|
||||||
|
|||||||
6
README.md.new
Normal file
6
README.md.new
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# 智队中枢(PIT Router)
|
||||||
|
|
||||||
|
> PIT 网关路由应用 - Personal Intelligent Team Gateway Router Service
|
||||||
|
|
||||||
|
**中文名**:智队中枢
|
||||||
|
**英文名**:PIT Router
|
||||||
378
docs/AUTO_DEPLOY.md
Normal file
378
docs/AUTO_DEPLOY.md
Normal file
@@ -0,0 +1,378 @@
|
|||||||
|
# 智队中枢 - 自动部署方案
|
||||||
|
|
||||||
|
> Gitea 仓库提交后自动更新 Docker 容器
|
||||||
|
|
||||||
|
## 📋 方案对比
|
||||||
|
|
||||||
|
| 方案 | 复杂度 | 优点 | 缺点 |
|
||||||
|
|------|--------|------|------|
|
||||||
|
| **方案 1: Webhook + 脚本** | ⭐ 简单 | 实现快、依赖少 | 功能单一 |
|
||||||
|
| **方案 2: Gitea Actions** | ⭐⭐⭐ 中等 | 功能强大、可扩展 | 需要配置 Runner |
|
||||||
|
| **方案 3: Watchtower** | ⭐⭐ 简单 | 自动化程度高 | 需要镜像仓库 |
|
||||||
|
|
||||||
|
**推荐方案**:方案 1(Webhook + 脚本)+ 方案 3(Watchtower)组合
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 方案 1: Gitea Webhook + 更新脚本
|
||||||
|
|
||||||
|
### 架构流程
|
||||||
|
|
||||||
|
```
|
||||||
|
开发者推送代码
|
||||||
|
↓
|
||||||
|
Gitea 仓库接收推送
|
||||||
|
↓
|
||||||
|
触发 Webhook (push 事件)
|
||||||
|
↓
|
||||||
|
Webhook 服务接收请求
|
||||||
|
↓
|
||||||
|
验证签名 + 执行更新脚本
|
||||||
|
↓
|
||||||
|
拉取最新代码 → 构建镜像 → 重启容器
|
||||||
|
↓
|
||||||
|
发送通知(可选)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 实现步骤
|
||||||
|
|
||||||
|
#### 1. 创建更新脚本
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# /www/wwwroot/pit-router/auto-update.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
LOG_FILE="/var/log/pit-router-update.log"
|
||||||
|
REPO_DIR="/www/wwwroot/pit-router"
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
log "========== 开始自动更新 =========="
|
||||||
|
|
||||||
|
cd "$REPO_DIR"
|
||||||
|
|
||||||
|
# 1. 拉取最新代码
|
||||||
|
log "拉取最新代码..."
|
||||||
|
git fetch origin
|
||||||
|
git reset --hard origin/main
|
||||||
|
|
||||||
|
# 2. 停止旧容器
|
||||||
|
log "停止旧容器..."
|
||||||
|
docker-compose down
|
||||||
|
|
||||||
|
# 3. 构建新镜像
|
||||||
|
log "构建新镜像..."
|
||||||
|
docker-compose build --no-cache
|
||||||
|
|
||||||
|
# 4. 启动新容器
|
||||||
|
log "启动新容器..."
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# 5. 等待健康检查
|
||||||
|
log "等待服务启动..."
|
||||||
|
sleep 10
|
||||||
|
|
||||||
|
# 6. 健康检查
|
||||||
|
if curl -sf http://localhost:1999/health > /dev/null; then
|
||||||
|
log "✅ 更新成功!服务运行正常"
|
||||||
|
else
|
||||||
|
log "❌ 更新失败!服务未正常启动"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "========== 更新完成 =========="
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. 创建 Webhook 服务
|
||||||
|
|
||||||
|
```python
|
||||||
|
# /www/wwwroot/pit-router/webhook-server.py
|
||||||
|
|
||||||
|
from flask import Flask, request, jsonify
|
||||||
|
import hmac
|
||||||
|
import hashlib
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# 配置
|
||||||
|
WEBHOOK_SECRET = os.environ.get('WEBHOOK_SECRET', 'your-webhook-secret')
|
||||||
|
UPDATE_SCRIPT = '/www/wwwroot/pit-router/auto-update.sh'
|
||||||
|
|
||||||
|
def verify_signature(payload, signature):
|
||||||
|
"""验证 Webhook 签名"""
|
||||||
|
expected = 'sha256=' + hmac.new(
|
||||||
|
WEBHOOK_SECRET.encode(),
|
||||||
|
payload,
|
||||||
|
hashlib.sha256
|
||||||
|
).hexdigest()
|
||||||
|
return hmac.compare_digest(expected, signature)
|
||||||
|
|
||||||
|
@app.route('/webhook/pit-router', methods=['POST'])
|
||||||
|
def handle_webhook():
|
||||||
|
"""处理 Gitea Webhook"""
|
||||||
|
# 验证签名
|
||||||
|
signature = request.headers.get('X-Gitea-Signature', '')
|
||||||
|
if not verify_signature(request.data, signature):
|
||||||
|
return jsonify({'error': 'Invalid signature'}), 401
|
||||||
|
|
||||||
|
# 解析事件
|
||||||
|
event = request.headers.get('X-Gitea-Event', '')
|
||||||
|
if event != 'push':
|
||||||
|
return jsonify({'message': 'Event ignored'}), 200
|
||||||
|
|
||||||
|
data = request.json
|
||||||
|
ref = data.get('ref', '')
|
||||||
|
|
||||||
|
# 只处理 main 分支
|
||||||
|
if ref != 'refs/heads/main':
|
||||||
|
return jsonify({'message': 'Branch ignored'}), 200
|
||||||
|
|
||||||
|
# 执行更新脚本
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
[UPDATE_SCRIPT],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=300
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
return jsonify({
|
||||||
|
'status': 'success',
|
||||||
|
'message': 'Update completed',
|
||||||
|
'output': result.stdout
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
'status': 'error',
|
||||||
|
'message': result.stderr
|
||||||
|
}), 500
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return jsonify({'error': 'Update timeout'}), 500
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='0.0.0.0', port=5001)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. 创建 systemd 服务
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# /etc/systemd/system/pit-router-webhook.service
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=PIT Router Webhook Server
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=root
|
||||||
|
WorkingDirectory=/www/wwwroot/pit-router
|
||||||
|
Environment=WEBHOOK_SECRET=your-webhook-secret
|
||||||
|
ExecStart=/usr/bin/python3 /www/wwwroot/pit-router/webhook-server.py
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. 配置 Gitea Webhook
|
||||||
|
|
||||||
|
```bash
|
||||||
|
TOKEN="509149b911143135084d20c44fc97a462a16945a"
|
||||||
|
|
||||||
|
# 创建 Webhook
|
||||||
|
curl -X POST "http://localhost:3000/api/v1/repos/yunxiafei/pit-router/hooks" \
|
||||||
|
-H "Authorization: token $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"type": "gitea",
|
||||||
|
"config": {
|
||||||
|
"url": "http://localhost:5001/webhook/pit-router",
|
||||||
|
"content_type": "json",
|
||||||
|
"secret": "your-webhook-secret"
|
||||||
|
},
|
||||||
|
"events": ["push"],
|
||||||
|
"active": true
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 方案 2: Gitea Actions(推荐用于复杂项目)
|
||||||
|
|
||||||
|
### 架构流程
|
||||||
|
|
||||||
|
```
|
||||||
|
开发者推送代码
|
||||||
|
↓
|
||||||
|
Gitea Actions 触发
|
||||||
|
↓
|
||||||
|
Runner 执行工作流
|
||||||
|
↓
|
||||||
|
构建 → 测试 → 推送镜像 → 部署
|
||||||
|
↓
|
||||||
|
通知结果
|
||||||
|
```
|
||||||
|
|
||||||
|
### 工作流配置
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# .gitea/workflows/deploy.yml
|
||||||
|
|
||||||
|
name: Build and Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Build Image
|
||||||
|
run: |
|
||||||
|
docker build -t pit-router:latest .
|
||||||
|
|
||||||
|
- name: Run Tests
|
||||||
|
run: |
|
||||||
|
docker run --rm pit-router:latest python -m pytest tests/
|
||||||
|
|
||||||
|
- name: Deploy
|
||||||
|
run: |
|
||||||
|
docker-compose down
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
- name: Health Check
|
||||||
|
run: |
|
||||||
|
sleep 10
|
||||||
|
curl -sf http://localhost:1999/health || exit 1
|
||||||
|
```
|
||||||
|
|
||||||
|
### 配置 Gitea Actions Runner
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 创建 Runner Token
|
||||||
|
TOKEN="509149b911143135084d20c44fc97a462a16945a"
|
||||||
|
|
||||||
|
curl -X POST "http://localhost:3000/api/v1/admin/runners/registration-token" \
|
||||||
|
-H "Authorization: token $TOKEN"
|
||||||
|
|
||||||
|
# 安装 Runner
|
||||||
|
docker run -d \
|
||||||
|
--name gitea-runner \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
-v /www/wwwroot/pit-router:/workspace \
|
||||||
|
-e GITEA_INSTANCE_URL=http://localhost:3000 \
|
||||||
|
-e GITEA_RUNNER_REGISTRATION_TOKEN=<token> \
|
||||||
|
gitea/act_runner:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 方案 3: Watchtower(自动监控镜像更新)
|
||||||
|
|
||||||
|
### 适用场景
|
||||||
|
|
||||||
|
适用于已经推送到镜像仓库的场景。
|
||||||
|
|
||||||
|
### 配置
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yaml 添加 watchtower
|
||||||
|
|
||||||
|
watchtower:
|
||||||
|
image: containrrr/watchtower
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
environment:
|
||||||
|
- WATCHTOWER_CLEANUP=true
|
||||||
|
- WATCHTOWER_POLL_INTERVAL=300 # 每 5 分钟检查一次
|
||||||
|
- WATCHTOWER_NOTIFICATIONS=email
|
||||||
|
- WATCHTOWER_NOTIFICATION_EMAIL_FROM=alert@example.com
|
||||||
|
- WATCHTOWER_NOTIFICATION_EMAIL_TO=admin@example.com
|
||||||
|
- WATCHTOWER_NOTIFICATION_EMAIL_SERVER=smtp.example.com
|
||||||
|
restart: unless-stopped
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 推荐方案
|
||||||
|
|
||||||
|
### 简单场景(当前推荐)
|
||||||
|
|
||||||
|
**方案 1: Webhook + 脚本**
|
||||||
|
|
||||||
|
| 项目 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 复杂度 | 低 |
|
||||||
|
| 依赖 | Flask + 简单脚本 |
|
||||||
|
| 部署时间 | 30 分钟 |
|
||||||
|
| 维护成本 | 低 |
|
||||||
|
|
||||||
|
### 复杂场景(未来扩展)
|
||||||
|
|
||||||
|
**方案 2: Gitea Actions**
|
||||||
|
|
||||||
|
| 项目 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 复杂度 | 中 |
|
||||||
|
| 依赖 | Gitea Runner |
|
||||||
|
| 功能 | 测试、构建、部署一体化 |
|
||||||
|
| 维护成本 | 中 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 实施清单
|
||||||
|
|
||||||
|
### 方案 1 实施步骤
|
||||||
|
|
||||||
|
- [ ] 创建更新脚本 `auto-update.sh`
|
||||||
|
- [ ] 创建 Webhook 服务 `webhook-server.py`
|
||||||
|
- [ ] 创建 systemd 服务
|
||||||
|
- [ ] 配置 Gitea Webhook
|
||||||
|
- [ ] 测试自动更新流程
|
||||||
|
|
||||||
|
### 预计时间
|
||||||
|
|
||||||
|
- 首次部署:30 分钟
|
||||||
|
- 后续维护:几乎无
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 注意事项
|
||||||
|
|
||||||
|
1. **安全性**
|
||||||
|
- Webhook 必须验证签名
|
||||||
|
- 限制 Webhook 访问来源 IP
|
||||||
|
- 敏感信息使用环境变量
|
||||||
|
|
||||||
|
2. **可靠性**
|
||||||
|
- 更新失败自动回滚
|
||||||
|
- 健康检查确认成功
|
||||||
|
- 日志记录完整
|
||||||
|
|
||||||
|
3. **生产环境**
|
||||||
|
- 考虑蓝绿部署
|
||||||
|
- 考虑金丝雀发布
|
||||||
|
- 监控告警
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*文档版本: v1.0 | 创建时间: 2026-03-14 | 作者: 小白 🐶*
|
||||||
Reference in New Issue
Block a user