Article
Micronaut is a modern JVM-based framework designed for building modular, easily testable microservices and serverless applications. When combined with Kubernetes, it provides a powerful platform for building scalable, resilient cloud-native applications with minimal resource consumption and fast startup times.
What is Micronaut Kubernetes Integration?
Micronaut provides first-class support for Kubernetes through dedicated modules that enable service discovery, configuration management, client-side load balancing, and health checks specifically designed for Kubernetes environments.
Key Benefits for Java Developers:
- Fast Startup Time: Compile-time dependency injection reduces startup time
- Low Memory Footprint: Minimal runtime reflection usage
- Native Kubernetes Support: Built-in service discovery and configuration
- Reactive Programming: Built-in support for reactive streams
- GraalVM Native Image: Support for native compilation
- Minimal Boilerplate: Reduced configuration through annotations
Setup and Dependencies
1. Maven Configuration
<!-- pom.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>micronaut-kubernetes-app</artifactId>
<version>1.0.0</version>
<properties>
<micronaut.version>4.2.0</micronaut.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<exec.mainClass>com.example.Application</exec.mainClass>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-bom</artifactId>
<version>${micronaut.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Micronaut Core -->
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-inject</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-http-server-netty</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-http-client</artifactId>
<scope>compile</scope>
</dependency>
<!-- Kubernetes Integration -->
<dependency>
<groupId>io.micronaut.kubernetes</groupId>
<artifactId>micronaut-kubernetes-discovery-client</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut.kubernetes</groupId>
<artifactId>micronaut-kubernetes-client</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut.kubernetes</groupId>
<artifactId>micronaut-kubernetes-informer</artifactId>
<scope>compile</scope>
</dependency>
<!-- Health Checks -->
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-management</artifactId>
<scope>compile</scope>
</dependency>
<!-- Configuration -->
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-runtime</artifactId>
<scope>compile</scope>
</dependency>
<!-- Metrics -->
<dependency>
<groupId>io.micronaut.micrometer</groupId>
<artifactId>micronaut-micrometer-core</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.micronaut.micrometer</groupId>
<artifactId>micronaut-micrometer-registry-prometheus</artifactId>
<scope>compile</scope>
</dependency>
<!-- Validation -->
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-validation</artifactId>
<scope>compile</scope>
</dependency>
<!-- Testing -->
<dependency>
<groupId>io.micronaut.test</groupId>
<artifactId>micronaut-test-junit5</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.micronaut.maven</groupId>
<artifactId>micronaut-maven-plugin</artifactId>
<version>${micronaut.version}</version>
</plugin>
</plugins>
</build>
</project>
2. Gradle Configuration
// build.gradle
plugins {
id("java")
id("io.micronaut.application") version "4.2.0"
}
version = "1.0.0"
group = "com.example"
repositories {
mavenCentral()
}
micronaut {
runtime("netty")
testRuntime("junit5")
processing {
incremental(true)
annotations("com.example.*")
}
}
dependencies {
annotationProcessor("io.micronaut:micronaut-http-validation")
annotationProcessor("io.micronaut:micronaut-validation")
implementation("io.micronaut:micronaut-http-client")
implementation("io.micronaut:micronaut-http-server-netty")
implementation("io.micronaut:micronaut-runtime")
implementation("io.micronaut:micronaut-validation")
implementation("io.micronaut:micronaut-management")
// Kubernetes
implementation("io.micronaut.kubernetes:micronaut-kubernetes-discovery-client")
implementation("io.micronaut.kubernetes:micronaut-kubernetes-client")
implementation("io.micronaut.kubernetes:micronaut-kubernetes-informer")
// Metrics
implementation("io.micronaut.micrometer:micronaut-micrometer-core")
implementation("io.micronaut.micrometer:micronaut-micrometer-registry-prometheus")
// Logging
runtimeOnly("ch.qos.logback:logback-classic")
// Testing
testImplementation("io.micronaut.test:micronaut-test-junit5")
}
application {
mainClass.set("com.example.Application")
}
java {
sourceCompatibility = JavaVersion.toVersion("17")
targetCompatibility = JavaVersion.toVersion("17")
}
Basic Application Setup
1. Application Configuration
# src/main/resources/application.yml micronaut: application: name: order-service version: 1.0.0 server: port: 8080 cors: enabled: true metrics: enabled: true export: prometheus: enabled: true step: PT1M descriptions: true management: endpoints: enabled: true health: enabled: true sensitive: false details-visible: ANONYMOUS info: enabled: true metrics: enabled: true loggers: enabled: true # Kubernetes Configuration kubernetes: client: namespace: default config: from-api: true discovery: enabled: true mode: endpoint service-name: order-service health-check: enabled: true port: 8080 path: /health # Logging logger: levels: io.micronaut.kubernetes: INFO com.example: DEBUG # Endpoints endpoints: health: enabled: true sensitive: false path: /health info: enabled: true sensitive: false path: /info metrics: enabled: true sensitive: false path: /metrics loggers: enabled: true write-sensitive: false path: /loggers
2. Main Application Class
package com.example;
import io.micronaut.runtime.Micronaut;
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class, args);
}
}
3. Basic REST Controller
package com.example.controller;
import io.micronaut.http.annotation.*;
import io.micronaut.http.HttpStatus;
import io.micronaut.scheduling.TaskExecutors;
import io.micronaut.scheduling.annotation.ExecuteOn;
import jakarta.validation.Valid;
import java.util.List;
@Controller("/api/orders")
@ExecuteOn(TaskExecutors.IO)
public class OrderController {
@Get
public List<Order> getOrders() {
return List.of(
new Order("1", "Pending", 99.99),
new Order("2", "Completed", 149.99)
);
}
@Get("/{id}")
public Order getOrder(String id) {
return new Order(id, "Processing", 199.99);
}
@Post
@Status(HttpStatus.CREATED)
public Order createOrder(@Body @Valid Order order) {
return order;
}
@Put("/{id}")
public Order updateOrder(String id, @Body @Valid Order order) {
return new Order(id, order.status(), order.amount());
}
@Delete("/{id}")
@Status(HttpStatus.NO_CONTENT)
public void deleteOrder(String id) {
// Delete logic
}
public record Order(String id, String status, double amount) {}
}
Kubernetes Service Discovery
1. Service Discovery Configuration
# src/main/resources/application.yml kubernetes: discovery: enabled: true mode: endpoint includes: - user-service - inventory-service - payment-service health-check: enabled: true timeout: 5s interval: 30s port: 8080 path: /health micronaut: http: services: user-service: urls: - http://user-service:8080 inventory-service: urls: - http://inventory-service:8080 payment-service: urls: - http://payment-service:8080
2. Declarative HTTP Client
package com.example.client;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Header;
import io.micronaut.http.client.annotation.Client;
import io.reactivex.rxjava3.core.Single;
import jakarta.validation.Valid;
import java.util.List;
@Client(id = "user-service") // Uses Kubernetes service discovery
public interface UserServiceClient {
@Get("/api/users/{userId}")
Single<User> getUser(String userId);
@Post("/api/users")
Single<User> createUser(@Body @Valid User user);
@Get("/api/users")
Single<List<User>> getUsers();
record User(String id, String name, String email, String status) {}
}
@Client(id = "inventory-service")
public interface InventoryServiceClient {
@Get("/api/inventory/{productId}")
Single<Inventory> getInventory(String productId);
@Post("/api/inventory/{productId}/reserve")
Single<Inventory> reserveInventory(String productId, int quantity);
record Inventory(String productId, int available, int reserved) {}
}
3. Using Service Discovery in Controller
package com.example.controller;
import com.example.client.UserServiceClient;
import com.example.client.InventoryServiceClient;
import io.micronaut.http.annotation.*;
import io.micronaut.scheduling.TaskExecutors;
import io.micronaut.scheduling.annotation.ExecuteOn;
import jakarta.inject.Inject;
import io.reactivex.rxjava3.core.Single;
@Controller("/api/orders")
@ExecuteOn(TaskExecutors.IO)
public class OrderProcessingController {
@Inject
private UserServiceClient userServiceClient;
@Inject
private InventoryServiceClient inventoryServiceClient;
@Post("/process")
public Single<OrderResponse> processOrder(@Body OrderRequest request) {
return userServiceClient.getUser(request.userId())
.flatMap(user -> {
if (!"ACTIVE".equals(user.status())) {
return Single.error(new RuntimeException("User is not active"));
}
return inventoryServiceClient.reserveInventory(request.productId(), request.quantity());
})
.map(inventory -> new OrderResponse(
"ORDER_CREATED",
"Order processed successfully",
inventory.available()
));
}
public record OrderRequest(String userId, String productId, int quantity) {}
public record OrderResponse(String status, String message, int availableQuantity) {}
}
Health Checks and Readiness
1. Custom Health Indicators
package com.example.health;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.health.HealthStatus;
import io.micronaut.management.health.indicator.HealthIndicator;
import io.micronaut.management.health.indicator.HealthResult;
import jakarta.inject.Singleton;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
@Singleton
public class DatabaseHealthIndicator implements HealthIndicator {
private final DatabaseService databaseService;
public DatabaseHealthIndicator(DatabaseService databaseService) {
this.databaseService = databaseService;
}
@Override
@NonNull
public Publisher<HealthResult> getResult() {
return Mono.fromCallable(() -> {
boolean isHealthy = databaseService.isConnected();
Map<String, Object> details = new HashMap<>();
details.put("database", "postgresql");
details.put("connection", isHealthy ? "established" : "failed");
HealthStatus status = isHealthy ? HealthStatus.UP : HealthStatus.DOWN;
return HealthResult.builder("database")
.status(status)
.details(details)
.build();
});
}
}
@Singleton
class ExternalServiceHealthIndicator implements HealthIndicator {
@Override
@NonNull
public Publisher<HealthResult> getResult() {
return Mono.fromCallable(() -> {
// Check external service connectivity
boolean isHealthy = checkExternalService();
Map<String, Object> details = new HashMap<>();
details.put("service", "payment-gateway");
details.put("responseTime", "45ms");
return HealthResult.builder("payment-service")
.status(isHealthy ? HealthStatus.UP : HealthStatus.DOWN)
.details(details)
.build();
});
}
private boolean checkExternalService() {
// Implementation to check external service
return true;
}
}
2. Kubernetes Liveness and Readiness
# src/main/resources/application.yml endpoints: health: enabled: true sensitive: false details-visible: ANONYMOUS liveness: enabled: true path: /health/liveness readiness: enabled: true path: /health/readiness info: enabled: true sensitive: false management: health: defaults: enabled: true disk-space: enabled: true threshold: 100MB cpu: enabled: true load-average: 4.0
3. Custom Readiness Check
package com.example.health;
import io.micronaut.context.annotation.Requires;
import io.micronaut.health.HealthStatus;
import io.micronaut.management.health.indicator.HealthIndicator;
import io.micronaut.management.health.indicator.HealthResult;
import jakarta.inject.Singleton;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
@Singleton
@Requires(bean = ApplicationStartupTracker.class)
public class ApplicationReadinessHealthIndicator implements HealthIndicator {
private final ApplicationStartupTracker startupTracker;
public ApplicationReadinessHealthIndicator(ApplicationStartupTracker startupTracker) {
this.startupTracker = startupTracker;
}
@Override
public Publisher<HealthResult> getResult() {
return Mono.fromCallable(() -> {
boolean isReady = startupTracker.isApplicationReady();
return HealthResult.builder("application")
.status(isReady ? HealthStatus.UP : HealthStatus.DOWN)
.detail("startup-complete", isReady)
.detail("services-initialized", startupTracker.getInitializedServices())
.build();
});
}
}
@Singleton
class ApplicationStartupTracker {
private volatile boolean applicationReady = false;
private volatile int initializedServices = 0;
public void markServiceInitialized() {
initializedServices++;
if (initializedServices >= 3) { // All required services initialized
applicationReady = true;
}
}
public boolean isApplicationReady() {
return applicationReady;
}
public int getInitializedServices() {
return initializedServices;
}
}
Kubernetes Configuration Management
1. ConfigMap and Secret Integration
# src/main/resources/application.yml micronaut: config-client: enabled: true kubernetes: client: config-maps: enabled: true names: - app-config - feature-flags secrets: enabled: true names: - database-secrets - api-keys # External configuration kubernetes: config: enabled: true sources: - name: app-config namespace: default - name: feature-flags namespace: default secrets: - name: database-secrets namespace: default
2. Configuration Beans
package com.example.config;
import io.micronaut.context.annotation.ConfigurationProperties;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
@ConfigurationProperties("app")
public class ApplicationConfig {
@NotBlank
private String name;
@NotBlank
private String version;
private DatabaseConfig database;
private SecurityConfig security;
private FeatureFlags features;
// Getters and Setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
public DatabaseConfig getDatabase() { return database; }
public void setDatabase(DatabaseConfig database) { this.database = database; }
public SecurityConfig getSecurity() { return security; }
public void setSecurity(SecurityConfig security) { this.security = security; }
public FeatureFlags getFeatures() { return features; }
public void setFeatures(FeatureFlags features) { this.features = features; }
@ConfigurationProperties("database")
public static class DatabaseConfig {
@NotBlank
private String url;
@NotBlank
private String username;
private int poolSize = 10;
private long connectionTimeout = 30000;
// Getters and Setters
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public int getPoolSize() { return poolSize; }
public void setPoolSize(int poolSize) { this.poolSize = poolSize; }
public long getConnectionTimeout() { return connectionTimeout; }
public void setConnectionTimeout(long connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
}
@ConfigurationProperties("security")
public static class SecurityConfig {
private boolean enabled = true;
private String jwtSecret;
private long tokenExpiration = 3600000; // 1 hour
// Getters and Setters
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public String getJwtSecret() { return jwtSecret; }
public void setJwtSecret(String jwtSecret) { this.jwtSecret = jwtSecret; }
public long getTokenExpiration() { return tokenExpiration; }
public void setTokenExpiration(long tokenExpiration) {
this.tokenExpiration = tokenExpiration;
}
}
@ConfigurationProperties("features")
public static class FeatureFlags {
private boolean newSearchEnabled = false;
private boolean premiumFeatures = false;
private boolean experimentalApi = false;
// Getters and Setters
public boolean isNewSearchEnabled() { return newSearchEnabled; }
public void setNewSearchEnabled(boolean newSearchEnabled) {
this.newSearchEnabled = newSearchEnabled;
}
public boolean isPremiumFeatures() { return premiumFeatures; }
public void setPremiumFeatures(boolean premiumFeatures) {
this.premiumFeatures = premiumFeatures;
}
public boolean isExperimentalApi() { return experimentalApi; }
public void setExperimentalApi(boolean experimentalApi) {
this.experimentalApi = experimentalApi;
}
}
}
3. Using Configuration in Services
package com.example.service;
import com.example.config.ApplicationConfig;
import jakarta.inject.Singleton;
import jakarta.inject.Inject;
@Singleton
public class OrderService {
private final ApplicationConfig config;
private final DatabaseService databaseService;
@Inject
public OrderService(ApplicationConfig config, DatabaseService databaseService) {
this.config = config;
this.databaseService = databaseService;
}
public void processOrder(Order order) {
// Use feature flags
if (config.getFeatures().isNewSearchEnabled()) {
// Use new search algorithm
processWithNewAlgorithm(order);
} else {
// Use legacy algorithm
processWithLegacyAlgorithm(order);
}
// Use database configuration
databaseService.configure(
config.getDatabase().getUrl(),
config.getDatabase().getUsername(),
config.getDatabase().getPoolSize()
);
}
private void processWithNewAlgorithm(Order order) {
// New processing logic
}
private void processWithLegacyAlgorithm(Order order) {
// Legacy processing logic
}
}
Kubernetes Deployment Manifests
1. Deployment Manifest
# k8s/deployment.yml apiVersion: apps/v1 kind: Deployment metadata: name: order-service namespace: default labels: app: order-service version: "1.0.0" team: backend spec: replicas: 3 selector: matchLabels: app: order-service template: metadata: labels: app: order-service version: "1.0.0" annotations: prometheus.io/scrape: "true" prometheus.io/port: "8080" prometheus.io/path: "/metrics" spec: serviceAccountName: order-service-account containers: - name: order-service image: my-registry/order-service:1.0.0 ports: - containerPort: 8080 name: http - containerPort: 9090 name: management env: - name: JAVA_OPTS value: "-Xmx512m -Xms256m -XX:+UseG1GC" - name: MICRONAUT_ENVIRONMENTS value: "kubernetes" - name: KUBERNETES_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace livenessProbe: httpGet: path: /health/liveness port: 8080 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 readinessProbe: httpGet: path: /health/readiness port: 8080 initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 3 startupProbe: httpGet: path: /health/readiness port: 8080 initialDelaySeconds: 0 periodSeconds: 5 failureThreshold: 30 resources: requests: memory: "512Mi" cpu: "250m" limits: memory: "1Gi" cpu: "500m" securityContext: runAsNonRoot: true runAsUser: 1000 allowPrivilegeEscalation: false capabilities: drop: - ALL --- apiVersion: v1 kind: Service metadata: name: order-service labels: app: order-service spec: ports: - port: 80 targetPort: 8080 protocol: TCP name: http - port: 9090 targetPort: 9090 protocol: TCP name: management selector: app: order-service type: ClusterIP
2. ConfigMap and Secrets
# k8s/config.yml apiVersion: v1 kind: ConfigMap metadata: name: app-config data: application.yml: | app: name: order-service version: 1.0.0 database: url: jdbc:postgresql://postgresql:5432/orders poolSize: 15 connectionTimeout: 30000 features: newSearchEnabled: true premiumFeatures: false experimentalApi: false logging: level: com.example: INFO --- apiVersion: v1 kind: Secret metadata: name: database-secrets type: Opaque data: database-password: cG9zdGdyZXMxMjM= # base64 encoded api-key: bXktc2VjcmV0LWFwaS1rZXk= # base64 encoded
3. Horizontal Pod Autoscaler
# k8s/hpa.yml apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: order-service spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: order-service minReplicas: 3 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 - type: Resource resource: name: memory target: type: Utilization averageUtilization: 80 - type: Pods pods: metric: name: http_requests_per_second target: type: AverageValue averageValue: "100"
Advanced Features
1. Kubernetes Informers
package com.example.informer;
import io.kubernetes.client.informer.ResourceEventHandler;
import io.kubernetes.client.informer.SharedIndexInformer;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import io.micronaut.kubernetes.client.informer.Informer;
import jakarta.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class ConfigMapInformer {
private static final Logger LOG = LoggerFactory.getLogger(ConfigMapInformer.class);
@Informer(apiType = V1ConfigMap.class)
public SharedIndexInformer<V1ConfigMap> configMapInformer() {
return new SharedIndexInformer<V1ConfigMap>() {
@Override
public void addEventHandler(ResourceEventHandler<V1ConfigMap> handler) {
// Handle ConfigMap events
}
};
}
public void onConfigMapAdded(V1ConfigMap configMap) {
LOG.info("ConfigMap added: {}", configMap.getMetadata().getName());
// Update application configuration dynamically
}
public void onConfigMapUpdated(V1ConfigMap oldConfigMap, V1ConfigMap newConfigMap) {
LOG.info("ConfigMap updated: {}", newConfigMap.getMetadata().getName());
// Handle configuration updates
}
public void onConfigMapDeleted(V1ConfigMap configMap) {
LOG.info("ConfigMap deleted: {}", configMap.getMetadata().getName());
// Handle configuration removal
}
}
2. Distributed Tracing
package com.example.tracing;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.tracing.annotation.NewSpan;
import io.micronaut.tracing.annotation.SpanTag;
import jakarta.inject.Inject;
@Controller("/api/orders")
public class TracedOrderController {
@Inject
private OrderService orderService;
@Get("/{orderId}")
@NewSpan("get-order-details")
public Order getOrder(@SpanTag("order.id") String orderId) {
return orderService.findOrder(orderId);
}
@Get("/{orderId}/history")
@NewSpan("get-order-history")
public OrderHistory getOrderHistory(@SpanTag("order.id") String orderId) {
return orderService.getOrderHistory(orderId);
}
}
Build and Deployment
1. Dockerfile
FROM eclipse-temurin:17-jre as builder WORKDIR /app COPY target/micronaut-kubernetes-app-*.jar app.jar RUN java -Djarmode=layertools -jar app.jar extract FROM eclipse-temurin:17-jre RUN addgroup -g 1000 -S micronaut && adduser -u 1000 -S micronaut -G micronaut USER micronaut WORKDIR /app COPY --from=builder /app/dependencies/ ./ COPY --from=builder /app/spring-boot-loader/ ./ COPY --from=builder /app/snapshot-dependencies/ ./ COPY --from=builder /app/application/ ./ EXPOSE 8080 9090 ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
2. Maven Build Script
#!/bin/bash
# build-and-deploy.sh
set -e
ENVIRONMENT=${1:-dev}
VERSION=${2:-1.0.0}
echo "Building Micronaut application for $ENVIRONMENT"
# Build application
mvn clean package -DskipTests
# Build Docker image
docker build -t my-registry/order-service:$VERSION .
if [ "$ENVIRONMENT" == "prod" ]; then
# Push to registry
docker push my-registry/order-service:$VERSION
# Deploy to Kubernetes
kubectl apply -f k8s/
kubectl rollout status deployment/order-service
fi
echo "Build and deployment completed successfully!"
Best Practices
1. Resource Optimization
- Use compile-time dependency injection
- Enable GraalVM native image for production
- Configure proper resource limits
- Use reactive programming for I/O operations
2. Monitoring and Observability
- Enable Micrometer metrics
- Configure distributed tracing
- Use structured logging
- Implement comprehensive health checks
3. Security
- Use Kubernetes service accounts
- Implement security contexts
- Use secrets for sensitive data
- Enable TLS for service communication
Conclusion
Micronaut with Kubernetes provides a powerful combination for building cloud-native Java applications:
- Fast Startup: Compile-time DI enables rapid startup times
- Low Memory: Minimal runtime reflection reduces memory footprint
- Kubernetes Native: Built-in service discovery and configuration
- Reactive Ready: Built-in support for reactive programming
- Production Ready: Comprehensive health checks and metrics
By leveraging Micronaut's Kubernetes integration, Java teams can build highly scalable, resilient microservices that are optimized for cloud-native environments while maintaining developer productivity and application performance.
Pyroscope Profiling in Java
Explains how to use Pyroscope for continuous profiling in Java applications, helping developers analyze CPU and memory usage patterns to improve performance and identify bottlenecks.
https://macronepal.com/blog/pyroscope-profiling-in-java/
OpenTelemetry Metrics in Java: Comprehensive Guide
Provides a complete guide to collecting and exporting metrics in Java using OpenTelemetry, including counters, histograms, gauges, and integration with monitoring tools. (MACRO NEPAL)
https://macronepal.com/blog/opentelemetry-metrics-in-java-comprehensive-guide/
OTLP Exporter in Java: Complete Guide for OpenTelemetry
Explains how to configure OTLP exporters in Java to send telemetry data such as traces, metrics, and logs to monitoring systems using HTTP or gRPC protocols. (MACRO NEPAL)
https://macronepal.com/blog/otlp-exporter-in-java-complete-guide-for-opentelemetry/
Thanos Integration in Java: Global View of Metrics
Explains how to integrate Thanos with Java monitoring systems to create a scalable global metrics view across multiple Prometheus instances.
https://macronepal.com/blog/thanos-integration-in-java-global-view-of-metrics
Time Series with InfluxDB in Java: Complete Guide (Version 2)
Explains how to manage time-series data using InfluxDB in Java applications, including storing, querying, and analyzing metrics data.
https://macronepal.com/blog/time-series-with-influxdb-in-java-complete-guide-2
Time Series with InfluxDB in Java: Complete Guide
Provides an overview of integrating InfluxDB with Java for time-series data handling, including monitoring applications and managing performance metrics.
https://macronepal.com/blog/time-series-with-influxdb-in-java-complete-guide
Implementing Prometheus Remote Write in Java (Version 2)
Explains how to configure Java applications to send metrics data to Prometheus-compatible systems using the remote write feature for scalable monitoring.
https://macronepal.com/blog/implementing-prometheus-remote-write-in-java-a-complete-guide-2
Implementing Prometheus Remote Write in Java: Complete Guide
Provides instructions for sending metrics from Java services to Prometheus servers, enabling centralized monitoring and real-time analytics.
https://macronepal.com/blog/implementing-prometheus-remote-write-in-java-a-complete-guide
Building a TileServer GL in Java: Vector and Raster Tile Server
Explains how to build a TileServer GL in Java for serving vector and raster map tiles, useful for geographic visualization and mapping applications.
https://macronepal.com/blog/building-a-tileserver-gl-in-java-vector-and-raster-tile-server
Indoor Mapping in Java
Explains how to create indoor mapping systems in Java, including navigation inside buildings, spatial data handling, and visualization techniques.