[T.3] 团队项目:团队基础设施及 DevOps 准备

项目 内容
这个作业属于哪个课程 首页 - 2025年春季软件工程(罗杰、任健) - 北京航空航天大学 - 班级博客 - 博客园
这个作业的要求在哪里 T.3 团队项目:团队基础设施及 DevOps 准备 - 作业 - 2025年春季软件工程(罗杰、任健) - 班级博客 - 博客园
我在这个课程的目标是 在实践中掌握软件工程的基本方法,提高开发能力
这个作业在哪个具体方面帮助我实现目标 为接下来的团队开发做好基础性工作,学习项目部署上线流程

服务器

配置

我们配置了两台服务器,一台2G的低性能服务器用于部署部分中间件,一台8G高性能服务器用于部署前端,后端以及部分中间件。

服务器 Server-Aliyun-2G Server-Tencent-8G
CPU 2核(vCPU) 2
内存 2 GiB 8 GB
OS Ubuntu 24.04 64bit Ubuntu Server 22.04 LTS 64bit
系统盘 ESSD Entry云盘 40 GiB SSD云硬盘 80GB
数据盘 - -
带宽 3 Mbps 7 Mbps
地域 北京 北京
计费方式 包年包月 包年包月

分析

我们软工的项目最终会以微信小程序的方式分发,只会在Web端部署管理员页面,因此服务器选择主要是根据后端决定的。

我们的后端基于微服务架构开发,核心的业务由Spring框架实现,同时会部署基于Django开发的爬虫,这两者对内存的占用率较高。除了项目本身外,还需要部署nacos,redis,mq等微服务常用中间件,这里面的nacos对内存要求较高,redis对内存和CPU都有较高要求,并且为了保证服务的高可用,redis与mq未来可能要以集群方式部署提高容灾性。因此我们决定将内存大小作为选择服务器的主要因素。

采用两个服务器的原因一方面是为了节省开销(手里已经有一台阿里云的2G服务器),另一方面是提高系统的稳定性和可用性。

下图展示了开发环境中低负载情况下Spring项目的内存占用,可以看到在启动容器时将内存限制到512MB仍然有较高内存占用,为了应对生产环境下可能出现的高并发问题,我们选择了8G内存的服务器,而为了方便使用Docker部署进行环境隔离,我们选择了基于Docker镜像的Ubuntu服务器。

在数据存储方面,由于后端采用微服务架构,每个服务都可以单独使用一台数据库,因此我们决定利用阿里云的试用获得两至三台40GB存储的2核数据库,这可以满足我们对结构化数据存储的要求。我们的系统中需要存储的文件不多,大多数是文档和图片,因此决定使用阿里云最低配置的OSS服务,每个月费用大约为3元。

我们的应用最初会在北航校内进行推广,为了降低延迟,服务器地域都选择了北京。计费方式选择包年包月是因为腾讯云有购买服务器的优惠,我们购买了6个月最终花费408元。

我们最终对服务器的使用计划如下:

  • 阿里云2G服务器
    • nacos
    • redis
  • 腾讯云8G服务器
    • 后端Spring服务器
    • 后端Django爬虫
    • mq
    • nginx(管理员Web端)

团队管理

我们使用飞书进行团队协作和沟通。飞书具有优秀的文档协作功能,同时甘特图、日历等功能非常适合开发的任务安排。

代码仓库我们选择了Github,主要是通过Github Action来进行项目的自动构建和部署。

接口文档管理工具我们选择了Apifox+Swagger的方式。Apifox对IDEA提供了插件,可以根据Swagger注解自动将接口导入,减少了后端编写文档的工作量同时提高了接口文档的准确性;同时支持多种环境,方便在开发环境和测试环境中切换。

CI/CD

构建一个Web项目,分为前端和后端,我们对前端和后端单独配置了不同的CI/CD流程。Git仓库链接:https://github.com/XuanxuanRao/CI-CD-Demo

前端使用vue3,在构建时打包后通过nginx运行,并进行负载均衡和反向代理。nginx在服务器上通过Docker容器运行,因此只需要对代码进行打包,上传到服务器上nginx容器挂载的目录上。

name: Build frontend and deploy to Aliyun
on:
  push:
    branches:
      - main
    paths:
      - 'frontend/**'
      - '.github/workflows/deploy-frontend.yaml'
      
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    
    - name: Navigate to frontend directory
      run: cd frontend

    - name: npm install
      run: npm install
      working-directory: ./frontend

    - name: npm run build
      run: npm run build
      working-directory: ./frontend

    - name: Deploy
      uses: cross-the-world/scp-pipeline@master
      env:
        WELCOME: "ssh scp ssh pipelines"
        LASTSSH: "Doing something after copying"
      with:
        host: ${{ secrets.SSH_HOST }}
        user: ${{ secrets.SSH_USERNAME }}
        pass: ${{ secrets.SSH_PASSWORD }}
        connect_timeout: 10s
        local: './frontend/dist/*'
        remote: /home/nginx/html

    - name: Restart Docker Container
      uses: appleboy/ssh-action@master
      with:
        host: ${{ secrets.SSH_HOST }}
        username: ${{ secrets.SSH_USERNAME }}
        password: ${{ secrets.SSH_PASSWORD }}
        script: docker restart nginx

后端基于SpringBoot,采用微服务架构,需要对每个服务打成jar包并构建镜像,上传到Docker Hub,上传完成后服务器拉取镜像并通过docker compose运行所有服务。其中,中间件(如nacos,RabbitMQ)已经部署在另外一台服务器上,在部署时不需要启动这些中间件。

name: Build backend and deploy to Aliyun

on:
  push:
    branches:
      - main
    paths:
      - 'backend/**'
      - '.github/workflows/deploy-backend.yml'

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        service: [gateway, user-service, email-service]

    steps:
      - name: 拉取代码
        uses: actions/checkout@v4

      - name: 设置 JDK 11
        uses: actions/setup-java@v3
        with:
          distribution: 'temurin'
          java-version: '11'

      - name: 构建 ${{ matrix.service }}
        run: |
          cd backend
          mvn clean package -DskipTests -pl ${{ matrix.service }} -am

      - name: 登录 Docker Hub
        run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login --username=${{ secrets.DOCKER_USERNAME }} --password-stdin ${{ vars.DOCKER_HUB_ADDR }}
        
      - name: 构建 Docker 镜像
        run: |
          cd backend/${{ matrix.service }}
          docker build -t ${{ vars.DOCKER_HUB_ADDR }}/${{ vars.DOCKER_NAMESPACE }}/${{ matrix.service }}:latest .

      - name: 推送 Docker 镜像
        run: docker push ${{ vars.DOCKER_HUB_ADDR }}/${{ vars.DOCKER_NAMESPACE }}/${{ matrix.service }}:latest

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: 连接服务器并部署
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          password: ${{ secrets.SSH_PASSWORD }}
          script: |
            cd /home/project
            docker compose down
            for service in gateway user-service email-service; do
              docker pull ${{ vars.DOCKER_HUB_ADDR }}/${{ vars.DOCKER_NAMESPACE }}/$service:latest
            done
            docker compose --project-name survey-planet up -d

下图是后端Actions运行过程,首先进行三个微服务模块的构建并上传到镜像仓库(build),然后服务器拉取镜像并运行完成部署(deploy)。

这里,后端将配置文件中的敏感信息(如数据库、消息队列账号密码)也一起提交了,在实际开发中是不可取的,我最终选择的方案是通过环境变量来加载某些敏感信息。

但是考虑到一个服务器上会部署多个应用,微服务下每个应用有自己的环境,需要单独配置环境变量。在Spring项目中,开发时可以将环境变量以K=V的形式保存到一个名为.env的文件中(这个文件不应该被提交到仓库),然后把这个文件放在resources目录下,在运行项目时使用spring-dotenv来加载即可。而部署时,可以在用docker-compose启动时为每个容器指定环境变量,这样就保证了微服务架构下敏感信息不被提交到仓库也可以进行自动部署了。

<!-- 加载环境变量 -->
<dependency>
    <groupId>me.paulschwarz</groupId>
    <artifactId>spring-dotenv</artifactId>
    <version>4.0.0</version>
</dependency>

下面是团队成员触发CI/CD的记录

posted @ 2025-04-06 01:01  WOW114514  阅读(102)  评论(0)    收藏  举报