Docker部署Java应用案例

wen java案例 1

本文目录导读:

Docker部署Java应用案例

  1. 基础部署案例
  2. 高级部署案例
  3. 容器化最佳实践
  4. 部署命令和操作
  5. 监控和日志采集

我来提供一个完整的Docker部署Java应用的实例,从基础到进阶。

基础部署案例

简单的Spring Boot应用部署

项目结构:

my-java-app/
├── src/
│   ├── main/
│   │   ├── java/
│   │   └── resources/
│   └── test/
├── Dockerfile
├── docker-compose.yml
└── pom.xml (或 build.gradle)

使用Maven构建

# 多阶段构建
FROM maven:3.8.3-openjdk-11 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn clean package -DskipTests
# 运行阶段
FROM openjdk:11-jre-slim
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
ENV JAVA_OPTS="-Xms256m -Xmx512m"
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

使用Gradle构建

FROM gradle:7.2-jdk11 AS builder
WORKDIR /app
COPY build.gradle .
COPY gradlew .
COPY gradle ./gradle
RUN ./gradlew dependencies
COPY src ./src
RUN ./gradlew bootJar
FROM openjdk:11-jre-slim
WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Docker Compose配置

version: '3.8'
services:
  app:
    build: .
    container_name: java-app
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=dev
      - DB_HOST=mysql
      - DB_PORT=3306
      - DB_NAME=myapp
      - DB_USER=root
      - DB_PASSWORD=secret
    depends_on:
      - mysql
    networks:
      - app-network
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
  mysql:
    image: mysql:8.0
    container_name: mysql-db
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - MYSQL_DATABASE=myapp
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
    networks:
      - app-network
volumes:
  mysql_data:
networks:
  app-network:
    driver: bridge

高级部署案例

使用Dockerfile优化技巧

# 基础镜像选择
FROM eclipse-temurin:17-jre-alpine
# 添加非root用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
# 复制jar包并优化
COPY target/*.jar app.jar
# 设置时区
RUN apk add --no-cache tzdata \
    && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
    && echo "Asia/Shanghai" > /etc/timezone
# 切换到非root用户
USER appuser
# 暴露端口
EXPOSE 8080
# 健康检查
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \
  CMD wget -q --spider http://localhost:8080/actuator/health || exit 1
# JVM参数优化
ENV JAVA_OPTS="-Xms512m -Xmx1g -XX:MaxMetaspaceSize=256m -XX:+UseG1GC -XX:MaxGCPauseMillis=100"
# 启动命令
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

完整的生产环境部署

docker-compose.prod.yml:

version: '3.8'
services:
  app:
    image: registry.example.com/myapp:latest
    container_name: myapp-prod
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - CONFIG_SERVER_URL=http://config-server:8888
      - EUREKA_SERVER_URL=http://eureka:8761/eureka
    volumes:
      - app-logs:/app/logs
      - app-config:/app/config
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
        max_attempts: 3
        window: 120s
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
    networks:
      - microservice-network
    secrets:
      - db_password
  nginx:
    image: nginx:latest
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - app
    networks:
      - microservice-network
secrets:
  db_password:
    external: true
networks:
  microservice-network:
    driver: overlay
volumes:
  app-logs:
  app-config:

使用外部配置和JMX监控

version: '3.8'
services:
  app:
    build: .
    ports:
      - "8080:8080"
      - "1099:1099"  # JMX端口
    environment:
      - JAVA_OPTS=-Xms512m -Xmx1g \
        -Dcom.sun.management.jmxremote \
        -Dcom.sun.management.jmxremote.port=1099 \
        -Dcom.sun.management.jmxremote.authenticate=false \
        -Dcom.sun.management.jmxremote.ssl=false \
        -Djava.rmi.server.hostname=localhost
    volumes:
      - ./config:/app/config:ro  # 外部配置文件
      - ./logs:/app/logs         # 日志持久化
      - ./uploads:/app/uploads   # 文件上传目录
    command: >
      sh -c "java $JAVA_OPTS 
      -Dspring.config.location=file:/app/config/application.yml 
      -jar app.jar"

容器化最佳实践

多模块Maven项目部署

# 多阶段构建示例
FROM maven:3.8.3-openjdk-11 AS build
WORKDIR /workspace
# 复制pom文件
COPY pom.xml .
COPY common/pom.xml common/
COPY api/pom.xml api/
COPY service/pom.xml service/
# 先下载依赖
RUN mvn dependency:go-offline -B
# 复制源码
COPY common/ common/
COPY api/ api/
COPY service/ service/
# 只构建service模块
RUN mvn clean install -pl service -am -DskipTests
FROM openjdk:11-jre-slim
WORKDIR /app
COPY --from=build /workspace/service/target/*.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]

配置外部化

# 使用环境变量配置
spring:
  datasource:
    url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:myapp}
    username: ${DB_USER:root}
    password: ${DB_PASSWORD:secret}
# docker-compose覆盖配置
version: '3.8'
services:
  app:
    environment:
      - DB_HOST=production-db.com
      - DB_PORT=3306
      - DB_NAME=production_db
      - DB_USER=prod_user
      - DB_PASSWORD=${DB_PASSWORD}  # 从.env文件读取

.dockerignore文件

target/
*.class
*.jar
!.mvn/wrapper/maven-wrapper.jar
*.log
.DS_Store
.idea/
.vscode/
.git/
.gitignore
*.iml
HELP.md

部署命令和操作

基础命令

# 构建镜像
docker build -t myapp:latest .
# 运行容器
docker run -d -p 8080:8080 --name myapp myapp:latest
# 查看日志
docker logs -f myapp
# 进入容器
docker exec -it myapp /bin/bash
# 停止和删除
docker stop myapp && docker rm myapp

生产环境部署命令

# 使用Docker Compose部署
docker-compose up -d
docker-compose down
# 使用Docker Stack部署到Swarm
docker stack deploy -c docker-compose.prod.yml myapp
# 更新容器
docker-compose up -d --build --force-recreate
# 查看健康状态
docker ps --filter "health=health" --format "table {{.Names}}\t{{.Status}}"

自动化部署脚本

#!/bin/bash
# deploy.sh
set -e
echo "Building application..."
./mvnw clean package -DskipTests
echo "Building Docker image..."
docker build -t myapp:${VERSION} .
echo "Stopping old container..."
docker stop myapp || true
docker rm myapp || true
echo "Deploying new container..."
docker run -d \
  --name myapp \
  --network mynetwork \
  -p 8080:8080 \
  --env-file .env.prod \
  --restart unless-stopped \
  myapp:${VERSION}
echo "Deployment completed successfully!"
sleep 10
curl -f http://localhost:8080/actuator/health || exit 1

监控和日志采集

# 集成Prometheus和Elastic Stack
version: '3.8'
services:
  app:
    build: .
    expose:
      - "8080"
    environment:
      - MANAGEMENT_ENDPOINTS_WEB_EXPOSURE=*  
    labels:
      prometheus.io/scrape: "true"
      prometheus.io/path: "/actuator/prometheus"
      prometheus.io/port: "8080"
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
  filebeat:
    image: docker.elastic.co/beats/filebeat:7.13.0
    volumes:
      - app-logs:/var/log/app
      - ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.13.0
    environment:
      - discovery.type=single-node
    ports:
      - "9200:9200"
  kibana:
    image: docker.elastic.co/kibana/kibana:7.13.0
    ports:
      - "5601:5601"
    depends_on:
      - elasticsearch

这个完整的部署案例涵盖了从开发到生产的各种场景,你可以根据实际需求选择合适的方式,记住要根据你的应用特点调整JVM参数、资源限制和部署策略。

抱歉,评论功能暂时关闭!