feat:自动构建
This commit is contained in:
1 parent
183f5e7896
commit
6bcd8f4d09
14 files changed
+752
-112
No files matched your search
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
+1
-1
@@ -7,7 +7,7 @@ COPY backend/ ./
|
|||||||
RUN go build -ldflags="-s -w" -o license-manager cmd/main.go
|
RUN go build -ldflags="-s -w" -o license-manager cmd/main.go
|
||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
RUN apk --no-cache add ca-certificates tzdata
|
RUN apk --no-cache add ca-certificates tzdata curl
|
||||||
WORKDIR /app/backend/cmd
|
WORKDIR /app/backend/cmd
|
||||||
COPY --from=builder /app/backend/license-manager ./license-manager
|
COPY --from=builder /app/backend/license-manager ./license-manager
|
||||||
COPY backend/configs/ ../configs/
|
COPY backend/configs/ ../configs/
|
||||||
|
|||||||
@@ -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。
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
- **前端**:Vue.js 3+ 配合现代化UI组件
|
- **前端**:Vue.js 3+ 配合现代化UI组件
|
||||||
- **后端**:Go 1.23+ 使用Gin框架、GORM、Viper配置和Logrus日志
|
- **后端**:Go 1.23+ 使用Gin框架、GORM、Viper配置和Logrus日志
|
||||||
- **数据库**:PostgreSQL 12+ / MySQL 8+
|
- **数据库**:MySQL 8+
|
||||||
- **配置**:YAML格式配置文件
|
- **配置**:YAML格式配置文件
|
||||||
- **部署**:Docker、单机部署或系统服务
|
- **部署**:Docker、单机部署或系统服务
|
||||||
|
|
||||||
@@ -52,73 +52,36 @@
|
|||||||
```bash
|
```bash
|
||||||
# 克隆项目
|
# 克隆项目
|
||||||
git clone https://github.com/cedar-v/license-manager.git
|
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
|
go build -o license-manager
|
||||||
|
|
||||||
# 配置应用
|
# 配置(二选一)
|
||||||
cp config.example.yaml config.yaml
|
# 1) 在当前目录创建配置文件(程序优先读取工作目录下的 config.yaml)
|
||||||
# 编辑 config.yaml 配置文件
|
# cp ../configs/config.example.yaml ./config.yaml && 编辑
|
||||||
|
# 2) 直接编辑 ../configs/config.yaml(程序会自动回退读取该文件)
|
||||||
|
|
||||||
# 运行应用
|
|
||||||
./license-manager
|
./license-manager
|
||||||
```
|
```
|
||||||
|
|
||||||
## Docker部署
|
## Docker 部署
|
||||||
|
|
||||||
|
推荐使用 Docker Compose,已提供开发与生产编排文件:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 构建Docker镜像(多阶段构建,包含前后端)
|
# 开发环境(首次建议带 --build)
|
||||||
docker build -t license-manager .
|
docker compose up -d --build
|
||||||
|
|
||||||
# 创建配置文件
|
# 生产环境
|
||||||
cp backend/configs/config.example.yaml config.yaml
|
docker compose -f docker-compose.prod.yml up -d
|
||||||
# 编辑 config.yaml 配置数据库等信息
|
|
||||||
|
|
||||||
# 使用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
|
curl http://localhost:18888/health
|
||||||
```
|
```
|
||||||
|
|
||||||
### Docker Compose部署(推荐)
|
完整部署说明(反向代理、健康检查、配置挂载等)请参见 `README-Docker.md`。
|
||||||
|
|
||||||
```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:
|
|
||||||
```
|
|
||||||
|
|
||||||
## 开源许可证
|
## 开源许可证
|
||||||
|
|
||||||
|
|||||||
+22
-53
@@ -22,7 +22,7 @@ License Manager is an independent software licensing platform that provides lice
|
|||||||
|
|
||||||
- **Frontend**: Vue.js 3+ with modern UI components
|
- **Frontend**: Vue.js 3+ with modern UI components
|
||||||
- **Backend**: Go 1.23+ with Gin framework, GORM, Viper configuration and Logrus logging
|
- **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
|
- **Configuration**: YAML format configuration files
|
||||||
- **Deployment**: Docker, single machine, or system service
|
- **Deployment**: Docker, single machine, or system service
|
||||||
|
|
||||||
@@ -63,73 +63,42 @@ GET /tools/{tool} - Tool download
|
|||||||
```bash
|
```bash
|
||||||
# Clone the repository
|
# Clone the repository
|
||||||
git clone https://github.com/cedar-v/license-manager.git
|
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
|
go build -o license-manager
|
||||||
|
|
||||||
# Configure the application
|
# Configuration (choose one)
|
||||||
cp config.example.yaml config.yaml
|
# 1) Create config.yaml in the working directory (the app looks here first)
|
||||||
# Edit config.yaml with your settings
|
# 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
|
./license-manager
|
||||||
```
|
```
|
||||||
|
|
||||||
## Docker Deployment
|
## Docker Deployment
|
||||||
|
|
||||||
|
We provide ready-to-use Docker Compose files for development and production:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build Docker image (multi-stage build with frontend and backend)
|
# Development (first run with --build is recommended)
|
||||||
docker build -t license-manager .
|
docker compose up -d --build
|
||||||
|
|
||||||
# Create configuration file
|
# Production
|
||||||
cp backend/configs/config.example.yaml config.yaml
|
docker compose -f docker-compose.prod.yml up -d
|
||||||
# Edit config.yaml to configure database and other settings
|
|
||||||
|
|
||||||
# Run with Docker
|
# Verify backend health
|
||||||
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
|
|
||||||
curl http://localhost:18888/health
|
curl http://localhost:18888/health
|
||||||
```
|
```
|
||||||
|
|
||||||
### Docker Compose Deployment (Recommended)
|
Notes
|
||||||
|
- Backend config is mounted by Compose:
|
||||||
```yaml
|
- Dev: `backend/configs/config.dev.yaml` → `/app/backend/cmd/config.yaml`
|
||||||
# docker-compose.yml
|
- Prod: `backend/configs/config.prod.yaml` → `/app/backend/cmd/config.yaml`
|
||||||
version: '3.8'
|
- Frontend Nginx proxies `/api/` to `backend:18888` inside the Docker network.
|
||||||
services:
|
- Health checks use `curl`; the backend image includes `curl`.
|
||||||
license-manager:
|
- For full details (reverse proxy, health checks, mounts, troubleshooting), see `README-Docker.md`.
|
||||||
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:
|
|
||||||
```
|
|
||||||
|
|
||||||
## Open Source License
|
## Open Source License
|
||||||
|
|
||||||
|
|||||||
@@ -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"]
|
||||||
|
|
||||||
|
|
||||||
@@ -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"]
|
||||||
|
|
||||||
|
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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/<owner>/<repo>-backend:<tag>`、`ghcr.io/<owner>/<repo>-frontend:<tag>`
|
||||||
|
- 已在部署机登录 GHCR:
|
||||||
|
```bash
|
||||||
|
docker login ghcr.io -u <YOUR_GITHUB_USER> -p <YOUR_GITHUB_TOKEN>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方式 A(推荐):直接使用生产编排(支持镜像变量)
|
||||||
|
生产编排文件 `docker-compose.prod.yml` 已支持通过环境变量替换镜像。
|
||||||
|
|
||||||
|
1) 在项目根目录创建 `.env`:
|
||||||
|
```env
|
||||||
|
# 指向 GHCR 镜像(可用具体版本号,如 v1.0.0)
|
||||||
|
BACKEND_IMAGE=ghcr.io/<owner>/<repo>-backend:latest
|
||||||
|
FRONTEND_IMAGE=ghcr.io/<owner>/<repo>-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/<owner>/<repo>-backend:v1.0.0 \
|
||||||
|
FRONTEND_IMAGE=ghcr.io/<owner>/<repo>-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` 的故障排查章节(健康检查、反向代理、容器间网络连通性等)。
|
||||||
|
|
||||||
|
|
||||||
@@ -18,6 +18,8 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
|||||||
// 加载环境变量文件(.env)
|
// 加载环境变量文件(.env)
|
||||||
const env = loadEnv(mode, process.cwd())
|
const env = loadEnv(mode, process.cwd())
|
||||||
|
|
||||||
|
const enableVisualizer = isProd && env.VITE_VISUALIZER === 'true'
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// 基础公共路径,从环境变量VITE_ADDRESS_BASE_URL获取
|
// 基础公共路径,从环境变量VITE_ADDRESS_BASE_URL获取
|
||||||
base: env.VITE_ADDRESS_BASE_URL,
|
base: env.VITE_ADDRESS_BASE_URL,
|
||||||
@@ -37,8 +39,8 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
|||||||
ext: '.gz', // 压缩文件扩展名
|
ext: '.gz', // 压缩文件扩展名
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// 打包分析插件(仅生产环境启用)
|
// 打包分析插件(仅在显式启用时生效;容器/CI 默认不打开浏览器)
|
||||||
isProd && visualizer({ open: true })
|
enableVisualizer && visualizer({ open: false })
|
||||||
].filter(Boolean), // 过滤掉false的插件
|
].filter(Boolean), // 过滤掉false的插件
|
||||||
|
|
||||||
// 开发服务器配置
|
// 开发服务器配置
|
||||||
|
|||||||
+3
-2
@@ -39,9 +39,10 @@ server {
|
|||||||
access_log off;
|
access_log off;
|
||||||
}
|
}
|
||||||
|
|
||||||
# API proxy to backend (if backend is on the same server)
|
# API proxy to backend (container network)
|
||||||
location /api/ {
|
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 Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
|||||||
Reference in new issue
Block a user