From 6bcd8f4d09d5fd8979866c52410202d816e9494d Mon Sep 17 00:00:00 2001 From: wood911010 Date: Sat, 9 Aug 2025 01:34:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E8=87=AA=E5=8A=A8=E6=9E=84=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 19 ++++ .github/workflows/release_backend.yml | 92 +++++++++++++++ .github/workflows/release_frontend.yml | 69 ++++++++++++ Dockerfile.backend | 2 +- README-Docker.md | 120 ++++++++++++++++++++ README.md | 71 +++--------- README_EN.md | 75 ++++--------- backend/configs/config.dev.yaml | 43 +++++++ backend/configs/config.prod.yaml | 43 +++++++ docker-compose.prod.yml | 150 +++++++++++++++++++++++++ docker-compose.yml | 91 +++++++++++++++ docs/使用CI镜像部署.md | 78 +++++++++++++ frontend/vite.config.ts | 6 +- nginx.conf | 5 +- 14 files changed, 752 insertions(+), 112 deletions(-) create mode 100644 .env.example create mode 100644 .github/workflows/release_backend.yml create mode 100644 .github/workflows/release_frontend.yml create mode 100644 README-Docker.md create mode 100644 backend/configs/config.dev.yaml create mode 100644 backend/configs/config.prod.yaml create mode 100644 docker-compose.prod.yml create mode 100644 docker-compose.yml create mode 100644 docs/使用CI镜像部署.md diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..45c8b1b --- /dev/null +++ b/.env.example @@ -0,0 +1,19 @@ +# 生产环境配置示例 +# 复制此文件为 .env 并修改相应配置 + +# MySQL配置 +MYSQL_ROOT_PASSWORD=your-strong-root-password +MYSQL_PASSWORD=your-strong-user-password +MYSQL_PORT=3306 + +# 应用端口配置 +BACKEND_PORT=18888 +FRONTEND_PORT=80 + +# Redis配置 (可选) +REDIS_PASSWORD=your-redis-password +REDIS_PORT=6379 + +# 镜像配置 (用于生产环境) +BACKEND_IMAGE=ghcr.io/your-org/license-manager-backend:latest +FRONTEND_IMAGE=ghcr.io/your-org/license-manager-frontend:latest \ No newline at end of file diff --git a/.github/workflows/release_backend.yml b/.github/workflows/release_backend.yml new file mode 100644 index 0000000..58effa2 --- /dev/null +++ b/.github/workflows/release_backend.yml @@ -0,0 +1,92 @@ +name: Release Backend Image + +on: + workflow_dispatch: + inputs: + version: + description: 'Release version (e.g., v1.0.0)' + required: true + type: string + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }}-backend + +jobs: + test-and-build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.23' + + - name: Cache Go modules + uses: actions/cache@v3 + with: + path: | + ~/.cache/go-build + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('backend/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Run tests + run: | + cd backend + go mod tidy + go mod verify + go test -v ./... + go build -ldflags="-s -w" -o license-manager cmd/main.go + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=latest + type=raw,value=${{ github.event.inputs.version }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile.backend + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Generate summary + run: | + echo "## 🚀 Backend Release Successful!" >> $GITHUB_STEP_SUMMARY + echo "**Version:** ${{ github.event.inputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "**Image:** ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" >> $GITHUB_STEP_SUMMARY + echo "**Tags:**" >> $GITHUB_STEP_SUMMARY + echo "- ${{ github.event.inputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "- latest" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Usage:" >> $GITHUB_STEP_SUMMARY + echo '```bash' >> $GITHUB_STEP_SUMMARY + echo "docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.inputs.version }}" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.github/workflows/release_frontend.yml b/.github/workflows/release_frontend.yml new file mode 100644 index 0000000..26ec270 --- /dev/null +++ b/.github/workflows/release_frontend.yml @@ -0,0 +1,69 @@ +name: Release Frontend Image + +on: + workflow_dispatch: + inputs: + version: + description: 'Release version (e.g., v1.0.0)' + required: true + type: string + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }}-frontend + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=latest + type=raw,value=${{ github.event.inputs.version }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile.frontend + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Generate summary + run: | + echo "## 🎉 Frontend Release Successful!" >> $GITHUB_STEP_SUMMARY + echo "**Version:** ${{ github.event.inputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "**Image:** ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" >> $GITHUB_STEP_SUMMARY + echo "**Tags:**" >> $GITHUB_STEP_SUMMARY + echo "- ${{ github.event.inputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "- latest" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Usage:" >> $GITHUB_STEP_SUMMARY + echo '```bash' >> $GITHUB_STEP_SUMMARY + echo "docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.inputs.version }}" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/Dockerfile.backend b/Dockerfile.backend index 8b5b77f..46c619c 100644 --- a/Dockerfile.backend +++ b/Dockerfile.backend @@ -7,7 +7,7 @@ COPY backend/ ./ RUN go build -ldflags="-s -w" -o license-manager cmd/main.go FROM alpine:latest -RUN apk --no-cache add ca-certificates tzdata +RUN apk --no-cache add ca-certificates tzdata curl WORKDIR /app/backend/cmd COPY --from=builder /app/backend/license-manager ./license-manager COPY backend/configs/ ../configs/ diff --git a/README-Docker.md b/README-Docker.md new file mode 100644 index 0000000..1f83ce8 --- /dev/null +++ b/README-Docker.md @@ -0,0 +1,120 @@ +# Docker 部署指南 + +本项目提供两种部署:开发环境与生产环境。以下说明已与当前 `docker-compose` 配置、镜像与反向代理实现对齐。 + +## 开发环境 + +使用 `docker compose`(Docker Desktop 或 Docker CE 20.10+): + +```bash +# 启动(首次建议带 --build) +docker compose up -d --build + +# 查看状态/日志 +docker compose ps +docker compose logs -f backend + +# 停止 +docker compose down +``` + +### 访问地址(开发) +- 前端: http://localhost:18080 +- 后端 API: http://localhost:18888 +- MySQL: localhost:13306(数据库、用户、密码见 compose) + +## 生产环境 + +使用 `docker-compose.prod.yml`: + +```bash +# 启动生产环境 +docker compose -f docker-compose.prod.yml up -d + +# 查看服务状态 +docker compose -f docker-compose.prod.yml ps +``` + +### 生产配置说明 +- 后端配置文件已通过卷挂载至容器内: + - 开发:`backend/configs/config.dev.yaml` → 容器内 `/app/backend/cmd/config.yaml` + - 生产:`backend/configs/config.prod.yaml` → 容器内 `/app/backend/cmd/config.yaml` +- 前端 Nginx 反向代理已指向容器网络内的 `backend:18888`,无需依赖 `host.docker.internal`。 +- 健康检查统一使用 `curl`,后端镜像已内置 `curl`。 +- 如需隐藏后端对外端口,可在生产 compose 中移除后端端口映射,仅通过前端或网关暴露。 + +## 服务说明 + +### MySQL 8.0 +- 端口:3306(生产)/ 13306(开发宿主机映射) +- 库:`license_manager`,用户:`license_user` +- 字符集/排序:`utf8mb4`/`utf8mb4_unicode_ci` +- 开启慢查询日志(生产) + +### 后端服务 +- 端口:18888 +- 健康检查:`/health` +- 配置:通过卷挂载 `config.dev.yaml` / `config.prod.yaml` 为容器内 `config.yaml` +- 日志:标准输出 + `./logs` 挂载目录 + +### 前端服务 +- 端口:80(生产)/ 18080(开发映射) +- 反向代理:`/api/` → `backend:18888` +- 静态资源:启用 Gzip 与长缓存 + +### Redis(可选,仅生产) +- 端口:6379,AOF 持久化 + +## 常用命令 + +```bash +# 重新构建镜像 +docker compose build + +# 启动指定服务 +docker compose up -d mysql backend + +# 进入容器 +docker compose exec backend sh +docker compose exec mysql mysql -uroot -p + +# 备份/恢复数据库 +docker compose exec mysql mysqldump -uroot -p license_manager > backup.sql +docker compose exec -T mysql mysql -uroot -p license_manager < backup.sql + +# 清理未使用资源 +docker system prune -f +``` + +## 故障排查 + +1) MySQL 启动失败 +```bash +docker compose logs mysql +docker compose down -v && docker compose up -d +``` + +2) 后端连接数据库失败 +```bash +# 确认 MySQL 健康 +docker compose ps + +# 检查容器内配置(已挂载到 /app/backend/cmd/config.yaml) +docker compose exec backend cat /app/backend/cmd/config.yaml +``` + +3) 前端无法访问后端 API +```bash +# 容器内连通性 +docker compose exec frontend sh -c "curl -sS http://backend:18888/health" + +# 检查 nginx 配置 +docker compose exec frontend cat /etc/nginx/conf.d/default.conf +``` + +## 注意事项 + +- 生产环境请修改默认账号、数据库口令与 JWT 秘钥(见 `backend/configs/config.prod.yaml`)。 +- 如使用较新 Docker,YAML 顶部 `version` 字段会被忽略,可按需移除提示。 +- `deploy.resources` 仅在 Swarm/K8s 生效,单机 Compose 可忽略。 +- 如置于公网,建议仅暴露前端端口,通过前端或网关转发后端 API。 \ No newline at end of file diff --git a/README.md b/README.md index b8c9897..dd824f2 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ - **前端**:Vue.js 3+ 配合现代化UI组件 - **后端**:Go 1.23+ 使用Gin框架、GORM、Viper配置和Logrus日志 -- **数据库**:PostgreSQL 12+ / MySQL 8+ +- **数据库**:MySQL 8+ - **配置**:YAML格式配置文件 - **部署**:Docker、单机部署或系统服务 @@ -52,73 +52,36 @@ ```bash # 克隆项目 git clone https://github.com/cedar-v/license-manager.git -cd license-manager/backend/cmd +cd license-manager -# 构建应用 +# 本地后端构建与运行(可选) +cd backend/cmd go build -o license-manager -# 配置应用 -cp config.example.yaml config.yaml -# 编辑 config.yaml 配置文件 +# 配置(二选一) +# 1) 在当前目录创建配置文件(程序优先读取工作目录下的 config.yaml) +# cp ../configs/config.example.yaml ./config.yaml && 编辑 +# 2) 直接编辑 ../configs/config.yaml(程序会自动回退读取该文件) -# 运行应用 ./license-manager ``` -## Docker部署 +## Docker 部署 + +推荐使用 Docker Compose,已提供开发与生产编排文件: ```bash -# 构建Docker镜像(多阶段构建,包含前后端) -docker build -t license-manager . +# 开发环境(首次建议带 --build) +docker compose up -d --build -# 创建配置文件 -cp backend/configs/config.example.yaml config.yaml -# 编辑 config.yaml 配置数据库等信息 +# 生产环境 +docker compose -f docker-compose.prod.yml up -d -# 使用Docker运行 -docker run -d \ - --name license-manager \ - -p 18888:18888 \ - -v $(pwd)/config.yaml:/app/config.yaml \ - license-manager - -# 查看运行状态 -docker logs license-manager - -# 健康检查 +# 验证后端健康 curl http://localhost:18888/health ``` -### Docker Compose部署(推荐) - -```yaml -# docker-compose.yml -version: '3.8' -services: - license-manager: - build: . - ports: - - "18888:18888" - volumes: - - ./config.yaml:/app/config.yaml - environment: - - GIN_MODE=release - restart: unless-stopped - - mysql: - image: mysql:8.0 - environment: - MYSQL_ROOT_PASSWORD: root@123 - MYSQL_DATABASE: license_manager - ports: - - "3306:3306" - volumes: - - mysql_data:/var/lib/mysql - restart: unless-stopped - -volumes: - mysql_data: -``` +完整部署说明(反向代理、健康检查、配置挂载等)请参见 `README-Docker.md`。 ## 开源许可证 diff --git a/README_EN.md b/README_EN.md index c21a02d..2484afd 100644 --- a/README_EN.md +++ b/README_EN.md @@ -22,7 +22,7 @@ License Manager is an independent software licensing platform that provides lice - **Frontend**: Vue.js 3+ with modern UI components - **Backend**: Go 1.23+ with Gin framework, GORM, Viper configuration and Logrus logging -- **Database**: PostgreSQL 12+ / MySQL 8+ +- **Database**: MySQL 8+ - **Configuration**: YAML format configuration files - **Deployment**: Docker, single machine, or system service @@ -63,73 +63,42 @@ GET /tools/{tool} - Tool download ```bash # Clone the repository git clone https://github.com/cedar-v/license-manager.git -cd license-manager/backend/cmd +cd license-manager -# Build the application +# Local backend build & run (optional) +cd backend/cmd go build -o license-manager -# Configure the application -cp config.example.yaml config.yaml -# Edit config.yaml with your settings +# Configuration (choose one) +# 1) Create config.yaml in the working directory (the app looks here first) +# cp ../configs/config.example.yaml ./config.yaml && edit it +# 2) Edit ../configs/config.yaml (the app will fall back to this path) -# Run the application ./license-manager ``` ## Docker Deployment +We provide ready-to-use Docker Compose files for development and production: + ```bash -# Build Docker image (multi-stage build with frontend and backend) -docker build -t license-manager . +# Development (first run with --build is recommended) +docker compose up -d --build -# Create configuration file -cp backend/configs/config.example.yaml config.yaml -# Edit config.yaml to configure database and other settings +# Production +docker compose -f docker-compose.prod.yml up -d -# Run with Docker -docker run -d \ - --name license-manager \ - -p 18888:18888 \ - -v $(pwd)/config.yaml:/app/config.yaml \ - license-manager - -# Check running status -docker logs license-manager - -# Health check +# Verify backend health curl http://localhost:18888/health ``` -### Docker Compose Deployment (Recommended) - -```yaml -# docker-compose.yml -version: '3.8' -services: - license-manager: - build: . - ports: - - "18888:18888" - volumes: - - ./config.yaml:/app/config.yaml - environment: - - GIN_MODE=release - restart: unless-stopped - - mysql: - image: mysql:8.0 - environment: - MYSQL_ROOT_PASSWORD: root@123 - MYSQL_DATABASE: license_manager - ports: - - "3306:3306" - volumes: - - mysql_data:/var/lib/mysql - restart: unless-stopped - -volumes: - mysql_data: -``` +Notes +- Backend config is mounted by Compose: + - Dev: `backend/configs/config.dev.yaml` → `/app/backend/cmd/config.yaml` + - Prod: `backend/configs/config.prod.yaml` → `/app/backend/cmd/config.yaml` +- Frontend Nginx proxies `/api/` to `backend:18888` inside the Docker network. +- Health checks use `curl`; the backend image includes `curl`. +- For full details (reverse proxy, health checks, mounts, troubleshooting), see `README-Docker.md`. ## Open Source License diff --git a/backend/configs/config.dev.yaml b/backend/configs/config.dev.yaml new file mode 100644 index 0000000..0bae70b --- /dev/null +++ b/backend/configs/config.dev.yaml @@ -0,0 +1,43 @@ +server: + host: "0.0.0.0" + port: 18888 + mode: "debug" + +database: + driver: "mysql" + host: "mysql" + port: 3306 + username: "license_user" + password: "license@123" + database: "license_manager" + charset: "utf8mb4" + parse_time: true + loc: "Local" + max_idle_conns: 10 + max_open_conns: 100 + conn_max_lifetime: 3600 + auto_migrate: true + +auth: + jwt: + secret: "license-manager-dev-secret-key-32-bytes" + expire_hours: 1 + refresh_threshold_minutes: 30 + admin: + username: "admin" + password: "admin@123" + security: + max_login_attempts: 5 + lockout_duration_minutes: 15 + +log: + level: "info" + format: "text" + +i18n: + enable: true + default_lang: "zh-CN" + config_path: "../configs/i18n/errors" + support_langs: ["zh-CN", "en-US", "ja-JP"] + + diff --git a/backend/configs/config.prod.yaml b/backend/configs/config.prod.yaml new file mode 100644 index 0000000..0fa1906 --- /dev/null +++ b/backend/configs/config.prod.yaml @@ -0,0 +1,43 @@ +server: + host: "0.0.0.0" + port: 18888 + mode: "release" + +database: + driver: "mysql" + host: "mysql" + port: 3306 + username: "license_user" + password: "license@123" + database: "license_manager" + charset: "utf8mb4" + parse_time: true + loc: "Local" + max_idle_conns: 50 + max_open_conns: 300 + conn_max_lifetime: 3600 + auto_migrate: true + +auth: + jwt: + secret: "please-change-in-prod" + expire_hours: 1 + refresh_threshold_minutes: 30 + admin: + username: "admin" + password: "changeMe!" + security: + max_login_attempts: 5 + lockout_duration_minutes: 15 + +log: + level: "info" + format: "json" + +i18n: + enable: true + default_lang: "zh-CN" + config_path: "../configs/i18n/errors" + support_langs: ["zh-CN", "en-US", "ja-JP"] + + diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..55114b3 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,150 @@ +version: '3.8' + +services: + # MySQL 8数据库 - 生产环境优化配置 + mysql: + image: mysql:8.0 + container_name: license-manager-mysql-prod + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root@123} + MYSQL_DATABASE: license_manager + MYSQL_USER: license_user + MYSQL_PASSWORD: ${MYSQL_PASSWORD:-license@123} + TZ: Asia/Shanghai + ports: + - "${MYSQL_PORT:-3306}:3306" + volumes: + - mysql_data:/var/lib/mysql + - ./mysql/conf.d:/etc/mysql/conf.d + command: > + --default-authentication-plugin=mysql_native_password + --character-set-server=utf8mb4 + --collation-server=utf8mb4_unicode_ci + --sql_mode=STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO + --max_connections=2000 + --innodb_buffer_pool_size=512M + --innodb_log_file_size=256M + --slow_query_log=1 + --slow_query_log_file=/var/lib/mysql/mysql-slow.log + --long_query_time=2 + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 60s + networks: + - license-manager-network + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + # 后端服务 - 生产环境 + backend: + image: ${BACKEND_IMAGE:-license-manager-backend:latest} + container_name: license-manager-backend-prod + restart: unless-stopped + environment: + - TZ=Asia/Shanghai + - GIN_MODE=release + ports: + - "${BACKEND_PORT:-18888}:18888" + volumes: + - ./backend/configs:/app/backend/configs:ro + - ./backend/configs/config.prod.yaml:/app/backend/cmd/config.yaml:ro + - ./logs:/app/logs + - ./data:/app/data + depends_on: + mysql: + condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://localhost:18888/health || exit 1"] + interval: 10s + timeout: 10s + retries: 6 + start_period: 60s + networks: + - license-manager-network + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + deploy: + resources: + limits: + memory: 512M + reservations: + memory: 256M + + # 前端服务 - 生产环境 + frontend: + image: ${FRONTEND_IMAGE:-license-manager-frontend:latest} + container_name: license-manager-frontend-prod + restart: unless-stopped + environment: + - TZ=Asia/Shanghai + ports: + - "${FRONTEND_PORT:-80}:80" + depends_on: + backend: + condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://localhost/ || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + networks: + - license-manager-network + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + deploy: + resources: + limits: + memory: 256M + reservations: + memory: 128M + + # Redis缓存 (可选,用于session存储和缓存) + redis: + image: redis:7-alpine + container_name: license-manager-redis-prod + restart: unless-stopped + ports: + - "${REDIS_PORT:-6379}:6379" + volumes: + - redis_data:/data + command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-redis@123} + healthcheck: + test: ["CMD", "redis-cli", "--raw", "incr", "ping"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + networks: + - license-manager-network + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + +volumes: + mysql_data: + driver: local + redis_data: + driver: local + +networks: + license-manager-network: + driver: bridge + ipam: + config: + - subnet: 172.20.0.0/16 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..5acdd0d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,91 @@ +version: '3.8' + +services: + # MySQL 8数据库 + mysql: + image: mysql:8.0 + container_name: license-manager-mysql + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: root@123 + MYSQL_DATABASE: license_manager + MYSQL_USER: license_user + MYSQL_PASSWORD: license@123 + TZ: Asia/Shanghai + ports: + - "13306:3306" + volumes: + - mysql_data:/var/lib/mysql + command: > + --default-authentication-plugin=mysql_native_password + --character-set-server=utf8mb4 + --collation-server=utf8mb4_unicode_ci + --sql_mode=STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO + --max_connections=1000 + --innodb_buffer_pool_size=256M + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 30s + networks: + - license-manager-network + + # 后端服务 + backend: + build: + context: . + dockerfile: Dockerfile.backend + container_name: license-manager-backend + restart: unless-stopped + environment: + - TZ=Asia/Shanghai + ports: + - "18888:18888" + volumes: + - ./backend/configs:/app/backend/configs + - ./logs:/app/logs + - ./backend/configs/config.dev.yaml:/app/backend/cmd/config.yaml:ro + depends_on: + mysql: + condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://localhost:18888/health || exit 1"] + interval: 10s + timeout: 10s + retries: 6 + start_period: 60s + networks: + - license-manager-network + + # 前端服务 + frontend: + build: + context: . + dockerfile: Dockerfile.frontend + container_name: license-manager-frontend + restart: unless-stopped + environment: + - TZ=Asia/Shanghai + ports: + - "18080:80" + depends_on: + backend: + condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://localhost/ || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 10s + networks: + - license-manager-network + +volumes: + mysql_data: + driver: local + +networks: + license-manager-network: + driver: bridge \ No newline at end of file diff --git a/docs/使用CI镜像部署.md b/docs/使用CI镜像部署.md new file mode 100644 index 0000000..7685dc2 --- /dev/null +++ b/docs/使用CI镜像部署.md @@ -0,0 +1,78 @@ +## 使用 CI 产物镜像替换本地构建的部署方案 + +本文说明如何使用 GitHub Actions 构建并推送到 GHCR 的镜像,直接替换本地 `docker compose up -d --build` 的构建流程,以实现更一致、更快的部署。 + +### 前置条件 +- 已通过仓库内工作流发布镜像: + - 后端:`.github/workflows/release_backend.yml` + - 前端:`.github/workflows/release_frontend.yml` +- 镜像仓库(GHCR)可访问:`ghcr.io//-backend:`、`ghcr.io//-frontend:` +- 已在部署机登录 GHCR: + ```bash + docker login ghcr.io -u -p + ``` + +### 方式 A(推荐):直接使用生产编排(支持镜像变量) +生产编排文件 `docker-compose.prod.yml` 已支持通过环境变量替换镜像。 + +1) 在项目根目录创建 `.env`: +```env +# 指向 GHCR 镜像(可用具体版本号,如 v1.0.0) +BACKEND_IMAGE=ghcr.io//-backend:latest +FRONTEND_IMAGE=ghcr.io//-frontend:latest + +# 可选:本地端口映射(如用于开发环境) +MYSQL_PORT=13306 +BACKEND_PORT=18888 +FRONTEND_PORT=18080 +``` + +2) 启动: +```bash +docker compose -f docker-compose.prod.yml up -d +``` + +3) 验证: +```bash +curl http://localhost:18888/health +``` + +说明: +- 该方式同时适用于生产/准生产/本地测试场景,只需通过 `.env` 控制端口与镜像标签即可。 +- 若镜像私有,请确保已成功 `docker login ghcr.io`。 + +### 方式 B:临时覆盖镜像(不创建 .env) +直接在一次性命令中覆盖镜像变量: +```bash +BACKEND_IMAGE=ghcr.io//-backend:v1.0.0 \ +FRONTEND_IMAGE=ghcr.io//-frontend:v1.0.0 \ +docker compose -f docker-compose.prod.yml up -d +``` + +### 常用操作 +- 拉取新版本并滚动更新: +```bash +docker compose -f docker-compose.prod.yml pull +docker compose -f docker-compose.prod.yml up -d +``` + +- 回滚到上一版本: +```bash +# 修改 .env 中的镜像标签为上一个版本 +docker compose -f docker-compose.prod.yml up -d +``` + +### 与开发编排的关系 +- 当前开发编排 `docker-compose.yml` 使用 `build` 本地构建。如果希望开发环境也使用远端镜像,推荐直接沿用方式 A(使用 `docker-compose.prod.yml`,并通过 `.env` 调整端口为开发习惯)。 +- 也可以手动修改 `docker-compose.yml`:将 `backend`、`frontend` 的 `build` 段替换为 `image: ${BACKEND_IMAGE}` / `image: ${FRONTEND_IMAGE}`,并在 `.env` 中声明对应镜像。 + +### 标签与架构 +- CI 工作流默认推送 `latest` 与手动输入的版本号(如 `v1.0.0`),并构建多架构镜像:`linux/amd64, linux/arm64`。 +- 生产环境建议使用显式版本标签部署,避免 `latest` 带来的不可预期变更。 + +### 故障排查 +- 无法拉取镜像:检查 GHCR 登录权限、镜像是否设为公开或部署机是否具备读取私有包权限。 +- 拉取缓慢或超时:配置镜像加速器或重试。 +- 启动后访问异常:参考 `README-Docker.md` 的故障排查章节(健康检查、反向代理、容器间网络连通性等)。 + + diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index ac4e8b9..c80feb0 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -18,6 +18,8 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => { // 加载环境变量文件(.env) const env = loadEnv(mode, process.cwd()) + const enableVisualizer = isProd && env.VITE_VISUALIZER === 'true' + return { // 基础公共路径,从环境变量VITE_ADDRESS_BASE_URL获取 base: env.VITE_ADDRESS_BASE_URL, @@ -37,8 +39,8 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => { ext: '.gz', // 压缩文件扩展名 }), - // 打包分析插件(仅生产环境启用) - isProd && visualizer({ open: true }) + // 打包分析插件(仅在显式启用时生效;容器/CI 默认不打开浏览器) + enableVisualizer && visualizer({ open: false }) ].filter(Boolean), // 过滤掉false的插件 // 开发服务器配置 diff --git a/nginx.conf b/nginx.conf index ddb4d9d..9dd2a09 100644 --- a/nginx.conf +++ b/nginx.conf @@ -39,9 +39,10 @@ server { access_log off; } - # API proxy to backend (if backend is on the same server) + # API proxy to backend (container network) location /api/ { - proxy_pass http://host.docker.internal:18888/api/; + # Keep original request path; backend service name resolves inside Docker network + proxy_pass http://backend:18888; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;