Skip to main content

Chapter 8: Deployment

Part II — Implementation

Microservices create many more deployment units than a monolith. This chapter surveys deployment options from containers to Kubernetes to FaaS, and introduces the key principles for managing deployments at scale.


The Deployment Challenge

Each microservice needs to be deployed independently. With 20 services, you have 20 deployment pipelines to manage. Deployment decisions become critical architectural decisions, not just infrastructure details.

The guiding principle: you want the ability to deploy any service to production at any time, independently, with minimal risk.


The Evolution of Deployment Targets

1. Physical Machines

Each service runs on dedicated hardware. Simple but wasteful, slow to provision, expensive.

2. Virtual Machines (VMs)

Multiple VMs per physical machine. Better utilization. Still relatively slow to spin up (minutes). Each VM carries a full OS.

3. Containers

A container shares the host OS kernel but has its own filesystem, process space, and networking. Start in seconds. Much more efficient than VMs.

Docker is the dominant container runtime for microservices. Every Spring Boot service should have a Dockerfile.

4. Container Orchestration (Kubernetes)

When you have dozens of services running in containers across multiple hosts, you need orchestration — automated deployment, scaling, health management, and networking.

Kubernetes (K8s) has become the de-facto standard.

5. Function-as-a-Service (FaaS)

Individual functions deployed to platforms like AWS Lambda, Google Cloud Functions, or Azure Functions. No servers to manage. Pay per invocation. Best for event-driven, intermittent workloads.


Containers and Docker

Why Containers for Microservices?

  • Isolation — each service has its own dependencies, no conflicts
  • Consistency — same container runs in dev, staging, and production
  • Speed — start in seconds vs. minutes for VMs
  • Portability — runs anywhere Docker runs

Dockerfile for a Spring Boot Service

FROM eclipse-temurin:21-jre-alpine

WORKDIR /app

# Copy the fat JAR
COPY build/libs/order-service-*.jar app.jar

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

EXPOSE 8080

# Use layered JAR for better Docker caching
ENTRYPOINT ["java", "-jar", "app.jar"]

Spring Boot Docker Layers (Optimization)

Spring Boot 2.3+ supports layered JARs for efficient Docker image caching:

# Build layered JAR
./gradlew bootJar

# Extract layers
java -Djarmode=layertools -jar build/libs/app.jar extract

This splits the JAR into layers (dependencies, snapshot-dependencies, application) so Docker only rebuilds changed layers.


Kubernetes

Kubernetes is an open-source container orchestration platform. It manages the lifecycle of containers across a cluster of machines.

Core Kubernetes Concepts

ResourcePurpose
PodSmallest deployable unit — one or more containers
DeploymentManages Pods; handles rolling updates and rollbacks
ServiceStable network endpoint for a set of Pods (DNS + load balancing)
ConfigMapInject non-secret configuration into Pods
SecretInject sensitive configuration (passwords, tokens)
NamespaceLogical isolation within a cluster
IngressHTTP routing from outside the cluster to Services

Spring Boot Service Deployment Manifest

apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: registry.example.com/order-service:abc1234
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: "production"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: order-service-secrets
key: database-url
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
ports:
- port: 80
targetPort: 8080

Spring Boot Actuator Health Endpoints

Kubernetes relies on health probes. Spring Actuator makes this easy:

# application.yml
management:
endpoint:
health:
probes:
enabled: true
health:
livenessState:
enabled: true
readinessState:
enabled: true
  • /actuator/health/liveness — is the process alive? (restart if not)
  • /actuator/health/readiness — is it ready to serve traffic? (stop routing if not)

Deployment Strategies

Rolling Update (Default in K8s)

Gradually replace old pods with new ones. Zero downtime if done correctly. K8s default.

spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Allow 1 extra pod during update
maxUnavailable: 0 # Never reduce capacity below desired

Blue/Green Deployment

Run two identical environments (blue = current, green = new). Switch traffic all at once. Allows instant rollback.

           ┌──────────────────────────────────────┐
│ Load Balancer │
└────────────┬─────────────────────────┘
(traffic) │

┌────────────────┐ ┌────────────────┐
│ Blue (v1.0) │ │ Green (v1.1) │
│ (live) │ │ (standby) │
└────────────────┘ └────────────────┘
Switch traffic to green, keep blue for rollback

Canary Release

Route a small percentage of traffic (e.g., 5%) to the new version. Monitor metrics. Gradually increase if healthy.

Feature Flags

Deploy new code but keep it disabled via a feature flag. Enables deployment to be separated from feature release.


Function-as-a-Service (FaaS)

FaaS platforms like AWS Lambda let you deploy individual functions without managing servers.

When FaaS Works Well for Microservices

  • Event-driven processing (S3 uploads, Kafka events, HTTP webhooks)
  • Intermittent workloads (no traffic most of the time)
  • Simple, stateless operations

When FaaS Doesn't Work Well

  • Long-running processes (Lambda has 15-minute max execution)
  • Low-latency requirements (cold starts add 100ms–1s latency)
  • Complex stateful workflows

Spring Cloud Function

Spring Cloud Function abstracts away the target runtime:

@Bean
public Function<OrderEvent, ShippingRequest> processOrder() {
return event -> new ShippingRequest(event.getOrderId(), event.getAddress());
}

This function can be deployed to AWS Lambda, Azure Functions, or run as a standard Spring Boot app — without code changes.


GitOps

GitOps treats infrastructure and deployment configuration as code, stored in Git. A GitOps operator (Argo CD, Flux) watches Git and automatically applies changes to the cluster.

Developer pushes to Git
→ Argo CD detects change
→ Argo CD applies Kubernetes manifests
→ Cluster reaches desired state

Benefits: auditable history, rollback = git revert, no manual kubectl commands in production.


Summary

ConceptOne-Line Summary
ContainersLightweight isolated runtime; use Docker for every microservice
KubernetesContainer orchestration — deployment, scaling, health, networking
Rolling updateGradually replace old pods — zero downtime by default
Blue/GreenRun two environments; instant traffic switch and rollback
CanaryGradually shift traffic to the new version; monitor before full rollout
FaaSServerless functions — great for event-driven, intermittent workloads
GitOpsGit as the source of truth for deployments; automated reconciliation