Skip to content

Spring Boot 部署

1. 部署概述

1.1 部署方式

方式说明适用场景
JAR 包部署内嵌服务器传统部署、云服务器
WAR 包部署外部服务器传统应用服务器
Docker 容器容器化部署云原生、Kubernetes
原生镜像GraalVM 编译快速启动、低内存

1.2 构建工具

bash
# Maven
mvn clean package

# Gradle
gradle clean build

2. JAR 包部署

2.1 构建配置

xml
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <excludes>
                    <exclude>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                    </exclude>
                </excludes>
            </configuration>
        </plugin>
    </plugins>
</build>

2.2 运行 JAR

bash
# 基本运行
java -jar myapp.jar

# 指定配置
java -jar myapp.jar --spring.profiles.active=prod

# 指定端口
java -jar myapp.jar --server.port=8081

# 后台运行
nohup java -jar myapp.jar > app.log 2>&1 &

# 指定 JVM 参数
java -Xms512m -Xmx2048m -jar myapp.jar

# 指定环境变量
java -Dspring.datasource.url=jdbc:mysql://prod-db:3306/mydb -jar myapp.jar

2.3 Systemd 服务

ini
# /etc/systemd/system/myapp.service
[Unit]
Description=My Spring Boot Application
After=syslog.target network.target

[Service]
User=myapp
ExecStart=/usr/bin/java -Xms512m -Xmx2048m -jar /opt/myapp/myapp.jar
SuccessExitStatus=143
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
bash
# 启用服务
sudo systemctl enable myapp

# 启动服务
sudo systemctl start myapp

# 查看状态
sudo systemctl status myapp

# 查看日志
sudo journalctl -u myapp -f

3. Docker 部署

3.1 Dockerfile

dockerfile
# 多阶段构建
FROM eclipse-temurin:21-jdk-alpine AS builder

WORKDIR /app
COPY pom.xml .
COPY src ./src

RUN apk add --no-cache maven && \
    mvn clean package -DskipTests

FROM eclipse-temurin:21-jre-alpine

WORKDIR /app

RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring

COPY --from=builder /app/target/*.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]

3.2 优化 Dockerfile

dockerfile
FROM eclipse-temurin:21-jre-alpine

WORKDIR /app

RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring

COPY target/*.jar app.jar

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
    CMD wget -q --spider http://localhost:8080/actuator/health || exit 1

ENTRYPOINT ["java", \
    "-Xms256m", \
    "-Xmx512m", \
    "-XX:+UseContainerSupport", \
    "-XX:MaxRAMPercentage=75.0", \
    "-jar", "app.jar"]

3.3 Docker Compose

yaml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/mydb
      - SPRING_DATASOURCE_USERNAME=root
      - SPRING_DATASOURCE_PASSWORD=password
      - SPRING_REDIS_HOST=redis
    depends_on:
      - db
      - redis
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 60s
    restart: always
  
  db:
    image: mysql:8.0
    environment:
      - MYSQL_ROOT_PASSWORD=password
      - MYSQL_DATABASE=mydb
    volumes:
      - mysql_data:/var/lib/mysql
    ports:
      - "3306:3306"
  
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

volumes:
  mysql_data:
  redis_data:
bash
# 启动服务
docker-compose up -d

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

# 停止服务
docker-compose down

# 重新构建
docker-compose up -d --build

4. Kubernetes 部署

4.1 Deployment

yaml
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:
    app: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: myapp:latest
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "prod"
        - name: SPRING_DATASOURCE_URL
          valueFrom:
            secretKeyRef:
              name: myapp-secret
              key: database-url
        - name: SPRING_DATASOURCE_USERNAME
          valueFrom:
            secretKeyRef:
              name: myapp-secret
              key: database-username
        - name: SPRING_DATASOURCE_PASSWORD
          valueFrom:
            secretKeyRef:
              name: myapp-secret
              key: database-password
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
        volumeMounts:
        - name: config
          mountPath: /config
          readOnly: true
      volumes:
      - name: config
        configMap:
          name: myapp-config

4.2 Service

yaml
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: myapp
spec:
  selector:
    app: myapp
  ports:
  - port: 80
    targetPort: 8080
  type: ClusterIP

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: myapp
            port:
              number: 80

4.3 ConfigMap 和 Secret

yaml
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: myapp-config
data:
  application.yml: |
    spring:
      datasource:
        url: jdbc:mysql://mysql:3306/mydb
      redis:
        host: redis

---
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: myapp-secret
type: Opaque
stringData:
  database-url: jdbc:mysql://mysql:3306/mydb
  database-username: root
  database-password: password

4.4 HPA 自动扩缩容

yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: myapp-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

5. GraalVM 原生镜像

5.1 添加依赖

xml
<dependency>
    <groupId>org.graalvm.buildtools</groupId>
    <artifactId>jvm-metadata-repository</artifactId>
    <version>0.3.8</version>
</dependency>

5.2 构建配置

xml
<build>
    <plugins>
        <plugin>
            <groupId>org.graalvm.buildtools</groupId>
            <artifactId>native-maven-plugin</artifactId>
            <configuration>
                <buildArgs>
                    <buildArg>--no-fallback</buildArg>
                    <buildArg>-H:+ReportExceptionStackTraces</buildArg>
                </buildArgs>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>
                <execution>
                    <id>process-aot</id>
                    <goals>
                        <goal>process-aot</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

5.3 构建原生镜像

bash
# 使用 GraalVM 构建
mvn -Pnative native:compile

# 使用 Spring Boot 构建
mvn -Pnative spring-boot:build-image

5.4 运行原生镜像

bash
# 直接运行
./target/myapp

# 启动时间通常在 0.1 秒以内
# 内存占用通常在 50MB 以内

6. 配置管理

6.1 外部配置

bash
# 配置文件位置
java -jar myapp.jar --spring.config.location=file:/etc/myapp/

# 额外配置文件
java -jar myapp.jar --spring.config.additional-location=file:/etc/myapp/

# 配置中心
java -jar myapp.jar --spring.config.import=optional:configserver:http://config-server:8888

6.2 环境变量配置

yaml
# application.yml
spring:
  datasource:
    url: ${DB_URL:jdbc:mysql://localhost:3306/mydb}
    username: ${DB_USERNAME:root}
    password: ${DB_PASSWORD:password}
  redis:
    host: ${REDIS_HOST:localhost}
    port: ${REDIS_PORT:6379}
bash
export DB_URL=jdbc:mysql://prod-db:3306/mydb
export DB_USERNAME=prod_user
export DB_PASSWORD=prod_password
java -jar myapp.jar

7. 日志管理

7.1 日志配置

yaml
logging:
  file:
    name: /var/log/myapp/application.log
    max-size: 100MB
    max-history: 30
  pattern:
    file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"

7.2 日志收集

yaml
# logback-spring.xml
<configuration>
    <appender name="JSON" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>/var/log/myapp/application.json</file>
        <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>/var/log/myapp/application.%d{yyyy-MM-dd}.json</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
    </appender>
    
    <root level="INFO">
        <appender-ref ref="JSON"/>
    </root>
</configuration>

8. 性能调优

8.1 JVM 参数

bash
java -Xms512m \
     -Xmx2048m \
     -XX:+UseG1GC \
     -XX:MaxGCPauseMillis=200 \
     -XX:+HeapDumpOnOutOfMemoryError \
     -XX:HeapDumpPath=/var/log/myapp/heapdump.hprof \
     -XX:+PrintGCDetails \
     -XX:+PrintGCDateStamps \
     -Xloggc:/var/log/myapp/gc.log \
     -jar myapp.jar

8.2 容器资源限制

yaml
# Kubernetes
resources:
  requests:
    memory: "512Mi"
    cpu: "250m"
  limits:
    memory: "1Gi"
    cpu: "500m"
bash
# Docker
docker run -m 1g --cpus=0.5 myapp:latest

9. 小结

本章学习了 Spring Boot 部署的核心内容:

内容要点
JAR 部署打包、运行、Systemd 服务
DockerDockerfile、Docker Compose
KubernetesDeployment、Service、HPA
原生镜像GraalVM、快速启动
配置管理外部配置、环境变量
日志管理日志配置、日志收集
性能调优JVM 参数、资源限制

下一章将学习微服务架构概述。