404 Not Found

404 Not Found


nginx

Docker Containerization Deployment

Docker is a revolution in deployment—build once, run anywhere, and say goodbye to the "it works on my machine" excuse.

1. What You'll Learn


2. A True Story of an Operations Engineer

(1) Pain Point: Inconsistent Environment

After Bob finished debugging OrderFlow locally and deployed it to production, the service failed to start due to mismatched MySQL driver versions, inconsistent JDK versions, and differing time zone configurations. Every deployment required half a day to troubleshoot environment discrepancies, and "It works on my machine" became the team's catchphrase. To make matters worse, the three servers required three separate manual deployments, and configurations were often overlooked.

(2) The Docker Solution

Docker packages the application and all its dependencies into a single image:

DOCKERFILE
FROM eclipse-temurin:17-jre-alpine
COPY target/orderflow-service.jar /app/app.jar
ENTRYPOINT ["java", "-jar", "/app/app.jar"]

Deploy with a single command, ensuring a completely consistent environment.

(3) Revenue

After Bob started using Docker, the image was built once locally, and all three servers pulled and ran it from the same source, eliminating environment inconsistencies. A single Docker Compose command starts the complete environment—OrderFlow, MySQL, and Redis—and new hires can get it running locally in just five minutes.


3. Multi-stage Dockerfile

(1) Multi-stage Build Process

100%
graph TD
    A["Stage 1: Builder<br/>Maven + JDK 17<br/>Compile + Package"] --> B["orderflow-service.jar"]
    B --> C["Stage 2: Runtime<br/>JRE 17 Alpine<br/>Copy JAR only"]
    C --> D["Final Image<br/>~150MB"]

(1) ▶ Example: Multi-stage Dockerfile

DOCKERFILE
# Stage 1: Build
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /build

# Copy pom.xml first for dependency caching
COPY pom.xml .
RUN mvn dependency:go-offline -B

# Copy source and build
COPY src ./src
RUN mvn package -DskipTests -B

# Stage 2: Runtime
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app

# Create non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# Copy JAR from builder
COPY --from=builder /build/target/*.jar app.jar

# Set ownership
RUN chown -R appuser:appgroup /app
USER appuser

# Expose port
EXPOSE 8080

# Health check
HEALTHCHECK --interval=30s --timeout=3s \
  CMD wget -qO- http://localhost:8080/actuator/health || exit 1

# Run application
ENTRYPOINT ["java", \
  "-XX:+UseG1GC", \
  "-XX:MaxRAMPercentage=75.0", \
  "-jar", "app.jar"]

Output:

TEXT
// Execution Successful
Base Image Size Security Use Cases
eclipse-temurin:17 ~450MB Chinese Development/Testing
eclipse-temurin:17-jre-alpine ~170MB Medium-High Recommended for Production
gcr.io/distroless/java17-debian12 ~100MB Maximum Ultimate Security

(2) ▶ Example: Distroless image

DOCKERFILE
# Stage 1: Build
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn package -DskipTests -B

# Stage 2: Distroless runtime
FROM gcr.io/distroless/java17-debian12
COPY --from=builder /build/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Output:

TEXT
// Execution Successful
💡 Tip: Distroless images do not have a shell (no sh/bash), so you cannot docker exec -it enter debug mode. They are recommended for production, but use the Alpine image for debugging.


4. .dockerignore and Image Optimization

(1) ▶ Example: .dockerignore

TEXT
# .dockerignore
.git
.github
.idea
*.md
target/
!target/*.jar
node_modules/
*.log
.env
docker-compose*.yml
Dockerfile*
Optimization Strategy Effect Description
.dockerignore Exclude unnecessary files Reduce the build context Do not send .git, .idea, etc.
First, COPY pom.xml Cache dependencies Skip downloading if dependencies haven't changed
Multi-stage build Reduce the final image size Does not include Maven or source code
JRE instead of JDK Saves ~200MB No compilation tools required at runtime
Alpine Base Image Reduced by ~280MB A minimal Linux system using musl libc

5. Docker Compose Multi-Service Orchestration

(1) ▶ Example: OrderFlow + MySQL + Redis

YAML
# docker-compose.yml
version: "3.9"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:8080"
    environment:
      SPRING_PROFILES_ACTIVE: prod
      DB_HOST: mysql
      DB_USERNAME: orderflow
      DB_PASSWORD: ${DB_PASSWORD:-orderflow123}
      REDIS_HOST: redis
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 3s
      retries: 5
    restart: unless-stopped

  mysql:
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment:
      MYSQL_DATABASE: orderflow
      MYSQL_USER: orderflow
      MYSQL_PASSWORD: ${DB_PASSWORD:-orderflow123}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root123}
    volumes:
      - mysql-data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 10

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 5

volumes:
  mysql-data:
  redis-data:

Output:

TEXT
CONTAINER ID   IMAGE          STATUS         PORTS
abc123         nginx:latest   Up 2 hours     0.0.0.0:80->80/tcp

(2) ▶ Example: Startup and Verification

BASH
# Build and start all services
docker compose up -d --build

# Check service status
docker compose ps

# View application logs
docker compose logs -f app

# Test API
curl http://localhost:8080/actuator/health

# Stop all services
docker compose down -v

Output:

TEXT
CONTAINER ID   IMAGE     STATUS    
abc123         latest    Up 2 hours
Command Description
docker compose up -d Start in the background
docker compose down Stop and delete container
docker compose logs -f app View App Logs
docker compose ps View Service Status
docker compose build Build image only

6. Environment Variable Injection

(1) ▶ Example: Externalizing Configuration

YAML
# application-prod.yml (in Docker image)
spring:
  datasource:
    url: jdbc:mysql://${DB_HOST:localhost}:3306/orderflow
    username: ${DB_USERNAME:orderflow}
    password: ${DB_PASSWORD:defaultpass}
  data:
    redis:
      host: ${REDIS_HOST:localhost}
      port: ${REDIS_PORT:6379}
Configuration Method Priority Applicable Scenarios
Docker Compose environment High Easy Deployment
.env File Chinese Local Development
K8s ConfigMap / Secret High Production
TEXT
# .env file
DB_PASSWORD=secure_password_here
DB_ROOT_PASSWORD=root_secure_here
REDIS_PASSWORD=redis_secure_here

7. Comprehensive Example: Full Dockerization of OrderFlow

DOCKERFILE
# Dockerfile
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn package -DskipTests -B

FROM eclipse-temurin:17-jre-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder /build/target/*.jar app.jar
RUN chown -R appuser:appgroup /app
USER appuser
EXPOSE 8080 8081
HEALTHCHECK --interval=30s --timeout=3s \
  CMD wget -qO- http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-XX:+UseG1GC", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]
YAML
# docker-compose.yml
version: "3.9"
services:
  app:
    build: .
    ports:
      - "8080:8080"
      - "8081:8081"
    environment:
      SPRING_PROFILES_ACTIVE: prod
      DB_HOST: mysql
      DB_USERNAME: orderflow
      DB_PASSWORD: ${DB_PASSWORD:-orderflow123}
      REDIS_HOST: redis
      MANAGEMENT_SERVER_PORT: "8081"
    depends_on:
      mysql: { condition: service_healthy }
      redis: { condition: service_healthy }
    restart: unless-stopped

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: orderflow
      MYSQL_USER: orderflow
      MYSQL_PASSWORD: ${DB_PASSWORD:-orderflow123}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root123}
    volumes: [mysql-data:/var/lib/mysql]
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s; timeout: 5s; retries: 10

  redis:
    image: redis:7-alpine
    volumes: [redis-data:/data]
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s; timeout: 3s; retries: 5

volumes:
  mysql-data:
  redis-data:

❓ FAQ

Q Why are Docker images so large?
A By default, the full JDK image (~450MB) is used. Optimization methods: 1) Use JRE instead of JDK (~170MB); 2) Use the Alpine base image; 3) Use Distroless (~100MB); 4) Use the native GraalVM image (~50MB).
Q How do I debug an application in a Docker container?
A 1) docker compose logs -f app View logs; 2) docker compose exec app sh Enter the container (Alpine image); 3) Remote debugging: ENTRYPOINT ["java", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005", "-jar", "app.jar"].
Q What is the relationship between depends_on and healthcheck?
A depends_on only ensures the order in which containers start; it does not guarantee that the service is ready. When used in conjunction with condition: service_healthy, Docker Compose will wait for the health check to pass before starting the dependent service.
Q How can I reduce image build time?
A 1) First, copy pom.xml and mvn dependency:go-offline to take advantage of Docker layer caching; 2) Use .dockerignore to exclude irrelevant files; 3) Use BuildKit cache mounting.
Q Can Docker Compose be used in production?
A Docker Compose is suitable for local development and simple deployments. For production environments, we recommend Docker Swarm or Kubernetes, which offer features such as auto-scaling, rolling updates, and self-healing.
Q What are some pitfalls of the Alpine image?
A 1) It uses musl libc instead of glibc, so some native libraries may be incompatible; 2) Time zone data requires an additional installation; 3) DNS resolution has a bug under high concurrency (ndots issue). Alpine is recommended for JRE-only scenarios.

📖 Summary


📝 Exercises

  1. Basic Exercise (Difficulty ⭐): Write a Dockerfile for OrderFlow, build the image, run it locally, and verify that /actuator/health returns "UP."

  2. Advanced Exercise (Difficulty: ⭐⭐): Write a docker-compose.yml file to orchestrate the three services—OrderFlow, MySQL, and Redis—and configure healthcheck and depends_on to ensure the services start in the correct order.

  3. Challenge (Difficulty: ⭐⭐⭐): Optimize the image to under 150 MB, use the Distroless base image, configure the JVM memory parameters (MaxRAMPercentage), and add a non-root user and security-related configurations.

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏