Traefik Mesh is a simple yet powerful service mesh that provides traffic management, observability, and security features without requiring complex configuration. It's designed to be lightweight and easy to use.
Traefik Mesh Architecture Overview
Key Components:
- Data Plane: Proxies (Traefik) that handle actual traffic
- Control Plane: Manages configuration and service discovery
- CRDs (Custom Resource Definitions): Kubernetes resources for configuration
- mTLS: Automatic mutual TLS between services
- Access Control: Traffic policies and routing rules
Java Application → Traefik Mesh → Services
- Applications run normally, unaware of the mesh
- Traefik proxies handle service-to-service communication
- Configuration via Kubernetes CRDs
Prerequisites and Dependencies
Maven Dependencies
<properties> <spring-boot.version>3.1.0</spring-boot.version> <micrometer.version>1.11.5</micrometer.version> <resilience4j.version>2.1.0</resilience4j.version> <kubernetes-client.version>6.7.2</kubernetes-client.version> </properties> <dependencies> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring-boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>${spring-boot.version}</version> </dependency> <!-- Observability --> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> <version>${micrometer.version}</version> </dependency> <!-- Resilience --> <dependency> <groupId>io.github.resilience4j</groupId> <artifactId>resilience4j-spring-boot3</artifactId> <version>${resilience4j.version}</version> </dependency> <dependency> <groupId>io.github.resilience4j</groupId> <artifactId>resilience4j-micrometer</artifactId> <version>${resilience4j.version}</version> </dependency> <!-- Kubernetes Client --> <dependency> <groupId>io.kubernetes</groupId> <artifactId>client-java</artifactId> <version>${kubernetes-client.version}</version> </dependency> </dependencies> Kubernetes Manifests for Traefik Mesh
# traefik-mesh-installation.yaml apiVersion: v1 kind: Namespace metadata: name: traefik-mesh --- apiVersion: helm.cattle.io/v1 kind: HelmChart metadata: name: traefik-mesh namespace: traefik-mesh spec: chart: traefik-mesh repo: https://helm.traefik.io/mesh targetNamespace: traefik-mesh set: dashboard.enabled: true metrics.prometheus.enabled: true tracing.jaeger.enabled: true acp.enabled: true
Java Application Configuration
1. Spring Boot Application with Mesh Awareness
@SpringBootApplication @EnableConfigurationProperties(MeshProperties.class) public class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); } @Bean public RestTemplate meshAwareRestTemplate(MeshProperties meshProperties) { RestTemplate restTemplate = new RestTemplate(); // Add headers for mesh tracing and context propagation restTemplate.setInterceptors(Collections.singletonList( new MeshHeaderInterceptor(meshProperties) )); return restTemplate; } @Bean public WebClient meshAwareWebClient(MeshProperties meshProperties) { return WebClient.builder() .filter(new MeshWebClientFilter(meshProperties)) .build(); } } @ConfigurationProperties(prefix = "mesh") @Data public class MeshProperties { private boolean enabled = true; private String serviceName; private String clusterName = "default"; private String namespace = "default"; private Tracing tracing = new Tracing(); private Metrics metrics = new Metrics(); @Data public static class Tracing { private boolean enabled = true; private String headerName = "x-request-id"; private String traceIdHeader = "x-b3-traceid"; private String spanIdHeader = "x-b3-spanid"; } @Data public static class Metrics { private boolean enabled = true; private String path = "/actuator/prometheus"; } } 2. Mesh Header Interceptor
@Component public class MeshHeaderInterceptor implements ClientHttpRequestInterceptor { private final MeshProperties meshProperties; private final Tracer tracer; public MeshHeaderInterceptor(MeshProperties meshProperties) { this.meshProperties = meshProperties; this.tracer = GlobalOpenTelemetry.getTracer("mesh-interceptor"); } @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { // Add mesh-specific headers addMeshHeaders(request); // Add tracing headers if available addTracingHeaders(request); // Add service identity headers addServiceIdentityHeaders(request); return execution.execute(request, body); } private void addMeshHeaders(HttpRequest request) { if (meshProperties.isEnabled()) { request.getHeaders().add("x-mesh-service", meshProperties.getServiceName()); request.getHeaders().add("x-mesh-namespace", meshProperties.getNamespace()); request.getHeaders().add("x-mesh-cluster", meshProperties.getClusterName()); } } private void addTracingHeaders(HttpRequest request) { if (meshProperties.getTracing().isEnabled()) { Span currentSpan = Span.current(); if (!currentSpan.getSpanContext().equals(SpanContext.getInvalid())) { String traceId = currentSpan.getSpanContext().getTraceId(); String spanId = currentSpan.getSpanContext().getSpanId(); request.getHeaders().add(meshProperties.getTracing().getTraceIdHeader(), traceId); request.getHeaders().add(meshProperties.getTracing().getSpanIdHeader(), spanId); } // Add request ID for correlation request.getHeaders().add(meshProperties.getTracing().getHeaderName(), UUID.randomUUID().toString()); } } private void addServiceIdentityHeaders(HttpRequest request) { // Add headers that might be used for ACP (Access Control Policies) request.getHeaders().add("x-forwarded-for", getClientIp()); request.getHeaders().add("x-forwarded-proto", "https"); request.getHeaders().add("x-forwarded-host", getHostName()); } private String getClientIp() { // Implementation to get client IP return "unknown"; } private String getHostName() { try { return InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { return "unknown"; } } } 3. WebClient Filter for Reactive Applications
@Component public class MeshWebClientFilter implements ExchangeFilterFunction { private final MeshProperties meshProperties; public MeshWebClientFilter(MeshProperties meshProperties) { this.meshProperties = meshProperties; } @Override public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) { if (!meshProperties.isEnabled()) { return next.exchange(request); } // Add mesh headers to the request ClientRequest filteredRequest = ClientRequest.from(request) .headers(headers -> { addMeshHeaders(headers); addTracingHeaders(headers); addServiceIdentityHeaders(headers); }) .build(); return next.exchange(filteredRequest); } private void addMeshHeaders(HttpHeaders headers) { headers.add("x-mesh-service", meshProperties.getServiceName()); headers.add("x-mesh-namespace", meshProperties.getNamespace()); headers.add("x-mesh-cluster", meshProperties.getClusterName()); } private void addTracingHeaders(HttpHeaders headers) { if (meshProperties.getTracing().isEnabled()) { Span currentSpan = Span.current(); if (!currentSpan.getSpanContext().equals(SpanContext.getInvalid())) { headers.add(meshProperties.getTracing().getTraceIdHeader(), currentSpan.getSpanContext().getTraceId()); headers.add(meshProperties.getTracing().getSpanIdHeader(), currentSpan.getSpanContext().getSpanId()); } headers.add(meshProperties.getTracing().getHeaderName(), UUID.randomUUID().toString()); } } private void addServiceIdentityHeaders(HttpHeaders headers) { headers.add("x-forwarded-for", getClientIp()); headers.add("x-forwarded-proto", "https"); headers.add("x-forwarded-host", getHostName()); } private String getClientIp() { return "unknown"; } private String getHostName() { try { return InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { return "unknown"; } } } Traffic Management with Traefik Mesh
1. TrafficSplit Configuration (Kubernetes CRD)
# trafficsplit-canary.yaml apiVersion: split.smi-spec.io/v1alpha4 kind: TrafficSplit metadata: name: order-service-canary namespace: default spec: service: order-service backends: - service: order-service-v1 weight: 90 - service: order-service-v2 weight: 10
2. Java Service with Canary Awareness
@Service @Slf4j public class OrderService { private final RestTemplate restTemplate; private final MeshProperties meshProperties; private final MeterRegistry meterRegistry; private final Counter canaryRequestsCounter; private final Timer canaryResponseTimer; public OrderService(RestTemplate restTemplate, MeshProperties meshProperties, MeterRegistry meterRegistry) { this.restTemplate = restTemplate; this.meshProperties = meshProperties; this.meterRegistry = meterRegistry; // Initialize metrics this.canaryRequestsCounter = Counter.builder("mesh.canary.requests") .description("Number of requests to canary services") .tag("service", "order-service") .register(meterRegistry); this.canaryResponseTimer = Timer.builder("mesh.canary.response.time") .description("Response time for canary requests") .tag("service", "order-service") .register(meterRegistry); } @Retry(name = "orderService", fallbackMethod = "fallbackCreateOrder") @CircuitBreaker(name = "orderService") @RateLimiter(name = "orderService") @Timed(value = "order.create", description = "Time taken to create order") public Order createOrder(CreateOrderRequest request) { log.info("Creating order for customer: {}", request.getCustomerId()); // The actual service call - Traefik Mesh handles routing String serviceUrl = determineServiceUrl(); canaryRequestsCounter.increment(); return canaryResponseTimer.record(() -> { try { ResponseEntity<Order> response = restTemplate.postForEntity( serviceUrl, request, Order.class); logCanaryHit(serviceUrl); return response.getBody(); } catch (HttpClientErrorException | HttpServerErrorException e) { log.error("Service call failed: {} - {}", e.getStatusCode(), e.getResponseBodyAsString()); throw new ServiceException("Failed to create order", e); } }); } private String determineServiceUrl() { // In canary deployment, Traefik Mesh handles routing // We just call the main service, and mesh routes based on TrafficSplit return String.format("http://order-service.%s.svc.cluster.local:8080/api/orders", meshProperties.getNamespace()); } private void logCanaryHit(String serviceUrl) { // Log when hitting canary services for monitoring if (serviceUrl.contains("canary") || serviceUrl.contains("v2")) { log.warn("Request routed to canary service: {}", serviceUrl); } } private Order fallbackCreateOrder(CreateOrderRequest request, Exception e) { log.warn("Fallback triggered for order creation: {}", e.getMessage()); // Return a default order or cached response return Order.builder() .id("fallback-" + UUID.randomUUID().toString()) .status(OrderStatus.PENDING) .customerId(request.getCustomerId()) .build(); } } 3. TrafficTarget for Access Control
# traffictarget.yaml apiVersion: access.smi-spec.io/v1alpha3 kind: TrafficTarget metadata: name: order-service-target namespace: default spec: destination: kind: ServiceAccount name: order-service namespace: default rules: - kind: HTTPRouteGroup name: order-service-routes matches: - orders-get - orders-post sources: - kind: ServiceAccount name: payment-service namespace: default - kind: ServiceAccount name: inventory-service namespace: default --- apiVersion: specs.smi-spec.io/v1alpha4 kind: HTTPRouteGroup metadata: name: order-service-routes namespace: default spec: matches: - name: orders-get pathRegex: /api/orders/.* methods: - GET - name: orders-post pathRegex: /api/orders methods: - POST
4. Access Control in Java
@Component @Slf4j public class MeshAccessControl { private final MeshProperties meshProperties; public MeshAccessControl(MeshProperties meshProperties) { this.meshProperties = meshProperties; } public boolean validateAccess(HttpServletRequest request, String operation) { // Extract headers that Traefik Mesh might set String sourceService = request.getHeader("x-forwarded-for"); String userAgent = request.getHeader("user-agent"); String meshService = request.getHeader("x-mesh-service"); log.debug("Access validation - Source: {}, Operation: {}, MeshService: {}", sourceService, operation, meshService); // Implement custom access logic if needed // Traefik Mesh ACP handles most of this, but you can add additional checks return isAllowedSource(sourceService, operation); } private boolean isAllowedSource(String sourceService, String operation) { // Custom business logic for access control if (sourceService == null) { return true; // Allow direct access (for testing) } // Example: Only allow payment service to create orders if ("POST".equals(operation) && sourceService.contains("payment-service")) { return true; } // Example: Allow inventory service to read orders if ("GET".equals(operation) && sourceService.contains("inventory-service")) { return true; } return false; } } @RestController @RequestMapping("/api/orders") @Slf4j public class OrderController { private final OrderService orderService; private final MeshAccessControl accessControl; public OrderController(OrderService orderService, MeshAccessControl accessControl) { this.orderService = orderService; this.accessControl = accessControl; } @PostMapping public ResponseEntity<Order> createOrder(@RequestBody CreateOrderRequest request, HttpServletRequest httpRequest) { // Validate access using mesh headers if (!accessControl.validateAccess(httpRequest, "POST")) { log.warn("Access denied for order creation from: {}", httpRequest.getHeader("x-forwarded-for")); return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); } Order order = orderService.createOrder(request); return ResponseEntity.ok(order); } @GetMapping("/{orderId}") public ResponseEntity<Order> getOrder(@PathVariable String orderId, HttpServletRequest httpRequest) { // Validate access using mesh headers if (!accessControl.validateAccess(httpRequest, "GET")) { log.warn("Access denied for order retrieval from: {}", httpRequest.getHeader("x-forwarded-for")); return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); } Order order = orderService.getOrder(orderId); return ResponseEntity.ok(order); } } Observability with Traefik Mesh
1. Metrics Configuration
# service-monitor.yaml apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: order-service-monitor namespace: default labels: app: order-service spec: selector: matchLabels: app: order-service endpoints: - port: http-metrics path: /actuator/prometheus interval: 15s
2. Spring Boot Actuator Configuration
# application.yaml management: endpoints: web: exposure: include: health,info,metrics,prometheus endpoint: health: show-details: always metrics: enabled: true prometheus: enabled: true metrics: export: prometheus: enabled: true distribution: percentiles-histogram: http.server.requests: true tags: application: order-service namespace: ${MESH_NAMESPACE:default} cluster: ${MESH_CLUSTER:default} 3. Custom Mesh Metrics
@Component public class MeshMetricsService { private final MeterRegistry meterRegistry; private final Map<String, Counter> serviceCallCounters = new ConcurrentHashMap<>(); private final Map<String, Timer> serviceCallTimers = new ConcurrentHashMap<>(); public MeshMetricsService(MeterRegistry meterRegistry) { this.meterRegistry = meterRegistry; } public void recordServiceCall(String targetService, String operation, long durationMs, boolean success) { String counterName = "mesh.service.calls"; String timerName = "mesh.service.duration"; // Get or create counter Counter counter = serviceCallCounters.computeIfAbsent(targetService, service -> Counter.builder(counterName) .tag("target_service", service) .tag("operation", operation) .tag("success", String.valueOf(success)) .register(meterRegistry)); counter.increment(); // Record duration Timer timer = serviceCallTimers.computeIfAbsent(targetService, service -> Timer.builder(timerName) .tag("target_service", service) .tag("operation", operation) .register(meterRegistry)); timer.record(durationMs, TimeUnit.MILLISECONDS); } public void recordCircuitBreakerState(String breakerName, String state) { Counter.builder("mesh.circuitbreaker.state") .tag("breaker", breakerName) .tag("state", state) .register(meterRegistry) .increment(); } public void recordRetryAttempt(String operation, int attempt) { Counter.builder("mesh.retry.attempts") .tag("operation", operation) .tag("attempt", String.valueOf(attempt)) .register(meterRegistry) .increment(); } } 4. Distributed Tracing Integration
@Component public class MeshTracingService { private final Tracer tracer; private final MeshProperties meshProperties; public MeshTracingService(MeshProperties meshProperties) { this.meshProperties = meshProperties; this.tracer = GlobalOpenTelemetry.getTracer("mesh-tracing"); } public Span startMeshSpan(String operationName, String targetService) { return tracer.spanBuilder(operationName) .setAttribute("mesh.service", meshProperties.getServiceName()) .setAttribute("mesh.target.service", targetService) .setAttribute("mesh.namespace", meshProperties.getNamespace()) .setAttribute("mesh.operation", operationName) .startSpan(); } public void recordMeshSpanError(Span span, String errorType, String errorMessage) { span.setStatus(StatusCode.ERROR, errorMessage); span.setAttribute("error.type", errorType); span.recordException(new RuntimeException(errorMessage)); } public void addMeshAttributes(Span span, Map<String, Object> attributes) { attributes.forEach((key, value) -> { if (value instanceof String) { span.setAttribute(key, (String) value); } else if (value instanceof Long) { span.setAttribute(key, (Long) value); } else if (value instanceof Double) { span.setAttribute(key, (Double) value); } else if (value instanceof Boolean) { span.setAttribute(key, (Boolean) value); } }); } } Resilience Patterns with Traefik Mesh
1. Circuit Breaker Configuration
# application-resilience.yaml resilience4j: circuitbreaker: instances: orderService: register-health-indicator: true sliding-window-size: 10 sliding-window-type: COUNT_BASED minimum-number-of-calls: 5 permitted-number-of-calls-in-half-open-state: 3 automatic-transition-from-open-to-half-open-enabled: true wait-duration-in-open-state: 10s failure-rate-threshold: 50 event-consumer-buffer-size: 10 retry: instances: orderService: max-attempts: 3 wait-duration: 500ms ratelimiter: instances: orderService: limit-for-period: 10 limit-refresh-period: 1s timeout-duration: 0 allow-health-indicator-to-fail: true subscribe-for-events: true timelimiter: instances: orderService: timeout-duration: 5s cancel-running-future: true
2. Resilient Service Client
@Service @Slf4j public class ResilientInventoryClient { private final RestTemplate restTemplate; private final MeshMetricsService metricsService; private final MeshTracingService tracingService; private final CircuitBreakerRegistry circuitBreakerRegistry; private final RetryRegistry retryRegistry; private final CircuitBreaker circuitBreaker; private final Retry retry; public ResilientInventoryClient(RestTemplate restTemplate, MeshMetricsService metricsService, MeshTracingService tracingService, CircuitBreakerRegistry circuitBreakerRegistry, RetryRegistry retryRegistry) { this.restTemplate = restTemplate; this.metricsService = metricsService; this.tracingService = tracingService; this.circuitBreakerRegistry = circuitBreakerRegistry; this.retryRegistry = retryRegistry; this.circuitBreaker = circuitBreakerRegistry.circuitBreaker("inventoryService"); this.retry = retryRegistry.retry("inventoryService"); } public boolean checkInventory(List<OrderItem> items) { Span span = tracingService.startMeshSpan("inventory.check", "inventory-service"); try (Scope scope = span.makeCurrent()) { long startTime = System.currentTimeMillis(); // Use Circuit Breaker and Retry Boolean result = Retry.decorateSupplier(retry, CircuitBreaker.decorateSupplier(circuitBreaker, () -> callInventoryService(items)) ).get(); long duration = System.currentTimeMillis() - startTime; // Record metrics metricsService.recordServiceCall("inventory-service", "check", duration, true); span.setStatus(StatusCode.OK); return Boolean.TRUE.equals(result); } catch (Exception e) { long duration = System.currentTimeMillis() - startTime; metricsService.recordServiceCall("inventory-service", "check", duration, false); tracingService.recordMeshSpanError(span, "inventory_call_failed", e.getMessage()); log.error("Inventory check failed: {}", e.getMessage()); throw new ServiceException("Inventory service unavailable", e); } finally { span.end(); } } private Boolean callInventoryService(List<OrderItem> items) { String url = "http://inventory-service.default.svc.cluster.local:8080/api/inventory/check"; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity<List<OrderItem>> request = new HttpEntity<>(items, headers); ResponseEntity<Boolean> response = restTemplate.exchange( url, HttpMethod.POST, request, Boolean.class); return response.getBody(); } @EventListener public void onCircuitBreakerEvent(CircuitBreakerOnStateTransitionEvent event) { metricsService.recordCircuitBreakerState( event.getCircuitBreakerName(), event.getStateTransition().getToState().name() ); log.info("Circuit breaker state changed: {} -> {}", event.getCircuitBreakerName(), event.getStateTransition().getToState()); } @EventListener public void onRetryEvent(RetryOnRetryEvent event) { metricsService.recordRetryAttempt( "inventoryService", event.getNumberOfRetryAttempts() ); log.warn("Retry attempt {} for operation: {}", event.getNumberOfRetryAttempts(), event.getName()); } } Kubernetes Deployment with Traefik Mesh
1. Deployment Configuration
# deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: order-service namespace: default labels: app: order-service version: v1 spec: replicas: 3 selector: matchLabels: app: order-service version: v1 template: metadata: labels: app: order-service version: v1 annotations: prometheus.io/scrape: "true" prometheus.io/path: "/actuator/prometheus" prometheus.io/port: "8080" traefik.mesh.smi-spec.io/traffic-split: "order-service" spec: serviceAccountName: order-service containers: - name: order-service image: order-service:latest ports: - containerPort: 8080 name: http - containerPort: 8081 name: http-metrics env: - name: MESH_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: MESH_SERVICE_NAME value: "order-service" - name: MESH_CLUSTER value: "production" - name: JAVA_OPTS value: "-javaagent:/app/opentelemetry-javaagent.jar" - name: OTEL_SERVICE_NAME value: "order-service" - name: OTEL_EXPORTER_OTLP_ENDPOINT value: "http://otel-collector:4317" resources: requests: memory: "512Mi" cpu: "250m" limits: memory: "1Gi" cpu: "500m" livenessProbe: httpGet: path: /actuator/health port: 8081 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /actuator/health/readiness port: 8081 initialDelaySeconds: 15 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: order-service namespace: default labels: app: order-service spec: selector: app: order-service ports: - port: 8080 targetPort: 8080 name: http - port: 8081 targetPort: 8081 name: http-metrics type: ClusterIP
2. ServiceAccount for Access Control
# serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata: name: order-service namespace: default labels: app: order-service --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: default name: order-service rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["services"] verbs: ["get", "list"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: order-service namespace: default subjects: - kind: ServiceAccount name: order-service namespace: default roleRef: kind: Role name: order-service apiGroup: rbac.authorization.k8s.io
Testing and Monitoring
1. Integration Tests
@SpringBootTest @ActiveProfiles("test") @Testcontainers class MeshIntegrationTest { @Container static GenericContainer<?> traefikMesh = new GenericContainer<>("traefik/mesh:latest") .withExposedPorts(8080, 8081) .withEnv("MESH_ENABLED", "true"); @Autowired private TestRestTemplate restTemplate; @Test void shouldRouteThroughMesh() { // Test that requests are properly routed through the mesh ResponseEntity<String> response = restTemplate.getForEntity( "/api/orders/123", String.class); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); // Verify mesh headers are present HttpHeaders headers = response.getHeaders(); assertThat(headers.get("x-mesh-service")).isNotNull(); } } 2. Health Checks
@Component public class MeshHealthIndicator implements HealthIndicator { private final MeshProperties meshProperties; private final RestTemplate restTemplate; public MeshHealthIndicator(MeshProperties meshProperties, RestTemplate restTemplate) { this.meshProperties = meshProperties; this.restTemplate = restTemplate; } @Override public Health health() { if (!meshProperties.isEnabled()) { return Health.unknown() .withDetail("mesh", "disabled") .build(); } try { // Test mesh connectivity by calling a known service ResponseEntity<String> response = restTemplate.getForEntity( "http://traefik-mesh-dashboard.default.svc.cluster.local:8080", String.class); if (response.getStatusCode().is2xxSuccessful()) { return Health.up() .withDetail("mesh", "connected") .withDetail("service", meshProperties.getServiceName()) .build(); } else { return Health.down() .withDetail("mesh", "unreachable") .withDetail("status", response.getStatusCodeValue()) .build(); } } catch (Exception e) { return Health.down() .withDetail("mesh", "connection_failed") .withDetail("error", e.getMessage()) .build(); } } } Best Practices
- Use Semantic Service Names: Follow naming conventions for services
- Configure Proper Resource Limits: Ensure services have appropriate CPU/memory
- Implement Graceful Shutdown: Handle SIGTERM properly
- Use mTLS: Enable mutual TLS for service-to-service communication
- Monitor Mesh Metrics: Track mesh performance and health
- Test Failure Scenarios: Verify resilience patterns work correctly
// Graceful shutdown example @Component public class MeshShutdownHandler { private static final Logger logger = LoggerFactory.getLogger(MeshShutdownHandler.class); @EventListener public void onShutdown(ContextClosedEvent event) { logger.info("Application shutting down, cleaning up mesh connections..."); // Close connections, release resources // Traefik Mesh will handle draining connections logger.info("Mesh shutdown completed"); } } Conclusion
Traefik Mesh integration provides:
- Simplified service mesh without complex configuration
- Automatic mTLS for secure service communication
- Traffic management with canary deployments and load balancing
- Access control through SMI specifications
- Observability with built-in metrics and tracing
By implementing the patterns shown above, Java applications can fully leverage Traefik Mesh capabilities while maintaining clean separation of concerns. The mesh handles the infrastructure concerns, allowing applications to focus on business logic while benefiting from robust service mesh features.