Cloud Native 13 min read

Spring Cloud Microservices Hands‑On (Revised Part 10): Full Deployment with Docker Compose

After building the order, stock, and point services along with Nacos, Seata, Sentinel, Prometheus, Grafana, and SkyWalking, this guide shows how to package all components into Docker images and use a single Docker Compose command to start the entire microservice system with one click, while addressing common pitfalls.

Coder Trainee
Coder Trainee
Coder Trainee
Spring Cloud Microservices Hands‑On (Revised Part 10): Full Deployment with Docker Compose

In the previous nine installments we assembled a complete Spring Cloud microservice system consisting of order-service, stock-service, point-service, Nacos (service registry & config), Seata (distributed transaction), Sentinel (fault tolerance), Prometheus + Grafana (monitoring), and SkyWalking (tracing). Starting each component required multiple manual commands, which was cumbersome.

Goal : Use Docker Compose to package all services and achieve one‑click startup, automatic dependency handling, data persistence, and environment isolation.

Project Structure

spring-cloud-teaching-ep10/
├── docker-compose.yml          # main compose file
├── .env                       # environment variables
├── mysql/
│   └── init.sql              # DB init script
├── prometheus/
│   └── prometheus.yml       # Prometheus config
├── skywalking/
│   └── oap/
│       └── alarm-settings.yml # alarm config
├── services/
│   ├── order-service/
│   │   ├── Dockerfile
│   │   └── target/order-service.jar
│   ├── stock-service/
│   │   ├── Dockerfile
│   │   └── target/stock-service.jar
│   └── point-service/
│       ├── Dockerfile
│       └── target/point-service.jar
└── scripts/
    ├── build.sh               # build all services
    ├── start.sh               # start all services
    └── stop.sh                # stop all services

Service Dockerfiles

1. order-service/Dockerfile

FROM openjdk:17-jdk-slim

WORKDIR /app

# Install necessary tools
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

# Create non‑root user
RUN groupadd -r teaching && useradd -r -g teaching teaching

# Copy JAR
COPY target/order-service-*.jar app.jar

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
  CMD curl -f http://localhost:8081/actuator/health || exit 1

USER teaching

EXPOSE 8081

ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC"

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

2. stock-service/Dockerfile

FROM openjdk:17-jdk-slim

WORKDIR /app

RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
RUN groupadd -r teaching && useradd -r -g teaching teaching

COPY target/stock-service-*.jar app.jar

HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
  CMD curl -f http://localhost:8082/actuator/health || exit 1

USER teaching

EXPOSE 8082

ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC"

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

3. point-service/Dockerfile

FROM openjdk:17-jdk-slim

WORKDIR /app

RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
RUN groupadd -r teaching && useradd -r -g teaching teaching

COPY target/point-service-*.jar app.jar

HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
  CMD curl -f http://localhost:8083/actuator/health || exit 1

USER teaching

EXPOSE 8083

ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC"

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

docker-compose.yml (Main Compose File)

version: '3.8'

networks:
  teaching-network:
    driver: bridge

volumes:
  mysql-data:
  prometheus-data:
  grafana-data:
  skywalking-data:

services:
  # ==================== Infrastructure ====================
  mysql:
    image: mysql:8.0
    container_name: teaching-mysql
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root123}
      MYSQL_DATABASE: order_db
      TZ: Asia/Shanghai
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
      - ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql
    networks:
      - teaching-network
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    container_name: teaching-redis
    restart: unless-stopped
    command: redis-server --appendonly yes
    ports:
      - "6379:6379"
    networks:
      - teaching-network

  # ==================== Service Governance ====================
  nacos:
    image: nacos/nacos-server:v2.3.2
    container_name: teaching-nacos
    restart: unless-stopped
    environment:
      MODE: standalone
      PREFER_HOST_MODE: hostname
    ports:
      - "8848:8848"
      - "9848:9848"
    networks:
      - teaching-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8848/nacos/actuator/health"]
      interval: 30s
      timeout: 10s
      retries: 5

  sentinel:
    image: bladex/sentinel-dashboard:1.8.6
    container_name: teaching-sentinel
    restart: unless-stopped
    ports:
      - "8858:8858"
    environment:
      AUTH_USERNAME: ${SENTINEL_USERNAME:-sentinel}
      AUTH_PASSWORD: ${SENTINEL_PASSWORD:-sentinel}
    networks:
      - teaching-network

  seata-server:
    image: seataio/seata-server:1.7.0
    container_name: teaching-seata
    restart: unless-stopped
    ports:
      - "8091:8091"
      - "7091:7091"
    environment:
      SEATA_IP: seata-server
      SEATA_PORT: 8091
      STORE_MODE: file
    networks:
      - teaching-network

  # ==================== Monitoring ====================
  prometheus:
    image: prom/prometheus:latest
    container_name: teaching-prometheus
    restart: unless-stopped
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus-data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
    networks:
      - teaching-network
    depends_on:
      - order-service
      - stock-service
      - point-service

  grafana:
    image: grafana/grafana:latest
    container_name: teaching-grafana
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD:-admin}
    volumes:
      - grafana-data:/var/lib/grafana
    networks:
      - teaching-network
    depends_on:
      - prometheus

  # ==================== Tracing ====================
  elasticsearch:
    image: elasticsearch:7.17.0
    container_name: teaching-elasticsearch
    restart: unless-stopped
    environment:
      discovery.type: single-node
      ES_JAVA_OPTS: -Xms512m -Xmx512m
    ports:
      - "9200:9200"
    volumes:
      - skywalking-data:/usr/share/elasticsearch/data
    networks:
      - teaching-network

  skywalking-oap:
    image: apache/skywalking-oap-server:9.7.0
    container_name: teaching-skywalking-oap
    restart: unless-stopped
    depends_on:
      - elasticsearch
    environment:
      SW_STORAGE: elasticsearch
      SW_STORAGE_ES_CLUSTER_NODES: elasticsearch:9200
    ports:
      - "11800:11800"
      - "12800:12800"
    networks:
      - teaching-network

  skywalking-ui:
    image: apache/skywalking-ui:9.7.0
    container_name: teaching-skywalking-ui
    restart: unless-stopped
    depends_on:
      - skywalking-oap
    environment:
      SW_OAP_ADDRESS: http://skywalking-oap:12800
    ports:
      - "8088:8080"
    networks:
      - teaching-network

  # ==================== Business Services ====================
  order-service:
    build:
      context: ./services/order-service
      dockerfile: Dockerfile
    container_name: teaching-order-service
    restart: unless-stopped
    environment:
      SPRING_PROFILES_ACTIVE: docker
      NACOS_HOST: nacos
      DB_HOST: mysql
      SEATA_HOST: seata-server
    ports:
      - "8081:8081"
    depends_on:
      mysql:
        condition: service_healthy
      nacos:
        condition: service_healthy
    networks:
      - teaching-network

  stock-service:
    build:
      context: ./services/stock-service
      dockerfile: Dockerfile
    container_name: teaching-stock-service
    restart: unless-stopped
    environment:
      SPRING_PROFILES_ACTIVE: docker
      NACOS_HOST: nacos
      DB_HOST: mysql
      SEATA_HOST: seata-server
    ports:
      - "8082:8082"
    depends_on:
      - mysql
      - nacos
    networks:
      - teaching-network

  point-service:
    build:
      context: ./services/point-service
      dockerfile: Dockerfile
    container_name: teaching-point-service
    restart: unless-stopped
    environment:
      SPRING_PROFILES_ACTIVE: docker
      NACOS_HOST: nacos
      DB_HOST: mysql
      SEATA_HOST: seata-server
    ports:
      - "8083:8083"
    depends_on:
      - mysql
      - nacos
    networks:
      - teaching-network

Environment Variables (.env)

# .env
MYSQL_ROOT_PASSWORD=root123
SENTINEL_USERNAME=sentinel
SENTINEL_PASSWORD=sentinel
GRAFANA_PASSWORD=admin

Prometheus Configuration (Docker Environment)

# prometheus/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'order-service'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['order-service:8081']
        labels:
          service: 'order-service'

  - job_name: 'stock-service'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['stock-service:8082']
        labels:
          service: 'stock-service'

  - job_name: 'point-service'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['point-service:8083']
        labels:
          service: 'point-service'

Verification Steps

Check container status: docker-compose ps View logs: docker-compose logs -f order-service and docker-compose logs -f nacos Test order creation:

curl -X POST http://localhost:8081/api/order/create \
  -H "Content-Type: application/json" \
  -d '{"userId":1,"productId":100,"quantity":2,"amount":198}'

Common Issues & Pitfalls

Issue 1: Service startup order

Solution: Use depends_on together with healthcheck to ensure dependent services start first.

Issue 2: Accessing services inside containers

Solution: Use container names (e.g., mysql, nacos) instead of localhost for inter‑service communication.

Issue 3: Port conflicts

Solution: Modify the port mappings in the .env file.

Issue 4: Data loss

Solution: Persist data with Docker volumes.

Next Episode Preview

Spring Cloud Microservices Hands‑On (Revised Part 11): Production deployment with Kubernetes, covering cluster setup, Deployment configuration, Service exposure, Ingress routing, ConfigMap usage, and HPA auto‑scaling.

I'm Lao J, see you next time.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DockerMicroservicesContainerizationSpring BootSpring CloudDocker Compose
Coder Trainee
Written by

Coder Trainee

Experienced in Java and Python, we share and learn together. For submissions or collaborations, DM us.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.