GitHub Actions 自动化运维实战:Java + TypeScript 全栈项目 CI/CD 至阿里云

GitHub Actions 自动化运维实战:Java + TypeScript 全栈项目 CI/CD 至阿里云

摘要 :本文面向全栈工程师,详细讲解如何使用 GitHub Actions 将 Java Spring Boot 后端 + TypeScript (Vue/React) 前端 构成的单体仓库(Monorepo)项目,自动化构建并部署到阿里云 ECS。涵盖 Docker 多阶段构建、阿里云容器镜像服务(ACR)、Nginx 静态资源托管及 SSH 远程部署等高阶实战技巧。


一、项目架构与部署拓扑

在开始编码前,先明确我们的目标架构:

  • 前端 :TypeScript + Vue 3 / React,构建为静态资源(dist)。
  • 后端:Java 17 + Spring Boot,打包为 JAR。
  • 部署目标:阿里云 ECS(CentOS/Ubuntu)。
  • 容器化:Docker + Docker Compose。
  • 镜像仓库 :阿里云容器镜像服务(ACR - 免费且在国内访问速度快)。部署流程图
text 复制代码
代码 Push 至 GitHub 
  → GitHub Actions 触发 
  → 前端: npm install & build 
  → 后端: maven package 
  → 构建 Docker 镜像 
  → 推送至阿里云 ACR 
  → SSH 登录 ECS 
  → 拉取新镜像 & 重启容器

二、前置准备(阿里云侧配置)

2.1 开通阿里云容器镜像服务 (ACR)

  1. 进入阿里云控制台 -> 容器镜像服务。
  2. 创建命名空间 (如 my-project)。
  3. 创建镜像仓库 (如 fullstack-app),代码源选择本地仓库
  4. 记录仓库地址:registry.cn-hangzhou.aliyuncs.com/my-project/fullstack-app。### 2.2 配置 AccessKey
    为了能让 GitHub Actions 推送镜像,需要创建访问凭证:
  5. 阿里云控制台 -> AccessKey 管理 -> 创建 AccessKey。
  6. 务必保存好 AccessKey IDAccessKey Secret 。### 2.3 配置 ECS 服务器
    确保 ECS 已安装 Docker 和 Docker Compose。
    开放端口:8080 (Backend)、80 (Frontend/Nginx)。

三、项目结构与 Docker 配置

假设你的项目结构如下(Monorepo):

text 复制代码
.
├── .github/workflows/ci-cd.yml
├── backend
│   ├── src
│   └── pom.xml
├── frontend
│   ├── src
│   └── package.json
├── Dockerfile
└── docker-compose.yml

3.1 多阶段构建 Dockerfile (核心)

这个 Dockerfile 非常关键,它在一个文件中完成了前端编译、后端打包和最终运行环境的组装。

dockerfile 复制代码
# ================== 第一阶段:构建前端 ==================
FROM node:18-alpine AS frontend-build
WORKDIR /frontend
COPY frontend/package*.json ./
RUN npm install -g pnpm # 假设使用 pnpm
RUN pnpm install
COPY frontend/ .
RUN pnpm run build

# ================== 第二阶段:构建后端 ==================
FROM maven:3.9-eclipse-temurin-17 AS backend-build
WORKDIR /backend
COPY backend/pom.xml .
COPY backend/src ./src
RUN mvn clean package -DskipTests

# ================== 第三阶段:运行环境 ==================
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app

# 1. 拷贝后端 JAR 包
COPY --from=backend-build /backend/target/*.jar app.jar

# 2. 拷贝前端静态资源到 Nginx 目录 (或者直接用 Jar 托管,这里演示分离)
RUN apk add --no-cache nginx
COPY --from=frontend-build /frontend/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 8080 80

# 启动脚本:同时启动 Java 服务和 Nginx
ENTRYPOINT sh -c "java -jar app.jar & nginx -g 'daemon off;'"

3.2 Nginx 配置 (nginx.conf)

nginx 复制代码
events { worker_connections 1024; }

http {
    server {
        listen 80;
        root /usr/share/nginx/html;
        index index.html;

        # 解决前端路由刷新 404 问题
        location / {
            try_files $uri $uri/ /index.html;
        }

        # API 反向代理到 Spring Boot
        location /api/ {
            proxy_pass http://localhost:8080/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

四、GitHub Actions Workflow 实战配置

这是本文的核心。我们将配置一个 Workflow 来完成整个 CI/CD 流程。

4.1 配置 GitHub Secrets

在 GitHub 仓库 Settings -> Secrets and variables -> Actions 中添加以下变量:

Secret 名称 描述
ALIYUN_USERNAME 阿里云 ACR 用户名(通常是阿里云账号 ID)
ALIYUN_PASSWORD 阿里云 ACR 密码(刚才创建的 AccessKey Secret)
SERVER_HOST 阿里云 ECS 公网 IP
SERVER_USER ECS 登录用户名(如 root)
SSH_PRIVATE_KEY 登录 ECS 的私钥(对应公钥在 ~/.ssh/authorized_keys

4.2 CI/CD 流水线配置 (.github/workflows/deploy.yml)

yaml 复制代码
name: Java + TS Fullstack CI/CD to Aliyun

on:
  push:
    branches: [ "main" ]
  workflow_dispatch:

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    
    steps:
      - name: 1. 检出代码
        uses: actions/checkout@v4

      - name: 2. 登录阿里云 Docker Registry
        uses: docker/login-action@v3
        with:
          registry: registry.cn-hangzhou.aliyuncs.com
          username: ${{ secrets.ALIYUN_USERNAME }}
          password: ${{ secrets.ALIYUN_PASSWORD }}

      - name: 3. 提取 Docker 元数据 (标签)
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: registry.cn-hangzhou.aliyuncs.com/my-project/fullstack-app
          tags: |
            type=sha,prefix={{branch}}-
            type=raw,value=latest,enable={{is_default_branch}}

      - name: 4. 构建并推送 Docker 镜像
        uses: docker/build-push-action@v5
        with:
          context: .
          file: ./Dockerfile
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: 5. 部署到阿里云 ECS
        uses: appleboy/ssh-action@v1.0.0
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            # 登录阿里云镜像仓库
            docker login registry.cn-hangzhou.aliyuncs.com \
              -u ${{ secrets.ALIYUN_USERNAME }} \
              -p ${{ secrets.ALIYUN_PASSWORD }}

            # 拉取最新镜像
            docker pull registry.cn-hangzhou.aliyuncs.com/my-project/fullstack-app:latest

            # 进入项目目录
            cd /opt/fullstack-app

            # 写入最新的镜像标签到 .env 文件(供 docker-compose 使用)
            echo "IMAGE_TAG=latest" > .env
            
            # 停止旧容器,启动新容器
            docker-compose down
            docker-compose up -d

            # 清理未使用的镜像
            docker system prune -f
            
            echo "🚀 Deployment Successful!"

4.3 服务器上的 docker-compose.yml

在阿里云 ECS 的 /opt/fullstack-app/ 目录下创建此文件:

yaml 复制代码
version: '3.8'
services:
  app:
    image: registry.cn-hangzhou.aliyuncs.com/my-project/fullstack-app:${IMAGE_TAG:-latest}
    container_name: fullstack_app
    restart: always
    ports:
      - "80:80"
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
    volumes:
      - /data/logs:/app/logs # 挂载日志目录

五、关键优化与排错指南

5.1 构建速度优化

  • Maven 依赖缓存 :Java 构建慢通常是因为下载依赖。在 backend-build 阶段添加缓存:

    dockerfile 复制代码
    COPY backend/pom.xml .
    RUN mvn dependency:go-offline # 先下载依赖,利用 Docker 层缓存
    COPY backend/src ./src
  • pnpm/npm 缓存 :在 frontend-build 阶段,利用 GitHub Actions 的 cache action 缓存 node_modules

5.2 安全性增强

  • Secret 管理:绝对不要在前端代码中暴露 API Key。如果前端需要配置,请使用 Nginx 环境变量注入或运行时配置。
  • SSH 权限:建议使用非 root 用户部署,仅赋予 Docker 执行权限。
  • ACR 权限:建议为生产环境创建只读的 RAM 子账号用于拉取镜像。

5.3 常见问题排查

  1. SSH 连接失败:检查 ECS 安全组是否允许 GitHub Runner IP(GitHub 官方会公布 IP 段,但最简单是允许所有 IP 22 端口,依靠密钥保障安全)。
  2. Docker 登录失败 :检查 AccessKey 权限是否包含 cr.* 相关权限。
  3. 前端刷新 404 :确认 Nginx 配置中 location / 下是否有 try_files $uri $uri/ /index.html;
  4. 后端无法连接数据库 :检查 Spring Boot 的 application-prod.yml 配置,确保数据库地址是内网地址且安全组已放行。

六、总结

通过这套配置,你实现了:

  1. 全栈一体化构建:利用 Docker 多阶段构建完美解决了 Java 和 TypeScript 混合构建的问题。
  2. 国内极速分发:使用阿里云 ACR 避免了 Docker Hub 拉取慢的问题。
  3. 标准化部署:基于 Docker Compose 的部署方式,保证了环境的一致性。

下一步进阶

  • 引入 Nginx 负载均衡 实现多实例部署。
  • 结合 阿里云 OSS 存储前端静态资源,降低服务器带宽压力。
  • 使用 GitHub Environments 实现测试/生产的审批流程。

💡 互动话题:你们在生产环境中是全栈共用一个仓库(Monorepo),还是前后端分离仓库?在 CI/CD 中遇到了什么挑战?欢迎讨论!


相关推荐
重庆小透明1 小时前
深入探寻微服务【第三篇微服务的组件】
运维·微服务·架构
智脑API2 小时前
Codex config.toml check_for_update_on_startup 怎么关闭?离线环境与版本提醒排查
运维·服务器·前端
2401_8685347810 小时前
数仓开发落地手册
linux·运维
正在走向自律12 小时前
使用atop工具监控Linux系统指标
linux·运维·php·实时监控·atop分析内存
yyy(十一月限定版)13 小时前
017【入门】最小栈
linux·运维·服务器
2401_8949155315 小时前
GEO 源码部署如何实现精准地域分发?核心配置参数深度讲解
java·运维·服务器·后端·开源
万少15 小时前
我是如何感觉到被时代抛弃的
github
常乐か16 小时前
VMware Workstation Pro 17 搭建 Ubuntu 24.04 完整指南
linux·运维·ubuntu
2401_8582861117 小时前
OS82.【Linux】设计线程池
java·linux·运维·服务器·开发语言·算法·线程池