java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Docker与K8s部署Java应用

Docker与Kubernetes部署Java应用容器化实践指南

作者:程序员鸭梨

容器化是一种将应用程序及其依赖打包成一个可移植的单元的方法,容器能够在任何环境中运行,并且具有隔离性、可扩展性和高效性,这篇文章主要介绍了Docker与Kubernetes部署Java应用容器化实践指南的相关资料

今天我们来聊聊 Docker 与 Kubernetes 部署 Java 应用的最佳实践,这是容器化实践的重要技术。

一、容器化概述

容器化是一种将应用及其依赖打包为容器的技术,它提供了环境一致性、快速部署和资源隔离等优势。Docker 是目前最流行的容器化平台,而 Kubernetes 则是最流行的容器编排平台。

核心优势

二、Docker 容器化实践

1. Dockerfile 编写

# 基础镜像
FROM eclipse-temurin:25-jdk-alpine

# 设置工作目录
WORKDIR /app

# 复制依赖文件
COPY pom.xml ./

# 下载依赖
RUN mvn dependency:go-offline

# 复制源代码
COPY src ./src

# 构建应用
RUN mvn package -DskipTests

# 暴露端口
EXPOSE 8080

# 运行应用
CMD ["java", "-jar", "target/app.jar"]

2. 多阶段构建

# 构建阶段
FROM eclipse-temurin:25-jdk-alpine AS build
WORKDIR /app
COPY pom.xml ./
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests

# 运行阶段
FROM eclipse-temurin:25-jre-alpine
WORKDIR /app
COPY --from=build /app/target/app.jar ./
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]

3. 优化 Dockerfile

# 使用最小基础镜像
FROM eclipse-temurin:25-jre-alpine

# 设置时区
ENV TZ=Asia/Shanghai
RUN apk add --no-cache tzdata && ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# 创建非 root 用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

# 设置工作目录
WORKDIR /app

# 复制应用
COPY target/app.jar ./

# 暴露端口
EXPOSE 8080

# 运行应用
CMD ["java", "-jar", "app.jar"]

4. 构建和运行

# 构建镜像
docker build -t my-java-app:latest .

# 运行容器
docker run -d -p 8080:8080 --name my-app my-java-app:latest

# 查看容器状态
docker ps

# 查看容器日志
docker logs my-app

# 进入容器
docker exec -it my-app /bin/sh

5. Docker Compose

# docker-compose.yml
version: '3.8'
services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - DB_HOST=db
      - DB_PORT=5432
      - DB_NAME=mydb
      - DB_USER=user
      - DB_PASSWORD=password
    depends_on:
      - db
  db:
    image: postgres:13
    environment:
      - POSTGRES_DB=mydb
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres-data:/var/lib/postgresql/data

volumes:
  postgres-data:
# 启动服务
docker-compose up -d

# 停止服务
docker-compose down

# 查看服务状态
docker-compose ps

三、Kubernetes 部署实践

1. 部署配置

# deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-java-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-java-app
  template:
    metadata:
      labels:
        app: my-java-app
    spec:
      containers:
      - name: my-java-app
        image: my-java-app:latest
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "prod"
        - name: DB_HOST
          value: "db-service"
        - name: DB_PORT
          value: "5432"
        - name: DB_NAME
          value: "mydb"
        - name: DB_USER
          value: "user"
        - name: DB_PASSWORD
          value: "password"
        resources:
          limits:
            cpu: "1"
            memory: "1Gi"
          requests:
            cpu: "500m"
            memory: "512Mi"

2. 服务配置

# service.yml
apiVersion: v1
kind: Service
metadata:
  name: my-java-app-service
spec:
  selector:
    app: my-java-app
  ports:
  - port: 8080
    targetPort: 8080
  type: LoadBalancer

3. 配置管理

# configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-java-app-config
data:
  application.yml: |
    spring:
      profiles:
        active: prod
      datasource:
        url: jdbc:postgresql://db-service:5432/mydb
        username: user
        password: password
      jpa:
        hibernate:
          ddl-auto: update
        properties:
          hibernate:
            format_sql: true
# deployment.yml (with configmap)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-java-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-java-app
  template:
    metadata:
      labels:
        app: my-java-app
    spec:
      containers:
      - name: my-java-app
        image: my-java-app:latest
        ports:
        - containerPort: 8080
        volumeMounts:
        - name: config-volume
          mountPath: /app/config
        resources:
          limits:
            cpu: "1"
            memory: "1Gi"
          requests:
            cpu: "500m"
            memory: "512Mi"
      volumes:
      - name: config-volume
        configMap:
          name: my-java-app-config

4. 密钥管理

# secret.yml
apiVersion: v1
kind: Secret
metadata:
  name: my-java-app-secret
type: Opaque
data:
  db-password: dXNlci1wYXNzd29yZA==  # base64 encoded
  jwt-secret: c29tZS1qd3Qtc2VjcmV0
# deployment.yml (with secret)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-java-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-java-app
  template:
    metadata:
      labels:
        app: my-java-app
    spec:
      containers:
      - name: my-java-app
        image: my-java-app:latest
        ports:
        - containerPort: 8080
        env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: my-java-app-secret
              key: db-password
        - name: JWT_SECRET
          valueFrom:
            secretKeyRef:
              name: my-java-app-secret
              key: jwt-secret
        resources:
          limits:
            cpu: "1"
            memory: "1Gi"
          requests:
            cpu: "500m"
            memory: "512Mi"

5. 持久化存储

# persistentvolumeclaim.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-java-app-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: standard
# deployment.yml (with pvc)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-java-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-java-app
  template:
    metadata:
      labels:
        app: my-java-app
    spec:
      containers:
      - name: my-java-app
        image: my-java-app:latest
        ports:
        - containerPort: 8080
        volumeMounts:
        - name: data-volume
          mountPath: /app/data
        resources:
          limits:
            cpu: "1"
            memory: "1Gi"
          requests:
            cpu: "500m"
            memory: "512Mi"
      volumes:
      - name: data-volume
        persistentVolumeClaim:
          claimName: my-java-app-pvc

6. 水平自动伸缩

# horizontalpodautoscaler.yml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-java-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-java-app
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

7. 健康检查

# deployment.yml (with health checks)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-java-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-java-app
  template:
    metadata:
      labels:
        app: my-java-app
    spec:
      containers:
      - name: my-java-app
        image: my-java-app:latest
        ports:
        - containerPort: 8080
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 30
        resources:
          limits:
            cpu: "1"
            memory: "1Gi"
          requests:
            cpu: "500m"
            memory: "512Mi"

四、CI/CD 集成

1. Jenkins 流水线

pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                sh 'mvn clean package -DskipTests'
            }
        }
        stage('Test') {
            steps {
                sh 'mvn test'
            }
        }
        stage('Build Docker Image') {
            steps {
                sh 'docker build -t my-java-app:${BUILD_NUMBER} .'
                sh 'docker tag my-java-app:${BUILD_NUMBER} my-java-app:latest'
            }
        }
        stage('Push to Registry') {
            steps {
                sh 'docker push my-java-app:${BUILD_NUMBER}'
                sh 'docker push my-java-app:latest'
            }
        }
        stage('Deploy to Kubernetes') {
            steps {
                sh 'kubectl apply -f k8s/deployment.yml'
                sh 'kubectl apply -f k8s/service.yml'
                sh 'kubectl rollout status deployment/my-java-app'
            }
        }
    }
}

2. GitHub Actions

name: CI/CD Pipeline

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up JDK 25
      uses: actions/setup-java@v2
      with:
        java-version: '25'
        distribution: 'adopt'
    - name: Build with Maven
      run: mvn clean package -DskipTests
    - name: Run tests
      run: mvn test
    - name: Build Docker image
      run: docker build -t my-java-app:${{ github.sha }} .
    - name: Push to Docker Hub
      run: |
        docker tag my-java-app:${{ github.sha }} my-java-app:latest
        docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
        docker push my-java-app:${{ github.sha }}
        docker push my-java-app:latest
    - name: Deploy to Kubernetes
      run: |
        kubectl config use-context my-cluster
        kubectl apply -f k8s/deployment.yml
        kubectl apply -f k8s/service.yml
        kubectl rollout status deployment/my-java-app

五、监控与日志

1. 监控

# prometheus.yml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: my-java-app-monitor
  labels:
    release: prometheus
spec:
  selector:
    matchLabels:
      app: my-java-app
  endpoints:
  - port: 8080
    path: /actuator/prometheus
    interval: 15s

2. 日志

# deployment.yml (with logging)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-java-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-java-app
  template:
    metadata:
      labels:
        app: my-java-app
    spec:
      containers:
      - name: my-java-app
        image: my-java-app:latest
        ports:
        - containerPort: 8080
        env:
        - name: LOGGING_LEVEL_ROOT
          value: "info"
        - name: LOGGING_LEVEL_COM_EXAMPLE
          value: "debug"
        resources:
          limits:
            cpu: "1"
            memory: "1Gi"
          requests:
            cpu: "500m"
            memory: "512Mi"

六、实践案例:Java 微服务部署

场景描述

部署一个包含用户服务、订单服务、产品服务的 Java 微服务架构到 Kubernetes。

实现方案

1. 服务配置

用户服务

# user-service-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
    spec:
      containers:
      - name: user-service
        image: user-service:latest
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "prod"
        - name: EUREKA_CLIENT_SERVICEURL_DEFAULTZONE
          value: "http://eureka-service:8761/eureka/"
        resources:
          limits:
            cpu: "1"
            memory: "1Gi"
          requests:
            cpu: "500m"
            memory: "512Mi"
---
apiVersion: v1
kind: Service
metadata:
  name: user-service
spec:
  selector:
    app: user-service
  ports:
  - port: 8080
    targetPort: 8080
  type: ClusterIP

订单服务

# order-service-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
    spec:
      containers:
      - name: order-service
        image: order-service:latest
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "prod"
        - name: EUREKA_CLIENT_SERVICEURL_DEFAULTZONE
          value: "http://eureka-service:8761/eureka/"
        resources:
          limits:
            cpu: "1"
            memory: "1Gi"
          requests:
            cpu: "500m"
            memory: "512Mi"
---
apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  selector:
    app: order-service
  ports:
  - port: 8080
    targetPort: 8080
  type: ClusterIP

产品服务

# product-service-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: product-service
  template:
    metadata:
      labels:
        app: product-service
    spec:
      containers:
      - name: product-service
        image: product-service:latest
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "prod"
        - name: EUREKA_CLIENT_SERVICEURL_DEFAULTZONE
          value: "http://eureka-service:8761/eureka/"
        resources:
          limits:
            cpu: "1"
            memory: "1Gi"
          requests:
            cpu: "500m"
            memory: "512Mi"
---
apiVersion: v1
kind: Service
metadata:
  name: product-service
spec:
  selector:
    app: product-service
  ports:
  - port: 8080
    targetPort: 8080
  type: ClusterIP

Eureka 服务

# eureka-service-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: eureka-service
spec:
  replicas: 1
  selector:
    matchLabels:
      app: eureka-service
  template:
    metadata:
      labels:
        app: eureka-service
    spec:
      containers:
      - name: eureka-service
        image: eureka-service:latest
        ports:
        - containerPort: 8761
        resources:
          limits:
            cpu: "1"
            memory: "1Gi"
          requests:
            cpu: "500m"
            memory: "512Mi"
---
apiVersion: v1
kind: Service
metadata:
  name: eureka-service
spec:
  selector:
    app: eureka-service
  ports:
  - port: 8761
    targetPort: 8761
  type: ClusterIP

API 网关

# gateway-service-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gateway-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: gateway-service
  template:
    metadata:
      labels:
        app: gateway-service
    spec:
      containers:
      - name: gateway-service
        image: gateway-service:latest
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "prod"
        - name: EUREKA_CLIENT_SERVICEURL_DEFAULTZONE
          value: "http://eureka-service:8761/eureka/"
        resources:
          limits:
            cpu: "1"
            memory: "1Gi"
          requests:
            cpu: "500m"
            memory: "512Mi"
---
apiVersion: v1
kind: Service
metadata:
  name: gateway-service
spec:
  selector:
    app: gateway-service
  ports:
  - port: 8080
    targetPort: 8080
  type: LoadBalancer

2. 部署步骤

  1. 构建镜像:为每个服务构建 Docker 镜像
  2. 推送镜像:将镜像推送到 Docker 仓库
  3. 部署服务:使用 kubectl 部署所有服务
  4. 验证部署:检查服务状态和日志
  5. 配置监控:设置 Prometheus 和 Grafana 监控

七、最佳实践总结

1. Docker 最佳实践

2. Kubernetes 最佳实践

3. CI/CD 最佳实践

4. 监控与日志

八、总结与建议

Docker 与 Kubernetes 部署 Java 应用是现代应用部署的重要方式。通过合理使用容器化技术,我们可以:

  1. 提高部署效率:快速部署和扩展应用
  2. 增强系统可靠性:通过健康检查和自动伸缩提高系统可用性
  3. 改善资源利用率:容器占用资源少,提高服务器利用率
  4. 简化环境管理:容器在不同环境中运行一致
  5. 提高开发效率:开发环境与生产环境一致,减少环境问题

这其实可以更优雅一点,通过合理使用 Docker 和 Kubernetes,我们可以构建出更现代化、更可靠的 Java 应用部署方案。

到此这篇关于Docker与Kubernetes部署Java应用容器化实践指南的文章就介绍到这了,更多相关Docker与K8s部署Java应用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文