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)
- 进入阿里云控制台 -> 容器镜像服务。
- 创建命名空间 (如
my-project)。 - 创建镜像仓库 (如
fullstack-app),代码源选择本地仓库。 - 记录仓库地址:
registry.cn-hangzhou.aliyuncs.com/my-project/fullstack-app。### 2.2 配置 AccessKey
为了能让 GitHub Actions 推送镜像,需要创建访问凭证: - 阿里云控制台 -> AccessKey 管理 -> 创建 AccessKey。
- 务必保存好
AccessKey ID和AccessKey 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阶段添加缓存:dockerfileCOPY backend/pom.xml . RUN mvn dependency:go-offline # 先下载依赖,利用 Docker 层缓存 COPY backend/src ./src -
pnpm/npm 缓存 :在
frontend-build阶段,利用 GitHub Actions 的cacheaction 缓存node_modules。
5.2 安全性增强
- Secret 管理:绝对不要在前端代码中暴露 API Key。如果前端需要配置,请使用 Nginx 环境变量注入或运行时配置。
- SSH 权限:建议使用非 root 用户部署,仅赋予 Docker 执行权限。
- ACR 权限:建议为生产环境创建只读的 RAM 子账号用于拉取镜像。
5.3 常见问题排查
- SSH 连接失败:检查 ECS 安全组是否允许 GitHub Runner IP(GitHub 官方会公布 IP 段,但最简单是允许所有 IP 22 端口,依靠密钥保障安全)。
- Docker 登录失败 :检查 AccessKey 权限是否包含
cr.*相关权限。 - 前端刷新 404 :确认 Nginx 配置中
location /下是否有try_files $uri $uri/ /index.html;。 - 后端无法连接数据库 :检查 Spring Boot 的
application-prod.yml配置,确保数据库地址是内网地址且安全组已放行。
六、总结
通过这套配置,你实现了:
- 全栈一体化构建:利用 Docker 多阶段构建完美解决了 Java 和 TypeScript 混合构建的问题。
- 国内极速分发:使用阿里云 ACR 避免了 Docker Hub 拉取慢的问题。
- 标准化部署:基于 Docker Compose 的部署方式,保证了环境的一致性。
下一步进阶:
- 引入 Nginx 负载均衡 实现多实例部署。
- 结合 阿里云 OSS 存储前端静态资源,降低服务器带宽压力。
- 使用 GitHub Environments 实现测试/生产的审批流程。
💡 互动话题:你们在生产环境中是全栈共用一个仓库(Monorepo),还是前后端分离仓库?在 CI/CD 中遇到了什么挑战?欢迎讨论!