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/ 目录
This commit is contained in:
parent
db2f3af8a3
commit
e893755340
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"permissions": {
|
|
||||||
"allow": [
|
|
||||||
"mcp__plugin_ecc_context7__resolve-library-id",
|
|
||||||
"mcp__plugin_ecc_context7__query-docs",
|
|
||||||
"Bash(bun install:*)",
|
|
||||||
"Bash(bun add:*)",
|
|
||||||
"Bash(bunx tsc:*)",
|
|
||||||
"Bash(git status:*)",
|
|
||||||
"Bash(git add:*)",
|
|
||||||
"Bash(git commit:*)",
|
|
||||||
"Bash(bun run:*)"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
72
.dockerignore
Normal file
72
.dockerignore
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
bun.lockb
|
||||||
|
|
||||||
|
# Build output
|
||||||
|
dist
|
||||||
|
build
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Git
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.gitattributes
|
||||||
|
|
||||||
|
# CI/CD
|
||||||
|
.gitea
|
||||||
|
.github
|
||||||
|
.gitlab-ci.yml
|
||||||
|
|
||||||
|
# Scripts
|
||||||
|
scripts
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
docker-compose*.yml
|
||||||
|
Dockerfile
|
||||||
|
|
||||||
|
# Docs
|
||||||
|
docs
|
||||||
|
*.md
|
||||||
|
!README.md
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
coverage
|
||||||
|
.nyc_output
|
||||||
|
*.test.ts
|
||||||
|
*.test.ts
|
||||||
|
*.spec.ts
|
||||||
|
__tests__
|
||||||
|
|
||||||
|
# Tools
|
||||||
|
.eslintrc
|
||||||
|
.prettierrc
|
||||||
|
.editorconfig
|
||||||
|
|
||||||
|
# Drizzle
|
||||||
|
drizzle
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Temporary
|
||||||
|
tmp
|
||||||
|
temp
|
||||||
|
.tmp
|
||||||
32
.env.prod.example
Normal file
32
.env.prod.example
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# 生产环境变量模板
|
||||||
|
# 复制为 .env.prod 使用
|
||||||
|
|
||||||
|
# Database
|
||||||
|
DATABASE_URL=mysql://duoqi_prod:prod-password@your-rds-endpoint:3306/duoqi_prod
|
||||||
|
|
||||||
|
# JWT
|
||||||
|
JWT_SECRET=prod-super-secret-jwt-key-change-this
|
||||||
|
JWT_EXPIRES_IN=1h
|
||||||
|
JWT_REFRESH_EXPIRES_IN=30d
|
||||||
|
|
||||||
|
# Admin
|
||||||
|
ADMIN_TOKEN=prod-admin-token-change-this
|
||||||
|
|
||||||
|
# Huawei ID Kit
|
||||||
|
HUAWEI_CLIENT_ID=
|
||||||
|
HUAWEI_CLIENT_SECRET=
|
||||||
|
|
||||||
|
# Alibaba Cloud OSS
|
||||||
|
OSS_ACCESS_KEY_ID=
|
||||||
|
OSS_ACCESS_KEY_SECRET=
|
||||||
|
OSS_BUCKET=duoqi-assets
|
||||||
|
OSS_REGION=oss-cn-hangzhou
|
||||||
|
|
||||||
|
# Huawei IAP
|
||||||
|
HUAWEI_IAP_URL=https://subscr-drcn.iap.hicloud.com
|
||||||
|
HUAWEI_MERCHANT_ID=
|
||||||
|
|
||||||
|
# Application
|
||||||
|
PORT=3000
|
||||||
|
NODE_ENV=production
|
||||||
|
LOG_LEVEL=warn
|
||||||
198
.gitea/workflows/deploy.yml
Normal file
198
.gitea/workflows/deploy.yml
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
# Gitea Actions CI/CD 配置
|
||||||
|
# Duoqi API - 双分支工作流(develop → main)
|
||||||
|
#
|
||||||
|
# 工作流:
|
||||||
|
# develop push → quality → test → build → 自动部署测试环境
|
||||||
|
# main push → quality → test → build → 手动确认部署生产环境
|
||||||
|
|
||||||
|
name: CI/CD Pipeline
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, develop]
|
||||||
|
|
||||||
|
env:
|
||||||
|
DEPLOY_DIR: /opt/duoqi-api
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# ==================== 代码质量检查 ====================
|
||||||
|
quality:
|
||||||
|
name: Code Quality
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Bun
|
||||||
|
uses: oven-sh/setup-bun@v2
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: bun install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Run ESLint
|
||||||
|
run: bun run lint
|
||||||
|
|
||||||
|
- name: Type check
|
||||||
|
run: bun run typecheck
|
||||||
|
|
||||||
|
# ==================== 运行测试 ====================
|
||||||
|
test:
|
||||||
|
name: Unit Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: quality
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Bun
|
||||||
|
uses: oven-sh/setup-bun@v2
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: bun install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Run tests with coverage
|
||||||
|
run: bun run test:coverage
|
||||||
|
|
||||||
|
- name: Upload coverage
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: coverage-report
|
||||||
|
path: coverage/
|
||||||
|
|
||||||
|
# ==================== 构建测试镜像 ====================
|
||||||
|
build-test:
|
||||||
|
name: Build Test Image
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [quality, test]
|
||||||
|
if: github.ref == 'refs/heads/develop'
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build test image
|
||||||
|
run: |
|
||||||
|
docker build --build-arg NODE_ENV=test -t duoqi-api:test .
|
||||||
|
mkdir -p /tmp/images
|
||||||
|
docker save duoqi-api:test -o /tmp/images/duoqi-api-test.tar
|
||||||
|
|
||||||
|
- name: Upload image artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: docker-images-test
|
||||||
|
path: /tmp/images/
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
# ==================== 构建生产镜像 ====================
|
||||||
|
build-prod:
|
||||||
|
name: Build Production Image
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [quality, test]
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build production image
|
||||||
|
run: |
|
||||||
|
docker build -t duoqi-api:prod .
|
||||||
|
mkdir -p /tmp/images
|
||||||
|
docker save duoqi-api:prod -o /tmp/images/duoqi-api-prod.tar
|
||||||
|
|
||||||
|
- name: Upload image artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: docker-images-prod
|
||||||
|
path: /tmp/images/
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
# ==================== 部署到测试环境(develop push 自动触发) ====================
|
||||||
|
deploy-test:
|
||||||
|
name: Deploy to Test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build-test
|
||||||
|
if: github.ref == 'refs/heads/develop'
|
||||||
|
environment:
|
||||||
|
name: test
|
||||||
|
url: http://test-api.duoqi.me
|
||||||
|
steps:
|
||||||
|
- name: Download image artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: docker-images-test
|
||||||
|
path: /tmp/images/
|
||||||
|
|
||||||
|
- name: Load Docker image
|
||||||
|
run: docker load -i /tmp/images/duoqi-api-test.tar
|
||||||
|
|
||||||
|
- name: Deploy test environment
|
||||||
|
run: |
|
||||||
|
cd ${{ env.DEPLOY_DIR }}
|
||||||
|
docker-compose --profile test up -d --no-build api-test
|
||||||
|
|
||||||
|
- name: Health check
|
||||||
|
run: |
|
||||||
|
sleep 10
|
||||||
|
for i in {1..5}; do
|
||||||
|
if curl -f http://localhost:3001/health; then
|
||||||
|
echo "Test environment is healthy!"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "Health check attempt $i failed, retrying..."
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
echo "Test environment health check failed"
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
# ==================== 部署到生产环境(main push,手动确认) ====================
|
||||||
|
deploy-prod:
|
||||||
|
name: Deploy to Production
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build-prod
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
environment:
|
||||||
|
name: production
|
||||||
|
url: https://api.duoqi.me
|
||||||
|
steps:
|
||||||
|
- name: Download image artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: docker-images-prod
|
||||||
|
path: /tmp/images/
|
||||||
|
|
||||||
|
- name: Load Docker image
|
||||||
|
run: docker load -i /tmp/images/duoqi-api-prod.tar
|
||||||
|
|
||||||
|
- name: Deploy production
|
||||||
|
run: |
|
||||||
|
cd ${{ env.DEPLOY_DIR }}
|
||||||
|
|
||||||
|
# 备份当前镜像(用于回滚)
|
||||||
|
docker tag duoqi-api:prod duoqi-api:rollback 2>/dev/null || true
|
||||||
|
|
||||||
|
# 滚动更新
|
||||||
|
docker-compose up -d --no-build api-prod
|
||||||
|
|
||||||
|
- name: Health check
|
||||||
|
run: |
|
||||||
|
sleep 15
|
||||||
|
for i in {1..5}; do
|
||||||
|
if curl -f http://localhost:3000/health; then
|
||||||
|
echo "Production deployment successful!"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "Health check attempt $i failed, retrying..."
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
# 健康检查失败,自动回滚
|
||||||
|
echo "Health check failed! Rolling back..."
|
||||||
|
cd ${{ env.DEPLOY_DIR }}
|
||||||
|
docker tag duoqi-api:rollback duoqi-api:prod
|
||||||
|
docker-compose up -d --no-build api-prod
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
- name: Cleanup
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
docker image prune -f
|
||||||
|
rm -rf /tmp/images
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,3 +3,6 @@ dist/
|
|||||||
.env
|
.env
|
||||||
*.log
|
*.log
|
||||||
db/migrations/
|
db/migrations/
|
||||||
|
|
||||||
|
# Claude Code
|
||||||
|
.claude/
|
||||||
|
|||||||
48
Dockerfile
Normal file
48
Dockerfile
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# Duoqi API - Multi-stage Dockerfile
|
||||||
|
# 阶段 1: 构建依赖
|
||||||
|
FROM oven/bun:1.1 AS base
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 复制依赖文件
|
||||||
|
COPY package.json bun.lockb ./
|
||||||
|
|
||||||
|
# 阶段 2: 安装依赖
|
||||||
|
FROM base AS install
|
||||||
|
RUN bun install --frozen-lockfile --production
|
||||||
|
|
||||||
|
# 阶段 3: 构建应用
|
||||||
|
FROM base AS build
|
||||||
|
COPY --from=install /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 类型检查和编译
|
||||||
|
RUN bun run typecheck
|
||||||
|
RUN bun run build
|
||||||
|
|
||||||
|
# 阶段 4: 生产镜像
|
||||||
|
FROM oven/bun:1.1 AS release
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 创建非 root 用户
|
||||||
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
|
RUN adduser --system --uid 1001 bun
|
||||||
|
|
||||||
|
# 复制生产依赖和构建产物
|
||||||
|
COPY --from=install /app/node_modules ./node_modules
|
||||||
|
COPY --from=build /app/dist ./dist
|
||||||
|
COPY --from=build /app/db ./db
|
||||||
|
COPY --from=build /app/package.json ./
|
||||||
|
|
||||||
|
# 设置权限
|
||||||
|
RUN chown -R bun:nodejs /app
|
||||||
|
USER bun
|
||||||
|
|
||||||
|
# 暴露端口
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
||||||
|
CMD curl -f http://localhost:3000/health || exit 1
|
||||||
|
|
||||||
|
# 启动应用
|
||||||
|
CMD ["node", "dist/index.js"]
|
||||||
68
docker-compose.prod.yml
Normal file
68
docker-compose.prod.yml
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
# ========================================
|
||||||
|
# 服务器部署配置(单服务器 Phase 1)
|
||||||
|
# 包含生产环境和测试环境(按需启停)
|
||||||
|
#
|
||||||
|
# 用法:
|
||||||
|
# 生产环境:docker-compose up -d
|
||||||
|
# 含测试: docker-compose --profile test up -d
|
||||||
|
# 停止测试:docker-compose --profile test stop api-test
|
||||||
|
# ========================================
|
||||||
|
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
# ===== 生产环境 =====
|
||||||
|
api-prod:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
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
|
||||||
|
|
||||||
|
# ===== 测试环境(Docker profiles 按需启停) =====
|
||||||
|
api-test:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
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
|
||||||
71
docker-compose.yml
Normal file
71
docker-compose.yml
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# 本地开发环境 Docker Compose 配置
|
||||||
|
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
# ===== API 服务(热重载开发) =====
|
||||||
|
api:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: duoqi-api-dev
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
volumes:
|
||||||
|
- ./src:/app/src:ro
|
||||||
|
- ./db:/app/db:ro
|
||||||
|
networks:
|
||||||
|
- duoqi-network
|
||||||
|
depends_on:
|
||||||
|
mysql:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
# ===== MySQL 数据库(本地开发用) =====
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0
|
||||||
|
container_name: duoqi-mysql-dev
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: rootpassword
|
||||||
|
MYSQL_DATABASE: duoqi_dev
|
||||||
|
MYSQL_USER: duoqi
|
||||||
|
MYSQL_PASSWORD: duoqi
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
volumes:
|
||||||
|
- mysql-data:/var/lib/mysql
|
||||||
|
networks:
|
||||||
|
- duoqi-network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-prootpassword"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
# ===== Drizzle Studio =====
|
||||||
|
studio:
|
||||||
|
image: node:20-alpine
|
||||||
|
container_name: duoqi-studio-dev
|
||||||
|
restart: unless-stopped
|
||||||
|
working_dir: /app
|
||||||
|
command: npx drizzle-kit studio
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: mysql://duoqi:duoqi@mysql:3306/duoqi_dev
|
||||||
|
ports:
|
||||||
|
- "4983:4983"
|
||||||
|
networks:
|
||||||
|
- duoqi-network
|
||||||
|
depends_on:
|
||||||
|
mysql:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mysql-data:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
networks:
|
||||||
|
duoqi-network:
|
||||||
|
driver: bridge
|
||||||
987
docs/ci-deployment-guide.md
Normal file
987
docs/ci-deployment-guide.md
Normal file
@ -0,0 +1,987 @@
|
|||||||
|
# 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. 安装 Docker(Alibaba 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 Runner(CI/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
|
||||||
105
scripts/deploy.sh
Normal file
105
scripts/deploy.sh
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Duoqi API 部署脚本
|
||||||
|
# 用法:bash scripts/deploy.sh [命令] [环境]
|
||||||
|
#
|
||||||
|
# 命令:
|
||||||
|
# prod - 构建并部署生产环境
|
||||||
|
# test - 构建并部署测试环境
|
||||||
|
# test-stop - 停止测试环境(释放内存)
|
||||||
|
# status - 查看所有容器状态
|
||||||
|
# rollback - 回滚生产环境到上一版本
|
||||||
|
# logs - 查看生产日志
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
DEPLOY_DIR="/opt/duoqi-api"
|
||||||
|
COMMAND="${1:-status}"
|
||||||
|
ENV="${2:-prod}"
|
||||||
|
|
||||||
|
cd "$DEPLOY_DIR" 2>/dev/null || {
|
||||||
|
echo "Error: Deploy directory $DEPLOY_DIR not found"
|
||||||
|
echo "Please run server setup first."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$COMMAND" in
|
||||||
|
prod)
|
||||||
|
echo "=== Building and deploying production ==="
|
||||||
|
docker-compose build api-prod
|
||||||
|
docker-compose up -d api-prod
|
||||||
|
echo "Waiting for health check..."
|
||||||
|
sleep 15
|
||||||
|
if curl -sf http://localhost:3000/health > /dev/null; then
|
||||||
|
echo "✓ Production deployment successful!"
|
||||||
|
else
|
||||||
|
echo "✗ Production health check failed!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
test)
|
||||||
|
echo "=== Building and deploying test ==="
|
||||||
|
docker-compose build api-test
|
||||||
|
docker-compose --profile test up -d api-test
|
||||||
|
echo "Waiting for health check..."
|
||||||
|
sleep 10
|
||||||
|
if curl -sf http://localhost:3001/health > /dev/null; then
|
||||||
|
echo "✓ Test deployment successful!"
|
||||||
|
else
|
||||||
|
echo "✗ Test health check failed!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
test-stop)
|
||||||
|
echo "=== Stopping test environment ==="
|
||||||
|
docker-compose --profile test stop api-test
|
||||||
|
echo "✓ Test environment stopped"
|
||||||
|
free -h | head -2
|
||||||
|
;;
|
||||||
|
|
||||||
|
status)
|
||||||
|
echo "=== Container Status ==="
|
||||||
|
docker-compose ps
|
||||||
|
echo ""
|
||||||
|
echo "=== Memory Usage ==="
|
||||||
|
free -h | head -2
|
||||||
|
echo ""
|
||||||
|
echo "=== Disk Usage ==="
|
||||||
|
df -h / | tail -1
|
||||||
|
;;
|
||||||
|
|
||||||
|
rollback)
|
||||||
|
echo "=== Rolling back production ==="
|
||||||
|
if docker images | grep -q "duoqi-api:rollback"; then
|
||||||
|
docker tag duoqi-api:rollback duoqi-api:prod
|
||||||
|
docker-compose up -d --no-build api-prod
|
||||||
|
echo "✓ Rollback completed!"
|
||||||
|
else
|
||||||
|
echo "✗ No rollback image found!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
logs)
|
||||||
|
ENV="${2:-prod}"
|
||||||
|
if [ "$ENV" = "test" ]; then
|
||||||
|
docker-compose --profile test logs -f --tail=100 api-test
|
||||||
|
else
|
||||||
|
docker-compose logs -f --tail=100 api-prod
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "Usage: bash scripts/deploy.sh [command]"
|
||||||
|
echo ""
|
||||||
|
echo "Commands:"
|
||||||
|
echo " prod Build and deploy production"
|
||||||
|
echo " test Build and deploy test environment"
|
||||||
|
echo " test-stop Stop test environment (free memory)"
|
||||||
|
echo " status Show container status"
|
||||||
|
echo " rollback Rollback production"
|
||||||
|
echo " logs [env] View logs (prod/test, default: prod)"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Loading…
Reference in New Issue
Block a user