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