google-site-verification: google61fe8ba583a51912.html
Serverless Java Functions: OpenFaaS Java Templates

OpenFaaS (Functions as a Service) is a framework for building serverless functions with Docker and Kubernetes. Java templates for OpenFaaS enable you to run Java functions in a serverless environment with minimal overhead and fast cold starts.


What is OpenFaaS?

OpenFaaS makes it simple to deploy both functions and existing code to Kubernetes. Key features:

  • Container-native functions
  • Simple scaling and auto-scaling
  • Built-in UI and CLI
  • Multi-language support including Java
  • Enterprise-ready with async processing

OpenFaaS Java Architecture

[Java Function] → [OpenFaaS Template] → [Docker Image] → [OpenFaaS Gateway] → [Kubernetes] | | | | | Business logic Build wrapper Containerized API Gateway & Orchestration & dependencies function scaling engine platform

Hands-On Tutorial: Building Custom OpenFaaS Java Templates

Let's build comprehensive Java templates for OpenFaaS supporting various Java frameworks and use cases.

Step 1: Project Structure Setup

openfaas-java-templates/ ├── templates/ │ ├── java11-maven/ │ ├── java17-gradle/ │ ├── java21-spring-native/ │ ├── quarkus-native/ │ └── micronaut-function/ ├── functions/ │ ├── user-service/ │ ├── image-processor/ │ ├── data-transformer/ │ └── api-gateway/ ├── build-scripts/ └── kubernetes/

Step 2: Base Java Template Configuration

templates/java17-gradle/template.yml:

language: java17-gradle platform: x86_64 repository: https://github.com/openfaas/templates official: true description: Java 17 with Gradle template for OpenFaaS keywords: - java - gradle - openjdk - serverless - functions readme: | # Java 17 Gradle Template This template uses Java 17 with Gradle for building OpenFaaS functions. Features: - Java 17 LTS - Gradle build system - Fast cold starts with optimized JVM flags - Health checks and metrics - Multi-stage Docker build

templates/java17-gradle/Dockerfile:

# Multi-stage build for optimal image size FROM gradle:7.6-jdk17-alpine as builder WORKDIR /home/app COPY build.gradle settings.gradle ./ COPY src ./src RUN gradle build --no-daemon -x test # Runtime stage FROM openjdk:17-jre-slim # Install necessary packages RUN apt-get update && \ apt-get install -y --no-install-recommends curl && \ rm -rf /var/lib/apt/lists/* && \ apt-get clean # Create non-root user RUN addgroup --system app && adduser --system --ingroup app app WORKDIR /home/app COPY --from=builder /home/app/build/libs/*.jar app.jar COPY --from=builder /home/app/function/function.jar function.jar # OpenFaaS health check HEALTHCHECK --interval=5s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8080/_/health || exit 1 # JVM optimization for serverless ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -Xss256k -Djava.security.egd=file:/dev/./urandom" USER app EXPOSE 8080 CMD ["sh", "-c", "java $JAVA_OPTS -jar function.jar"]

templates/java17-gradle/build.gradle:

plugins { id 'java' id 'com.github.johnrengelman.shadow' version '8.1.1' } group = 'com.openfaas.function' version = '1.0.0' sourceCompatibility = '17' repositories { mavenCentral() } dependencies { // OpenFaaS function API implementation 'com.openfaas:model:1.0.2' implementation 'com.openfaas:entrypoint:1.0.2' // HTTP server implementation 'com.sun.net.httpserver:http:20070405' // JSON processing implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2' // Logging implementation 'org.slf4j:slf4j-simple:2.0.7' // Utilities implementation 'org.apache.commons:commons-lang3:3.13.0' implementation 'commons-io:commons-io:2.13.0' // Testing testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0' testImplementation 'org.assertj:assertj-core:3.24.2' } test { useJUnitPlatform() } jar { manifest { attributes( 'Main-Class': 'com.openfaas.entrypoint.App', 'Implementation-Title': project.name, 'Implementation-Version': project.version ) } } shadowJar { archiveBaseName.set('function') archiveClassifier.set('') archiveVersion.set('') mergeServiceFiles() // Relocate if necessary to avoid conflicts // relocate 'com.fasterxml.jackson', 'shadow.jackson' } build.dependsOn shadowJar

templates/java17-gradle/function/build.gradle:

dependencies { implementation project(':') } // Custom task to copy the fat JAR task copyFunctionJar(type: Copy) { from shadowJar into "${project.rootDir}/function" rename { 'function.jar' } } build.dependsOn copyFunctionJar

Step 3: Function Handler Base Classes

templates/java17-gradle/src/main/java/com/openfaas/function/core/AbstractFunction.java:

package com.openfaas.function.core; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.openfaas.model.IRequest; import com.openfaas.model.IResponse; import lombok.extern.slf4j.Slf4j; import java.util.HashMap; import java.util.Map; @Slf4j public abstract class AbstractFunction { protected final ObjectMapper objectMapper; protected AbstractFunction() { this.objectMapper = new ObjectMapper(); this.objectMapper.findAndRegisterModules(); } public abstract IResponse handle(IRequest request); protected Map<String, String> createSuccessHeaders() { Map<String, String> headers = new HashMap<>(); headers.put("Content-Type", "application/json"); headers.put("X-Powered-By", "OpenFaaS-Java"); return headers; } protected Map<String, String> createErrorHeaders() { Map<String, String> headers = new HashMap<>(); headers.put("Content-Type", "application/json"); headers.put("X-Error", "true"); return headers; } protected String toJson(Object object) { try { return objectMapper.writeValueAsString(object); } catch (JsonProcessingException e) { log.error("Failed to serialize object to JSON", e); return "{\"error\": \"Serialization failed\"}"; } } protected <T> T fromJson(String json, Class<T> clazz) { try { return objectMapper.readValue(json, clazz); } catch (JsonProcessingException e) { log.error("Failed to deserialize JSON: {}", json, e); throw new FunctionException("JSON deserialization failed", e); } } protected IResponse createSuccessResponse(Object body) { IResponse response = new com.openfaas.model.Response(); response.setStatusCode(200); response.setBody(toJson(body)); response.setHeaders(createSuccessHeaders()); return response; } protected IResponse createErrorResponse(String message, int statusCode) { IResponse response = new com.openfaas.model.Response(); response.setStatusCode(statusCode); response.setBody(toJson(Map.of( "error", true, "message", message, "timestamp", System.currentTimeMillis() ))); response.setHeaders(createErrorHeaders()); return response; } protected IResponse createValidationError(String message) { return createErrorResponse(message, 400); } protected IResponse createInternalError(String message) { return createErrorResponse(message, 500); } protected void logRequest(IRequest request) { log.info("Processing request - Method: {}, Path: {}, Body Size: {}", request.getMethod(), request.getPath(), request.getBody() != null ? request.getBody().length() : 0); } public static class FunctionException extends RuntimeException { public FunctionException(String message) { super(message); } public FunctionException(String message, Throwable cause) { super(message, cause); } } }

templates/java17-gradle/src/main/java/com/openfaas/function/core/AsyncFunction.java:

package com.openfaas.function.core; import com.openfaas.model.IRequest; import com.openfaas.model.IResponse; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public abstract class AsyncFunction extends AbstractFunction { private final ExecutorService executorService; protected AsyncFunction() { this.executorService = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors() ); } protected AsyncFunction(int threadPoolSize) { this.executorService = Executors.newFixedThreadPool(threadPoolSize); } public abstract CompletableFuture<IResponse> handleAsync(IRequest request); @Override public IResponse handle(IRequest request) { try { return handleAsync(request).get(); } catch (Exception e) { log.error("Async function execution failed", e); return createInternalError("Async execution failed: " + e.getMessage()); } } protected CompletableFuture<IResponse> processAsync(IRequest request) { return CompletableFuture.supplyAsync(() -> handle(request), executorService); } @Override protected void finalize() throws Throwable { executorService.shutdown(); super.finalize(); } }

Step 4: Sample Functions

functions/user-service/src/main/java/com/example/function/UserFunction.java:

package com.example.function; import com.openfaas.function.core.AbstractFunction; import com.openfaas.model.IRequest; import com.openfaas.model.IResponse; import lombok.extern.slf4j.Slf4j; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @Slf4j public class UserFunction extends AbstractFunction { private final Map<String, User> userStore; public UserFunction() { this.userStore = new ConcurrentHashMap<>(); // Initialize with some sample data initializeSampleData(); } @Override public IResponse handle(IRequest request) { logRequest(request); try { String method = request.getMethod(); String path = request.getPath(); return switch (method) { case "GET" -> handleGetRequest(path); case "POST" -> handlePostRequest(request); case "PUT" -> handlePutRequest(path, request); case "DELETE" -> handleDeleteRequest(path); default -> createErrorResponse("Method not allowed", 405); }; } catch (Exception e) { log.error("User function error", e); return createInternalError("Processing failed: " + e.getMessage()); } } private IResponse handleGetRequest(String path) { if ("/users".equals(path)) { // Return all users return createSuccessResponse(Map.of( "users", userStore.values(), "count", userStore.size() )); } else if (path.startsWith("/users/")) { // Return specific user String userId = extractUserId(path); User user = userStore.get(userId); if (user != null) { return createSuccessResponse(user); } else { return createErrorResponse("User not found", 404); } } else { return createErrorResponse("Not found", 404); } } private IResponse handlePostRequest(IRequest request) { try { UserCreateRequest createRequest = fromJson(request.getBody(), UserCreateRequest.class); // Validate input if (createRequest.getName() == null || createRequest.getEmail() == null) { return createValidationError("Name and email are required"); } // Create user User user = User.builder() .id(UUID.randomUUID().toString()) .name(createRequest.getName()) .email(createRequest.getEmail()) .createdAt(System.currentTimeMillis()) .build(); userStore.put(user.getId(), user); log.info("Created user: {}", user.getId()); return createSuccessResponse(Map.of( "user", user, "message", "User created successfully" )); } catch (Exception e) { return createValidationError("Invalid request body"); } } private IResponse handlePutRequest(String path, IRequest request) { String userId = extractUserId(path); User existingUser = userStore.get(userId); if (existingUser == null) { return createErrorResponse("User not found", 404); } try { UserUpdateRequest updateRequest = fromJson(request.getBody(), UserUpdateRequest.class); // Update user if (updateRequest.getName() != null) { existingUser.setName(updateRequest.getName()); } if (updateRequest.getEmail() != null) { existingUser.setEmail(updateRequest.getEmail()); } existingUser.setUpdatedAt(System.currentTimeMillis()); userStore.put(userId, existingUser); log.info("Updated user: {}", userId); return createSuccessResponse(Map.of( "user", existingUser, "message", "User updated successfully" )); } catch (Exception e) { return createValidationError("Invalid request body"); } } private IResponse handleDeleteRequest(String path) { String userId = extractUserId(path); User removedUser = userStore.remove(userId); if (removedUser != null) { log.info("Deleted user: {}", userId); return createSuccessResponse(Map.of( "message", "User deleted successfully", "userId", userId )); } else { return createErrorResponse("User not found", 404); } } private String extractUserId(String path) { return path.substring("/users/".length()); } private void initializeSampleData() { User sampleUser = User.builder() .id("sample-1") .name("John Doe") .email("[email protected]") .createdAt(System.currentTimeMillis()) .build(); userStore.put(sampleUser.getId(), sampleUser); } // DTO classes @Data @Builder @NoArgsConstructor @AllArgsConstructor public static class User { private String id; private String name; private String email; private Long createdAt; private Long updatedAt; } @Data @NoArgsConstructor @AllArgsConstructor public static class UserCreateRequest { private String name; private String email; } @Data @NoArgsConstructor @AllArgsConstructor public static class UserUpdateRequest { private String name; private String email; } }

functions/image-processor/src/main/java/com/example/function/ImageProcessorFunction.java:

package com.example.function; import com.openfaas.function.core.AbstractFunction; import com.openfaas.model.IRequest; import com.openfaas.model.IResponse; import lombok.extern.slf4j.Slf4j; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Base64; import java.util.Map; @Slf4j public class ImageProcessorFunction extends AbstractFunction { private static final int THUMBNAIL_WIDTH = 200; private static final int THUMBNAIL_HEIGHT = 200; @Override public IResponse handle(IRequest request) { logRequest(request); try { String operation = request.getQuery().get("operation"); if (operation == null) { return createValidationError("Operation parameter is required"); } return switch (operation) { case "thumbnail" -> createThumbnail(request); case "info" -> getImageInfo(request); case "convert" -> convertImage(request); default -> createErrorResponse("Unsupported operation: " + operation, 400); }; } catch (Exception e) { log.error("Image processing failed", e); return createInternalError("Image processing failed: " + e.getMessage()); } } private IResponse createThumbnail(IRequest request) { try { ImageRequest imageRequest = fromJson(request.getBody(), ImageRequest.class); byte[] imageData = Base64.getDecoder().decode(imageRequest.getImageData()); BufferedImage originalImage = ImageIO.read(new ByteArrayInputStream(imageData)); if (originalImage == null) { return createValidationError("Invalid image data"); } // Create thumbnail BufferedImage thumbnail = new BufferedImage(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, originalImage.getType()); Graphics2D g = thumbnail.createGraphics(); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.drawImage(originalImage, 0, 0, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, null); g.dispose(); // Convert back to bytes ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(thumbnail, "jpg", baos); byte[] thumbnailData = baos.toByteArray(); String thumbnailBase64 = Base64.getEncoder().encodeToString(thumbnailData); log.info("Created thumbnail - Original: {}x{}, Thumbnail: {}x{}", originalImage.getWidth(), originalImage.getHeight(), THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT); return createSuccessResponse(Map.of( "thumbnail", thumbnailBase64, "format", "jpg", "width", THUMBNAIL_WIDTH, "height", THUMBNAIL_HEIGHT, "size", thumbnailData.length )); } catch (Exception e) { return createValidationError("Invalid image data: " + e.getMessage()); } } private IResponse getImageInfo(IRequest request) { try { ImageRequest imageRequest = fromJson(request.getBody(), ImageRequest.class); byte[] imageData = Base64.getDecoder().decode(imageRequest.getImageData()); BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageData)); if (image == null) { return createValidationError("Invalid image data"); } return createSuccessResponse(Map.of( "width", image.getWidth(), "height", image.getHeight(), "type", image.getType(), "size", imageData.length )); } catch (Exception e) { return createValidationError("Invalid image data: " + e.getMessage()); } } private IResponse convertImage(IRequest request) { try { ImageConvertRequest convertRequest = fromJson(request.getBody(), ImageConvertRequest.class); byte[] imageData = Base64.getDecoder().decode(convertRequest.getImageData()); BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageData)); if (image == null) { return createValidationError("Invalid image data"); } String format = convertRequest.getFormat() != null ? convertRequest.getFormat() : "png"; ByteArrayOutputStream baos = new ByteArrayOutputStream(); boolean written = ImageIO.write(image, format, baos); if (!written) { return createErrorResponse("Unsupported format: " + format, 400); } byte[] convertedData = baos.toByteArray(); String convertedBase64 = Base64.getEncoder().encodeToString(convertedData); return createSuccessResponse(Map.of( "image", convertedBase64, "format", format, "size", convertedData.length )); } catch (Exception e) { return createValidationError("Conversion failed: " + e.getMessage()); } } // DTO classes @Data @NoArgsConstructor @AllArgsConstructor public static class ImageRequest { private String imageData; } @Data @NoArgsConstructor @AllArgsConstructor public static class ImageConvertRequest { private String imageData; private String format; } }

Step 5: Spring Native Template

templates/java21-spring-native/template.yml:

language: java21-spring-native platform: x86_64 description: Spring Boot 3 with Native Image for OpenFaaS keywords: - java - spring-boot - native - graalvm - serverless readme: | # Spring Boot Native Template This template uses Spring Boot 3 with GraalVM Native Image for ultra-fast cold starts. Features: - Spring Boot 3.2+ - GraalVM Native Image - Sub-100ms cold starts - Minimal memory footprint - Spring WebFlux for reactive programming

templates/java21-spring-native/Dockerfile:

# Build stage FROM ghcr.io/graalvm/native-image:ol8-java-21 as builder WORKDIR /build # Install necessary build tools RUN microdnf update -y && \ microdnf install -y findutils zip && \ microdnf clean all # Copy build files COPY gradlew . COPY gradle gradle COPY build.gradle settings.gradle ./ COPY src src # Build native image RUN ./gradlew nativeCompile --no-daemon # Runtime stage FROM oraclelinux:8-slim RUN microdnf update -y && \ microdnf install -y gcompat && \ microdnf clean all # Create non-root user RUN groupadd -r app && useradd -r -g app app WORKDIR /app COPY --from=builder /build/build/native/nativeCompile/function function USER app EXPOSE 8080 ENV _HANDLER=/app/function CMD ["/app/function"]

templates/java21-spring-native/src/main/java/com/example/SpringNativeFunction.java:

package com.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.ServerResponse; import static org.springframework.web.reactive.function.server.RouterFunctions.route; import static org.springframework.web.reactive.function.server.ServerResponse.ok; @SpringBootApplication public class SpringNativeFunction { public static void main(String[] args) { SpringApplication.run(SpringNativeFunction.class, args); } @RestController public static class FunctionController { @PostMapping("/**") public ResponseEntity<Object> handleRequest(@RequestBody(required = false) String body) { // Process request and return response return ResponseEntity.ok() .header("X-Powered-By", "Spring-Native-OpenFaaS") .body(Map.of( "message", "Hello from Spring Native!", "timestamp", System.currentTimeMillis(), "body", body != null ? body : "No body provided" )); } } // Alternative reactive approach @Bean public RouterFunction<ServerResponse> routes() { return route() .POST("/**", request -> ok().bodyValue(Map.of( "message", "Reactive response", "timestamp", System.currentTimeMillis() ))) .build(); } }

Step 6: Quarkus Native Template

templates/quarkus-native/src/main/java/com/example/QuarkusFunction.java:

package com.example; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.util.Map; @Path("/") public class QuarkusFunction { @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response handleRequest(Map<String, Object> input) { try { // Process the request String name = (String) input.getOrDefault("name", "World"); Map<String, Object> response = Map.of( "message", "Hello " + name + " from Quarkus Native!", "timestamp", System.currentTimeMillis(), "runtime", "Quarkus", "native", true ); return Response.ok(response) .header("X-Powered-By", "Quarkus-OpenFaaS") .build(); } catch (Exception e) { return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity(Map.of("error", e.getMessage())) .build(); } } }

templates/quarkus-native/Dockerfile:

FROM quay.io/quarkus/ubi-quarkus-native-image:22-java17 AS build WORKDIR /project COPY . . RUN ./mvnw package -Pnative -DskipTests FROM registry.access.redhat.com/ubi8/ubi-minimal:8.6 WORKDIR /work/ COPY --from=build /project/target/*-runner /work/function RUN chmod 775 /work EXPOSE 8080 CMD ["./function", "-Dquarkus.http.host=0.0.0.0"]

Step 7: Build and Deployment Scripts

build-scripts/build-function.sh:

#!/bin/bash set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # Configuration FUNCTION_NAME="${1}" TEMPLATE="${2:-java17-gradle}" REGISTRY="${REGISTRY:-ghcr.io/your-username}" TAG="${TAG:-latest}" log_info() { echo -e "${GREEN}[INFO]${NC} $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } validate_input() { if [ -z "$FUNCTION_NAME" ]; then log_error "Function name is required" echo "Usage: $0 <function-name> [template]" exit 1 fi if [ ! -d "functions/$FUNCTION_NAME" ]; then log_error "Function directory not found: functions/$FUNCTION_NAME" exit 1 fi if [ ! -d "templates/$TEMPLATE" ]; then log_error "Template not found: $TEMPLATE" exit 1 fi } build_function() { local function_dir="functions/$FUNCTION_NAME" local template_dir="templates/$TEMPLATE" local image_name="$REGISTRY/$FUNCTION_NAME:$TAG" log_info "Building function: $FUNCTION_NAME" log_info "Template: $TEMPLATE" log_info "Image: $image_name" # Copy template files to function directory cp -r "$template_dir"/* "$function_dir/" # Build the function cd "$function_dir" case $TEMPLATE in *gradle*) log_info "Building with Gradle..." ./gradlew build --no-daemon -x test ;; *maven*) log_info "Building with Maven..." ./mvnw package -DskipTests ;; *quarkus*) log_info "Building Quarkus native image..." ./mvnw package -Pnative -DskipTests ;; *) log_error "Unsupported build system for template: $TEMPLATE" exit 1 ;; esac # Build Docker image log_info "Building Docker image..." docker build -t "$image_name" . # Push to registry if [ "$PUSH" = "true" ]; then log_info "Pushing image to registry..." docker push "$image_name" fi log_info "Build completed: $image_name" } deploy_function() { local function_name="$1" local image_name="$REGISTRY/$function_name:$TAG" log_info "Deploying function: $function_name" # Deploy to OpenFaaS faas-cli deploy \ --name "$function_name" \ --image "$image_name" \ --env "JAVA_OPTS=-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0" \ --label "com.openfaas.scale.min=1" \ --label "com.openfaas.scale.max=10" \ --label "com.openfaas.scale.factor=20" \ --annotation "prometheus.io.scrape=true" \ --annotation "prometheus.io.port=8080" log_info "Function deployed: $function_name" } # Main execution main() { validate_input build_function if [ "$DEPLOY" = "true" ]; then deploy_function "$FUNCTION_NAME" fi } # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in --push) PUSH="true" shift ;; --deploy) DEPLOY="true" shift ;; --tag) TAG="$2" shift 2 ;; --registry) REGISTRY="$2" shift 2 ;; *) shift ;; esac done main

build-scripts/deploy-stack.yml:

version: 1.0 provider: name: openfaas gateway: http://127.0.0.1:8080 functions: user-service: lang: java17-gradle handler: ./functions/user-service image: ghcr.io/your-username/user-service:latest environment: JAVA_OPTS: "-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -Xss256k" LOG_LEVEL: "INFO" labels: com.openfaas.scale.min: "1" com.openfaas.scale.max: "10" com.openfaas.scale.factor: "20" environment: "production" annotations: prometheus.io.scrape: "true" prometheus.io.port: "8080" limits: memory: 256M cpu: 200m requests: memory: 128M cpu: 100m image-processor: lang: java17-gradle handler: ./functions/image-processor image: ghcr.io/your-username/image-processor:latest environment: JAVA_OPTS: "-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -Xmx128m" LOG_LEVEL: "INFO" labels: com.openfaas.scale.min: "1" com.openfaas.scale.max: "5" com.openfaas.scale.factor: "10" environment: "production" limits: memory: 512M cpu: 500m requests: memory: 256M cpu: 200m data-transformer: lang: java21-spring-native handler: ./functions/data-transformer image: ghcr.io/your-username/data-transformer:latest labels: com.openfaas.scale.min: "1" com.openfaas.scale.max: "20" environment: "production" annotations: prometheus.io.scrape: "true" limits: memory: 128M cpu: 100m requests: memory: 64M cpu: 50m

Step 8: Kubernetes Deployment

kubernetes/openfaas-namespace.yml:

apiVersion: v1 kind: Namespace metadata: name: openfaas labels: name: openfaas openfaas: "true"

kubernetes/java-function-deployment.yml:

apiVersion: apps/v1 kind: Deployment metadata: name: user-service namespace: openfaas-fn labels: app: user-service function: "true" spec: replicas: 2 selector: matchLabels: app: user-service template: metadata: labels: app: user-service function: "true" annotations: prometheus.io.scrape: "true" prometheus.io.port: "8080" spec: containers: - name: user-service image: ghcr.io/your-username/user-service:latest ports: - containerPort: 8080 name: http env: - name: JAVA_OPTS value: "-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -Xss256k -Djava.security.egd=file:/dev/./urandom" - name: LOG_LEVEL value: "INFO" resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "256Mi" cpu: "200m" livenessProbe: httpGet: path: /_/health port: 8080 initialDelaySeconds: 5 periodSeconds: 10 readinessProbe: httpGet: path: /_/health port: 8080 initialDelaySeconds: 2 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: user-service namespace: openfaas-fn labels: app: user-service function: "true" spec: type: ClusterIP ports: - port: 8080 targetPort: 8080 protocol: TCP name: http selector: app: user-service

Step 9: Monitoring and Observability

templates/java17-gradle/src/main/java/com/openfaas/function/core/MetricsCollector.java:

package com.openfaas.function.core; import io.prometheus.client.Counter; import io.prometheus.client.Gauge; import io.prometheus.client.Histogram; import io.prometheus.client.exporter.HTTPServer; import java.io.IOException; public class MetricsCollector { private static final Counter requestCounter = Counter.build() .name("function_requests_total") .help("Total number of function requests") .labelNames("function", "method", "status") .register(); private static final Histogram requestDuration = Histogram.build() .name("function_request_duration_seconds") .help("Function request duration in seconds") .labelNames("function") .register(); private static final Gauge activeRequests = Gauge.build() .name("function_active_requests") .help("Number of active requests") .labelNames("function") .register(); private static HTTPServer metricsServer; public static void startMetricsServer(int port) { try { metricsServer = new HTTPServer(port); } catch (IOException e) { throw new RuntimeException("Failed to start metrics server", e); } } public static void recordRequest(String functionName, String method, String status, long durationMs) { requestCounter.labels(functionName, method, status).inc(); requestDuration.labels(functionName).observe(durationMs / 1000.0); } public static Histogram.Timer startRequestTimer(String functionName) { activeRequests.labels(functionName).inc(); return requestDuration.labels(functionName).startTimer(); } public static void endRequestTimer(String functionName, Histogram.Timer timer) { timer.observeDuration(); activeRequests.labels(functionName).dec(); } }

Step 10: Usage Examples

Deploy a Function:

# Build and deploy user service ./build-scripts/build-function.sh user-service java17-gradle --push --deploy # Deploy with custom tag ./build-scripts/build-function.sh image-processor java17-gradle --tag v1.2.3 --push # Deploy using faas-cli faas-cli deploy -f build-scripts/deploy-stack.yml

Test Functions:

# Test user service curl -X POST http://localhost:8080/function/user-service \ -H "Content-Type: application/json" \ -d '{"name": "Alice", "email": "[email protected]"}' # Test image processor curl -X POST "http://localhost:8080/function/image-processor?operation=info" \ -H "Content-Type: application/json" \ -d '{"imageData": "base64-encoded-image"}' # Get function metrics curl http://localhost:8080/function/user-service/metrics

Monitor Functions:

# List functions faas-cli list # Check function logs faas-cli logs user-service # Scale function faas-cli scale user-service --min=3 --max=10

Best Practices

1. Performance Optimization

  • Use GraalVM Native Image for fastest cold starts
  • Optimize JVM flags for container environments
  • Implement connection pooling for external services
  • Use efficient serialization libraries

2. Resource Management

  • Set appropriate memory limits based on function requirements
  • Configure CPU requests and limits
  • Implement proper cleanup in functions
  • Use async processing for I/O operations

3. Security

  • Use non-root users in containers
  • Scan images for vulnerabilities
  • Implement input validation
  • Use secure communication between functions

Benefits of OpenFaaS Java Templates

  1. Fast Cold Starts: Optimized JVM configurations and native images
  2. Resource Efficiency: Minimal memory footprint and CPU usage
  3. Developer Experience: Familiar Java tooling and frameworks
  4. Production Ready: Built-in monitoring, scaling, and health checks
  5. Ecosystem Integration: Seamless Kubernetes and Docker integration

Conclusion

OpenFaaS Java templates provide a powerful foundation for building serverless Java functions that are:

  • High-performance with optimized cold starts
  • Resource-efficient with proper JVM tuning
  • Developer-friendly with familiar Java ecosystems
  • Production-ready with comprehensive monitoring
  • Scalable with built-in auto-scaling capabilities

By leveraging these templates and best practices, you can build robust, scalable serverless functions that take full advantage of Java's ecosystem while maintaining the benefits of serverless computing.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper