Skip to content
IRC-Coding IRC-Coding
Software Architektur Monolith Microservices API Gateway Service Mesh Container Orchestrierung

Software-Architektur: Monolith vs Microservices mit API Gateway & Service Mesh

Software-Architektur Vergleich: Monolith vs Microservices mit API Gateway, Service Mesh, Container, Orchestrierung. Praktische Beispiele und Best Practices.

S

schutzgeist

2 min read

Software-Architektur: Monolith vs Microservices mit API Gateway & Service Mesh

Dieser Beitrag ist eine umfassende Einführung in die Software-Architektur – inklusive Monolith vs Microservices mit API Gateway, Service Mesh, Container und Orchestrierung mit praktischen Beispielen.

In a Nutshell

Monolith-Architektur ist ein einziger, zusammenhängender Code-Block, während Microservices kleine, unabhängige Dienste sind. API Gateway koordiniert Kommunikation, Service Mesh verwaltet Microservice-Interaktionen.

Kompakte Fachbeschreibung

Software-Architektur ist die grundlegende Struktur eines Softwaresystems, bestehend aus Komponenten, deren Beziehungen und Prinzipien für Design und Evolution.

Architektur-Muster:

Monolith-Architektur

  • Konzept: Einheitliche Anwendung in einer Code-Basis
  • Deployment: Einmaliges Deployment der gesamten Anwendung
  • Kommunikation: Direkte Methodenaufrufe innerhalb der Anwendung
  • Vorteile: Einfache Entwicklung, Deployment, Debugging
  • Nachteile: Skalierungsprobleme, Technologie-Beschränkungen

Microservice-Architektur

  • Konzept: Kleine, autonome Dienste mit eigener Verantwortung
  • Deployment: Unabhängige Deployment-Fähigkeit
  • Kommunikation: Netzwerk-basierte API-Aufrufe
  • Vorteile: Skalierbarkeit, Technologie-Flexibilität, Resilienz
  • Nachteile: Komplexität, Netzwerk-Latenz, Verteilte Komplexität

API Gateway

  • Konzept: Zentraler Eingangspunkt für Client-Anfragen
  • Funktionen: Routing, Authentication, Rate Limiting, Load Balancing
  • Vorteile: Zentralisierte Verwaltung, Sicherheit, Monitoring
  • Implementierung: Kong, NGINX, AWS API Gateway

Service Mesh

  • Konzept: Infrastrukturebene für Microservice-Kommunikation
  • Funktionen: Service Discovery, Load Balancing, Circuit Breaking
  • Implementierung: Istio, Linkerd, Consul Connect
  • Vorteile: Transparenz, Observability, Security

Prüfungsrelevante Stichpunkte

  • Software-Architektur: Grundlegende Struktur von Softwaresystemen
  • Monolith: Einheitliche Anwendung mit gemeinsamer Code-Basis
  • Microservices: Kleine, unabhängige Dienste mit eigenem Lifecycle
  • API Gateway: Zentraler Eingangspunkt für Client-Kommunikation
  • Service Mesh: Infrastruktur für Microservice-Interaktionen
  • Container: Virtualisierung auf Betriebssystem-Ebene
  • Orchestrierung: Automatisierte Verwaltung von Containern
  • IHK-relevant: Moderne Architektur-Entscheidungen und -Muster

Kernkomponenten

  1. Architektur-Entscheidung: Monolith vs Microservices
  2. API-Management: Gateway, Routing, Security
  3. Service Discovery: Automatische Dienst-Erkennung
  4. Load Balancing: Verteilung von Anfragen
  5. Circuit Breaking: Schutz vor Kaskaden-Fehlern
  6. Containerisierung: Docker, Podman
  7. Orchestrierung: Kubernetes, Docker Swarm
  8. Monitoring: Logging, Metrics, Tracing

Praxisbeispiele

1. Monolith-Architektur mit Java Spring Boot

package com.example.monolith;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Service;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.beans.factory.annotation.Autowired;
import javax.persistence.*;
import java.util.List;
import java.util.Optional;

// Hauptanwendung
@SpringBootApplication
public class MonolithApplication {
    public static void main(String[] args) {
        SpringApplication.run(MonolithApplication.class, args);
    }
}

// User Entity
@Entity
@Table(name = "users")
class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, unique = true)
    private String username;
    
    @Column(nullable = false)
    private String email;
    
    @Column(nullable = false)
    private String password;
    
    // Getters und Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
}

// Order Entity
@Entity
@Table(name = "orders")
class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @ManyToOne
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
    
    @Column(nullable = false)
    private String product;
    
    @Column(nullable = false)
    private Double amount;
    
    @Column(nullable = false)
    private String status;
    
    // Getters und Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public User getUser() { return user; }
    public void setUser(User user) { this.user = user; }
    
    public String getProduct() { return product; }
    public void setProduct(String product) { this.product = product; }
    
    public Double getAmount() { return amount; }
    public void setAmount(Double amount) { this.amount = amount; }
    
    public String getStatus() { return status; }
    public void setStatus(String status) { this.status = status; }
}

// Repository Interfaces
interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);
    boolean existsByUsername(String username);
}

interface OrderRepository extends JpaRepository<Order, Long> {
    List<Order> findByUserId(Long userId);
    List<Order> findByStatus(String status);
}

// Business Logic Services
@Service
class UserService {
    @Autowired
    private UserRepository userRepository;
    
    public User createUser(User user) {
        if (userRepository.existsByUsername(user.getUsername())) {
            throw new IllegalArgumentException("Username already exists");
        }
        return userRepository.save(user);
    }
    
    public Optional<User> getUserById(Long id) {
        return userRepository.findById(id);
    }
    
    public Optional<User> getUserByUsername(String username) {
        return userRepository.findByUsername(username);
    }
    
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }
    
    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
}

@Service
class OrderService {
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private UserService userService;
    
    public Order createOrder(Long userId, String product, Double amount) {
        Optional<User> userOpt = userService.getUserById(userId);
        if (!userOpt.isPresent()) {
            throw new IllegalArgumentException("User not found");
        }
        
        Order order = new Order();
        order.setUser(userOpt.get());
        order.setProduct(product);
        order.setAmount(amount);
        order.setStatus("PENDING");
        
        return orderRepository.save(order);
    }
    
    public Optional<Order> getOrderById(Long id) {
        return orderRepository.findById(id);
    }
    
    public List<Order> getOrdersByUserId(Long userId) {
        return orderRepository.findByUserId(userId);
    }
    
    public Order updateOrderStatus(Long id, String status) {
        Optional<Order> orderOpt = orderRepository.findById(id);
        if (!orderOpt.isPresent()) {
            throw new IllegalArgumentException("Order not found");
        }
        
        Order order = orderOpt.get();
        order.setStatus(status);
        return orderRepository.save(order);
    }
    
    public void deleteOrder(Long id) {
        orderRepository.deleteById(id);
    }
}

// REST Controllers
@RestController
@RequestMapping("/api/users")
class UserController {
    @Autowired
    private UserService userService;
    
    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.createUser(user);
    }
    
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUserById(id)
            .orElseThrow(() -> new IllegalArgumentException("User not found"));
    }
    
    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers();
    }
    
    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
}

@RestController
@RequestMapping("/api/orders")
class OrderController {
    @Autowired
    private OrderService orderService;
    
    @PostMapping
    public Order createOrder(@RequestBody CreateOrderRequest request) {
        return orderService.createOrder(request.getUserId(), request.getProduct(), request.getAmount());
    }
    
    @GetMapping("/{id}")
    public Order getOrder(@PathVariable Long id) {
        return orderService.getOrderById(id)
            .orElseThrow(() -> new IllegalArgumentException("Order not found"));
    }
    
    @GetMapping("/user/{userId}")
    public List<Order> getOrdersByUser(@PathVariable Long userId) {
        return orderService.getOrdersByUserId(userId);
    }
    
    @PutMapping("/{id}/status")
    public Order updateOrderStatus(@PathVariable Long id, @RequestBody String status) {
        return orderService.updateOrderStatus(id, status);
    }
    
    @DeleteMapping("/{id}")
    public void deleteOrder(@PathVariable Long id) {
        orderService.deleteOrder(id);
    }
}

// DTOs
class CreateOrderRequest {
    private Long userId;
    private String product;
    private Double amount;
    
    // Getters und Setters
    public Long getUserId() { return userId; }
    public void setUserId(Long userId) { this.userId = userId; }
    
    public String getProduct() { return product; }
    public void setProduct(String product) { this.product = product; }
    
    public Double getAmount() { return amount; }
    public void setAmount(Double amount) { this.amount = amount; }
}

2. Microservice-Architektur mit Spring Cloud

// User Microservice
package com.example.userservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Service;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.beans.factory.annotation.Autowired;
import javax.persistence.*;
import java.util.List;
import java.util.Optional;

@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}

// User Entity (vereinfacht)
@Entity
class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String username;
    private String email;
    
    // Getters und Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);
}

@Service
class UserService {
    @Autowired
    private UserRepository userRepository;
    
    public User createUser(User user) {
        return userRepository.save(user);
    }
    
    public Optional<User> getUserById(Long id) {
        return userRepository.findById(id);
    }
    
    public Optional<User> getUserByUsername(String username) {
        return userRepository.findByUsername(username);
    }
    
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }
}

@RestController
@RequestMapping("/api/users")
class UserController {
    @Autowired
    private UserService userService;
    
    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.createUser(user);
    }
    
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUserById(id)
            .orElseThrow(() -> new RuntimeException("User not found"));
    }
    
    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers();
    }
}

// Order Microservice
package com.example.orderservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Service;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.beans.factory.annotation.Autowired;
import javax.persistence.*;
import java.util.List;
import java.util.Optional;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

// Feign Client für User Service
@FeignClient(name = "user-service")
interface UserServiceClient {
    @GetMapping("/api/users/{id}")
    User getUser(@PathVariable("id") Long id);
}

// Order Entity
@Entity
class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private Long userId;
    private String product;
    private Double amount;
    private String status;
    
    // Getters und Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public Long getUserId() { return userId; }
    public void setUserId(Long userId) { this.userId = userId; }
    
    public String getProduct() { return product; }
    public void setProduct(String product) { this.product = product; }
    
    public Double getAmount() { return amount; }
    public void setAmount(Double amount) { this.amount = amount; }
    
    public String getStatus() { return status; }
    public void setStatus(String status) { this.status = status; }
}

interface OrderRepository extends JpaRepository<Order, Long> {
    List<Order> findByUserId(Long userId);
}

@Service
class OrderService {
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private UserServiceClient userServiceClient;
    
    public Order createOrder(Order order) {
        // User-Validierung über Microservice-Aufruf
        try {
            User user = userServiceClient.getUser(order.getUserId());
            if (user == null) {
                throw new RuntimeException("User not found");
            }
        } catch (Exception e) {
            throw new RuntimeException("User service unavailable");
        }
        
        order.setStatus("PENDING");
        return orderRepository.save(order);
    }
    
    public Optional<Order> getOrderById(Long id) {
        return orderRepository.findById(id);
    }
    
    public List<Order> getOrdersByUserId(Long userId) {
        return orderRepository.findByUserId(userId);
    }
    
    public Order updateOrderStatus(Long id, String status) {
        Optional<Order> orderOpt = orderRepository.findById(id);
        if (!orderOpt.isPresent()) {
            throw new RuntimeException("Order not found");
        }
        
        Order order = orderOpt.get();
        order.setStatus(status);
        return orderRepository.save(order);
    }
}

@RestController
@RequestMapping("/api/orders")
class OrderController {
    @Autowired
    private OrderService orderService;
    
    @PostMapping
    public Order createOrder(@RequestBody Order order) {
        return orderService.createOrder(order);
    }
    
    @GetMapping("/{id}")
    public Order getOrder(@PathVariable Long id) {
        return orderService.getOrderById(id)
            .orElseThrow(() -> new RuntimeException("Order not found"));
    }
    
    @GetMapping("/user/{userId}")
    public List<Order> getOrdersByUser(@PathVariable Long userId) {
        return orderService.getOrdersByUserId(userId);
    }
    
    @PutMapping("/{id}/status")
    public Order updateOrderStatus(@PathVariable Long id, @RequestBody String status) {
        return orderService.updateOrderStatus(id, status);
    }
}

3. API Gateway mit Spring Cloud Gateway

package com.example.apigateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

@SpringBootApplication
public class ApiGatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(ApiGatewayApplication.class, args);
    }
    
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
            // User Service Routes
            .route("user-service", r -> r.path("/api/users/**")
                .uri("lb://user-service"))
            
            // Order Service Routes
            .route("order-service", r -> r.path("/api/orders/**")
                .uri("lb://order-service"))
            
            // Product Service Routes
            .route("product-service", r -> r.path("/api/products/**")
                .uri("lb://product-service"))
            
            // Authentication Service Routes
            .route("auth-service", r -> r.path("/api/auth/**")
                .uri("lb://auth-service"))
            
            .build();
    }
}

// Authentication Filter
@Component
class AuthenticationFilter implements GlobalFilter, Ordered {
    
    @Override
    public Mono<Void> filter(ServerHttpRequest request, GatewayFilterChain chain) {
        String path = request.getURI().getPath();
        
        // Public Endpoints ohne Authentication
        if (path.startsWith("/api/auth/login") || 
            path.startsWith("/api/auth/register") ||
            path.startsWith("/actuator")) {
            return chain.filter(request);
        }
        
        // Token aus Header extrahieren
        String authHeader = request.getHeaders().getFirst("Authorization");
        
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            return Mono.error(new RuntimeException("Missing or invalid token"));
        }
        
        String token = authHeader.substring(7);
        
        // Token validieren (vereinfacht)
        if (!isValidToken(token)) {
            return Mono.error(new RuntimeException("Invalid token"));
        }
        
        // User-Informationen zum Request hinzufügen
        ServerHttpRequest modifiedRequest = request.mutate()
            .header("X-User-ID", getUserIdFromToken(token))
            .header("X-User-Role", getUserRoleFromToken(token))
            .build();
        
        return chain.filter(modifiedRequest);
    }
    
    private boolean isValidToken(String token) {
        // Token-Validierung (vereinfacht)
        return token != null && !token.isEmpty();
    }
    
    private String getUserIdFromToken(String token) {
        // User-ID aus Token extrahieren
        return "123"; // Beispiel
    }
    
    private String getUserRoleFromToken(String token) {
        // User-Rolle aus Token extrahieren
        return "USER"; // Beispiel
    }
    
    @Override
    public int getOrder() {
        return -100; // Hohe Priorität
    }
}

// Rate Limiting Filter
@Component
class RateLimitingFilter implements GlobalFilter, Ordered {
    
    private final Map<String, Map<String, Integer>> rateLimitMap = new ConcurrentHashMap<>();
    private static final int RATE_LIMIT = 100; // 100 Anfragen pro Minute
    private static final int TIME_WINDOW = 60; // Sekunden
    
    @Override
    public Mono<Void> filter(ServerHttpRequest request, GatewayFilterChain chain) {
        String clientId = getClientId(request);
        String currentTimeWindow = getCurrentTimeWindow();
        
        rateLimitMap.putIfAbsent(clientId, new ConcurrentHashMap<>());
        Map<String, Integer> clientRequests = rateLimitMap.get(clientId);
        
        // Alte Zeitfenster entfernen
        clientRequests.entrySet().removeIf(entry -> 
            !entry.getKey().equals(currentTimeWindow));
        
        // Aktuellen Zähler erhöhen
        int currentCount = clientRequests.getOrDefault(currentTimeWindow, 0);
        
        if (currentCount >= RATE_LIMIT) {
            return Mono.error(new RuntimeException("Rate limit exceeded"));
        }
        
        clientRequests.put(currentTimeWindow, currentCount + 1);
        return chain.filter(request);
    }
    
    private String getClientId(ServerHttpRequest request) {
        // Client-ID aus IP oder Token extrahieren
        return request.getRemoteAddress() != null ? 
            request.getRemoteAddress().getAddress().getHostAddress() : "unknown";
    }
    
    private String getCurrentTimeWindow() {
        return String.valueOf(System.currentTimeMillis() / (TIME_WINDOW * 1000));
    }
    
    @Override
    public int getOrder() {
        return -99; // Nach Authentication
    }
}

// Load Balancing Configuration
@Component
class LoadBalancerConfig {
    
    @Bean
    public ReactorLoadBalancer<ServiceInstance> userServiceLoadBalancer(
            Environment environment,
            LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new RoundRobinLoadBalancer(
            loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),
            name);
    }
}

// Circuit Breaker Configuration
@Component
class CircuitBreakerConfig {
    
    @Bean
    public CircuitBreaker circuitBreaker() {
        CircuitBreakerConfig config = CircuitBreakerConfig.custom()
            .failureRateThreshold(50)
            .waitDurationInOpenState(Duration.ofMillis(1000))
            .slidingWindowSize(20)
            .minimumNumberOfCalls(10)
            .build();
        
        return CircuitBreaker.of("myCircuitBreaker", config);
    }
}

// API Gateway Controller für Health Checks
@RestController
@RequestMapping("/gateway")
class GatewayController {
    
    @GetMapping("/health")
    public Map<String, String> health() {
        Map<String, String> status = new HashMap<>();
        status.put("status", "UP");
        status.put("service", "api-gateway");
        status.put("timestamp", Instant.now().toString());
        return status;
    }
    
    @GetMapping("/routes")
    public Map<String, Object> getRoutes() {
        Map<String, Object> routes = new HashMap<>();
        routes.put("user-service", "/api/users/**");
        routes.put("order-service", "/api/orders/**");
        routes.put("product-service", "/api/products/**");
        routes.put("auth-service", "/api/auth/**");
        return routes;
    }
}

4. Docker Container für Microservices

# Dockerfile für User Service
FROM openjdk:11-jre-slim

# Working Directory
WORKDIR /app

# Application JAR kopieren
COPY target/user-service-0.0.1-SNAPSHOT.jar app.jar

# Expose Port
EXPOSE 8081

# Environment Variables
ENV JAVA_OPTS="-Xmx512m -Xms256m"
ENV SPRING_PROFILES_ACTIVE=docker

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

# Application starten
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
# Dockerfile für Order Service
FROM openjdk:11-jre-slim

WORKDIR /app

COPY target/order-service-0.0.1-SNAPSHOT.jar app.jar

EXPOSE 8082

ENV JAVA_OPTS="-Xmx512m -Xms256m"
ENV SPRING_PROFILES_ACTIVE=docker

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

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
# Dockerfile für API Gateway
FROM openjdk:11-jre-slim

WORKDIR /app

COPY target/api-gateway-0.0.1-SNAPSHOT.jar app.jar

EXPOSE 8080

ENV JAVA_OPTS="-Xmx512m -Xms256m"
ENV SPRING_PROFILES_ACTIVE=docker

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

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

5. Kubernetes Orchestrierung

# Namespace
apiVersion: v1
kind: Namespace
metadata:
  name: microservices

---
# User Service Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
  namespace: microservices
spec:
  replicas: 3
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
    spec:
      containers:
      - name: user-service
        image: user-service:latest
        ports:
        - containerPort: 8081
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "kubernetes"
        - name: SPRING_DATASOURCE_URL
          value: "jdbc:postgresql://postgres-service:5432/userdb"
        - name: SPRING_DATASOURCE_USERNAME
          value: "postgres"
        - name: SPRING_DATASOURCE_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: password
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8081
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8081
          initialDelaySeconds: 5
          periodSeconds: 5
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"

---
# User Service Service
apiVersion: v1
kind: Service
metadata:
  name: user-service
  namespace: microservices
spec:
  selector:
    app: user-service
  ports:
  - protocol: TCP
    port: 8081
    targetPort: 8081
  type: ClusterIP

---
# Order Service Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
  namespace: microservices
spec:
  replicas: 2
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
    spec:
      containers:
      - name: order-service
        image: order-service:latest
        ports:
        - containerPort: 8082
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "kubernetes"
        - name: USER_SERVICE_URL
          value: "http://user-service:8081"
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8082
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8082
          initialDelaySeconds: 5
          periodSeconds: 5
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"

---
# Order Service Service
apiVersion: v1
kind: Service
metadata:
  name: order-service
  namespace: microservices
spec:
  selector:
    app: order-service
  ports:
  - protocol: TCP
    port: 8082
    targetPort: 8082
  type: ClusterIP

---
# API Gateway Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-gateway
  namespace: microservices
spec:
  replicas: 2
  selector:
    matchLabels:
      app: api-gateway
  template:
    metadata:
      labels:
        app: api-gateway
    spec:
      containers:
      - name: api-gateway
        image: api-gateway:latest
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "kubernetes"
        livenessProbe:
          httpGet:
            path: /gateway/health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /gateway/health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"

---
# API Gateway Service
apiVersion: v1
kind: Service
metadata:
  name: api-gateway
  namespace: microservices
spec:
  selector:
    app: api-gateway
  ports:
  - protocol: TCP
    port: 8080
    targetPort: 8080
  type: LoadBalancer

---
# Horizontal Pod Autoscaler für User Service
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: user-service-hpa
  namespace: microservices
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
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

---
# ConfigMap für Konfiguration
apiVersion: v1
kind: ConfigMap
metadata:
  name: microservices-config
  namespace: microservices
data:
  application.yml: |
    spring:
      cloud:
        kubernetes:
          discovery:
            enabled: true
          config:
            enabled: true
    management:
      endpoints:
        web:
          exposure:
            include: health,info,metrics,prometheus
      endpoint:
        health:
          show-details: always

---
# Secret für sensible Daten
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
  namespace: microservices
type: Opaque
data:
  password: cGFzc3dvcmQxMjM=  # Base64 encoded "password123"
  username: cG9zdGdyZXM=      # Base64 encoded "postgres"

6. Service Mesh mit Istio

# Istio Gateway
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: microservices-gateway
  namespace: microservices
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"

---
# Virtual Service für API Gateway
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: api-gateway-vs
  namespace: microservices
spec:
  hosts:
  - "*"
  gateways:
  - microservices-gateway
  http:
  - match:
    - uri:
        prefix: /
    route:
    - destination:
        host: api-gateway
        port:
          number: 8080

---
# Destination Rules für Load Balancing
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: user-service-dr
  namespace: microservices
spec:
  host: user-service
  trafficPolicy:
    loadBalancer:
      simple: ROUND_ROBIN
    connectionPool:
      tcp:
        maxConnections: 100
      http:
        http1MaxPendingRequests: 50
        maxRequestsPerConnection: 10
    circuitBreaker:
      consecutiveErrors: 3
      interval: 30s
      baseEjectionTime: 30s

---
# Service Entry für externe Services
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: external-api
  namespace: microservices
spec:
  hosts:
  - external-api.example.com
  ports:
  - number: 443
    name: https
    protocol: HTTPS
  resolution: DNS

---
# Authorization Policy
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: allow-api-access
  namespace: microservices
spec:
  selector:
    matchLabels:
      app: api-gateway
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account"]
  - to:
    - operation:
        methods: ["GET", "POST", "PUT", "DELETE"]

---
# Request Authentication
apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
  name: jwt-auth
  namespace: microservices
spec:
  selector:
    matchLabels:
      app: api-gateway
  jwtRules:
  - issuer: "https://auth.example.com"
    jwksUri: "https://auth.example.com/.well-known/jwks.json"
    forwardOriginalToken: true

Architektur-Vergleich

Monolith vs Microservices

KriteriumMonolithMicroservices
KomplexitätGeringerHoch
DeploymentEinfachKomplex
SkalierungVertikalHorizontal
TechnologieEinheitlichFlexibel
FehlerisolierungSchlechtGut
Team-StrukturZentralVerteilt
DevelopmentSchnellLangsamer

Deployment-Strategien

StrategieBeschreibungVorteileNachteile
Big BangAlles auf einmalEinfachHochriskant
Blue-GreenParallel-SystemeZero-DowntimeRessourcen
CanarySchrittweiseSicherKomplex
RollingSchrittweiseEffizientLangsam

Container-Technologien

Docker vs Podman

FeatureDockerPodman
DaemonJaNein
RootlessBegrenztVoll
PodsNeinJa
DockerfileJaJa
ComposeJaBegrenzt

Container-Registry

RegistryFeaturesPreis
Docker HubPublic/PrivateFreemium
GitHub PackagesIntegrationFree
AWS ECRCloud-NativPay-per-use
Google GCRCloud-NativPay-per-use

Orchestrierung-Plattformen

Kubernetes vs Docker Swarm

FeatureKubernetesDocker Swarm
KomplexitätHochGering
SkalierungAutoManuell
Service MeshJaNein
Learning CurveSteilFlach
CommunityGroßMittel

Service Mesh Implementierungen

Istio vs Linkerd

FeatureIstioLinkerd
ComplexityHochGering
FeaturesUmfassendFokussiert
PerformanceGutSehr gut
RessourcenHochGering
IntegrationUmfassendEinfach

API Gateway Features

Core-Funktionen

  • Routing: Anfragen zu richtigen Services
  • Authentication: JWT, OAuth2, API Keys
  • Authorization: Rollenbasierte Zugriffe
  • Rate Limiting: Anfragelimits pro Client
  • Load Balancing: Verteilung der Last
  • Caching: Response-Caching
  • Logging: Request/Response Logging

Advanced Features

  • Circuit Breaking: Schutz vor Ausfällen
  • Retry Logic: Automatische Wiederholungen
  • Request/Response Transformation: Daten-Transformation
  • API Versioning: Versionsverwaltung
  • Analytics: Nutzungsstatistiken

Monitoring & Observability

Drei Säulen der Observability

  1. Metrics: Quantitative Daten (Prometheus, Grafana)
  2. Logging: Ereignis-basierte Daten (ELK Stack, Fluentd)
  3. Tracing: Request-Verfolgung (Jaeger, Zipkin)

Tools im Microservice-Kontext

# Prometheus Monitoring
apiVersion: v1
kind: ServiceMonitor
metadata:
  name: user-service-monitor
  namespace: microservices
spec:
  selector:
    matchLabels:
      app: user-service
  endpoints:
  - port: http
    path: /actuator/prometheus
    interval: 30s

Best Practices

Microservice-Design

  • Single Responsibility: Ein Service, eine Aufgabe
  • Database per Service: Eigene Datenbank pro Service
  • Async Communication: Event-basierte Kommunikation
  • Circuit Breaking: Schutz vor Kaskaden-Fehlern
  • Health Checks: Überwachung der Service-Gesundheit

Container-Best Practices

# Best Practice Dockerfile
FROM alpine:latest

# Non-root User
RUN addgroup -g 1001 -S appgroup && \
    adduser -u 1001 -S appuser -G appgroup

USER appuser

# Minimal Image
COPY --from=builder /app/target/app.jar /app/app.jar

WORKDIR /app

EXPOSE 8080

# Health Check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1

ENTRYPOINT ["java", "-jar", "app.jar"]

Vorteile und Nachteile

Vorteile von Microservices

  • Skalierbarkeit: Unabhängige Skalierung pro Service
  • Flexibilität: Verschiedene Technologien möglich
  • Resilienz: Ausfall eines Services不影响其他
  • Team-Autonomie: Unabhängige Entwicklungsteams
  • Schnellere Deployment: Kleine, häufige Deployments

Nachteile von Microservices

  • Komplexität: Verteilte Systeme sind komplex
  • Netzwerk-Latenz: Kommunikation über Netzwerk
  • Daten-Konsistenz: Verteilte Transaktionen
  • Monitoring: Komplexere Überwachung
  • Debugging: Schwierigere Fehlersuche

Häufige Prüfungsfragen

  1. Was sind die Hauptunterschiede zwischen Monolith und Microservices? Monolith ist eine Einheit, Microservices sind kleine, unabhängige Dienste mit eigener Verantwortung.

  2. Erklären Sie die Rolle eines API Gateways! API Gateway ist zentraler Eingangspunkt für Client-Anfragen mit Routing, Authentication und Rate Limiting.

  3. Wann verwendet man Container und wann Orchestrierung? Container für Isolation und Portabilität, Orchestrierung für automatisierte Verwaltung vieler Container.

  4. Was ist Service Mesh und warum wird es benötigt? Service Mesh ist Infrastrukturebene für Microservice-Kommunikation mit Service Discovery und Security.

Wichtigste Quellen

  1. https://microservices.io/
  2. https://kubernetes.io/
  3. https://istio.io/
  4. https://spring.io/projects/spring-cloud
Zurück zum Blog
Share:

Ähnliche Beiträge