From 589197e2840068cc0d21f64d294155839d47b908 Mon Sep 17 00:00:00 2001 From: huang Date: Mon, 19 Jan 2026 14:56:40 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20Docker=20=E5=92=8C=20CI/CD?= =?UTF-8?q?=20=E9=85=8D=E7=BD=AE=EF=BC=88=E6=97=A0=20SSH=20=E6=96=B9?= =?UTF-8?q?=E6=A1=88=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 61 +++ .gitea/workflows/deploy.yaml | 90 +++++ .gitignore | 1 + Dockerfile.api | 69 ++++ Dockerfile.worker | 53 +++ configs/config.yaml | 57 +-- docker-compose.prod.yml | 58 +++ docker/entrypoint-api.sh | 39 ++ docs/deployment/deployment-guide.md | 500 +++++++++++++++++++++++++ docs/deployment/gitea-secrets-setup.md | 114 ++++++ 10 files changed, 1000 insertions(+), 42 deletions(-) create mode 100644 .dockerignore create mode 100644 .gitea/workflows/deploy.yaml create mode 100644 Dockerfile.api create mode 100644 Dockerfile.worker create mode 100644 docker-compose.prod.yml create mode 100644 docker/entrypoint-api.sh create mode 100644 docs/deployment/deployment-guide.md create mode 100644 docs/deployment/gitea-secrets-setup.md diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..9e50c35 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,61 @@ +# Git 相关 +.git +.gitignore +.gitea + +# 文档和规范 +README.md +AGENTS.md +CLAUDE.md +docs/ +specs/ +openspec/ +*.md + +# 构建产物 +bin/ +api +worker +main +*.exe +*.dll +*.so +*.dylib + +# 测试和覆盖率 +tests/ +*_test.go +*.test +*.out +coverage*.out + +# 日志文件 +logs/ +*.log + +# 临时文件 +tmp/ +temp/ +*.tmp +*.swp +*.swo +*~ + +# IDE 配置 +.vscode/ +.idea/ +.claude/ +.sisyphus/ +*.iml + +# 环境配置(不打包,使用挂载) +.env +.env.* + +# 其他 +scripts/ +Makefile +openapi.yaml +opencode.json +ai-gateway.conf +.emdash.json diff --git a/.gitea/workflows/deploy.yaml b/.gitea/workflows/deploy.yaml new file mode 100644 index 0000000..992c52c --- /dev/null +++ b/.gitea/workflows/deploy.yaml @@ -0,0 +1,90 @@ +name: 构建并部署到测试环境(无 SSH) + +on: + push: + branches: + - main + - dev + - test + +env: + REGISTRY: registry.boss160.cn + API_IMAGE: registry.boss160.cn/junhong/cmp-fiber-api + WORKER_IMAGE: registry.boss160.cn/junhong/cmp-fiber-worker + DEPLOY_DIR: /home/qycard001/app/junhong_cmp + +jobs: + build-and-deploy: + runs-on: docker + + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 设置镜像标签 + id: tag + run: | + if [ "${{ github.ref }}" = "refs/heads/main" ]; then + echo "tag=latest" >> $GITHUB_OUTPUT + elif [ "${{ github.ref }}" = "refs/heads/dev" ]; then + echo "tag=dev" >> $GITHUB_OUTPUT + elif [ "${{ github.ref }}" = "refs/heads/test" ]; then + echo "tag=test" >> $GITHUB_OUTPUT + else + echo "tag=unknown" >> $GITHUB_OUTPUT + fi + + - name: 登录 Docker Registry + run: | + echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login ${{ env.REGISTRY }} -u ${{ secrets.REGISTRY_USERNAME }} --password-stdin + + - name: 构建 API 镜像 + run: | + docker build -f Dockerfile.api -t ${{ env.API_IMAGE }}:${{ steps.tag.outputs.tag }} . + docker tag ${{ env.API_IMAGE }}:${{ steps.tag.outputs.tag }} ${{ env.API_IMAGE }}:${{ github.sha }} + + - name: 构建 Worker 镜像 + run: | + docker build -f Dockerfile.worker -t ${{ env.WORKER_IMAGE }}:${{ steps.tag.outputs.tag }} . + docker tag ${{ env.WORKER_IMAGE }}:${{ steps.tag.outputs.tag }} ${{ env.WORKER_IMAGE }}:${{ github.sha }} + + - name: 推送镜像到 Registry + run: | + docker push ${{ env.API_IMAGE }}:${{ steps.tag.outputs.tag }} + docker push ${{ env.API_IMAGE }}:${{ github.sha }} + docker push ${{ env.WORKER_IMAGE }}:${{ steps.tag.outputs.tag }} + docker push ${{ env.WORKER_IMAGE }}:${{ github.sha }} + + - name: 部署到本地(仅 main 分支) + if: github.ref == 'refs/heads/main' + run: | + cd ${{ env.DEPLOY_DIR }} + + echo "拉取最新镜像..." + docker-compose -f docker-compose.prod.yml pull + + echo "执行滚动更新..." + docker-compose -f docker-compose.prod.yml up -d --no-deps + + echo "等待服务健康检查..." + sleep 10 + + echo "清理旧镜像(保留最近 3 个版本)..." + docker images ${{ env.API_IMAGE }} --format "{{.ID}}" | tail -n +4 | xargs -r docker rmi -f || true + docker images ${{ env.WORKER_IMAGE }} --format "{{.ID}}" | tail -n +4 | xargs -r docker rmi -f || true + + echo "清理悬空镜像..." + docker image prune -f + + echo "部署完成!" + docker-compose -f docker-compose.prod.yml ps + + - name: 构建结果通知 + if: always() + run: | + if [ "${{ job.status }}" = "success" ]; then + echo "✅ 构建成功: ${{ steps.tag.outputs.tag }}" + echo "📦 镜像标签: ${{ github.sha }}" + else + echo "❌ 构建失败" + fi diff --git a/.gitignore b/.gitignore index 70202ef..00fdd15 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,4 @@ cmd/api/api api .gitignore worker +ai-gateway.conf diff --git a/Dockerfile.api b/Dockerfile.api new file mode 100644 index 0000000..9c18845 --- /dev/null +++ b/Dockerfile.api @@ -0,0 +1,69 @@ +# ================================ +# 阶段 1: 构建阶段 +# ================================ +FROM golang:1.23.4-alpine AS builder + +# 设置工作目录 +WORKDIR /build + +# 安装必要的构建工具 +RUN apk add --no-cache git ca-certificates tzdata + +# 复制 go.mod 和 go.sum(利用 Docker 缓存) +COPY go.mod go.sum ./ +RUN go mod download + +# 复制源代码 +COPY . . + +# 编译 API 服务(静态链接) +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ + -ldflags="-w -s" \ + -o /build/api \ + cmd/api/main.go + +# 下载 golang-migrate 工具 +RUN go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest + +# ================================ +# 阶段 2: 运行阶段 +# ================================ +FROM alpine:latest + +# 安装运行时依赖 +RUN apk add --no-cache ca-certificates tzdata bash curl + +# 设置时区 +ENV TZ=Asia/Shanghai + +# 创建非 root 用户 +RUN addgroup -g 1000 appuser && \ + adduser -D -u 1000 -G appuser appuser + +# 设置工作目录 +WORKDIR /app + +# 从构建阶段复制二进制文件 +COPY --from=builder /build/api /app/api +COPY --from=builder /go/bin/migrate /usr/local/bin/migrate + +# 复制配置文件和迁移文件 +COPY configs /app/configs +COPY migrations /app/migrations + +# 复制启动脚本 +COPY docker/entrypoint-api.sh /app/entrypoint.sh +RUN chmod +x /app/entrypoint.sh + +# 切换到非 root 用户 +USER appuser + +# 暴露端口 +EXPOSE 8088 + +# 健康检查 +HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \ + CMD curl -f http://localhost:8088/health || exit 1 + +# 启动命令 +ENTRYPOINT ["/app/entrypoint.sh"] diff --git a/Dockerfile.worker b/Dockerfile.worker new file mode 100644 index 0000000..8c57b2c --- /dev/null +++ b/Dockerfile.worker @@ -0,0 +1,53 @@ +# ================================ +# 阶段 1: 构建阶段 +# ================================ +FROM golang:1.23.4-alpine AS builder + +# 设置工作目录 +WORKDIR /build + +# 安装必要的构建工具 +RUN apk add --no-cache git ca-certificates tzdata + +# 复制 go.mod 和 go.sum(利用 Docker 缓存) +COPY go.mod go.sum ./ +RUN go mod download + +# 复制源代码 +COPY . . + +# 编译 Worker 服务(静态链接) +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ + -ldflags="-w -s" \ + -o /build/worker \ + cmd/worker/main.go + +# ================================ +# 阶段 2: 运行阶段 +# ================================ +FROM alpine:latest + +# 安装运行时依赖 +RUN apk add --no-cache ca-certificates tzdata bash + +# 设置时区 +ENV TZ=Asia/Shanghai + +# 创建非 root 用户 +RUN addgroup -g 1000 appuser && \ + adduser -D -u 1000 -G appuser appuser + +# 设置工作目录 +WORKDIR /app + +# 从构建阶段复制二进制文件 +COPY --from=builder /build/worker /app/worker + +# 复制配置文件 +COPY configs /app/configs + +# 切换到非 root 用户 +USER appuser + +# 启动命令 +CMD ["/app/worker"] diff --git a/configs/config.yaml b/configs/config.yaml index fb3bb65..77c42d3 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -37,56 +37,29 @@ queue: timeout: "10m" logging: - level: "info" - development: false + level: "debug" # 开发环境使用 debug 级别 + development: true # 启用开发模式(美化日志输出) app_log: filename: "logs/app.log" - max_size: 100 # MB - max_backups: 30 - max_age: 30 # 天 - compress: true + max_size: 100 + max_backups: 10 # 开发环境保留较少备份 + max_age: 7 # 7天 + compress: false # 开发环境不压缩 access_log: filename: "logs/access.log" - max_size: 500 # MB - max_backups: 90 - max_age: 90 # 天 - compress: true + max_size: 100 + max_backups: 10 + max_age: 7 + compress: false middleware: - # 认证中间件开关 - enable_auth: true - - # 限流中间件开关(默认禁用,按需启用) - enable_rate_limiter: false - - # 限流器配置 + enable_auth: true # 开发环境可选禁用认证 + enable_rate_limiter: true rate_limiter: - # 每个时间窗口允许的最大请求数 - # 建议值: - # - 公开 API(严格): 60-100 - # - 公开 API(宽松): 1000-5000 - # - 内部 API: 5000-10000 - max: 100 - - # 时间窗口(限流重置周期) - # 支持格式: - # - "30s" (30秒) - # - "1m" (1分钟,推荐) - # - "5m" (5分钟) - # - "1h" (1小时) + max: 1000 expiration: "1m" + storage: "redis" - # 限流存储方式 - # 选项: - # - "memory": 内存存储(单机部署,快速,重启后重置) - # - "redis": Redis存储(分布式部署,持久化,跨服务器共享) - # 建议: - # - 开发/测试环境:使用 "memory" - # - 生产环境(单机):使用 "memory" - # - 生产环境(多机):使用 "redis" - storage: "memory" - -# 短信服务配置 sms: gateway_url: "https://gateway.sms.whjhft.com:8443/sms" username: "JH0001" # TODO: 替换为实际的短信服务账号 @@ -96,7 +69,7 @@ sms: # JWT 配置 jwt: - secret_key: "your-secret-key-change-this-in-production" # TODO: 生产环境必须修改 + secret_key: "dev-secret-key-for-testing-only-32chars!" token_duration: "168h" # C 端个人客户 JWT Token 有效期(7天) access_token_ttl: "24h" # B 端访问令牌有效期(24小时) refresh_token_ttl: "168h" # B 端刷新令牌有效期(7天) diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..e03c697 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,58 @@ +version: '3.8' + +services: + api: + image: registry.boss160.cn/junhong/cmp-fiber-api:latest + container_name: junhong-cmp-api + restart: unless-stopped + ports: + - "8088:8088" + environment: + - CONFIG_ENV=prod + - DB_HOST=${DB_HOST} + - DB_PORT=${DB_PORT} + - DB_USER=${DB_USER} + - DB_PASSWORD=${DB_PASSWORD} + - DB_NAME=${DB_NAME} + - DB_SSLMODE=${DB_SSLMODE} + volumes: + - ./configs:/app/configs:ro + - ./logs:/app/logs + - ./.env:/app/.env:ro + networks: + - junhong-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8088/health"] + interval: 30s + timeout: 3s + retries: 3 + start_period: 10s + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + worker: + image: registry.boss160.cn/junhong/cmp-fiber-worker:latest + container_name: junhong-cmp-worker + restart: unless-stopped + environment: + - CONFIG_ENV=prod + volumes: + - ./configs:/app/configs:ro + - ./logs:/app/logs + networks: + - junhong-network + depends_on: + api: + condition: service_healthy + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + +networks: + junhong-network: + driver: bridge diff --git a/docker/entrypoint-api.sh b/docker/entrypoint-api.sh new file mode 100644 index 0000000..b0f0eb9 --- /dev/null +++ b/docker/entrypoint-api.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -e + +echo "=========================================" +echo "君鸿卡管系统 API 服务启动中..." +echo "=========================================" + +# 检查必要的环境变量 +if [ -z "$DB_HOST" ]; then + echo "错误: DB_HOST 环境变量未设置" + exit 1 +fi + +# 构建数据库连接 URL +DB_URL="postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=${DB_SSLMODE}" + +echo "检查数据库连接..." +# 等待数据库就绪(最多等待 30 秒) +for i in {1..30}; do + if migrate -path /app/migrations -database "$DB_URL" version > /dev/null 2>&1; then + echo "数据库连接成功" + break + fi + echo "等待数据库就绪... ($i/30)" + sleep 1 +done + +# 执行数据库迁移 +echo "执行数据库迁移..." +if migrate -path /app/migrations -database "$DB_URL" up; then + echo "数据库迁移完成" +else + echo "警告: 数据库迁移失败或无新迁移" +fi + +# 启动 API 服务 +echo "启动 API 服务..." +echo "=========================================" +exec /app/api diff --git a/docs/deployment/deployment-guide.md b/docs/deployment/deployment-guide.md new file mode 100644 index 0000000..274768e --- /dev/null +++ b/docs/deployment/deployment-guide.md @@ -0,0 +1,500 @@ +# 君鸿卡管系统 - 部署指南 + +本文档提供从零开始部署君鸿卡管系统到测试环境的完整步骤。 + +--- + +## 架构概览 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 部署架构图 │ +└───────────────────────────┬─────────────────────────────────────┘ + │ + ┌───────────────────▼───────────────────┐ + │ 开发者 Push 代码到 Gitea │ + │ (main/dev/test 分支) │ + └───────────────────┬───────────────────┘ + │ + ┌───────────────────▼───────────────────┐ + │ 部署服务器 (47.111.166.169) │ + │ │ + │ ┌─────────────────────────────────┐ │ + │ │ Act Runner (docker-runner-01) │ │ + │ │ 自动触发工作流 │ │ + │ └───────────┬─────────────────────┘ │ + │ │ │ + │ ┌───────────▼─────────────────────┐ │ + │ │ 构建 Docker 镜像 │ │ + │ │ - API 镜像 │ │ + │ │ - Worker 镜像 │ │ + │ └───────────┬─────────────────────┘ │ + │ │ │ + │ ┌───────────▼─────────────────────┐ │ + │ │ Push 到私有 Docker Registry │ │ + │ │ registry.boss160.cn │ │ + │ └───────────┬─────────────────────┘ │ + │ │ │ + │ ┌───────────▼─────────────────────┐ │ + │ │ 本地部署(无 SSH) │ │ + │ │ - Pull 镜像 │ │ + │ │ - 滚动更新容器 │ │ + │ │ - 清理旧镜像(保留 3 个) │ │ + │ └───────────┬─────────────────────┘ │ + │ │ │ + │ ┌───────────▼─────────────────────┐ │ + │ │ 服务运行中 │ │ + │ │ - API: 0.0.0.0:8088 │ │ + │ │ - Worker: 后台任务处理 │ │ + │ └──────────────────────────────────┘ │ + └───────────────────────────────────────┘ +``` + +**关键优势**:Act Runner 在部署服务器本地运行,无需 SSH,配置简单! + +--- + +## 前置准备 + +### 1. 服务器环境要求 + +- **操作系统**:Ubuntu 20.04+ / CentOS 7+ +- **Docker**:20.10+ +- **Docker Compose**:1.29+ +- **内存**:至少 2GB +- **磁盘**:至少 20GB +- **Act Runner**:已部署并注册到 Gitea(✅ 你已经有了 docker-runner-01) + +### 2. 外部依赖服务 + +系统依赖以下外部服务(需提前部署): + +- **PostgreSQL 14+**:数据库 +- **Redis 6.0+**:缓存和队列 + +--- + +## 第一步:服务器初始化 + +### 1.1 确认 Docker 环境 + +```bash +# SSH 到服务器 +ssh qycard001@47.111.166.169 -p 52022 + +# 验证 Docker 和 Docker Compose +docker --version +docker-compose --version + +# 验证 Act Runner 正在运行 +docker ps | grep runner +# 应该能看到 docker-runner-01 +``` + +### 1.2 创建部署目录 + +```bash +# 创建部署目录 +mkdir -p /home/qycard001/app/junhong_cmp +cd /home/qycard001/app/junhong_cmp + +# 创建必要的子目录 +mkdir -p configs logs +``` + +### 1.3 准备配置文件 + +#### 创建 `.env` 文件(数据库迁移配置) + +```bash +cat > /home/qycard001/app/junhong_cmp/.env << 'EOF' +MIGRATIONS_DIR=migrations +DB_HOST=cxd.whcxd.cn +DB_PORT=16159 +DB_USER=erp_pgsql +DB_PASSWORD=erp_2025 +DB_NAME=junhong_cmp_test +DB_SSLMODE=disable +EOF +``` + +#### 创建 `configs/config.yaml`(应用配置) + +```bash +cat > /home/qycard001/app/junhong_cmp/configs/config.yaml << 'EOF' +server: + port: 8088 + read_timeout: 60 + write_timeout: 60 + +database: + host: cxd.whcxd.cn + port: 16159 + user: erp_pgsql + password: erp_2025 + dbname: junhong_cmp_test + sslmode: disable + max_open_conns: 100 + max_idle_conns: 10 + +redis: + host: 你的Redis地址 + port: 6379 + password: "" + db: 0 + +logging: + level: info + output: logs/app.log + max_size: 100 + max_backups: 7 + max_age: 30 + compress: true + +middleware: + enable_auth: true + enable_rate_limiter: false +EOF +``` + +**重要**:将 `你的Redis地址` 替换为实际的 Redis 地址。 + +### 1.4 复制部署文件 + +从代码仓库复制 `docker-compose.prod.yml` 到服务器: + +```bash +# 在服务器上执行 +cd /home/qycard001/app/junhong_cmp + +# 方式1: 使用 Git(推荐) +git clone <你的仓库地址> temp +cp temp/docker-compose.prod.yml ./docker-compose.prod.yml +rm -rf temp + +# 方式2: 从本地上传 +# 在本地执行: +# scp -P 52022 docker-compose.prod.yml qycard001@47.111.166.169:/home/qycard001/app/junhong_cmp/ +``` + +--- + +## 第二步:配置 Gitea Secrets + +详细步骤请参考 [Gitea Secrets 配置说明](./gitea-secrets-setup.md)。 + +**只需要配置 2 个 Secrets**(非常简单!): + +| Secret 名称 | 值 | +|------------|-----| +| `REGISTRY_USERNAME` | `junhong_admin` | +| `REGISTRY_PASSWORD` | `JunHong@2025!Registry` | + +**配置步骤**: +1. 进入 Gitea 仓库 → 设置 → Secrets +2. 添加 `REGISTRY_USERNAME`,值为 `junhong_admin` +3. 添加 `REGISTRY_PASSWORD`,值为 `JunHong@2025!Registry` +4. 完成! + +--- + +## 第三步:首次手动部署 + +在自动化 CI/CD 生效前,先进行一次手动部署验证环境。 + +### 3.1 登录 Docker Registry + +```bash +# 在服务器上执行 +ssh qycard001@47.111.166.169 -p 52022 + +docker login registry.boss160.cn -u junhong_admin +# 输入密码:JunHong@2025!Registry +``` + +### 3.2 手动构建镜像(可选) + +如果 CI/CD 还未运行,可以手动构建: + +```bash +cd <代码仓库目录> + +# 构建 API 镜像 +docker build -f Dockerfile.api -t registry.boss160.cn/junhong/cmp-fiber-api:latest . + +# 构建 Worker 镜像 +docker build -f Dockerfile.worker -t registry.boss160.cn/junhong/cmp-fiber-worker:latest . + +# 推送镜像 +docker push registry.boss160.cn/junhong/cmp-fiber-api:latest +docker push registry.boss160.cn/junhong/cmp-fiber-worker:latest +``` + +### 3.3 启动服务 + +```bash +cd /home/qycard001/app/junhong_cmp + +# 拉取镜像 +docker-compose -f docker-compose.prod.yml pull + +# 启动服务 +docker-compose -f docker-compose.prod.yml up -d + +# 查看服务状态 +docker-compose -f docker-compose.prod.yml ps + +# 查看日志 +docker-compose -f docker-compose.prod.yml logs -f +``` + +### 3.4 验证部署 + +```bash +# 测试 API 健康检查 +curl http://localhost:8088/health + +# 预期输出: +# {"code":0,"msg":"ok","data":{"status":"healthy"},"timestamp":1234567890} + +# 查看容器状态 +docker ps | grep junhong +``` + +--- + +## 第四步:自动化部署 + +配置完成后,每次 Push 代码到 `main` / `dev` / `test` 分支时,会自动触发构建。 + +### 工作流触发规则 + +| 分支 | 镜像标签 | 是否自动部署 | +|------|---------|------------| +| `main` | `latest` | ✅ 是 | +| `dev` | `dev` | ❌ 否(仅构建镜像)| +| `test` | `test` | ❌ 否(仅构建镜像)| + +### 触发自动部署 + +```bash +# 在本地提交代码 +git add . +git commit -m "测试自动部署" +git push origin main +``` + +### 监控部署进度 + +1. 在 Gitea 仓库页面点击 **Actions** 标签 +2. 查看最新的工作流运行状态 +3. 点击工作流查看详细日志 + +**预期日志输出**: +``` +✅ 检出代码 +✅ 设置镜像标签: latest +✅ 登录 Docker Registry +✅ 构建 API 镜像 +✅ 构建 Worker 镜像 +✅ 推送镜像到 Registry +✅ 部署到本地 + - 拉取最新镜像... + - 执行滚动更新... + - 等待服务健康检查... + - 清理旧镜像(保留最近 3 个版本)... + - 部署完成! +✅ 构建结果通知 +``` + +--- + +## 运维操作 + +### 查看服务日志 + +```bash +cd /home/qycard001/app/junhong_cmp + +# 查看 API 日志 +docker-compose -f docker-compose.prod.yml logs -f api + +# 查看 Worker 日志 +docker-compose -f docker-compose.prod.yml logs -f worker + +# 查看应用日志文件 +tail -f logs/app.log | jq . +tail -f logs/access.log | jq . +``` + +### 重启服务 + +```bash +cd /home/qycard001/app/junhong_cmp + +# 重启所有服务 +docker-compose -f docker-compose.prod.yml restart + +# 重启单个服务 +docker-compose -f docker-compose.prod.yml restart api +docker-compose -f docker-compose.prod.yml restart worker +``` + +### 手动执行数据库迁移 + +```bash +# 进入 API 容器 +docker exec -it junhong-cmp-api bash + +# 查看当前迁移版本 +migrate -path /app/migrations -database "$(cat /app/.env | grep DB_ | xargs)" version + +# 执行迁移(通常容器启动时会自动执行) +migrate -path /app/migrations -database "$(cat /app/.env | grep DB_ | xargs)" up +``` + +### 回滚到旧版本 + +```bash +cd /home/qycard001/app/junhong_cmp + +# 查看可用镜像 +docker images | grep cmp-fiber + +# 示例输出: +# registry.boss160.cn/junhong/cmp-fiber-api latest abc123 2 hours ago 50MB +# registry.boss160.cn/junhong/cmp-fiber-api def456 def456 1 day ago 50MB + +# 修改 docker-compose.prod.yml 中的镜像标签 +# 将 :latest 改为具体的 commit SHA +sed -i 's/:latest/:def456/g' docker-compose.prod.yml + +# 重新部署 +docker-compose -f docker-compose.prod.yml pull +docker-compose -f docker-compose.prod.yml up -d +``` + +### 清理磁盘空间 + +```bash +# 清理未使用的镜像 +docker image prune -a -f + +# 清理未使用的容器 +docker container prune -f + +# 清理未使用的卷 +docker volume prune -f + +# 一键清理所有(谨慎使用) +docker system prune -a -f --volumes +``` + +--- + +## 常见问题 + +### Q1: 容器启动失败,显示 "unhealthy" + +**排查步骤**: +1. 查看容器日志:`docker-compose -f docker-compose.prod.yml logs api` +2. 检查配置文件是否正确(数据库连接、Redis 连接) +3. 确认外部依赖(PostgreSQL、Redis)是否可访问 +4. 手动测试健康检查:`curl http://localhost:8088/health` + +### Q2: 数据库迁移失败 + +**可能原因**: +- 数据库连接信息错误 +- 迁移文件损坏 +- 数据库权限不足 + +**解决方法**: +```bash +# 检查 .env 文件配置 +cat /home/qycard001/app/junhong_cmp/.env + +# 手动测试数据库连接 +docker run --rm postgres:14 psql "postgresql://erp_pgsql:erp_2025@cxd.whcxd.cn:16159/junhong_cmp_test?sslmode=disable" -c "SELECT 1;" + +# 查看迁移日志 +docker logs junhong-cmp-api | grep migrate +``` + +### Q3: CI/CD 工作流失败 + +**常见原因**: +1. **Secrets 未配置或错误**:检查 Gitea Secrets 是否配置了 `REGISTRY_USERNAME` 和 `REGISTRY_PASSWORD` +2. **Registry 认证失败**:验证用户名密码是否正确 +3. **磁盘空间不足**:执行 `df -h` 检查磁盘空间 + +**排查方法**: +- 在 Gitea Actions 页面查看详细日志 +- 在服务器上手动测试 Registry 登录:`docker login registry.boss160.cn -u junhong_admin` + +### Q4: 镜像拉取慢或超时 + +**解决方法**: +```bash +# 检查 Registry 连接 +curl -I https://registry.boss160.cn + +# 检查网络 +ping registry.boss160.cn + +# 查看 Docker 日志 +journalctl -u docker -f +``` + +### Q5: Act Runner 权限问题 + +**问题现象**:工作流报错 `permission denied` 或 `cannot connect to Docker daemon` + +**解决方法**: +```bash +# 确认 Act Runner 用户在 docker 组中 +docker exec -it docker-runner-01 groups + +# 如果缺少 docker 组,重启 Act Runner 容器 +docker restart docker-runner-01 +``` + +--- + +## 安全建议 + +1. **定期更新**: + - 定期更新 Docker 和 Docker Compose + - 及时应用系统安全补丁 + +2. **防火墙配置**: + ```bash + # 仅开放必要端口 + sudo ufw allow 52022/tcp # SSH + sudo ufw allow 8088/tcp # API(如果需要外部访问) + sudo ufw enable + ``` + +3. **日志监控**: + - 配置日志轮转,防止磁盘占满 + - 定期审查访问日志和错误日志 + +4. **备份策略**: + - 定期备份数据库 + - 定期备份配置文件(`.env`、`config.yaml`) + +--- + +## 下一步 + +部署完成后: +1. ✅ 配置域名和反向代理(Nginx / Caddy) +2. ✅ 启用 HTTPS(Let's Encrypt) +3. ✅ 配置监控和告警(Prometheus + Grafana) +4. ✅ 设置自动备份脚本 + +--- + +## 支持 + +如有问题,请联系运维团队或提交 Issue。 diff --git a/docs/deployment/gitea-secrets-setup.md b/docs/deployment/gitea-secrets-setup.md new file mode 100644 index 0000000..6e65f71 --- /dev/null +++ b/docs/deployment/gitea-secrets-setup.md @@ -0,0 +1,114 @@ +# Gitea Secrets 配置说明 + +本文档说明如何在 Gitea 中配置 CI/CD 工作流所需的 Secrets(密钥)。 + +## 重要说明 + +因为 Act Runner 运行在部署服务器本地,所以**不需要 SSH 连接**,配置非常简单! + +--- + +## 配置步骤 + +### 1. 进入仓库设置 + +1. 打开你的 Gitea 仓库页面 +2. 点击右上角的 **设置(Settings)** 按钮 +3. 在左侧菜单中找到 **Secrets** 或 **密钥** 选项 + +### 2. 添加以下 Secrets + +点击 **添加 Secret** 按钮,逐个添加以下密钥: + +#### Docker Registry 认证信息 + +| Secret 名称 | 值 | 说明 | +|------------|-----|------| +| `REGISTRY_USERNAME` | `junhong_admin` | Docker Registry 用户名 | +| `REGISTRY_PASSWORD` | `JunHong@2025!Registry` | Docker Registry 密码 | + +**就这么简单!只需要 2 个 Secrets!** ✅ + +--- + +## 配置完成检查清单 + +- [ ] `REGISTRY_USERNAME` 已配置 +- [ ] `REGISTRY_PASSWORD` 已配置 + +--- + +## 验证配置 + +### 测试 Registry 登录 + +在部署服务器上手动测试 Registry 连接: + +```bash +# 登录测试 +docker login registry.boss160.cn -u junhong_admin +# 输入密码:JunHong@2025!Registry + +# 预期输出:Login Succeeded +``` + +如果登录成功,说明配置正确。 + +--- + +## 常见问题 + +### Q1: 工作流显示 "secret not found" + +**原因**:Secret 名称拼写错误或未添加 + +**解决方法**: +1. 检查 Secret 名称是否完全一致(区分大小写) +2. 确认 2 个 Secrets 都已添加 + +### Q2: Registry 登录失败 + +**原因**:用户名或密码错误 + +**解决方法**: +```bash +# 在服务器上手动测试登录 +docker login registry.boss160.cn -u junhong_admin +# 输入密码:JunHong@2025!Registry + +# 如果失败,检查 Registry 认证配置 +cat /home/qycard001/registry/auth/htpasswd +``` + +### Q3: 为什么不需要 SSH 密钥? + +**回答**:因为你的 Act Runner (`docker-runner-01`) 运行在部署服务器本地,可以直接执行 `docker-compose` 命令,不需要通过 SSH 连接到其他机器。 + +**架构示意**: +``` +┌──────────────────────────────────────────┐ +│ 部署服务器 (47.111.166.169) │ +│ │ +│ ┌────────────────┐ ┌───────────────┐ │ +│ │ Act Runner │──→│ Docker Daemon │ │ +│ │(docker-runner) │ │ │ │ +│ └────────────────┘ └───────────────┘ │ +│ ↓ ↓ │ +│ 执行工作流 部署容器 │ +└──────────────────────────────────────────┘ +``` + +--- + +## 安全注意事项 + +1. **绝不**在代码中硬编码密钥信息 +2. **定期更换** Registry 密码 +3. **监控** Gitea Actions 日志,防止密钥泄露 +4. 如果 Secrets 泄露,**立即**更换密码 + +--- + +## 下一步 + +配置完成后,参考 [部署指南](./deployment-guide.md) 进行首次部署。