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.
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 servicesService 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-networkEnvironment Variables (.env)
# .env
MYSQL_ROOT_PASSWORD=root123
SENTINEL_USERNAME=sentinel
SENTINEL_PASSWORD=sentinel
GRAFANA_PASSWORD=adminPrometheus 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Coder Trainee
Experienced in Java and Python, we share and learn together. For submissions or collaborations, DM us.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
