Contents

Sidecar Pattern

Sidecar Pattern

It deploys helper components (sidecars) alongside main microservices to handle cross-cutting concerns like logging, monitoring and configuration management. This allows the main services to focus solely on business logic.

  • Centralizes tasks such as logging and monitoring, reducing complexity in the main service.
  • Commonly used in Kubernetes environments, where each microservice runs with a sidecar for auxiliary functions.

Key concepts:

  • Independent: The sidecar is developed and deployed independently of the main application.
  • Proximate: It resides on the same host or pod to ensure low latency.
  • Lifecycle-Bound: If the main application stops, the sidecar usually stops as well.

Common use cases:

Use CaseDescriptionExamples
Service Mesh ProxyHandles mTLS, circuit breaking, and routing.Envoy, Linkerd
Log CollectionTail logs from the main app and ship them to ELK/Splunk.Fluentd, Logstash
MonitoringCollects metrics (Prometheus) and health checks.Prometheus Exporter
ConfigurationPeriodically pulls config updates and refreshes the app.Spring Cloud Config, Consul
SecurityHandles authentication/authorization (AuthZ) tokens.Open Policy Agent (OPA)

Advantages

  • Separation of Concerns: Developers focus on business logic; DevOps focus on the sidecar.
  • Polyglot Support: You can use the same security or logging sidecar for Java, Go, and Python services.
  • No Code Intrusion: You don’t “pollute” your Java code with infrastructure boilerplate.

Disadvantages

  • Resource Overhead: Each pod now runs two containers, increasing memory usage.
  • Latency: Inter-process communication (IPC) adds a tiny overhead to network calls.
  • Debugging: It can be harder to troubleshoot network issues when there is a “hidden” proxy in the middle.

Implementation patterns

  • Ambassador Pattern: A sidecar specifically for outbound network requests.
  • Adapter Pattern: A sidecar that standardizes the inbound/outbound interface of an application.

Java code examples

  1. The Migration Sidecar (Ambassador Pattern)

The Sidecar Configuration (NGINX): This sidecar runs alongside your Java app. Your Java code always calls localhost:9000. The sidecar decides where to send it.

# Simplified NGINX Sidecar Config
http {
    upstream legacy_monolith {
        server monolith.production.svc:8080;
    }
    upstream new_user_service {
        server user-service.production.svc:8080;
    }

    server {
        listen 9000;

        # Strangled Route: Send users to the new microservice
        location /api/v1/users {
            proxy_pass http://new_user_service;
        }

        # Legacy Route: Everything else goes to the monolith
        location / {
            proxy_pass http://legacy_monolith;
        }
    }
}
@Service
public class UserServiceProxy {
    private final RestTemplate restTemplate;

    public UserServiceProxy(RestTemplateBuilder builder) {
        // The Java app ONLY talks to the sidecar on localhost
        this.restTemplate = builder.rootUri("http://localhost:9000").build();
    }

    public UserDTO getUser(String id) {
        // Logic is routed by the sidecar, not the code
        return restTemplate.getForObject("/api/v1/users/" + id, UserDTO.class);
    }
}
  1. The Observability Sidecar (Adapter Pattern)

If you have a Java app using an older metrics format, you use a sidecar to “translate” it for Prometheus without touching the legacy code.

Kubernetes config

apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-service
spec:
  template:
    spec:
      containers:
        # 1. PRIMARY CONTAINER: Your Java Revenue System
        - name: example-app
          image: example-app:2.1
          volumeMounts:
            - name: logs-vol
              mountPath: /app/logs

        # 2. SIDECAR CONTAINER: Log Shipper (Fluentd)
        - name: log-shipper
          image: fluentd:latest
          volumeMounts:
            - name: logs-vol
              mountPath: /var/log/app
          # This sidecar reads the files revenue-app writes and ships to ELK
          
      volumes:
        - name: logs-vol
          emptyDir: {} # Shared disk space between both containers