Skip to content

nemisolv/java-stateless-for-k8s-test

Repository files navigation

Stateless Spring Boot Application with K8s Deployment Testing

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.

πŸš€ Features

  • 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

πŸ“‹ Supported Services

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

βš™οΈ Configuration

Application Configuration Files

application-k8s.yaml - Kubernetes Production Environment

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: always

application-local.yaml - Local Development Environment

spring:
  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: ALWAYS

🐳 Docker Deployment

Build and Deploy Script

The sync_docker.sh script handles the complete build and deployment process:

# Make script executable
chmod +x sync_docker.sh

# Run deployment
./sync_docker.sh

What the script does:

  1. Builds the JAR file using Gradle
  2. Creates Docker image with both local and DockerHub tags
  3. Pushes to local registry (docker-registry:4000/java-stateless)
  4. Pushes to DockerHub (nemisolv/java-stateless)
  5. Runs the container locally
  6. Cleans up old images

Manual Docker Commands

# 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:latest

☸️ Kubernetes Deployment

Configuration Methods

This application supports 2 configuration methods for K8s deployment:

Method 1: Environment Variables (Recommended)

  • Use environment variables in deployment
  • Sensitive data stored in Kubernetes Secrets
  • Easy to manage and secure

Method 2: ConfigMap File Override

  • Mount application-k8s.yaml via ConfigMap
  • Override configuration files at /config/
  • Support for complex configuration scenarios

K8s Manifest Examples

1. Secrets for Sensitive Data

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==  # ftppass

2. ConfigMap for File Override (Optional)

apiVersion: 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: always

3. Deployment with Both Methods

apiVersion: 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: LoadBalancer

Configuration Priority

Spring Boot configuration priority (highest to lowest):

  1. Environment Variables (Method 1)
  2. ConfigMap files at /config/ (Method 2)
  3. Default application.yaml in JAR

Quick Deployment

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

Configuration Management

Method 1: Environment Variables Only

# Deploy without ConfigMap (only env vars)
kubectl apply -f secrets.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml

Method 2: ConfigMap File Override

# 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

ConfigMap Management Script

#!/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"

πŸ§ͺ Testing Scenarios

Scenario 1: Database-Only Testing

app:
  deployments:
    - mariadb
#    - redis
#    - kafka
#    - ftp

Use case: Test database connectivity and JPA operations only.

Scenario 2: Database + Cache Testing

app:
  deployments:
    - mariadb
    - redis
#    - kafka
#    - ftp

Use case: Test database with Redis caching layer.

Scenario 3: Full Stack Testing

app:
  deployments:
    - mariadb
    - redis
    - kafka
    - ftp

Use case: Test complete application with all services.

Scenario 4: Message Processing Only

app:
  deployments:
    - kafka
#    - mariadb
#    - redis
#    - ftp

Use case: Test Kafka message processing without database.

πŸ“Š Monitoring and Logs

Application Logs

# 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 Checks

  • Health endpoint: http://localhost:8080/actuator/health
  • Info endpoint: http://localhost:8080/actuator/info
  • Metrics: http://localhost:8080/actuator/metrics

Expected Log Output

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.

πŸ”§ Development

Prerequisites

  • Java 17+
  • Gradle 7+
  • Docker
  • Kubernetes cluster (for K8s testing)

Local Development

# Clone repository
git clone <repository-url>
cd stateless

# Run application
./gradlew bootRun

# Run with specific profile
./gradlew bootRun --args='--spring.profiles.active=local'

Building

# Build JAR
./gradlew build

# Run tests
./gradlew test

# Clean build
./gradlew clean build

πŸš€ Quick Start

  1. Configure services in application-local.yaml or application-k8s.yaml
  2. Run locally: ./gradlew bootRun
  3. Deploy with Docker: ./sync_docker.sh
  4. Deploy to K8s: Apply K8s manifests
  5. Monitor: Check logs and health endpoints

πŸ“ Notes

  • Services are only instantiated when enabled in the deployments list
  • 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

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors