qq_26950675头像
关注

第50课-项目部署上线

第 50 课:项目部署上线

本课目标:掌握CI/CD基础流程,使用GitHub Actions自动化部署,完成Linux服务器部署、Nginx配置、Docker容器化部署。


一、概念讲解

1.1 部署流程概览

开发 → 推送代码 → CI/CD自动构建 → 部署服务器 → 验证上线

┌─────────────────────────────────────────────────────┐
│                  部署流程                             │
├──────────┬──────────┬──────────┬──────────┬─────────┤
│  代码提交  │ 自动构建  │ 镜像打包  │ 服务器部署 │ 健康检查 │
│   Git    │  Maven   │  Docker  │  Docker  │  Actuator│
└──────────┴──────────┴──────────┴──────────┴─────────┘

1.2 CI/CD概念

CI(持续集成):
代码合并 → 自动构建 → 自动测试 → 反馈结果

CD(持续交付/部署):
构建产物 → 自动部署到测试环境 → 手动/自动发布到生产环境

1.3 部署架构

生产环境架构:
┌─────────────────────────────────────────────────┐
│                    Nginx (80/443)                │
│                    反向代理+SSL                   │
├────────────────────┬────────────────────────────┤
│   / (前端静态资源)  │   /api/ (后端API)           │
│   Vue构建产物       │   Spring Boot              │
├────────────────────┼────────────────────────────┤
│                    │                             │
│   Docker容器1      │    Docker容器2              │
│   nginx:alpine     │    my-app:latest            │
└────────────────────┴────────────────────────────┘
                         │
          ┌──────────────┴──────────────┐
          │                             │
    ┌─────┴─────┐               ┌───────┴─────┐
    │  MySQL    │               │   Redis     │
    │  Docker   │               │   Docker    │
    └───────────┘               └─────────────┘

二、语法格式

2.1 GitHub Actions工作流语法

name: CI/CD Pipeline
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup JDK
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
      - name: Build
        run: mvn clean package -DskipTests

2.2 Docker部署命令

# 构建并启动
docker-compose up -d --build

# 查看状态
docker-compose ps

# 查看日志
docker-compose logs -f app

# 停止服务
docker-compose down

三、代码案例

3.1 GitHub Actions工作流

# .github/workflows/deploy.yml
name: Build and Deploy

on:
  push:
    branches: [ main ]

env:
  IMAGE_NAME: my-app
  SERVER_HOST: ${{ secrets.SERVER_HOST }}
  SERVER_USER: ${{ secrets.SERVER_USER }}

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      # 1. 检出代码
      - name: Checkout
        uses: actions/checkout@v4

      # 2. 配置JDK
      - name: Setup JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: 'maven'

      # 3. Maven打包
      - name: Build with Maven
        run: mvn clean package -DskipTests

      # 4. 构建Docker镜像
      - name: Build Docker image
        run: |
          docker build -t $IMAGE_NAME:${{ github.sha }} .
          docker tag $IMAGE_NAME:${{ github.sha }} $IMAGE_NAME:latest

      # 5. 推送到Docker Hub(可选)
      - name: Login to Docker Hub
        if: success()
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Push to Docker Hub
        if: success()
        run: |
          docker push $IMAGE_NAME:${{ github.sha }}
          docker push $IMAGE_NAME:latest

      # 6. 部署到服务器
      - name: Deploy to server
        if: success()
        uses: appleboy/ssh-[email protected]
        with:
          host: $SERVER_HOST
          username: $SERVER_USER
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /opt/deploy
            docker-compose pull
            docker-compose up -d --remove-orphans
            docker image prune -f
            echo "部署完成!"

3.2 Dockerfile

# Dockerfile
FROM eclipse-temurin:17-jre-jammy

# 设置时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# 创建非root用户
RUN groupadd -r appuser && useradd -r -g appuser appuser

# 设置工作目录
WORKDIR /app

# 复制jar包
COPY target/my-app.jar app.jar

# 切换用户
USER appuser

# 暴露端口
EXPOSE 8080

# JVM优化参数
ENTRYPOINT ["java", \
  "-XX:+UseContainerSupport", \
  "-XX:MaxRAMPercentage=75.0", \
  "-XX:InitialRAMPercentage=50.0", \
  "-Djava.security.egd=file:/dev/./urandom", \
  "-jar", \
  "app.jar", \
  "--spring.profiles.active=prod"]

3.3 docker-compose.yml

version: '3.8'

services:
  # 应用服务
  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: my-app
    restart: always
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - DB_HOST=mysql
      - DB_PORT=3306
      - DB_NAME=demo_prod
      - DB_USERNAME=root
      - DB_PASSWORD=${DB_PASSWORD:-root123}
      - REDIS_HOST=redis
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_started
    networks:
      - app-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/api/actuator/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  # Nginx
  nginx:
    image: nginx:alpine
    container_name: nginx-proxy
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/ssl:/etc/nginx/ssl
      - ./frontend/dist:/usr/share/nginx/html
    depends_on:
      - app
    networks:
      - app-network

  # MySQL
  mysql:
    image: mysql:8.0
    container_name: mysql-db
    restart: always
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-root123}
      MYSQL_DATABASE: demo_prod
      MYSQL_CHARSET: utf8mb4
      TZ: Asia/Shanghai
    volumes:
      - mysql-data:/var/lib/mysql
      - ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql
    command: --default-authentication-plugin=caching_sha2_password
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - app-network

  # Redis
  redis:
    image: redis:7-alpine
    container_name: redis-cache
    restart: always
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    command: redis-server --appendonly yes
    networks:
      - app-network

volumes:
  mysql-data:
  redis-data:

networks:
  app-network:
    driver: bridge

3.4 Nginx配置

# nginx/nginx.conf
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    # 日志格式
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main;

    # Gzip压缩
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css application/json application/javascript
               text/xml application/xml application/xml+rss text/javascript;

    # 上游服务
    upstream backend {
        server app:8080;
    }

    server {
        listen 80;
        server_name example.com;

        # 前端静态资源
        location / {
            root /usr/share/nginx/html;
            index index.html;
            try_files $uri $uri/ /index.html;
        }

        # API反向代理
        location /api/ {
            proxy_pass http://backend/api/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_connect_timeout 30s;
            proxy_read_timeout 60s;
            proxy_send_timeout 60s;
        }

        # 静态资源缓存
        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
            expires 7d;
            add_header Cache-Control "public, no-transform";
        }

        # 禁止访问隐藏文件
        location ~ /\. {
            deny all;
        }
    }
}

3.5 部署脚本

#!/bin/bash
# deploy.sh - 一键部署脚本

set -e

echo "=========================================="
echo "         项目自动化部署脚本"
echo "=========================================="

# 配置
APP_NAME="my-app"
DEPLOY_DIR="/opt/deploy"
SQL_DIR="./sql"

# 1. 打包
echo "[1/5] Maven打包..."
mvn clean package -DskipTests
echo "打包完成!"

# 2. 构建前端(如果有)
echo "[2/5] 构建前端..."
if [ -d "./frontend" ]; then
    cd frontend
    npm install
    npm run build
    cd ..
    echo "前端构建完成!"
fi

# 3. 构建Docker镜像
echo "[3/5] 构建Docker镜像..."
docker build -t $APP_NAME:latest .
echo "镜像构建完成!"

# 4. 停止旧容器
echo "[4/5] 停止旧容器..."
cd $DEPLOY_DIR 2>/dev/null || mkdir -p $DEPLOY_DIR
docker-compose down 2>/dev/null || true

# 5. 启动新容器
echo "[5/5] 启动新容器..."
docker-compose up -d

# 等待启动
echo "等待服务启动..."
sleep 15

# 健康检查
echo "执行健康检查..."
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/api/actuator/health)
if [ "$HTTP_CODE" = "200" ]; then
    echo "=========================================="
    echo "    部署成功!服务已启动"
    echo "    访问地址: http://$(hostname):80"
    echo "=========================================="
else
    echo "=========================================="
    echo "    部署失败!HTTP状态码: $HTTP_CODE"
    echo "    查看日志: docker-compose logs -f"
    echo "=========================================="
    exit 1
fi

▶ 运行结果:

==========================================
         项目自动化部署脚本
==========================================
[1/5] Maven打包...
[INFO] BUILD SUCCESS
打包完成!
[2/5] 构建前端...
[3/5] 构建Docker镜像...
Successfully built abc123def456
Successfully tagged my-app:latest
镜像构建完成!
[4/5] 停止旧容器...
[5/5] 启动新容器...
Creating network "deploy_app-network" with the default driver
Creating deploy_app_1    ... done
Creating deploy_mysql_1  ... done
Creating deploy_redis_1  ... done
Creating deploy_nginx_1  ... done
等待服务启动...
执行健康检查...
==========================================
    部署成功!服务已启动
    访问地址: http://your-server-ip:80
==========================================

3.6 SQL初始化脚本

-- sql/init.sql
CREATE DATABASE IF NOT EXISTS demo_prod DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;

USE demo_prod;

-- 用户表
CREATE TABLE IF NOT EXISTS sys_user (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    email VARCHAR(100) NOT NULL UNIQUE,
    password VARCHAR(200) NOT NULL,
    role VARCHAR(20) DEFAULT 'USER',
    status INT DEFAULT 1,
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

-- 商品表
CREATE TABLE IF NOT EXISTS sys_product (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    price DECIMAL(10,2) NOT NULL,
    stock INT DEFAULT 0,
    category_id BIGINT,
    status INT DEFAULT 1,
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';

-- 初始化管理员(密码: admin123,BCrypt加密)
INSERT INTO sys_user (username, email, password, role, status)
VALUES ('admin', '[email protected]',
        '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi',
        'ADMIN', 1)
ON DUPLICATE KEY UPDATE username = username;

3.7 服务器初始化脚本

#!/bin/bash
# server-init.sh - 服务器初始化脚本

echo "=== 服务器初始化 ==="

# 1. 更新系统
sudo apt update && sudo apt upgrade -y

# 2. 安装Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER

# 3. 安装Docker Compose
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

# 4. 创建部署目录
sudo mkdir -p /opt/deploy
sudo chown $USER:$USER /opt/deploy

# 5. 配置防火墙
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 22/tcp
sudo ufw --force enable

echo "=== 服务器初始化完成 ==="

▶ 运行结果:

=== 服务器初始化 ===
Hit:1 http://archive.ubuntu.com/ubuntu jammy InRelease
Get:2 http://archive.ubuntu.com/ubuntu jammy-updates InRelease [119 kB]
...
Reading package lists... Done
Building dependency tree... Done
...
[OK] Docker installed successfully
=== 服务器初始化完成 ===

3.8 生产环境配置

# application-prod.yml
server:
  port: 8080
  tomcat:
    max-threads: 200
    min-spare-threads: 20

spring:
  datasource:
    url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:demo_prod}?useSSL=true&serverTimezone=Asia/Shanghai
    username: ${DB_USERNAME:root}
    password: ${DB_PASSWORD:root123}
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5

  redis:
    host: ${REDIS_HOST:localhost}
    port: 6379

  jpa:
    hibernate:
      ddl-auto: none
    show-sql: false

logging:
  level:
    root: INFO
    com.example: INFO

management:
  endpoints:
    web:
      exposure:
        include: health,info

3.9 监控配置(可选)

# docker-compose.monitoring.yml
version: '3.8'

services:
  # Prometheus
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
    networks:
      - app-network

  # Grafana
  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana-data:/var/lib/grafana
    networks:
      - app-network

volumes:
  grafana-data:

networks:
  app-network:
    external: true
# monitoring/prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'spring-boot'
    metrics_path: '/api/actuator/prometheus'
    static_configs:
      - targets: ['app:8080']

四、常见错误

4.1 GitHub Actions SSH连接失败

原因:SSH密钥配置错误或服务器未开放22端口。

解决:

  1. 生成SSH密钥:ssh-keygen -t rsa
  2. 将公钥添加到服务器~/.ssh/authorized_keys
  3. 在GitHub Secrets中配置私钥

4.2 Docker容器启动失败

排查步骤:

# 查看容器状态
docker-compose ps

# 查看日志
docker-compose logs app

# 进入容器调试
docker exec -it my-app bash

4.3 Nginx 502 Bad Gateway

原因:后端服务未启动或连接失败。

解决:检查Docker容器是否运行,网络是否连通。

4.4 数据库连接失败

现象:Communications link failure

解决:

  1. 检查MySQL容器是否运行
  2. 检查连接地址是否正确(Docker内用服务名,非localhost)
  3. 检查用户名密码是否正确

五、课后练习

  1. 配置GitHub Actions,实现push到main分支时自动构建并部署
  2. 编写一键部署脚本,包含打包、构建、启动、健康检查全流程
  3. 配置Nginx实现前端静态资源服务和API反向代理
  4. 搭建Prometheus+Grafana监控,查看应用运行指标

六、本课小结

知识点说明
GitHub ActionsCI/CD自动化工具,支持代码推送后自动构建部署
Docker容器化技术,确保开发、测试、生产环境一致
docker-compose多容器编排,一键启动应用+数据库+缓存+代理
Nginx反向代理+负载均衡+静态资源服务
部署脚本自动化部署流程,减少人工操作错误
健康检查Actuator端点监控应用状态,确保服务可用

本课程持续更新中,欢迎关注!

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/qq_26950675/article/details/166601558

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--