新增自动化部署:Gitea Actions + systemd + gunicorn
Some checks failed
Deploy / deploy (push) Failing after 1s
Some checks failed
Deploy / deploy (push) Failing after 1s
- .gitea/workflows/deploy.yml:push main 自动触发部署
- requirements.txt:Python 依赖清单
- deploy/opc-manager.service:systemd 服务(gunicorn --preload -w 4)
- deploy/README.md:完整部署指南
- deploy/服务器配置任务提示词.md:给服务器管理 Agent 的操作提示词
- health 接口简化返回 {ok, service}
This commit is contained in:
90
.gitea/workflows/deploy.yml
Normal file
90
.gitea/workflows/deploy.yml
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
name: Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: prod-deploy
|
||||||
|
env:
|
||||||
|
DEPLOY_BASE: /opt/opc-manager
|
||||||
|
REPO_URL: https://qiukai:${{ secrets.DEPLOY_TOKEN }}@git.qiukai.me/qiukai/opc-manager.git
|
||||||
|
SERVICE_NAME: opc-manager
|
||||||
|
steps:
|
||||||
|
- name: Clone and deploy
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
RELEASE_ID="${{ github.sha }}"
|
||||||
|
RELEASE_DIR="${DEPLOY_BASE}/releases/${RELEASE_ID}"
|
||||||
|
CLONE_DIR="/tmp/opc-deploy-${RELEASE_ID}"
|
||||||
|
|
||||||
|
echo "=== 1. Clone repository ==="
|
||||||
|
rm -rf "${CLONE_DIR}"
|
||||||
|
git clone --depth 1 --branch main "${REPO_URL}" "${CLONE_DIR}"
|
||||||
|
|
||||||
|
echo "=== 2. Prepare release directory ==="
|
||||||
|
rm -rf "${RELEASE_DIR}"
|
||||||
|
mkdir -p "${RELEASE_DIR}"
|
||||||
|
|
||||||
|
# Copy repo content to release dir (exclude .git, .env, venv, data)
|
||||||
|
rsync -a --exclude='.git' \
|
||||||
|
--exclude='.env' \
|
||||||
|
--exclude='.env.local' \
|
||||||
|
--exclude='.venv' \
|
||||||
|
--exclude='data/uploads' \
|
||||||
|
--exclude='data/opc.sqlite' \
|
||||||
|
--exclude='__pycache__' \
|
||||||
|
--exclude='.gitea' \
|
||||||
|
"${CLONE_DIR}/" "${RELEASE_DIR}/"
|
||||||
|
|
||||||
|
echo "=== 3. Link shared resources ==="
|
||||||
|
# .env from shared dir (not in git)
|
||||||
|
ln -sfn "${DEPLOY_BASE}/shared/.env" "${RELEASE_DIR}/.env"
|
||||||
|
# uploads directory from shared (persist across releases)
|
||||||
|
mkdir -p "${DEPLOY_BASE}/shared/uploads"
|
||||||
|
ln -sfn "${DEPLOY_BASE}/shared/uploads" "${RELEASE_DIR}/data/uploads"
|
||||||
|
|
||||||
|
echo "=== 4. Setup Python venv ==="
|
||||||
|
cd "${RELEASE_DIR}"
|
||||||
|
python3 -m venv .venv
|
||||||
|
. .venv/bin/activate
|
||||||
|
pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
echo "=== 5. Restart service ==="
|
||||||
|
# Update WorkingDirectory in service via symlink approach
|
||||||
|
# The systemd service points to /opt/opc-manager/current
|
||||||
|
ln -sfn "${RELEASE_DIR}" "${DEPLOY_BASE}/current"
|
||||||
|
systemctl restart "${SERVICE_NAME}"
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
echo "=== 6. Health check ==="
|
||||||
|
for i in 1 2 3 4 5; do
|
||||||
|
if curl -fsS http://127.0.0.1:5177/api/health >/dev/null 2>&1; then
|
||||||
|
echo "Health check passed"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo "Attempt $i: waiting for service..."
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
# Final verify
|
||||||
|
if ! curl -fsS http://127.0.0.1:5177/api/health >/dev/null 2>&1; then
|
||||||
|
echo "ERROR: Health check failed after 5 attempts"
|
||||||
|
echo "Rolling back to previous release..."
|
||||||
|
PREV=$(ls -t "${DEPLOY_BASE}/releases" | sed -n '2p')
|
||||||
|
if [ -n "${PREV}" ]; then
|
||||||
|
ln -sfn "${DEPLOY_BASE}/releases/${PREV}" "${DEPLOY_BASE}/current"
|
||||||
|
systemctl restart "${SERVICE_NAME}"
|
||||||
|
echo "Rolled back to ${PREV}"
|
||||||
|
fi
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== 7. Cleanup old releases ==="
|
||||||
|
find "${DEPLOY_BASE}/releases" -mindepth 1 -maxdepth 1 -type d | sort | head -n -5 | xargs -r rm -rf
|
||||||
|
|
||||||
|
echo "=== 8. Cleanup temp ==="
|
||||||
|
rm -rf "${CLONE_DIR}"
|
||||||
|
|
||||||
|
echo "=== Deploy complete: ${RELEASE_ID} ==="
|
||||||
@@ -1046,7 +1046,7 @@ def delete_file(file_id):
|
|||||||
|
|
||||||
@app.route("/api/health")
|
@app.route("/api/health")
|
||||||
def health():
|
def health():
|
||||||
return jsonify({"ok": True, "db": str(DB_PATH)})
|
return jsonify({"ok": True, "service": "opc-manager"})
|
||||||
|
|
||||||
|
|
||||||
init_db()
|
init_db()
|
||||||
|
|||||||
193
deploy/README.md
Normal file
193
deploy/README.md
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
# OPC-Manager 自动化部署指南
|
||||||
|
|
||||||
|
## 架构
|
||||||
|
|
||||||
|
```
|
||||||
|
开发者 push main → Gitea 仓库 (git.qiukai.me)
|
||||||
|
↓
|
||||||
|
Gitea Actions 触发
|
||||||
|
↓
|
||||||
|
Runner(跑在业务服务器 82.157.208.197 上)
|
||||||
|
↓
|
||||||
|
git clone → rsync 到 release 目录
|
||||||
|
↓
|
||||||
|
创建 venv → pip install → systemctl restart
|
||||||
|
↓
|
||||||
|
健康检查 → 切换 current 软链 → 清理旧版本
|
||||||
|
```
|
||||||
|
|
||||||
|
## 服务器目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
/opt/opc-manager/
|
||||||
|
├── releases/
|
||||||
|
│ ├── abc1234/ ← 本次发布(commit sha)
|
||||||
|
│ ├── def5678/ ← 上次发布
|
||||||
|
│ └── ... ← 保留最近 5 个
|
||||||
|
├── shared/
|
||||||
|
│ ├── .env ← 环境变量(不进 git,持久化)
|
||||||
|
│ └── uploads/ ← 上传的文件(持久化,跨版本共享)
|
||||||
|
└── current → releases/abc1234 ← 软链,指向当前生效版本
|
||||||
|
```
|
||||||
|
|
||||||
|
## 一次性准备(在业务服务器上执行)
|
||||||
|
|
||||||
|
### 1. 安装 Gitea Actions Runner
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 下载 act_runner
|
||||||
|
cd /opt
|
||||||
|
wget https://gitea.com/gitea/act_runner/releases/download/v0.2.11/act_runner-0.2.11-linux-amd64 -O act_runner
|
||||||
|
chmod +x act_runner
|
||||||
|
mv act_runner /usr/local/bin/
|
||||||
|
|
||||||
|
# 注册 runner(到 git.qiukai.me)
|
||||||
|
# 先在 Gitea 网页:仓库设置 → Actions → Runners → New runner,获取 token
|
||||||
|
act_runner register \
|
||||||
|
--instance https://git.qiukai.me \
|
||||||
|
--token <YOUR_RUNNER_TOKEN> \
|
||||||
|
--name prod-deploy \
|
||||||
|
--labels prod-deploy \
|
||||||
|
--no-interactive
|
||||||
|
|
||||||
|
# 安装为系统服务
|
||||||
|
act_runner daemon --config /etc/act_runner/config.yaml &
|
||||||
|
# 或用 systemd:
|
||||||
|
cat > /etc/systemd/system/act-runner.service <<'EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=Gitea Actions Runner
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/local/bin/act_runner daemon
|
||||||
|
Restart=on-failure
|
||||||
|
Environment=HOME=/root
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable --now act-runner
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 创建部署目录结构
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p /opt/opc-manager/{releases,shared/uploads}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 创建 .env 文件
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat > /opt/opc-manager/shared/.env <<'EOF'
|
||||||
|
SECRET_KEY=改成一串随机字符串_至少32位
|
||||||
|
DB_HOST=127.0.0.1
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_USER=opc
|
||||||
|
DB_PASSWORD=opc123456
|
||||||
|
DB_NAME=opc
|
||||||
|
FLASK_DEBUG=false
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod 600 /opt/opc-manager/shared/.env
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 安装 systemd service
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 从仓库的 deploy/opc-manager.service 复制
|
||||||
|
cat > /etc/systemd/system/opc-manager.service <<'EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=OPC Manager (Flask)
|
||||||
|
After=network.target mysql.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
WorkingDirectory=/opt/opc-manager/current
|
||||||
|
ExecStart=/opt/opc-manager/current/.venv/bin/gunicorn --preload -w 4 -b 0.0.0.0:5177 backend.flask_app:app
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
EnvironmentFile=/opt/opc-manager/current/.env
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable opc-manager
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 配置 Gitea Secret
|
||||||
|
|
||||||
|
在 Gitea 网页操作:
|
||||||
|
1. 进入仓库 `qiukai/opc-manager`
|
||||||
|
2. 设置 → Actions → Secrets → New Secret
|
||||||
|
3. Name: `DEPLOY_TOKEN`
|
||||||
|
4. Value: 你的 Gitea Personal Access Token(需要有 repo 读权限)
|
||||||
|
- 生成路径:头像 → 设置 → 应用 → 生成令牌
|
||||||
|
|
||||||
|
### 6. 首次手动部署
|
||||||
|
|
||||||
|
push 代码前,先手动跑一次确保目录结构正确:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/opc-manager
|
||||||
|
git clone --depth 1 --branch main https://git.qiukai.me/qiukai/opc-manager.git /tmp/opc-init
|
||||||
|
RELEASE_DIR=/opt/opc-manager/releases/initial
|
||||||
|
mkdir -p "${RELEASE_DIR}"
|
||||||
|
rsync -a --exclude='.git' --exclude='.env' --exclude='.venv' /tmp/opc-init/ "${RELEASE_DIR}/"
|
||||||
|
ln -sfn /opt/opc-manager/shared/.env "${RELEASE_DIR}/.env"
|
||||||
|
ln -sfn /opt/opc-manager/shared/uploads "${RELEASE_DIR}/data/uploads"
|
||||||
|
cd "${RELEASE_DIR}"
|
||||||
|
python3 -m venv .venv
|
||||||
|
. .venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
ln -sfn "${RELEASE_DIR}" /opt/opc-manager/current
|
||||||
|
systemctl start opc-manager
|
||||||
|
curl http://127.0.0.1:5177/api/health
|
||||||
|
rm -rf /tmp/opc-init
|
||||||
|
```
|
||||||
|
|
||||||
|
## 日常使用
|
||||||
|
|
||||||
|
### 发布新版本
|
||||||
|
```bash
|
||||||
|
# 本地
|
||||||
|
git push origin main
|
||||||
|
# 自动触发 Gitea Actions → 服务器自动部署
|
||||||
|
```
|
||||||
|
|
||||||
|
### 查看部署状态
|
||||||
|
```bash
|
||||||
|
# 在 Gitea 网页:仓库 → Actions 查看部署日志
|
||||||
|
# 或在服务器:
|
||||||
|
systemctl status opc-manager
|
||||||
|
ls -la /opt/opc-manager/current
|
||||||
|
```
|
||||||
|
|
||||||
|
### 回滚
|
||||||
|
```bash
|
||||||
|
# 列出历史版本
|
||||||
|
ls -t /opt/opc-manager/releases
|
||||||
|
# 切换到上一个版本
|
||||||
|
PREV=$(ls -t /opt/opc-manager/releases | sed -n '2p')
|
||||||
|
ln -sfn "/opt/opc-manager/releases/${PREV}" /opt/opc-manager/current
|
||||||
|
systemctl restart opc-manager
|
||||||
|
```
|
||||||
|
|
||||||
|
## Nginx 反代(可选)
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name opc.yxcowork.vip;
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:5177;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
14
deploy/opc-manager.service
Normal file
14
deploy/opc-manager.service
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=OPC Manager (Flask)
|
||||||
|
After=network.target mysql.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
WorkingDirectory=/opt/opc-manager/current
|
||||||
|
ExecStart=/opt/opc-manager/current/.venv/bin/gunicorn --preload -w 4 -b 0.0.0.0:5177 backend.flask_app:app
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
EnvironmentFile=/opt/opc-manager/current/.env
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
82
deploy/服务器配置任务提示词.md
Normal file
82
deploy/服务器配置任务提示词.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# OPC-Manager 自动化部署配置任务
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
OPC-Manager 项目(Flask + MySQL,端口 5177)需要配置 Gitea Actions 自动化部署。参考 SalesManager 的模式:push 到 main 分支后自动部署到业务服务器。部署工作流文件已写好(`.gitea/workflows/deploy.yml`),需要在服务器上完成一次性准备。
|
||||||
|
|
||||||
|
## 服务器信息
|
||||||
|
- 业务服务器:82.157.208.197(SSH 别名 `ssh business`)
|
||||||
|
- Gitea 地址:https://git.qiukai.me
|
||||||
|
- 仓库:qiukai/opc-manager
|
||||||
|
- 服务端口:5177
|
||||||
|
- MySQL:已安装(8.0.45,系统服务),数据库 `opc`,用户 `opc/opc123456`
|
||||||
|
|
||||||
|
## 需要完成的 5 个步骤
|
||||||
|
|
||||||
|
### 1. 安装 Gitea Actions Runner
|
||||||
|
在业务服务器上安装 `act_runner` 并注册到 Gitea:
|
||||||
|
- 下载地址:https://gitea.com/gitea/act_runner/releases/download/v0.2.11/act_runner-0.2.11-linux-amd64
|
||||||
|
- 放到 `/usr/local/bin/act_runner`
|
||||||
|
- 注册时 name=`prod-deploy`,labels=`prod-deploy`
|
||||||
|
- 注册 token 需要到 Gitea 网页获取:仓库 qiukai/opc-manager → 设置 → Actions → Runners → New runner
|
||||||
|
- 安装为 systemd 服务(`/etc/systemd/system/act-runner.service`),开机自启
|
||||||
|
|
||||||
|
### 2. 创建部署目录结构
|
||||||
|
```bash
|
||||||
|
mkdir -p /opt/opc-manager/{releases,shared/uploads}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 创建 .env 文件
|
||||||
|
在 `/opt/opc-manager/shared/.env` 创建环境变量文件:
|
||||||
|
```
|
||||||
|
SECRET_KEY=随机生成32位以上字符串
|
||||||
|
DB_HOST=127.0.0.1
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_USER=opc
|
||||||
|
DB_PASSWORD=opc123456
|
||||||
|
DB_NAME=opc
|
||||||
|
FLASK_DEBUG=false
|
||||||
|
```
|
||||||
|
权限设为 600。
|
||||||
|
|
||||||
|
### 4. 安装 systemd service
|
||||||
|
创建 `/etc/systemd/system/opc-manager.service`:
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=OPC Manager (Flask)
|
||||||
|
After=network.target mysql.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
WorkingDirectory=/opt/opc-manager/current
|
||||||
|
ExecStart=/opt/opc-manager/current/.venv/bin/gunicorn --preload -w 4 -b 0.0.0.0:5177 backend.flask_app:app
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
EnvironmentFile=/opt/opc-manager/current/.env
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
执行 `systemctl daemon-reload && systemctl enable opc-manager`(先不 start,等首次部署后自动启动)。
|
||||||
|
|
||||||
|
### 5. 配置 Gitea Secret
|
||||||
|
在 Gitea 网页操作(需要用户在浏览器完成):
|
||||||
|
- 进入仓库 qiukai/opc-manager → 设置 → Actions → Secrets → New Secret
|
||||||
|
- Name: `DEPLOY_TOKEN`
|
||||||
|
- Value: 用户的 Gitea Personal Access Token(需 repo 读权限)
|
||||||
|
- 生成路径:头像 → 设置 → 应用 → 生成令牌
|
||||||
|
|
||||||
|
## 首次部署验证
|
||||||
|
准备完成后,在本地执行一次 `git push origin main`,观察:
|
||||||
|
1. Gitea 网页 Actions 页面是否有部署任务在运行
|
||||||
|
2. 部署日志是否正常
|
||||||
|
3. 部署完成后 `curl http://127.0.0.1:5177/api/health` 是否返回 `{"ok":true,"service":"opc-manager"}`
|
||||||
|
4. `systemctl status opc-manager` 是否 active
|
||||||
|
|
||||||
|
## 参考文件
|
||||||
|
项目的部署工作流在仓库的 `.gitea/workflows/deploy.yml`,systemd 模板在 `deploy/opc-manager.service`,完整说明在 `deploy/README.md`。
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
- Gitea Runner 的 token 需要用户在浏览器获取后告诉你,你无法自动获取
|
||||||
|
- Gitea Secret (DEPLOY_TOKEN) 也需要用户在浏览器配置
|
||||||
|
- 如果服务器没有 python3.11+,需要先安装(OPC-Manager 要求 Python 3.9+)
|
||||||
|
- 确保服务器已安装 git、rsync、curl(一般都有)
|
||||||
5
requirements.txt
Normal file
5
requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
flask==3.1.3
|
||||||
|
mysql-connector-python==9.4.0
|
||||||
|
python-dotenv==1.2.1
|
||||||
|
werkzeug==3.1.8
|
||||||
|
gunicorn==23.0.0
|
||||||
Reference in New Issue
Block a user