当前位置: 首页 > news >正文

CI/CD (持续集成/持续部署) GitHub Actions 自动构建

好的,这是一个相当全面的 CI/CD (持续集成/持续部署) 流程,涉及到 GitHub Actions 自动构建 Docker 镜像并将其部署到你的阿里云 ECS (Elastic Compute Service) 服务器上。下面是详细的步骤:

这个方案假设:

  • 你已经在阿里云上有一台 ECS 服务器,并且可以 SSH 访问。
  • 你的 Spring Boot 后端和 React 前端分别是两个独立的 GitHub 公共仓库。
  • 你希望每次向这两个仓库的特定分支(例如 main)推送代码时,都能自动更新部署在 ECS 上的应用。

我们将使用 Alibaba Cloud Container Registry (ACR) 来存储我们构建的 Docker 镜像,这是一个推荐的做法,比直接在 ECS 上构建镜像更规范和高效。


阶段一:阿里云 ECS 服务器准备

  1. 安装 Docker 和 Docker Compose:
    • 通过 SSH 连接到你的阿里云 ECS。
    • 安装 Docker:

sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io sudo systemctl start docker sudo systemctl enable docker * 安装 Docker Compose:bash sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose docker-compose --version # 验证安装 * (可选但推荐) 将当前用户添加到 `docker` 组,以避免每次都使用 `sudo`:bash sudo usermod -aG docker $USER newgrp docker # 或者重新登录使组生效 ```

  1. 配置安全组:

    • 登录阿里云控制台,找到你的 ECS 实例。
    • 配置其安全组规则:
      • 添加入方向规则以允许 HTTP (端口 80) 和 HTTPS (端口 443) 流量。
      • 确保 SSH (端口 22) 对你的 IP 地址或 GitHub Actions 的 IP 范围开放 (如果需要更严格的限制,但通常使用 SSH Key 更安全)。
      • MySQL 端口 (3306) 不建议对公网开放。它将在 Docker 内部网络中被后端访问。
  2. 创建部署目录和准备 db.sql:

    • 在 ECS 上创建一个用于存放 docker-compose.yml 和数据库初始化脚本的目录: Bash

      mkdir -p ~/my-fullstack-app/db_init
      cd ~/my-fullstack-app
      
    • 重要: 将你后端项目中的 db.sql 文件上传到 ECS 的 ~/my-fullstack-app/db_init/ 目录下。你可以使用 scp 命令: Bash

      # 从你的本地机器执行
      scp path/to/your/local/db.sql your_ecs_user@your_ecs_ip:~/my-fullstack-app/db_init/db.sql
      
  3. 准备 docker-compose.yml 文件在 ECS 上: 在 ECS 的 ~/my-fullstack-app/ 目录下创建一个 docker-compose.yml 文件。这个文件将引用我们稍后推送到 ACR 的镜像。

    YAML

    # ~/my-fullstack-app/docker-compose.yml on your ECS server
    version: '3.8'services:backend:# 镜像名称将从 ACR 拉取,例如: registry.cn-hangzhou.aliyuncs.com/your-acr-namespace/springboot-backend:latestimage: ACR_REGISTRY_URL/YOUR_ACR_NAMESPACE/springboot-backend:latest # <--- 稍后替换container_name: my-springboot-apprestart: unless-stoppedports:- "8080:8080"environment:- SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/mydatabase?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC- SPRING_DATASOURCE_USERNAME=myuser- SPRING_DATASOURCE_PASSWORD=mypassword- SPRING_JPA_HIBERNATE_DDL_AUTO=update # 或者根据你的 db.sql 决定depends_on:db:condition: service_healthynetworks:- my-app-networkfrontend:# 镜像名称将从 ACR 拉取,例如: registry.cn-hangzhou.aliyuncs.com/your-acr-namespace/react-frontend:latestimage: ACR_REGISTRY_URL/YOUR_ACR_NAMESPACE/react-frontend:latest # <--- 稍后替换container_name: my-react-apprestart: unless-stoppedports:- "80:80"depends_on:- backendnetworks:- my-app-networkdb:image: mysql:8.0container_name: my-mysql-dbrestart: unless-stoppedenvironment:MYSQL_DATABASE: mydatabaseMYSQL_USER: myuserMYSQL_PASSWORD: mypasswordMYSQL_ROOT_PASSWORD: rootpassword # 请使用强密码volumes:- mysql_data_ecs:/var/lib/mysql- ./db_init/db.sql:/docker-entrypoint-initdb.d/init.sql # 从 ECS 本地挂载# 不建议将 3306 映射到公网,除非你有特定的安全措施# ports:#   - "3306:3306"healthcheck:test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost", "-u$$MYSQL_USER", "-p$$MYSQL_PASSWORD"]interval: 10stimeout: 5sretries: 5start_period: 30snetworks:- my-app-networknetworks:my-app-network:driver: bridgevolumes:mysql_data_ecs: # Docker volume on ECS for MySQL data persistence
    

    注意:

    • ACR_REGISTRY_URL/YOUR_ACR_NAMESPACE/springboot-backend:latestACR_REGISTRY_URL/YOUR_ACR_NAMESPACE/react-frontend:latest 是占位符。你需要在创建 ACR 仓库后替换它们。
    • 数据库密码等敏感信息,更安全的做法是使用 Docker secrets 或 ECS 参数存储,但这里为了简化,直接写在环境变量中。

阶段二:阿里云容器镜像服务 (ACR) 设置

  1. 开通 ACR 服务: 如果你还没有开通,请在阿里云控制台开通“容器镜像服务 ACR”。选择个人版(免费)或企业版。
  2. 创建命名空间 (Namespace): 命名空间用于组织你的镜像。例如,你可以创建一个叫 my-apps 或你公司/项目名的命名空间。
  3. 创建镜像仓库 (Repository):
    • 在你的命名空间下,分别为后端和前端创建两个镜像仓库。
      • 例如:springboot-backend
      • 例如:react-frontend
    • 记下你的 ACR Registry URL (例如 registry.cn-hangzhou.aliyuncs.com) 和完整的仓库名称 (例如 registry.cn-hangzhou.aliyuncs.com/your-namespace/springboot-backend)。
  4. 获取 ACR 访问凭证:
    • 在 ACR 控制台的“访问凭证”部分,设置固定密码或创建临时令牌。GitHub Actions 将使用这些凭证来推送镜像。

阶段三:GitHub Secrets 配置

在你的每一个 GitHub 仓库(后端和前端)中,都需要配置以下 Secrets。进入仓库的 Settings -> Secrets and variables -> Actions -> New repository secret

  • ACR_REGISTRY_URL: 你的 ACR 实例的访问域名 (例如 registry.cn-hangzhou.aliyuncs.com)。
  • ACR_USERNAME: 你的 ACR 登录用户名。
  • ACR_PASSWORD: 你的 ACR 登录密码。
  • ECS_HOST: 你阿里云 ECS 的公网 IP 地址。
  • ECS_USER: 你用于 SSH 登录 ECS 的用户名 (例如 root 或你创建的部署用户)。
  • ECS_SSH_PRIVATE_KEY: 用于 SSH 登录 ECS 的私钥。
    • 生成 SSH 密钥对: 如果没有,可以在本地用 ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa_ecs 生成。
    • 将公钥 (~/.ssh/id_rsa_ecs.pub 的内容) 添加到 ECS 服务器上对应用户的 ~/.ssh/authorized_keys 文件中。
    • 将私钥 (~/.ssh/id_rsa_ecs 的内容) 复制到 GitHub Secret ECS_SSH_PRIVATE_KEY 中。确保复制完整内容,包括 -----BEGIN RSA PRIVATE KEY----------END RSA PRIVATE KEY-----
  • DOCKER_COMPOSE_PATH: ECS 上 docker-compose.yml 文件所在的绝对路径 (例如 /home/your_ecs_user/my-fullstack-app)。

阶段四:Dockerfile 修改 (不再从 Dockerfile 内部 clone)

GitHub Actions 会先 checkout 代码,所以 Dockerfile 不需要再 git clone。它们应该假设源代码已经在构建上下文中。

1. 后端 Spring Boot ( backend/Dockerfile - 在你的后端仓库中)

Dockerfile

# 使用一个包含 JDK 的基础镜像
FROM openjdk:17-jdk-slim# 设置工作目录 (源代码将被 GitHub Actions checkout 到这里)
WORKDIR /app# 复制 Maven Wrapper 相关文件
COPY .mvn/ .mvn
COPY mvnw .
COPY pom.xml .# 下载依赖 (利用 Docker 缓存机制)
RUN ./mvnw dependency:go-offline -B# 复制源代码
COPY src ./src# 打包应用
RUN ./mvnw package -DskipTests# ---- 运行阶段 ----
# 使用一个更小的 JRE 镜像来运行应用
FROM openjdk:17-jre-slimWORKDIR /app# 从构建阶段复制 JAR 文件
COPY --from=0 /app/target/*.jar app.jar # 注意 --from=0 (指向第一个 FROM 指令)# 暴露 Spring Boot 应用的端口
EXPOSE 8080# 运行应用
ENTRYPOINT ["java", "-jar", "app.jar"]

2. 前端 React ( frontend/Dockerfile - 在你的前端仓库中)

Dockerfile

# ---- 构建阶段 ----
FROM node:18-alpineWORKDIR /app# 复制 package.json 和 package-lock.json (或 yarn.lock)
COPY package*.json ./# 安装依赖
RUN npm install
# 如果使用 yarn:
# COPY yarn.lock ./
# RUN yarn install# 复制所有项目文件
COPY . .# 构建应用
RUN npm run build
# 如果使用 yarn:
# RUN yarn build# ---- 运行阶段 ----
FROM nginx:stable-alpine# 复制 Nginx 配置文件 (这个文件也应该在你的前端仓库中,例如 frontend/nginx.conf)
COPY nginx.conf /etc/nginx/conf.d/default.conf# 从构建阶段复制构建好的静态文件到 Nginx 的 www 目录
COPY --from=0 /app/dist /usr/share/nginx/html # 注意 --from=0# 暴露 Nginx 端口
EXPOSE 80# 启动 Nginx
CMD ["nginx", "-g", "daemon off;"]

确保 frontend/nginx.conf 文件也在你的前端仓库中 (与 Dockerfile 同级或在可被 COPY 的路径)。内容与之前提供的 nginx.conf 相同。


阶段五:GitHub Actions Workflow 文件

在你的后端 GitHub 仓库.github/workflows/ 目录下创建一个 deploy-backend.yml 文件:

backend/.github/workflows/deploy-backend.yml

YAML

name: Deploy Backend to Alibaba Cloud ECSon:push:branches:- main # 或者你的主部署分支,例如 masterjobs:build-and-deploy-backend:runs-on: ubuntu-lateststeps:- name: Checkout codeuses: actions/checkout@v3- name: Set up JDK 17uses: actions/setup-java@v3with:java-version: '17'distribution: 'temurin' # 或其他你偏好的 JDK 发行版- name: Login to Alibaba Cloud Container Registryuses: docker/login-action@v2with:registry: ${{ secrets.ACR_REGISTRY_URL }}username: ${{ secrets.ACR_USERNAME }}password: ${{ secrets.ACR_PASSWORD }}- name: Build and push Docker image for Backenduses: docker/build-push-action@v4with:context: ./backend # 确保这是你后端 Dockerfile 所在的目录相对于仓库根目录file: ./backend/Dockerfile # Dockerfile 的路径push: truetags: |${{ secrets.ACR_REGISTRY_URL }}/YOUR_ACR_NAMESPACE/springboot-backend:${{ github.sha }}${{ secrets.ACR_REGISTRY_URL }}/YOUR_ACR_NAMESPACE/springboot-backend:latest# YOUR_ACR_NAMESPACE 替换为你的ACR命名空间- name: Deploy to ECS via SSHuses: appleboy/ssh-action@masterwith:host: ${{ secrets.ECS_HOST }}username: ${{ secrets.ECS_USER }}key: ${{ secrets.ECS_SSH_PRIVATE_KEY }}script: |cd ${{ secrets.DOCKER_COMPOSE_PATH }}echo "Pulling latest backend image..."docker pull ${{ secrets.ACR_REGISTRY_URL }}/YOUR_ACR_NAMESPACE/springboot-backend:latestecho "Updating backend service..."# 更新 docker-compose.yml 中的 backend image tag (如果需要动态指定 github.sha)# sed -i "s|image: ${{ secrets.ACR_REGISTRY_URL }}/YOUR_ACR_NAMESPACE/springboot-backend:.*|image: ${{ secrets.ACR_REGISTRY_URL }}/YOUR_ACR_NAMESPACE/springboot-backend:${{ github.sha }}|g" docker-compose.yml# 或者直接使用 latest 标签,如当前配置docker-compose -f docker-compose.yml up -d --no-deps backendecho "Cleaning up old Docker images..."docker image prune -af

在你的前端 GitHub 仓库.github/workflows/ 目录下创建一个 deploy-frontend.yml 文件:

frontend/.github/workflows/deploy-frontend.yml

YAML

name: Deploy Frontend to Alibaba Cloud ECSon:push:branches:- main # 或者你的主部署分支jobs:build-and-deploy-frontend:runs-on: ubuntu-lateststeps:- name: Checkout codeuses: actions/checkout@v3- name: Set up Node.jsuses: actions/setup-node@v3with:node-version: '18' # 与你 Dockerfile 中一致- name: Login to Alibaba Cloud Container Registryuses: docker/login-action@v2with:registry: ${{ secrets.ACR_REGISTRY_URL }}username: ${{ secrets.ACR_USERNAME }}password: ${{ secrets.ACR_PASSWORD }}- name: Build and push Docker image for Frontenduses: docker/build-push-action@v4with:context: ./frontend # 确保这是你前端 Dockerfile 和 nginx.conf 所在的目录file: ./frontend/Dockerfilepush: truetags: |${{ secrets.ACR_REGISTRY_URL }}/YOUR_ACR_NAMESPACE/react-frontend:${{ github.sha }}${{ secrets.ACR_REGISTRY_URL }}/YOUR_ACR_NAMESPACE/react-frontend:latest# YOUR_ACR_NAMESPACE 替换为你的ACR命名空间- name: Deploy to ECS via SSHuses: appleboy/ssh-action@masterwith:host: ${{ secrets.ECS_HOST }}username: ${{ secrets.ECS_USER }}key: ${{ secrets.ECS_SSH_PRIVATE_KEY }}script: |cd ${{ secrets.DOCKER_COMPOSE_PATH }}echo "Pulling latest frontend image..."docker pull ${{ secrets.ACR_REGISTRY_URL }}/YOUR_ACR_NAMESPACE/react-frontend:latestecho "Updating frontend service..."docker-compose -f docker-compose.yml up -d --no-deps frontendecho "Cleaning up old Docker images..."docker image prune -af

重要:

  • 在上面两个 YAML 文件中,将 YOUR_ACR_NAMESPACE 替换为你在阿里云 ACR 中创建的实际命名空间。
  • contextfile 路径在 docker/build-push-action 中要指向你仓库中 Dockerfile 的正确位置。如果 Dockerfile 就在仓库根目录,context 可以是 .file 可以是 ./Dockerfile。我假设你的 Dockerfile 在各自项目的子目录 (backend/frontend/) 中。如果你的项目文件和 Dockerfile 都在仓库根目录,那么 context: .file: ./Dockerfile请根据你的仓库结构调整。
  • tags 部分,我们同时推送了 latest 标签和一个基于 Git commit SHA 的唯一标签。在 ECS 上的 docker-compose.yml 我们目前配置的是拉取 latest

阶段六:首次部署和后续操作

  1. 手动触发或首次推送:

    • 确保所有 GitHub Secrets 都已正确设置。
    • 确保 ECS 上的 ~/my-fullstack-app/docker-compose.yml~/my-fullstack-app/db_init/db.sql 文件已就绪。
    • 将上述 deploy-backend.ymldeploy-frontend.yml 文件提交到各自仓库的 .github/workflows/ 目录下,并推送到 main 分支。
    • 这会触发 GitHub Actions workflow。你可以在 GitHub 仓库的 "Actions" 标签页查看执行过程。
  2. 首次在 ECS 上运行 docker-compose up -d:

    • 在第一个 Action 成功将镜像推送到 ACR 后,你可能需要手动在 ECS 上首次完整启动所有服务,特别是数据库服务,以创建网络和数据卷: Bash

      # 在 ECS 的 ~/my-fullstack-app/ 目录下
      docker-compose up -d
      
      这会拉取 latest 标签的镜像 (如果 Actions 已经推送了),并启动所有服务,包括 MySQL 并执行 db.sql
  3. 后续自动部署:

    • 之后,每当你向后端或前端仓库的 main 分支推送代码,对应的 GitHub Action 就会自动触发:
      1. 构建新的 Docker 镜像。
      2. 将新镜像推送到 ACR (覆盖 latest 标签并添加 commit SHA 标签)。
      3. SSH 到你的 ECS。
      4. 拉取最新的镜像 (带有 latest 标签)。
      5. 使用 docker-compose up -d --no-deps <service-name> 平滑地只更新发生变化的服务,而不会中断其他服务 (例如数据库)。

这是一个相当复杂的流程,但一旦设置好,就能实现非常高效的自动化部署。仔细检查每个步骤中的路径、名称和凭证。如果遇到问题,GitHub Actions 的日志输出会是排查错误的关键。

相关文章:

  • GitLab-CI将项目Wiki自动部署到文档中心
  • 卷积神经网络(CNN)深度讲解
  • 【HarmonyOS5】DevEco Studio 预览器与模拟工具详解
  • 基于文本挖掘与情感分析的B站《唐探1900》弹幕研究
  • 使用Cursor生成需求文档+UI设计图
  • 【微服务】SpringBoot 对接飞书审批流程使用详解
  • Python GDAL 库离线安装
  • NTFS0x90属性和0xa0属性和0xb0属性的一一对应关系是index_entry中的index_node中VCN和runlist和bitmap
  • Mybatis框架的构建(IDEA)
  • 【C++】21. 红黑树的实现
  • JWT与布隆过滤器结合使用指南
  • C++编程单例模式详细解释---模拟一个网络配置管理器,负责管理和分发网络连接参数
  • 分布式缓存:三万字详解Redis
  • 华为OD机试真题—— 矩阵匹配(2025B卷:200分)Java/python/JavaScript/C/C++/GO最佳实现
  • Redis数据安全分析
  • 上海医日健集团物联网专利技术领跑智慧药房赛道
  • Lua 脚本在 Redis 中的运用-24 (使用 Lua 脚本实现原子计数器)
  • (27)运动目标检测 之 分类(如YOLO) 数据集自动划分
  • 大语言模型在软件工程中的应用、影响与展望
  • 什么是 Spring MVC 的异步请求处理?
  • 怎么建设免费网站/广州现在有什么病毒感染
  • 如何做公司的网站建设/推广文案范文100字
  • 嘉兴云推广网站/google广告投放技巧
  • angular 做网站/软文营销范文
  • 南阳网站建设优化/seo技术外包 乐云践新专家
  • 网站的主要功能/企业网络推广方式