Files
mqtt_power/DEPLOYMENT_GUIDE.md

22 KiB
Raw Blame History

CI/CD 部署指南 (OneDev + Docker)

1. 简介

本文档旨在指导如何为 mqtt-charging-system 项目(包含 springboot-init-main 后端和 charging_web_app 前端)配置一套基于 OneDev 和 Docker 的持续集成与持续部署 (CI/CD) 流程。实现目标是当代码提交到 OneDev 管理的 Git 仓库的特定分支(如 main)后,自动触发构建、 Docker 镜像打包与推送,并最终自动部署到线上服务器。

采用技术栈:

  • 一体化 DevOps 平台 (代码仓库, CI/CD): OneDev
  • 容器化: Docker & Docker Compose
  • 部署目标: 运行 Docker 的 Linux 服务器

2. 先决条件

在开始配置 CI/CD 流程之前,请确保以下条件已满足:

  • OneDev 服务器:
    • 已成功搭建并运行 OneDev 服务。您可以从 https://onedev.io/ 下载并按照官方文档进行安装(通常作为 Docker 容器或直接运行 JAR 包)。
    • 项目代码已在 OneDev 中创建或导入。
  • 线上应用服务器:
    • 一台或多台 Linux 服务器,用于运行前端和后端应用容器。
    • 服务器已安装 Docker Engine 和 Docker Compose。
    • 服务器具有 SSH 访问权限,并为 CI/CD 工具准备了 SSH 用户推荐使用密钥认证。OneDev 的执行器 (Executor) 将通过此用户连接服务器。
    • 服务器防火墙已配置,允许访问应用所需端口(例如后端 7529前端 3000以及可能的反向代理端口如 80, 443
  • Docker 镜像仓库:
    • 一个 Docker Registry 用于存储构建好的应用镜像。可以是:
      • Docker Hub (公共或私有)
      • OneDev 自带的容器镜像仓库 (如果 OneDev 版本支持并已启用,或者通过插件)
      • Harbor, Quay.io 等私有化部署的 Registry
      • 云服务商提供的 Registry (如阿里云 ACR, AWS ECR, Google GCR)
    • 准备好访问该 Registry 的凭证 (用户名和密码/Access Token)。这些凭证将作为 Secrets 配置在 OneDev 中。
  • 域名 (可选但推荐): 为您的 OneDev 服务器和应用准备域名,并配置好 DNS 解析。
  • 对 Docker 和 Linux 基本命令有了解

3. CI/CD 流程概览

  1. 代码提交: 开发者将代码改动推送到 OneDev 仓库的指定分支 (例如 main)。
  2. 构建触发: OneDev 自动检测到代码变更,并根据项目根目录下的 .onedev-buildspec.yml 文件中定义的触发器 (Triggers) 和作业 (Jobs) 启动构建流程。
  3. 作业执行: OneDev 的执行器 (Executor) 根据构建规范执行定义的作业步骤:
    • 代码检出: 执行器自动检出指定提交的代码。
    • 应用构建:
      • 后端 (springboot-init-main): 使用 Maven 构建工具 (如 mvn package) 打包成 JAR 文件。
      • 前端 (charging_web_app): 使用 Node.js 和 npm/yarn (如 npm run build) 构建生产版本的静态文件和 Next.js 服务。
    • Docker 镜像构建与推送:
      • 使用项目中的 Dockerfile 构建 Docker 镜像。
      • 构建成功后,将镜像标记版本号并推送到指定的 Docker 镜像仓库。
    • 应用部署:
      • 通过 SSH 连接到线上应用服务器。
      • 在服务器上执行预设脚本(通常是 Docker Compose 命令)来拉取新镜像并重启服务。
  4. 完成与反馈: 构建完成后OneDev 界面会显示构建状态、日志和产物。可以配置通知。

4. 详细步骤

步骤 4.1: 服务器环境准备

(此部分与之前 Gitea+Drone CI 指南中的服务器准备基本一致,请确保应用服务器满足 Docker, Docker Compose 和 SSH 的要求。OneDev 服务器自身的安装请参考官方文档。)

  1. 选择并配置 Linux 服务器 (应用服务器):
    • 建议使用主流 Linux 发行版,如 Ubuntu, CentOS。
    • 确保服务器资源充足CPU, 内存, 磁盘空间)。
    • 内存建议 (测试环境OneDev 和应用分开部署或合并部署,不考虑高并发):
      • OneDev 服务器: OneDev 自身运行(特别是包含 Elasticsearch 用于搜索)可能需要 2GB-4GB RAM 或更多,具体取决于仓库数量、用户数和活动情况。请参考 OneDev 官方的资源建议。
      • 应用服务器 (仅运行后端和前端应用容器): 2GB RAM 通常足够。
      • 单服务器部署 (OneDev Server, 执行器, 后端应用, 前端应用均在同一台机器): 由于 OneDev 自身和构建过程的资源消耗,推荐至少 4GB-8GB RAM。具体数值高度依赖 OneDev 的负载和构建任务的复杂度。对于纯测试且OneDev负载不高的情况4GB可能是底限。
      • 如果服务器内存确实有限需要严格控制各进程的内存使用并可能遇到性能瓶颈或OOM问题。
  2. 安装 Docker Engine (应用服务器): (同前)
  3. 安装 Docker Compose (应用服务器): (同前)
  4. 配置 SSH (应用服务器):
    • 确保 SSH 服务正在运行。
    • 为 OneDev 执行器创建一个专用的 SSH 用户,或使用现有用户。
    • 强烈推荐使用 SSH 密钥对进行认证。私钥将作为 Secret 配置在 OneDev 中,用于部署作业。
      • 在可以安全操作的地方生成 SSH 密钥对。
      • 将公钥 (~/.ssh/id_rsa.pub) 内容添加到应用服务器上对应用户的 ~/.ssh/authorized_keys 文件中。
      • 确保 .ssh 目录权限为 700authorized_keys 文件权限为 600
  5. 防火墙配置 (应用服务器): (同前)

步骤 4.2: OneDev 服务器安装与配置 (简述)

  • 安装 OneDev: 请遵循 OneDev 官方网站 (https://onedev.io/) 的最新安装指南。常见的安装方式包括:
    • Docker 容器: 这是推荐且较简单的方式。官方会提供 docker run 命令或 docker-compose.yml 示例。
    • 直接运行 JAR: 下载 server-bootstrap.jar 并运行。
  • 初始配置: 安装完成后,首次访问 OneDev 服务会引导进行管理员账户创建和基本配置。
  • 项目创建/导入: 在 OneDev 中创建您的 mqtt-charging-system 项目,或从其他 Git 源导入。
  • 执行器 (Executor) 配置: OneDev 需要配置执行器来运行构建作业。默认情况下可能有一个内置的 Docker Executor但您也可以配置远程服务器作为 Shell Executor (通过 SSH) 或 Kubernetes Executor。对于 Docker 构建和 SSH 部署,确保执行器有能力运行 Docker 命令和 SSH 命令。
    • 如果 OneDev 服务器自身资源充足且安装了 Docker可以使用其内置的 Docker Executor。
    • 如果希望在专门的构建机或应用服务器上执行某些部署步骤,可能需要配置 Server Shell Executor 或 Agent Shell Executor并确保这些执行环境安装了必要的工具 (如 docker-compose, ssh client)。

步骤 4.3: 项目 Docker化

(此部分与之前指南中的 Dockerfile 内容保持不变,因为 Dockerfile 本身与 CI/CD 工具无关。)

  • springboot-init-main/Dockerfile: (同前)
  • charging_web_app/Dockerfile: (同前)

步骤 4.4: OneDev 构建规范配置 (.onedev-buildspec.yml)

在您的 OneDev 项目 (mqtt-charging-system) 的根目录下创建 .onedev-buildspec.yml 文件 (或者通过 OneDev UI 配置构建规范)。

# .onedev-buildspec.yml

# 定义构建作业 (Jobs)
jobs:
  # --- 后端 Spring Boot 应用作业 ---
  - name: "Backend CI/CD"
    # 执行器选择:指定一个有能力运行 Maven 和 Docker 命令的执行器
    # 这通常是一个配置了 Docker-in-Docker 或可以访问 Docker socket 的 Docker Executor
    # 或者是一个安装了 Maven 和 Docker 的 Shell Executor
    executor: "docker-executor" # 替换为你在 OneDev 中配置的执行器名称
    # 触发器:当 main 分支有代码提交,且 springboot-init-main 目录下文件发生变化时触发
    triggers:
      - type: "branch-push"
        branches: "main"
        paths: "springboot-init-main/**"
    # 构建步骤 (Steps)
    steps:
      # 1. 检出代码 (通常是隐式的,或由 OneDev 自动处理第一步)
      - checkout # OneDev 内置的检出步骤

      # 2. 构建 Spring Boot JAR
      - group: "Build Backend JAR"
        steps:
          - image: "maven:3.8-openjdk-8" # 使用 Maven Docker 镜像执行命令
            shell: "sh"
            commands: |
              echo "Building Spring Boot JAR..."
              cd springboot-init-main
              mvn clean package -DskipTests -B

      # 3. 构建并推送后端 Docker 镜像
      - group: "Publish Backend Docker Image"
        steps:
          # 使用 OneDev 内置的 Docker Build & Push 命令 (或手动 docker 命令)
          # 假设 executor 有 docker cli
          - shell: "sh"
            # 需要将 Docker Hub 凭证配置为 Job Secrets: DOCKER_USER, DOCKER_PASSWORD
            # 以及 Docker 仓库地址: DOCKER_REGISTRY (可选, 默认 docker.io)
            commands: |
              echo "Logging in to Docker Registry..."
              echo $env.get(\'DOCKER_PASSWORD\') | docker login -u $env.get(\'DOCKER_USER\') --password-stdin $env.get(\'DOCKER_REGISTRY\', \'docker.io\')
              
              echo "Building and Pushing Backend Docker Image..."
              cd springboot-init-main
              IMAGE_NAME="${env.get(\'DOCKER_REGISTRY\', \'docker.io\')}/your-docker-repo/mqtt-springboot-app"
              # 使用 @file:job.commitHash 获取当前提交的短哈希
              docker build -t ${IMAGE_NAME}:latest -t ${IMAGE_NAME}:$(echo \"@file:job.commitHash@\" | cut -c1-8) .
              docker push ${IMAGE_NAME}:latest
              docker push ${IMAGE_NAME}:$(echo \"@file:job.commitHash@\" | cut -c1-8)
              
              echo "Logging out from Docker Registry..."
              docker logout $env.get(\'DOCKER_REGISTRY\', \'docker.io\')
            # OneDev 提供了更集成的 Docker 构建步骤,可以研究使用以简化脚本
            # - build-docker-image: ... (参考 OneDev 文档)
            # - push-docker-image: ... (参考 OneDev 文档)

      # 4. 部署后端到服务器
      - group: "Deploy Backend to Server"
        steps:
          - shell: "sh"
            # 需要将 SSH 私钥配置为 Job Secret: SERVER_SSH_KEY
            # 以及服务器地址: SERVER_HOST, SSH用户: SERVER_SSH_USER
            # 服务器部署路径: BACKEND_DEPLOY_PATH (例如 /opt/deploy/mqtt-charging-system/backend)
            commands: |
              echo "Deploying Backend to Server..."
              mkdir -p ~/.ssh
              echo "$env.get(\'SERVER_SSH_KEY\')" > ~/.ssh/id_rsa
              chmod 600 ~/.ssh/id_rsa
              ssh -o StrictHostKeyChecking=no $env.get(\'SERVER_SSH_USER\')@$env.get(\'SERVER_HOST\') \
                "cd $env.get(\'BACKEND_DEPLOY_PATH\') && docker-compose pull backend-app && docker-compose up -d --remove-orphans backend-app"
              echo "Backend deployment completed."

  # --- 前端 Next.js 应用作业 ---
  - name: "Frontend CI/CD"
    executor: "docker-executor" # 替换为合适的执行器
    triggers:
      - type: "branch-push"
        branches: "main"
        paths: "charging_web_app/**"
    steps:
      - checkout

      # 2. 构建 Next.js 应用
      - group: "Build Frontend App"
        steps:
          - image: "node:18-alpine"
            shell: "sh"
            commands: |
              echo "Building Next.js App..."
              cd charging_web_app
              npm ci
              npm run build

      # 3. 构建并推送前端 Docker 镜像
      - group: "Publish Frontend Docker Image"
        steps:
          - shell: "sh"
            commands: |
              echo "Logging in to Docker Registry..."
              echo $env.get(\'DOCKER_PASSWORD\') | docker login -u $env.get(\'DOCKER_USER\') --password-stdin $env.get(\'DOCKER_REGISTRY\', \'docker.io\')
              
              echo "Building and Pushing Frontend Docker Image..."
              cd charging_web_app
              IMAGE_NAME="${env.get(\'DOCKER_REGISTRY\', \'docker.io\')}/your-docker-repo/mqtt-nextjs-app"
              docker build -t ${IMAGE_NAME}:latest -t ${IMAGE_NAME}:$(echo \"@file:job.commitHash@\" | cut -c1-8) .
              docker push ${IMAGE_NAME}:latest
              docker push ${IMAGE_NAME}:$(echo \"@file:job.commitHash@\" | cut -c1-8)
              
              echo "Logging out from Docker Registry..."
              docker logout $env.get(\'DOCKER_REGISTRY\', \'docker.io\')

      # 4. 部署前端到服务器
      - group: "Deploy Frontend to Server"
        steps:
          - shell: "sh"
            commands: |
              echo "Deploying Frontend to Server..."
              mkdir -p ~/.ssh
              echo "$env.get(\'SERVER_SSH_KEY\')" > ~/.ssh/id_rsa
              chmod 600 ~/.ssh/id_rsa
              ssh -o StrictHostKeyChecking=no $env.get(\'SERVER_SSH_USER\')@$env.get(\'SERVER_HOST\') \
                "cd $env.get(\'FRONTEND_DEPLOY_PATH\') && docker-compose pull frontend-app && docker-compose up -d --remove-orphans frontend-app"
              echo "Frontend deployment completed."

# 可以定义参数 (Parameters) 和属性 (Properties) 以供作业使用
# parameters:
# properties:
  • 重要说明:
    • 上述 .onedev-buildspec.yml 是一个示例,具体语法和可用步骤请务必参考您正在使用的 OneDev 版本的官方文档。
    • executor: 指定运行此作业的 OneDev 执行器。您需要在 OneDev 中预先配置好执行器 (例如 Docker Executor, Shell Executor)。
    • triggers: 定义了何时触发此作业。
    • steps: 定义了作业的具体操作。OneDev 有很多内置的步骤类型 (如 checkout, setup-jdk, run-docker-command, build-docker-image, ssh-command-step 等),使用内置步骤通常比手写 shell 脚本更简洁和安全。强烈建议查阅 OneDev 文档,使用其推荐的步骤来操作 Docker 和 SSH。
    • Secrets/Variables:
      • $env.get('SECRET_NAME')$get('job_secret_name') (具体语法看OneDev版本) 用于从 OneDev 作业 Secrets 中获取敏感信息 (如 DOCKER_USER, DOCKER_PASSWORD, SERVER_SSH_KEY)。
      • your-docker-repo 需要替换为您的 Docker 仓库名/组织名。
      • SERVER_HOST, SERVER_SSH_USER, BACKEND_DEPLOY_PATH, FRONTEND_DEPLOY_PATH 等也应该是作为 Job Secrets 或 Parameters 传入。
      • @file:job.commitHash@ 是 OneDev 提供的一个变量,用于获取当前构建的 Git 提交哈希。
    • 错误处理与健壮性: 实际脚本中应添加错误检查和更完善的日志。
    • OneDev 内置步骤: 对于 Docker 构建/推送和 SSH 命令执行OneDev 提供了专门的步骤类型,使用它们会比直接写 shell 命令更方便,并能更好地处理凭证和输出。例如,有专门的 Push to Docker Registry 步骤,可以直接配置仓库、凭证等。

步骤 4.5: 服务器部署配置 (Docker Compose)

在您的应用服务器上,创建一个部署根目录(例如 /opt/deploy/mqtt-charging-system/),并在该目录下放置一个统一的 docker-compose.yml 文件。这个文件将管理所有服务包括数据库、MQTT Broker 以及您的后端和前端应用。

  • 统一的 docker-compose.yml (示例路径: /opt/deploy/mqtt-charging-system/docker-compose.yml):
    • 该文件的内容已在本指南前文(或作为单独文件)提供。它应包含 mysql-db, emqx, backend-app, 和 frontend-app 服务。
    • 确保 backend-appfrontend-app 服务中的 image 指向您在 CI/CD 流程中构建和推送到 Docker Registry 的镜像地址。
    • 通过 environment 部分为 backend-app 注入数据库连接信息(如 DB_HOST: mysql-db, DB_USER, DB_PASSWORD, DB_NAME)和 MQTT Broker 信息 (MQTT_BROKER_URL: tcp://emqx:1883),使其连接到 Compose 网络内的相应服务。
    • MySQL 和 EMQX 的敏感配置(如密码)应通过环境变量从外部(例如服务器上的 .env 文件或 CI 工具注入的环境变量)设置,而不是硬编码在 docker-compose.yml 的默认值中。

步骤 4.6: OneDev Secrets 管理

OneDev 提供了强大的 Secrets 管理功能,用于安全地存储 CI/CD 流程中所需的敏感信息。

  1. 打开 OneDev 上的 mqtt-charging-system 项目。

  2. 导航到项目 设置 (Settings) -> 构建设置 (Build Settings) -> 作业密秘 (Job Secrets)。

  3. 点击 添加密秘 (Add Secret) 来创建以下 Secrets (名称需与 .onedev-buildspec.yml 中引用的名称一致):

    • DOCKER_USER: 你的 Docker 镜像仓库的用户名。
    • DOCKER_PASSWORD: 你的 Docker 镜像仓库的密码或 Access Token (标记为密码类型)。
    • SERVER_SSH_KEY: 部署服务器的 SSH 私钥内容 (标记为文件类型或多行文本)。
    • SERVER_HOST: 部署服务器的 IP 地址或主机名。
    • SERVER_SSH_USER: 用于 SSH 连接的用户名。
    • DEPLOY_PATH: 应用在服务器上的统一部署路径 (例如 /opt/deploy/mqtt-charging-system).
    • DOCKER_REGISTRY (可选): 如果不是 Docker Hub则为您的 Docker 仓库地址 (例如 gitea.yourdomain.com:5000)。
    • 新增Secrets (用于 docker-compose.yml 中的数据库和Broker):
      • MYSQL_ROOT_PASSWORD_SECRET: MySQL root用户的密码。
      • MYSQL_DATABASE_SECRET: 数据库名称 (例如 mqtt_power)。
      • MYSQL_USER_SECRET: 应用连接数据库的用户名 (例如 appuser)。
      • MYSQL_PASSWORD_SECRET: 应用连接数据库的密码。
      • (如果 EMQX 需要认证,则添加相应 Secrets)
  4. 授权: 确保这些 Secrets 仅被授权给需要它们的构建作业或分支访问。

    注意: 在 .onedev-buildspec.yml 的部署脚本中,您需要一种方式将这些 Secrets (如 MYSQL_ROOT_PASSWORD_SECRET) 传递给部署服务器,以便 docker-compose up 命令可以访问它们作为环境变量。这可以通过以下几种方式实现:

    • 通过 SSH 动态创建 .env 文件: 在 SSH 部署脚本中,从 OneDev Secrets 读取值,并在部署目录动态生成一个 .env 文件,docker-compose 会自动加载它。
    • 直接在 docker-compose up 命令前导出环境变量: export MYSQL_ROOT_PASSWORD=$env.get('MYSQL_ROOT_PASSWORD_SECRET'); docker-compose up -d (这种方式环境变量仅对当前 SSH 会话的该命令有效,需要小心处理引号和转义)。
    • OneDev 可能有更高级的方法来处理部署时的环境变量注入,请查阅其文档。

步骤 4.7: (移除/简化 Gitea Webhook 配置)

由于 OneDev 是一体化平台,它会自动监控其管理的 Git 仓库的变更。当符合 .onedev-buildspec.yml 中定义的 triggers 条件时,构建会自动触发。因此,不需要像 Gitea+Drone CI 那样手动配置外部 Webhook。

5. 部署触发与监控

  • 触发: 当所有配置完成后,向 OneDev 项目的 main 分支(或其他在构建规范中配置的分支)推送代码提交,并且提交包含了相应目录 (springboot-init-main/**charging_web_app/**) 的文件变更,将自动触发 OneDev 中定义的相应 CI/CD 作业。
  • 监控:
    • 在 OneDev 界面,您可以进入项目的 构建 (Builds) 标签页,实时查看构建列表、每个构建的详细步骤、日志、状态以及最终结果。
    • OneDev 允许配置构建完成后的通知 (例如 Email, Webhook 到其他系统等)。
    • 在应用服务器上,使用 docker psdocker logs 进行容器监控。

6. 回滚策略 (简述)

(与之前类似OneDev 也支持通过标签重新运行旧的构建,或手动部署旧的 Docker 镜像版本。)

  • 手动回滚:
    • 使用 OneDev 构建产出的 Docker 镜像标签 (例如基于 commit hash 的标签) 在服务器上手动通过 docker-compose.yml 指向旧版本并重启服务。
    • 在 OneDev 的构建历史中,找到上一个成功的部署构建,检查其使用的 commit并可能基于该 commit 创建一个新的部署或重新运行(如果构建规范支持参数化部署特定版本)。
  • 自动化回滚 (高级): 可以在 .onedev-buildspec.yml 中设计更复杂的逻辑,例如部署后运行健康检查,如果失败则自动触发另一个作业来部署上一个稳定版本。

7. 注意事项与常见问题

  • Secrets 安全: 严格管理 OneDev 中的 Job Secrets。特别注意部署到服务器时如何安全地传递和使用 docker-compose.yml 所需的环境变量(如数据库密码)。
  • .onedev-buildspec.yml 语法: YAML 语法对缩进敏感。强烈建议使用 OneDev UI 内置的编辑器来编写或校验构建规范,它通常会提供语法高亮和提示。
  • OneDev 执行器 (Executor): 正确配置和选择执行器至关重要。确保执行器有执行作业步骤所需的环境和权限 (如 Docker, Maven, Node.js, SSH客户端)。
  • OneDev 文档: OneDev 功能迭代较快,本文档中的构建规范示例可能不是最新的或最优的。请务必参考您正在使用的 OneDev 版本的官方文档,特别是关于 CI/CD 步骤、变量和 Secrets 的部分。
  • 路径与上下文: 注意 OneDev 作业中执行命令时的工作目录。
  • 统一的 docker-compose.yml: 确保所有服务定义正确,网络配置允许服务间通信,卷的挂载用于持久化数据。
  • 环境变量注入: 仔细规划如何在部署时将 OneDev Job Secrets 安全地传递给 docker-compose.yml 使用的环境变量。避免在版本控制中存储包含敏感默认值的 .env 文件。
  • (其他注意事项与之前类似,如 Dockerfile 优化, 权限, 网络, 资源消耗, 首次手动部署验证, 日志调试, 版本控制, 依赖缓存, Monorepo 策略, 数据库迁移等这些普适性的CI/CD原则依然适用。)

本文档提供了使用 OneDev 配置 CI/CD 的基础框架。根据实际需求和 OneDev 的具体版本功能,您可能需要调整和扩展这些步骤。