duoqi-api/docs/ci-deployment-guide.md
Wang Zhuoxuan e893755340 feat: 添加 CI/CD 部署方案(Gitea + Docker + 双分支工作流)
- Dockerfile: 多阶段构建,oven/bun 基础镜像,非 root 用户
- docker-compose.yml: 本地开发环境(API + MySQL + Drizzle Studio)
- docker-compose.prod.yml: 服务器部署(prod + test,Docker profiles)
- .gitea/workflows/deploy.yml: 双分支 CI/CD(develop→测试, main→生产)
- docs/ci-deployment-guide.md: 完整部署指南(Alibaba Cloud Linux 3)
- scripts/deploy.sh: 手动部署运维脚本
- .env.prod.example: 生产环境变量模板
- .dockerignore: 排除非构建文件
- .gitignore: 排除 .claude/ 目录
2026-04-16 12:44:14 +08:00

988 lines
30 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Duoqi API 部署与持续集成方案
> 基于 Gitea + 阿里云轻量应用服务器 (Alibaba Cloud Linux 3) 的私有化部署方案
## 目录
- [架构概览](#架构概览)
- [Phase 1: 单服务器方案(当前)](#phase-1-单服务器方案当前)
- [资源规划](#资源规划)
- [服务器初始化](#服务器初始化)
- [Gitea 安装与配置](#gitea-安装与配置)
- [环境隔离策略](#环境隔离策略)
- [CI/CD 流程](#cicd-流程)
- [部署操作](#部署操作)
- [Nginx 配置](#nginx-配置)
- [Phase 2: 多服务器扩展(未来)](#phase-2-多服务器扩展未来)
- [架构演进](#架构演进)
- [私有镜像仓库](#私有镜像仓库)
- [负载均衡与水平扩展](#负载均衡与水平扩展)
- [运维管理](#运维管理)
- [故障排查](#故障排查)
- [附录](#附录)
---
## 架构概览
### 为什么选择 Gitea 而不是 GitLab
| 对比项 | Gitea | GitLab CE |
|--------|-------|-----------|
| 最低内存 | ~200MB | ~4GB |
| 安装复杂度 | 单二进制文件 | 需要多组件 |
| CI/CD | Gitea Actions兼容 GitHub Actions 语法) | GitLab CI/CD |
| 容器镜像仓库 | 内置(可选) | 内置 |
| 适合场景 | 小团队、资源有限 | 大团队、功能全面 |
| **2C/2G 可行性** | **✅ 完全可行** | **❌ 内存不足** |
### 单服务器资源分配2C/2G
```
┌─────────────────────────────────────────────────────────────────────┐
│ 阿里云轻量应用服务器 (2C/2G) │
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ Nginx 反向代理 (:80/:443 ← 唯一对外入口) │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌────────────────────────┐│ │
│ │ │ api.duoqi.me│ │test-api. │ │ git.duoqi.me ││ │
│ │ │ → :3000 prod │ │duoqi.me │ │ → :3200 Gitea (IP限制) ││ │
│ │ │ │ │ → :3001 test │ │ ││ │
│ │ └──────────────┘ └──────────────┘ └────────────────────────┘│ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │
│ │ duoqi-api │ │ duoqi-api │ │ Gitea + Act Runner │ │
│ │ (prod) │ │ (test) │ │ 代码托管 + CI/CD │ │
│ │ 127.0.0.1 │ │ 127.0.0.1 │ │ 127.0.0.1:3200 │ │
│ │ :3000 │ │ :3001 │ │ ~200MB │ │
│ │ ~300MB │ │ ~200MB │ └──────────────────────────┘ │
│ └─────────────┘ └──────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 阿里云 RDS MySQL (外置,仅内网可达) │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ duoqi_prod │ │ duoqi_test │ │ │
│ │ └──────────────┘ └──────────────┘ │ │
│ └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
```
**内存预算:**
| 组件 | 预估占用 | 说明 |
|------|----------|------|
| OS + 系统 | ~200MB | Alibaba Cloud Linux 3 |
| Docker | ~150MB | Docker Daemon |
| Gitea | ~200MB | 含 Act Runner |
| Nginx | ~50MB | 反向代理 |
| duoqi-api (prod) | ~300MB | 生产容器 |
| duoqi-api (test) | ~200MB | 测试容器(按需启停) |
| **合计** | **~1.1GB** | **剩余 ~900MB 缓冲** |
---
## Phase 1: 单服务器方案(当前)
### 资源规划
| 资源 | 配置 | 用途 |
|------|------|------|
| 轻量应用服务器 | 2C/2G/40GB SSD | Gitea + Docker + Nginx |
| 阿里云 RDS MySQL | 1C/1G 基础版 | duoqi_prod + duoqi_test |
| 域名 + SSL | api.duoqi.me | 生产环境入口 |
### 服务器初始化
> 系统选择 **Alibaba Cloud Linux 3**(阿里云官方优化内核,内存占用更低,与 RDS 兼容性更好)
```bash
# SSH 登录
ssh root@your-server-ip
# 1. 更新系统
dnf update -y
# 2. 安装基础工具
dnf install -y curl git nginx certbot python3-certbot-nginx
# 3. 安装 DockerAlibaba Cloud Linux 内置 Docker 源)
dnf install -y docker docker-compose
systemctl enable --now docker
# 4. 配置防火墙firewalld
# 所有服务通过 Nginx 反向代理,只需开放 22/80/443
systemctl enable --now firewalld
firewall-cmd --permanent --add-service=ssh
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
# 5. 创建项目目录
mkdir -p /opt/duoqi-api
mkdir -p /opt/gitea
mkdir -p /opt/backups
# 6. 验证
cat /etc/os-release # 确认 Alibaba Cloud Linux
docker --version
nginx -v
```
### Gitea 安装与配置
#### 方式一:二进制安装(推荐,更省内存)
> Gitea 是单个 Go 二进制文件,直接运行在宿主机上比 Docker 方式省 ~50MB 内存,且 systemd 管理更可靠。
> 在 2C/2G 服务器上,省下的每一 MB 都有意义。
```bash
# 下载 Gitea
wget -O /usr/local/bin/gitea https://dl.gitea.io/gitea/1.22/gitea-1.22-linux-amd64
chmod +x /usr/local/bin/gitea
# 创建用户和目录
groupadd --system git
useradd --system --gid git --shell /bin/bash -m git
mkdir -p /var/lib/gitea/{custom,data,log}
chown -R git:git /var/lib/gitea
mkdir -p /etc/gitea
chown git:git /etc/gitea
# 创建 systemd 服务
cat > /etc/systemd/system/gitea.service << 'EOF'
[Unit]
Description=Gitea
After=network.target
[Service]
User=git
WorkingDirectory=/var/lib/gitea
ExecStart=/usr/local/bin/gitea web -c /etc/gitea/app.ini --port 3200 --http-addr 127.0.0.1
Restart=always
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
[Install]
WantedBy=multi-user.target
EOF
# 启动
systemctl enable gitea
systemctl start gitea
# 等待 Gitea 生成默认配置文件
sleep 3
systemctl stop gitea
```
**配置反向代理适配(关键步骤):**
> Gitea 需要知道自己对外是 `https://git.duoqi.me`,否则 Git clone URL 和 Web UI 链接会指向 `127.0.0.1:3200`。
```bash
# 编辑 Gitea 配置文件
cat >> /etc/gitea/app.ini << 'EOF'
[server]
DOMAIN = git.duoqi.me
ROOT_URL = https://git.duoqi.me/
HTTP_ADDR = 127.0.0.1
HTTP_PORT = 3200
SSH_DOMAIN = git.duoqi.me
SSH_PORT = 22
SSH_LISTEN_PORT = 22
OFFLINE_MODE = true ; 禁用 Gravatar 等外部服务,加快页面加载
[security]
INSTALL_LOCK = true ; 已完成安装,禁止再次访问安装页面
[service]
REGISTER_EMAIL_CONFIRM = false ; 按需开启
DISABLE_REGISTRATION = true ; 禁止公开注册,仅管理员创建账号
[other]
SHOW_FOOTER_VERSION = false ; 隐藏版本号,减少信息泄露
EOF
# 修正权限
chown git:git /etc/gitea/app.ini
chmod 640 /etc/gitea/app.ini
# 启动
systemctl start gitea
```
#### 方式二Docker 部署(备选)
```bash
cat > /opt/gitea/docker-compose.yml << 'EOF'
version: '3.8'
services:
gitea:
image: gitea/gitea:latest
container_name: gitea
restart: unless-stopped
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__server__DOMAIN=git.duoqi.me
- GITEA__server__ROOT_URL=https://git.duoqi.me/
- GITEA__server__HTTP_ADDR=127.0.0.1
- GITEA__server__HTTP_PORT=3200
- GITEA__server__SSH_DOMAIN=git.duoqi.me
volumes:
- ./data:/data
- /etc/localtime:/etc/localtime:ro
network_mode: host ; 需要绑定 127.0.0.1,使用 host 网络
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
EOF
cd /opt/gitea && docker-compose up -d
```
> **注意**: Docker 方式会额外占用 ~50MB 内存(容器运行时开销),仅当需要快速试用或测试时使用。
#### Gitea 初始化配置
```
1. 浏览器访问 https://git.duoqi.me通过 Nginx 反向代理)
首次启动时也可临时访问 http://127.0.0.1:3200
2. 首次配置(安装页面):
- 数据库SQLite3默认
- 服务器域名git.duoqi.me
- Gitea HTTP 监听端口3200
- SSH 服务端口22
- 管理员账号:创建 admin 用户
3. 创建仓库duoqi-api
4. 推送代码HTTP
git remote add gitea https://git.duoqi.me/admin/duoqi-api.git
git push gitea main
git checkout -b develop
git push gitea develop
或使用 SSH
git remote add gitea git@git.duoqi.me:admin/duoqi-api.git
git push gitea main
git checkout -b develop
git push gitea develop
```
#### 安装 Act RunnerCI/CD 执行器)
```bash
# 下载 Act Runner
wget -O /usr/local/bin/act_runner https://gitea.com/gitea/act_runner/releases/latest/download/act_runner-linux-amd64
chmod +x /usr/local/bin/act_runner
# 在 Gitea Web UI 中生成 Token
# Settings → Actions → Runners → Create new Runner → 复制 Token
# 创建专用工作目录register 和 daemon 必须在同一目录)
mkdir -p /opt/act-runner
# 注册 Runner
cd /opt/act-runner && act_runner register \
--instance http://localhost:3200 \
--token YOUR_RUNNER_TOKEN \
--name duoqi-runner \
--labels ubuntu-latest:docker://oven/bun:latest
# 创建 systemd 服务
cat > /etc/systemd/system/act-runner.service << 'EOF'
[Unit]
Description=Gitea Act Runner
After=docker.service
[Service]
WorkingDirectory=/opt/act-runner
ExecStart=/usr/local/bin/act_runner daemon
Restart=always
Environment=HOME=/root
[Install]
WantedBy=multi-user.target
EOF
# 启动
systemctl daemon-reload
systemctl enable act-runner
systemctl start act-runner
# 验证:确认 active (running),且 Gitea Web UI 中 Runner 显示在线
systemctl status act-runner
```
### 环境隔离策略
#### RDS 数据库隔离
```sql
-- 连接到 RDS
mysql -h your-rds-endpoint -u root -p
-- 创建生产库
CREATE DATABASE duoqi_prod CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 创建测试库
CREATE DATABASE duoqi_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 创建用户并授权
CREATE USER 'duoqi_prod'@'%' IDENTIFIED BY 'prod-password';
GRANT ALL PRIVILEGES ON duoqi_prod.* TO 'duoqi_prod'@'%';
CREATE USER 'duoqi_test'@'%' IDENTIFIED BY 'test-password';
GRANT ALL PRIVILEGES ON duoqi_test.* TO 'duoqi_test'@'%';
FLUSH PRIVILEGES;
```
#### 环境配置文件
**生产环境** `/opt/duoqi-api/.env.prod`
```env
DATABASE_URL=mysql://duoqi_prod:prod-password@your-rds-endpoint:3306/duoqi_prod
JWT_SECRET=prod-super-secret-jwt-key
JWT_EXPIRES_IN=1h
JWT_REFRESH_EXPIRES_IN=30d
ADMIN_TOKEN=prod-admin-token
PORT=3000
NODE_ENV=production
LOG_LEVEL=warn
# ... 其他生产环境变量
```
**测试环境** `/opt/duoqi-api/.env.test`
```env
DATABASE_URL=mysql://duoqi_test:test-password@your-rds-endpoint:3306/duoqi_test
JWT_SECRET=test-secret-key
JWT_EXPIRES_IN=1h
JWT_REFRESH_EXPIRES_IN=30d
ADMIN_TOKEN=test-admin-token
PORT=3001
NODE_ENV=test
LOG_LEVEL=debug
# ... 其他测试环境变量可使用测试用的华为、OSS配置
```
#### Docker Compose 环境隔离
**服务器 compose 文件** `/opt/duoqi-api/docker-compose.yml`
```yaml
version: '3.8'
services:
# ===== 生产环境 =====
api-prod:
build:
context: /opt/gitea/data/git/repositories/admin/duoqi-api.git
dockerfile: Dockerfile
image: duoqi-api:prod
container_name: duoqi-api-prod
restart: unless-stopped
env_file: .env.prod
ports:
- "3000:3000"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
deploy:
resources:
limits:
memory: 400M
# ===== 测试环境(按需启停) =====
api-test:
build:
context: /opt/gitea/data/git/repositories/admin/duoqi-api.git
dockerfile: Dockerfile
image: duoqi-api:test
container_name: duoqi-api-test
restart: "no" # 不自动重启,手动控制
env_file: .env.test
ports:
- "3001:3001"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: "json-file"
options:
max-size: "5m"
max-file: "2"
deploy:
resources:
limits:
memory: 300M
profiles:
- test # 使用 profiles 按需启停
```
**启停命令:**
```bash
# 只启动生产环境(默认)
docker-compose up -d
# 启动生产 + 测试环境
docker-compose --profile test up -d
# 停止测试环境(释放内存)
docker-compose --profile test stop api-test
# 重新构建并启动
docker-compose up -d --build api-prod
docker-compose --profile test up -d --build api-test
```
### CI/CD 流程
#### 双分支工作流
```
develop 分支(开发测试) main 分支(生产发布)
│ │
▼ ▼
┌───────────┐ ┌───────────┐
│ quality │ ← Lint + 类型检查 │ quality │ ← Lint + 类型检查
└─────┬─────┘ └─────┬─────┘
▼ ▼
┌───────────┐ ┌───────────┐
│ test │ ← 单元测试 + 覆盖率 │ test │ ← 单元测试 + 覆盖率
└─────┬─────┘ └─────┬─────┘
▼ ▼
┌────────────┐ ┌────────────┐
│ build-test │ ← 构建测试镜像 │ build-prod │ ← 构建生产镜像
└─────┬──────┘ └─────┬──────┘
▼ ▼
┌─────────────┐ ┌─────────────┐
│ deploy-test │ ← 自动部署到测试 │ deploy-prod │ ← 手动确认后部署生产
└─────────────┘ └─────────────┘
```
#### 日常开发流程
```bash
# 1. 在 develop 上开发
git checkout develop
# ... 编写代码 ...
git add .
git commit -m "feat: 新功能描述"
git push origin develop
# → 自动触发quality → test → build-test → deploy-test
# 2. 在测试环境验证http://test-api.duoqi.me
# 如果发现问题,继续在 develop 上修改并 push
# 3. 测试通过,合并到 main 发布
git checkout main
git merge develop
git push origin main
# → 自动触发quality → test → build-prod
# → 在 Gitea Web UI 手动确认 deploy-prod
```
#### 触发规则
| 事件 | quality | test | build | deploy |
|------|---------|------|-------|--------|
| push 到 `develop` | ✅ | ✅ | build-test | deploy-test自动 |
| push 到 `main` | ✅ | ✅ | build-prod | deploy-prod**手动确认** |
> **注意**`develop` 分支需要在 Gitea 仓库中手动创建:
> ```bash
> git checkout -b develop
> git push origin develop
> ```
#### 关键设计说明
| 设计决策 | 原因 |
|---------|------|
| 双分支隔离develop + main | develop 是试验场main 是稳定版本,互不干扰 |
| 不同分支构建不同镜像 | develop 只构建 test 镜像main 只构建 prod 镜像,节省 2C/2G 服务器资源 |
| 本地构建镜像,无外部仓库 | 单服务器不需要镜像中转,节省资源 |
| 测试环境使用 Docker profiles | 按需启停,节省内存 |
| 生产部署手动确认 | 防止误操作,确保人工验证后才上线 |
| 使用 Gitea Actions | 兼容 GitHub Actions 语法,学习成本低 |
### 部署操作
#### 手动部署(首次或紧急)
```bash
# SSH 登录服务器
ssh root@your-server-ip
cd /opt/duoqi-api
# 从 Gitea 拉取最新代码
git -C /opt/duoqi-api/repo pull # 如果用 git clone 方式
# 或使用部署脚本
bash scripts/deploy.sh prod
```
#### 使用部署脚本
```bash
# 部署到生产环境
bash scripts/deploy.sh prod
# 部署到测试环境
bash scripts/deploy.sh test
# 停止测试环境
bash scripts/deploy.sh test-stop
# 查看状态
bash scripts/deploy.sh status
# 回滚
bash scripts/deploy.sh rollback prod
```
### Nginx 配置
> 所有服务通过 Nginx 统一入口,后端端口仅绑定 localhost防火墙只开 22/80/443。
```bash
# 生产环境 API
cat > /etc/nginx/conf.d/duoqi-api.conf << 'EOF'
server {
listen 80;
server_name api.duoqi.me;
location / {
proxy_pass http://localhost:3000;
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;
}
}
EOF
# 测试环境 API仅限内部访问
cat > /etc/nginx/conf.d/duoqi-api-test.conf << 'EOF'
server {
listen 80;
server_name test-api.duoqi.me;
# 限制访问 IP开发团队办公网络
# allow 123.56.78.90;
# deny all;
location / {
proxy_pass http://localhost:3001;
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;
}
}
EOF
# Gitea 代码仓库(限制访问 IP仅团队可访问
cat > /etc/nginx/conf.d/gitea.conf << 'EOF'
server {
listen 80;
server_name git.duoqi.me;
# 限制访问 IP开发团队办公网络
# allow 123.56.78.90;
# deny all;
location / {
proxy_pass http://127.0.0.1:3200;
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;
# Gitea 需要较大的请求体git push
client_max_body_size 100M;
}
}
EOF
# 验证并生效
nginx -t
systemctl enable --now nginx
systemctl reload nginx
```
#### 配置 HTTPS
```bash
# 为三个域名统一申请证书
certbot --nginx -d api.duoqi.me -d test-api.duoqi.me -d git.duoqi.me
# 自动续期验证
certbot renew --dry-run
```
> [!NOTE]
>
> 在子域名还没有配置解析的前提下,会影响证书的签发。后期如果新增子域名,并且需要同一张证书时,可以通过`--expand`参数进行申请。
```bash
# 方案1先申请已就绪的子域名
sudo certbot --nginx -d example.com -d www.example.com
# 后续子域名就绪后,再扩容证书
sudo certbot --nginx --expand \
-d example.com -d www.example.com -d api.example.com -d blog.example.com
```
##### 常见错误
```bash
# ❌ 错误:只写新增域名,会导致原有域名被移除!
sudo certbot --nginx --expand -d api.example.com -d blog.example.com
# ✅ 正确:列出所有域名(原有 + 新增)
sudo certbot --nginx --expand \
-d example.com -d www.example.com -d api.example.com -d blog.example.com
```
---
## Phase 2: 多服务器扩展(未来)
### 架构演进
```
Phase 1 (当前) Phase 2 (扩展后)
┌──────────────┐ ┌─────────────────────────────────┐
│ 单台服务器 │ │ 负载均衡 (SLB/Nginx) │
│ │ │ api.duoqi.me → 443/80 │
│ Gitea │ └──────────┬──────────────────────┘
│ duoqi-api │ ──────▶ │
│ duoqi-api │ ┌──────────┴──────────┐
│ (prod+test) │ │ │
│ Nginx │ ┌────┴────┐ ┌─────┴────┐
└──────────────┘ │ API-1 │ │ API-2 │
│ (prod) │ │ (prod) │
┌──────────────┐ └─────────┘ └──────────┘
│ RDS MySQL │ ┌─────────────┐ ┌──────────────┐
│ duoqi_prod │ │ CI/CD 服务器 │ │ 测试服务器 │
│ duoqi_test │ │ Gitea │ │ duoqi-api │
└──────────────┘ │ Harbor 镜像 │ │ (test) │
└─────────────┘ └──────────────┘
```
### 扩展触发条件
当出现以下情况时,考虑从 Phase 1 升级到 Phase 2
| 指标 | Phase 1 上限 | Phase 2 起步 |
|------|-------------|-------------|
| 日活用户 | ~1,000 | >1,000 |
| 内存使用率 | 持续 >80% | — |
| CPU 使用率 | 持续 >70% | — |
| 需要独立测试服务器 | 否 | 是 |
| 需要多实例高可用 | 否 | 是 |
### 私有镜像仓库
多服务器场景下需要镜像仓库统一分发:
**方案一Gitea 内置 Container Registry推荐**
```
# 在 Gitea 的 app.ini 中启用
[packages]
ENABLED = true
STORAGE_TYPE = local
# 推送镜像
docker build -t your-server-ip:3200/admin/duoqi-api:latest .
docker push your-server-ip:3200/admin/duoqi-api:latest
```
**方案二Harbor大规模场景**
```yaml
# docker-compose.harbor.yml
# 需要独立服务器或至少 4GB 内存
services:
harbor:
image: goharbor/harbor:latest
# ... Harbor 企业级镜像仓库
```
### 负载均衡与水平扩展
```yaml
# Phase 2: 多实例 docker-compose.yml
# API 服务器上的配置
version: '3.8'
services:
api:
image: your-registry/duoqi-api:latest
deploy:
replicas: 2 # 运行 2 个实例
resources:
limits:
cpus: '1'
memory: 512M
env_file: .env.prod
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 15s
timeout: 5s
retries: 3
```
**阿里云 SLB 配置:**
```
1. 创建负载均衡实例
2. 添加后端服务器API-1, API-2
3. 配置健康检查路径:/health
4. 配置 SSL 证书
5. 域名 DNS 解析到 SLB 公网 IP
```
### Phase 2 CI/CD 流程
```
推送代码 → Gitea Actions → 构建 + 推送到私有镜像仓库
┌───────────────┼───────────────┐
▼ ▼ ▼
部署到测试服务器 部署到 API-1 部署到 API-2
(自动) (手动/灰度) (手动/灰度)
```
---
## 运维管理
### 日常维护命令
```bash
# 查看所有容器状态
docker-compose ps
# 查看生产日志
docker-compose logs -f api-prod
# 查看测试日志
docker-compose -f docker-compose.yml --profile test logs -f api-test
# 重启生产环境
docker-compose restart api-prod
# 进入容器调试
docker-compose exec api-prod sh
# 查看资源使用
docker stats --no-stream
```
### 数据库管理
```bash
# 测试库重置(清空测试数据)
mysql -h your-rds-endpoint -u duoqi_test -p -e "DROP DATABASE duoqi_test; CREATE DATABASE duoqi_test CHARACTER SET utf8mb4;"
# 执行迁移
docker-compose exec api-prod npx drizzle-kit migrate
# 导入种子数据到测试库
docker-compose --profile test exec api-test bun run db:seed
# 备份生产库
mysqldump -h your-rds-endpoint -u duoqi_prod -p duoqi_prod > /opt/backups/duoqi_prod_$(date +%Y%m%d).sql
```
### 备份策略
```bash
# 创建备份脚本
cat > /opt/backups/backup.sh << 'SCRIPT'
#!/bin/bash
set -e
BACKUP_DIR="/opt/backups"
DATE=$(date +%Y%m%d_%H%M%S)
# 数据库备份
mysqldump -h your-rds-endpoint -u duoqi_prod -pyour-password duoqi_prod \
| gzip > "$BACKUP_DIR/db_prod_$DATE.sql.gz"
# Gitea 仓库备份
sudo -u git gitea dump -c /etc/gitea/app.ini
# 备份文件生成在当前目录gitea-dump-*.zip
# 保留最近 7 天的备份
find $BACKUP_DIR -name "*.gz" -mtime +7 -delete
echo "[$DATE] Backup completed"
SCRIPT
chmod +x /opt/backups/backup.sh
# 定时任务(每天凌晨 2 点)
echo "0 2 * * * /opt/backups/backup.sh >> /opt/backups/backup.log 2>&1" | crontab -
```
---
## 故障排查
### 常见问题
#### 1. 内存不足
```bash
# 查看内存使用
free -h
# 停止测试环境释放内存
docker-compose --profile test stop api-test
# 清理 Docker 缓存
docker system prune -f
# 查看 Docker 磁盘占用
docker system df
```
#### 2. 容器启动失败
```bash
# 查看退出日志
docker-compose logs api-prod
# 检查环境变量
docker-compose config
# 检查端口冲突
netstat -tlnp | grep -E '3000|3001'
```
#### 3. Gitea Runner 不可用
```bash
# 检查 Runner 状态
systemctl status act-runner
# 重启 Runner
systemctl restart act-runner
# 查看日志
journalctl -u act-runner -f
```
#### 4. 数据库连接失败
```bash
# 从服务器测试 RDS 连通性
mysql -h your-rds-endpoint -u duoqi_prod -p -e "SELECT 1;"
# 检查 RDS 白名单是否包含服务器 IP
# 阿里云 RDS 控制台 → 数据安全性 → 白名单设置
```
### 回滚操作
```bash
# 查看本地镜像历史
docker images | grep duoqi-api
# 回滚到上一个版本
cd /opt/duoqi-api
docker-compose down api-prod
# 修改 docker-compose.yml 使用指定版本镜像
docker-compose up -d api-prod
```
---
## 附录
### A. 完整目录结构(服务器端)
```
/opt/
├── gitea/ # Gitea 代码托管
│ ├── docker-compose.yml
│ └── data/ # Gitea 数据(仓库、配置)
├── duoqi-api/ # 应用部署
│ ├── docker-compose.yml # 包含 prod + test 配置
│ ├── .env.prod # 生产环境变量
│ ├── .env.test # 测试环境变量
│ ├── Dockerfile # 镜像构建
│ └── repo/ # Git 仓库(用于构建)
├── backups/ # 备份目录
│ ├── backup.sh
│ └── *.sql.gz
└── scripts/ # 运维脚本
└── deploy.sh
```
### B. Gitea Actions 与 GitHub Actions 语法对照
| 特性 | GitHub Actions | Gitea Actions |
|------|---------------|---------------|
| 文件路径 | `.github/workflows/` | `.gitea/workflows/` |
| 语法 | YAML | 相同(完全兼容) |
| 触发条件 | `on: push` | 相同 |
| Runner | GitHub 托管 | 自托管 Act Runner |
| 镜像 | `runs-on: ubuntu-latest` | 相同,或自定义标签 |
### C. 安全建议
| 项目 | 建议 |
|------|------|
| SSH | 禁用密码登录,仅密钥认证 |
| 防火墙 | firewalld 仅开放 22/80/443后端端口全部绑定 localhost |
| 测试环境 | 通过 Nginx 限制访问 IP |
| 数据库 | 生产用户和测试用户严格分离权限 |
| JWT | 生产与测试使用不同密钥 |
| SSL | 生产环境必须启用 HTTPS |
| 内核更新 | Alibaba Cloud Linux 安全补丁自动推送,及时更新 |
### D. Ubuntu 与 Alibaba Cloud Linux 命令速查
| 操作 | Ubuntu (apt) | Alibaba Cloud Linux (dnf) |
|------|-------------|--------------------------|
| 更新系统 | `apt update && apt upgrade -y` | `dnf update -y` |
| 安装软件 | `apt install -y <pkg>` | `dnf install -y <pkg>` |
| 安装 Docker | `curl -fsSL https://get.docker.com \| sh` | `dnf install -y docker` |
| 防火墙 | `ufw allow 80/tcp` | `firewall-cmd --permanent --add-port=80/tcp && firewall-cmd --reload` |
| 创建用户 | `adduser --system --group git` | `groupadd --system git && useradd --system --gid git git` |
| Nginx 配置 | `/etc/nginx/sites-available/` | `/etc/nginx/conf.d/` |
---
**文档版本**: v5.0.0 (双分支工作流)
**最后更新**: 2026-04-16
**维护者**: Duoqi Team