Cloud Native 12 min read

Containerizing Spring Cloud Microservices with Docker and Kubernetes (Part 9)

This article explains why traditional deployment is problematic, then walks through building Docker images, composing services with Docker‑Compose, deploying to a Kubernetes cluster, setting up CI/CD pipelines, and addressing common pitfalls such as slow starts and service discovery failures.

Coder Trainee
Coder Trainee
Coder Trainee
Containerizing Spring Cloud Microservices with Docker and Kubernetes (Part 9)

Why Containerize?

Traditional deployment on physical/virtual machines leads to environment inconsistency, dependency conflicts, resource waste, scaling difficulty, and complex releases. Containerization provides consistent environments, isolation, elastic scaling, and fast deployment.

Docker Image Build

Base Dockerfile

# user-service/Dockerfile
# Multi‑stage build
FROM maven:3.8-openjdk-11 AS builder

WORKDIR /app

# Cache dependencies
COPY pom.xml .
COPY common/pom.xml ./common/
COPY user-service/pom.xml ./user-service/

RUN mvn dependency:go-offline

# Copy source and build
COPY common ./common/
COPY user-service ./user-service/

RUN mvn clean package -pl user-service -am -DskipTests

# Runtime stage
FROM openjdk:11-jre-slim

WORKDIR /app

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

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

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

# Optional SkyWalking agent
COPY --from=builder /app/skywalking-agent /skywalking-agent

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

USER blog
EXPOSE 8081

ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC"
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -javaagent:/skywalking-agent/skywalking-agent.jar -jar app.jar"]

Build Commands

# Build image
docker build -t blog/user-service:1.0.0 -f user-service/Dockerfile .

# List image
docker images | grep blog

# Run container
docker run -d --name user-service -p 8081:8081 blog/user-service:1.0.0

# Push to registry
docker tag blog/user-service:1.0.0 your-registry/blog/user-service:1.0.0
docker push your-registry/blog/user-service:1.0.0

Docker‑Compose Orchestration

A complete docker-compose.yml defines networks, volumes, and services for MySQL, Redis, Nacos, user‑service, article‑service, and gateway, with environment variables such as SPRING_PROFILES_ACTIVE=docker and health checks.

One‑Click Startup

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

# View logs
docker-compose logs -f

# Scale user service to 3 instances
docker-compose up -d --scale user-service=3

# Stop all services
docker-compose down

# Remove data volumes
docker-compose down -v

Kubernetes Deployment

Namespace and ConfigMap

# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: blog-system

---
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: blog-config
  namespace: blog-system
data:
  application.yml: |
    spring:
      cloud:
        nacos:
          discovery:
            server-addr: nacos:8848

Service Manifests

# user-service-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
  namespace: blog-system
  labels:
    app: user-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
    spec:
      containers:
      - name: user-service
        image: blog/user-service:1.0.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8081
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "k8s"
        - name: NACOS_HOST
          value: "nacos"
        resources:
          requests:
            memory: "256Mi"
            cpu: "200m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8081
          initialDelaySeconds: 60
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8081
          initialDelaySeconds: 30
          periodSeconds: 5
---
# user-service-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: user-service
  namespace: blog-system
spec:
  selector:
    app: user-service
  ports:
  - port: 8081
    targetPort: 8081
  type: ClusterIP

Gateway Deployment and Service

# gateway-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gateway
  namespace: blog-system
spec:
  replicas: 2
  selector:
    matchLabels:
      app: gateway
  template:
    metadata:
      labels:
        app: gateway
    spec:
      containers:
      - name: gateway
        image: blog/gateway:1.0.0
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "k8s"
        - name: NACOS_HOST
          value: "nacos"
---
# gateway-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: gateway
  namespace: blog-system
spec:
  selector:
    app: gateway
  ports:
  - port: 8080
    targetPort: 8080
  type: LoadBalancer

Ingress and Autoscaling

# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: blog-ingress
  namespace: blog-system
spec:
  ingressClassName: nginx
  rules:
  - host: api.blog.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: gateway
            port:
              number: 8080
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: user-service-hpa
  namespace: blog-system
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: user-service
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

Deployment Commands

# Create namespace
kubectl create namespace blog-system

# Apply ConfigMap
kubectl apply -f configmap.yaml

# Deploy services
kubectl apply -f user-service-deployment.yaml
kubectl apply -f user-service-service.yaml
kubectl apply -f gateway-deployment.yaml
kubectl apply -f gateway-service.yaml

# Check status
kubectl get pods -n blog-system
kubectl get svc -n blog-system

# Scale
kubectl scale deployment user-service -n blog-system --replicas=5

# Rolling update
kubectl set image deployment/user-service -n blog-system user-service=blog/user-service:1.0.1
kubectl rollout status deployment/user-service -n blog-system

# Rollback
kubectl rollout undo deployment/user-service -n blog-system

CI/CD Pipeline with GitHub Actions

# .github/workflows/deploy.yml
name: Build and Deploy
on:
  push:
    branches: [main]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        service: [user-service, article-service, gateway]
    steps:
    - uses: actions/checkout@v3
    - name: Set up JDK 11
      uses: actions/setup-java@v3
      with:
        java-version: '11'
        distribution: 'temurin'
    - name: Build with Maven
      run: mvn clean package -pl ${{ matrix.service }} -am -DskipTests
    - name: Build Docker image
      run: |
        docker build -t blog/${{ matrix.service }}:${{ github.sha }} \
          -f ${{ matrix.service }}/Dockerfile .
    - name: Push to Docker Hub
      run: |
        docker push blog/${{ matrix.service }}:${{ github.sha }}
    - name: Deploy to K8s
      run: |
        kubectl set image deployment/${{ matrix.service }} \
          ${{ matrix.service }}=blog/${{ matrix.service }}:${{ github.sha }} \
          -n blog-system

Common Issues and Solutions

Slow container start‑up : add JVM container‑aware options

ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:InitialRAMPercentage=50 -XX:MaxRAMPercentage=75"

.

Service discovery failure : avoid localhost inside containers; use service names (e.g., nacos:8848).

Health‑check failure : give enough start‑up time with initialDelaySeconds: 60 and appropriate periodSeconds.

Log disk exhaustion : configure Logback rolling policy with max-history: 7 and max-size: 100MB.

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.

DockerCI/CDmicroservicesKubernetesSpring 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.