A Spring Boot application designed for testing Kubernetes deployments with configurable service enablement. This application allows you to selectively enable/disable services (MariaDB, Redis, Kafka, FTP) through configuration files, making it perfect for testing different deployment scenarios in K8s environments.
- Configurable Service Deployment: Enable/disable services via YAML configuration
- K8s Testing Ready: Optimized for Kubernetes deployment testing
- Resource Efficient: No unnecessary beans or processes when services are disabled
| Service | Configuration Key | Description |
|---|---|---|
| MariaDB | mariadb |
Database connectivity and DAO operations |
| Redis | redis |
Caching and session management |
| Kafka | kafka |
Message streaming and event processing |
| FTP | ftp |
File transfer operations and scheduling |
spring:
application:
name: java-k8s-prod
profiles:
active: k8s
app:
# Database Configuration
mariadb:
url: jdbc:mysql://mariadb-primary:3306/demo_db
username: ${DB_USERNAME:root}
password: ${DB_PASSWORD:root}
driver-class-name: com.mysql.cj.jdbc.Driver
pool-name: DatasourceK8s
connection-timeout: 30000
idle-timeout: 1200000
# Redis Configuration
redis:
host: ${REDIS_HOST:redis-service}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
# Kafka Configuration
kafka:
bootstrap-servers: ${KAFKA_BOOTSTRAP_SERVERS:kafka-service:9092}
group-id: ${KAFKA_GROUP_ID:stateless-group}
auto-offset-reset: earliest
enable-auto-commit: true
# FTP Configuration
ftp-config:
host: ${FTP_HOST:ftp-service}
port: ${FTP_PORT:21}
username: ${FTP_USERNAME:ftpuser}
password: ${FTP_PASSWORD:ftppass}
dir: ${FTP_DIR:/uploads}
passiveMode: true
# Service Deployment Control
deployments:
- mariadb
- redis
- kafka
# - ftp # Comment out to disable FTP service
# Management Endpoints
management:
endpoints:
web:
exposure:
include: health,info,metrics,env
endpoint:
health:
show-details: alwaysspring:
application:
name: java-k8s-local
profiles:
active: local
server:
port: 8080
app:
# Database Configuration (Local)
mariadb:
url: jdbc:mysql://192.168.110.196:30006/demo_db
username: root
password: nam123
driver-class-name: com.mysql.cj.jdbc.Driver
pool-name: DatasourceLocal
connection-timeout: 30000
idle-timeout: 1200000
# Redis Configuration (Local)
redis:
host: 192.168.110.196
port: 30079
password: nam123
# Kafka Configuration (Local)
kafka:
bootstrap-servers: localhost:9092
group-id: stateless-local-group
auto-offset-reset: earliest
enable-auto-commit: true
# FTP Configuration (Local)
ftp-config:
host: 192.168.110.196
port: 30212
username: ems
password: Ems@2021
dir: /home/ems
passiveMode: true
# Service Deployment Control
deployments:
- mariadb
- redis
- kafka
# - ftp # Disable FTP for local testing
# Management Endpoints
management:
endpoints:
web:
exposure:
include: env, configprops, health
endpoint:
env:
show-values: ALWAYSThe sync_docker.sh script handles the complete build and deployment process:
# Make script executable
chmod +x sync_docker.sh
# Run deployment
./sync_docker.shWhat the script does:
- Builds the JAR file using Gradle
- Creates Docker image with both local and DockerHub tags
- Pushes to local registry (
docker-registry:4000/java-stateless) - Pushes to DockerHub (
nemisolv/java-stateless) - Runs the container locally
- Cleans up old images
# Build image
docker build -t java-stateless:latest .
# Tag for registries
docker tag java-stateless:latest docker-registry:4000/java-stateless:latest
docker tag java-stateless:latest nemisolv/java-stateless:latest
# Push to registries
docker push docker-registry:4000/java-stateless:latest
docker push nemisolv/java-stateless:latest
# Run container
docker run -d --name java-stateless -p 8080:8080 java-stateless:latestThis application supports 2 configuration methods for K8s deployment:
- Use environment variables in deployment
- Sensitive data stored in Kubernetes Secrets
- Easy to manage and secure
- Mount
application-k8s.yamlvia ConfigMap - Override configuration files at
/config/ - Support for complex configuration scenarios
apiVersion: v1
kind: Secret
metadata:
name: java-stateless-secrets
namespace: default
type: Opaque
data:
# Base64 encoded values
# To encode: echo -n "value" | base64
db-username: cm9vdA== # root
db-password: cm9vdA== # root
redis-password: "" # empty
ftp-username: ZnRwdXNlcg== # ftpuser
ftp-password: ZnRwcGFzcw== # ftppassapiVersion: v1
kind: ConfigMap
metadata:
name: java-stateless-config
namespace: default
data:
application-k8s.yaml: |
spring:
application:
name: java-k8s-prod
profiles:
active: k8s
app:
# Database Configuration
mariadb:
url: jdbc:mysql://mariadb-primary:3306/demo_db
username: ${DB_USERNAME:root}
password: ${DB_PASSWORD:root}
driver-class-name: com.mysql.cj.jdbc.Driver
pool-name: DatasourceK8s
connection-timeout: 30000
idle-timeout: 1200000
# Redis Configuration
redis:
host: ${REDIS_HOST:redis-service}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
# Kafka Configuration
kafka:
bootstrap-servers: ${KAFKA_BOOTSTRAP_SERVERS:kafka-service:9092}
group-id: ${KAFKA_GROUP_ID:stateless-group}
auto-offset-reset: earliest
enable-auto-commit: true
# FTP Configuration
ftp-config:
host: ${FTP_HOST:ftp-service}
port: ${FTP_PORT:21}
username: ${FTP_USERNAME:ftpuser}
password: ${FTP_PASSWORD:ftppass}
dir: ${FTP_DIR:/uploads}
passiveMode: true
# Service Deployment Control
deployments:
- mariadb
- redis
- kafka
# - ftp # Comment out to disable FTP service
# Management Endpoints
management:
endpoints:
web:
exposure:
include: health,info,metrics,env
endpoint:
health:
show-details: alwaysapiVersion: apps/v1
kind: Deployment
metadata:
name: java-stateless
labels:
app: java-stateless
annotations:
# This annotation will change when ConfigMap changes, triggering a restart
checksum/config: ""
spec:
replicas: 2
selector:
matchLabels:
app: java-stateless
template:
metadata:
labels:
app: java-stateless
annotations:
# This annotation will change when ConfigMap changes, triggering a restart
checksum/config: ""
spec:
containers:
- name: java-stateless
image: nemisolv/java-stateless:latest
ports:
- containerPort: 8080
env:
# Method 1: Environment Variables
- name: SPRING_PROFILES_ACTIVE
value: "k8s"
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: java-stateless-secrets
key: db-username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: java-stateless-secrets
key: db-password
- name: REDIS_HOST
value: "redis-service"
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: java-stateless-secrets
key: redis-password
- name: KAFKA_BOOTSTRAP_SERVERS
value: "kafka-service:9092"
- name: FTP_USERNAME
valueFrom:
secretKeyRef:
name: java-stateless-secrets
key: ftp-username
- name: FTP_PASSWORD
valueFrom:
secretKeyRef:
name: java-stateless-secrets
key: ftp-password
volumeMounts:
# Method 2: ConfigMap File Override (Optional)
- name: config-volume
mountPath: /config
readOnly: true
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 60
periodSeconds: 30
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
volumes:
# Method 2: ConfigMap File Override (Optional)
- name: config-volume
configMap:
name: java-stateless-config
---
apiVersion: v1
kind: Service
metadata:
name: java-stateless-service
spec:
selector:
app: java-stateless
ports:
- port: 8080
targetPort: 8080
type: LoadBalancerSpring Boot configuration priority (highest to lowest):
- Environment Variables (Method 1)
- ConfigMap files at
/config/(Method 2) - Default application.yaml in JAR
Use the provided deployment script:
# Deploy everything
cd k8s
./deploy.sh
# Or deploy manually
kubectl apply -f secrets.yaml
kubectl apply -f configmap.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
./update-configmap-checksum.sh# Deploy without ConfigMap (only env vars)
kubectl apply -f secrets.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml# Deploy with ConfigMap file override
kubectl apply -f secrets.yaml
kubectl apply -f configmap.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
./update-configmap-checksum.sh#!/bin/bash
# update-configmap-checksum.sh
NAMESPACE=${NAMESPACE:-default}
DEPLOYMENT_NAME=${DEPLOYMENT_NAME:-java-stateless}
CONFIGMAP_NAME=${CONFIGMAP_NAME:-java-stateless-config}
# Get the current ConfigMap resource version
CONFIG_CHECKSUM=$(kubectl get configmap $CONFIGMAP_NAME -n $NAMESPACE -o jsonpath='{.metadata.resourceVersion}')
# Update the deployment annotation
kubectl patch deployment $DEPLOYMENT_NAME -n $NAMESPACE -p "{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"checksum/config\":\"$CONFIG_CHECKSUM\"}}}}}"
echo "Updated deployment $DEPLOYMENT_NAME with ConfigMap checksum: $CONFIG_CHECKSUM"app:
deployments:
- mariadb
# - redis
# - kafka
# - ftpUse case: Test database connectivity and JPA operations only.
app:
deployments:
- mariadb
- redis
# - kafka
# - ftpUse case: Test database with Redis caching layer.
app:
deployments:
- mariadb
- redis
- kafka
- ftpUse case: Test complete application with all services.
app:
deployments:
- kafka
# - mariadb
# - redis
# - ftpUse case: Test Kafka message processing without database.
# View container logs
docker logs java-stateless
# Follow logs
docker logs -f java-stateless
# View K8s pod logs
kubectl logs -f deployment/java-stateless- Health endpoint:
http://localhost:8080/actuator/health - Info endpoint:
http://localhost:8080/actuator/info - Metrics:
http://localhost:8080/actuator/metrics
When services are enabled:
INFO - MariaDB service is enabled for bean: JdbcDataSourceConfig
INFO - Redis service is enabled for bean: RedisService
INFO - Kafka service is enabled for bean: KafkaService
INFO - FTP scheduling is enabled. Upload and list file tasks will run every 10 seconds.
When services are disabled:
WARN - FTP service is not enabled in deployment configuration. Service will be created but functionality may be limited.
WARN - FTP scheduling is disabled. Upload and list file tasks will be skipped.
- Java 17+
- Gradle 7+
- Docker
- Kubernetes cluster (for K8s testing)
# Clone repository
git clone <repository-url>
cd stateless
# Run application
./gradlew bootRun
# Run with specific profile
./gradlew bootRun --args='--spring.profiles.active=local'# Build JAR
./gradlew build
# Run tests
./gradlew test
# Clean build
./gradlew clean build- Configure services in
application-local.yamlorapplication-k8s.yaml - Run locally:
./gradlew bootRun - Deploy with Docker:
./sync_docker.sh - Deploy to K8s: Apply K8s manifests
- Monitor: Check logs and health endpoints
- Services are only instantiated when enabled in the
deploymentslist - Scheduled tasks automatically skip when required services are disabled
- Configuration supports environment variables for K8s deployment
- All services include proper health checks and monitoring endpoints