google-site-verification: google61fe8ba583a51912.html
Jenkins X for Kubernetes CI/CD in Java

Comprehensive Jenkins X Implementation Guide

1. Jenkins X Configuration Files

jenkins-x.yml - Main Pipeline Configuration

# jenkins-x.yml buildPack: maven-java11 projectType: libraries dockerRegistry: ghcr.io pipelineConfig: pipelines: pullRequest: pipeline: agent: image: maven:3.8.6-openjdk-11-slim stages: - name: setup steps: - name: setup-environment command: | echo "Setting up Java CI environment" java -version mvn -version resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 512Mi - name: tests steps: - name: unit-tests command: | mvn clean test resources: requests: cpu: 1000m memory: 2Gi limits: cpu: 2000m memory: 4Gi - name: integration-tests command: | mvn verify -Pintegration-tests resources: requests: cpu: 1000m memory: 2Gi limits: cpu: 2000m memory: 4Gi - name: analysis steps: - name: code-quality command: | mvn sonar:sonar -Dsonar.projectKey=my-java-app resources: requests: cpu: 1000m memory: 2Gi limits: cpu: 2000m memory: 4Gi - name: security-scan command: | mvn org.owasp:dependency-check-maven:check resources: requests: cpu: 1000m memory: 2Gi limits: cpu: 2000m memory: 4Gi release: pipeline: agent: image: maven:3.8.6-openjdk-11-slim environment: - name: DOCKER_REGISTRY value: ghcr.io - name: VERSION value: ${VERSION} - name: MAVEN_OPTS value: -Xmx2g stages: - name: build-and-package steps: - name: build command: | mvn clean package -DskipTests -Pprod resources: requests: cpu: 2000m memory: 4Gi limits: cpu: 4000m memory: 8Gi - name: docker-build command: | docker build -t ${DOCKER_REGISTRY}/${ORG}/${APP_NAME}:${VERSION} . - name: docker-push command: | docker push ${DOCKER_REGISTRY}/${ORG}/${APP_NAME}:${VERSION} - name: quality-gate steps: - name: code-coverage command: | mvn jacoco:check - name: vulnerability-scan command: | trivy image ${DOCKER_REGISTRY}/${ORG}/${APP_NAME}:${VERSION} - name: deploy-to-staging steps: - name: deploy command: | jx step helm apply --name ${APP_NAME} resources: requests: cpu: 500m memory: 1Gi limits: cpu: 1000m memory: 2Gi - name: validation steps: - name: smoke-tests command: | mvn verify -Psmoke-tests - name: api-tests command: | mvn verify -Papi-tests - name: performance-tests command: | mvn gatling:test -Pperformance-tests - name: promote-to-production steps: - name: promote command: | jx promote --app ${APP_NAME} --version ${VERSION} --env production when: branch: main containerOptions: resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 512Mi env: - name: MAVEN_OPTS value: -Xmx1024m - name: GRADLE_OPTS value: -Xmx1024m # Preview environments configuration previewEnvironment: ingress: domain: preview.example.com resources: requests: cpu: 200m memory: 256Mi limits: cpu: 500m memory: 512Mi

2. Kubernetes Deployment Manifests

Helm Chart Structure

charts/ └── my-java-app/ ├── Chart.yaml ├── values.yaml ├── templates/ │ ├── deployment.yaml │ ├── service.yaml │ ├── ingress.yaml │ ├── configmap.yaml │ └── hpa.yaml

charts/my-java-app/Chart.yaml

apiVersion: v2 name: my-java-app description: A Spring Boot Java application type: application version: 0.1.0 appVersion: "1.0.0" dependencies: - name: postgresql version: 12.1.0 repository: https://charts.bitnami.com/bitnami condition: postgresql.enabled

charts/my-java-app/values.yaml

# Default values for my-java-app replicaCount: 2 image: repository: ghcr.io/my-org/my-java-app tag: latest pullPolicy: IfNotPresent nameOverride: "" fullnameOverride: "" service: type: ClusterIP port: 8080 targetPort: 8080 ingress: enabled: true className: "nginx" annotations: kubernetes.io/ingress.class: nginx cert-manager.io/cluster-issuer: letsencrypt-prod nginx.ingress.kubernetes.io/rewrite-target: / hosts: - host: my-app.example.com paths: - path: / pathType: Prefix tls: - secretName: my-java-app-tls hosts: - my-app.example.com resources: requests: cpu: 500m memory: 1Gi limits: cpu: 1000m memory: 2Gi autoscaling: enabled: true minReplicas: 2 maxReplicas: 10 targetCPUUtilizationPercentage: 80 targetMemoryUtilizationPercentage: 85 probes: liveness: path: /actuator/health/liveness initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 readiness: path: /actuator/health/readiness initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 3 startup: path: /actuator/health/readiness initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 10 config: spring: profiles: active: "kubernetes" logging: level: com.example: INFO org.springframework: INFO postgresql: enabled: true auth: postgresPassword: "changeme" database: "mydatabase" username: "myuser" password: "mypassword" # Jenkins X specific values jenkins-x: preview: enabled: true domain: preview.example.com

charts/my-java-app/templates/deployment.yaml

apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "my-java-app.fullname" . }} labels: {{- include "my-java-app.labels" . | nindent 4 }} app.kubernetes.io/component: api app.kubernetes.io/part-of: {{ .Chart.Name }} spec: replicas: {{ .Values.replicaCount }} selector: matchLabels: {{- include "my-java-app.selectorLabels" . | nindent 6 }} template: metadata: labels: {{- include "my-java-app.selectorLabels" . | nindent 8 }} version: {{ .Chart.AppVersion | quote }} annotations: checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} prometheus.io/scrape: "true" prometheus.io/port: "8080" prometheus.io/path: "/actuator/prometheus" spec: containers: - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http containerPort: 8080 protocol: TCP livenessProbe: httpGet: path: {{ .Values.probes.liveness.path }} port: http initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }} periodSeconds: {{ .Values.probes.liveness.periodSeconds }} timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }} failureThreshold: {{ .Values.probes.liveness.failureThreshold }} readinessProbe: httpGet: path: {{ .Values.probes.readiness.path }} port: http initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }} periodSeconds: {{ .Values.probes.readiness.periodSeconds }} timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }} failureThreshold: {{ .Values.probes.readiness.failureThreshold }} startupProbe: httpGet: path: {{ .Values.probes.startup.path }} port: http initialDelaySeconds: {{ .Values.probes.startup.initialDelaySeconds }} periodSeconds: {{ .Values.probes.startup.periodSeconds }} timeoutSeconds: {{ .Values.probes.startup.timeoutSeconds }} failureThreshold: {{ .Values.probes.startup.failureThreshold }} resources: {{- toYaml .Values.resources | nindent 12 }} env: - name: SPRING_PROFILES_ACTIVE value: {{ .Values.config.spring.profiles.active | quote }} - name: JAVA_OPTS value: "-Xmx{{ .Values.resources.limits.memory | replace "Gi" "g" | replace "Mi" "m" }} -XX:+UseContainerSupport -XX:+UseG1GC" - name: MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE value: "health,info,prometheus,metrics" envFrom: - configMapRef: name: {{ include "my-java-app.fullname" . }}-config - secretRef: name: {{ include "my-java-app.fullname" . }}-secrets securityContext: runAsNonRoot: true runAsUser: 1000 allowPrivilegeEscalation: false capabilities: drop: - ALL imagePullSecrets: - name: ghcr-secret securityContext: runAsNonRoot: true runAsUser: 1000 fsGroup: 1000

charts/my-java-app/templates/hpa.yaml

{{- if .Values.autoscaling.enabled }} apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: {{ include "my-java-app.fullname" . }} labels: {{- include "my-java-app.labels" . | nindent 4 }} spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: {{ include "my-java-app.fullname" . }} minReplicas: {{ .Values.autoscaling.minReplicas }} maxReplicas: {{ .Values.autoscaling.maxReplicas }} metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} - type: Resource resource: name: memory target: type: Utilization averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} behavior: scaleDown: stabilizationWindowSeconds: 300 policies: - type: Pods value: 1 periodSeconds: 60 - type: Percent value: 50 periodSeconds: 60 selectPolicy: Min scaleUp: stabilizationWindowSeconds: 60 policies: - type: Pods value: 2 periodSeconds: 60 - type: Percent value: 50 periodSeconds: 60 selectPolicy: Max {{- end }}

3. Java Application with Kubernetes Support

Spring Boot Application Configuration

// Application.java package com.example.myapp; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; @SpringBootApplication @EnableConfigurationProperties(ApplicationProperties.class) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

Kubernetes Health Indicators

// KubernetesHealthIndicator.java package com.example.myapp.health; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component; import org.springframework.jdbc.core.JdbcTemplate; @Component public class KubernetesHealthIndicator implements HealthIndicator { private final JdbcTemplate jdbcTemplate; private final ApplicationProperties properties; public KubernetesHealthIndicator(JdbcTemplate jdbcTemplate, ApplicationProperties properties) { this.jdbcTemplate = jdbcTemplate; this.properties = properties; } @Override public Health health() { try { // Check database connectivity jdbcTemplate.execute("SELECT 1"); // Check application-specific health if (!isApplicationHealthy()) { return Health.down() .withDetail("application", "Application health check failed") .build(); } return Health.up() .withDetail("database", "Connected") .withDetail("application", "Healthy") .withDetail("version", properties.getVersion()) .build(); } catch (Exception e) { return Health.down() .withDetail("database", "Connection failed: " + e.getMessage()) .build(); } } private boolean isApplicationHealthy() { // Add application-specific health checks return true; } } // LivenessHealthIndicator.java package com.example.myapp.health; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component; @Component public class LivenessHealthIndicator implements HealthIndicator { @Override public Health health() { // Simple liveness check - application is running return Health.up().build(); } } // ReadinessHealthIndicator.java package com.example.myapp.health; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component; import org.springframework.jdbc.core.JdbcTemplate; @Component public class ReadinessHealthIndicator implements HealthIndicator { private final JdbcTemplate jdbcTemplate; public ReadinessHealthIndicator(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public Health health() { try { // Check if application is ready to serve traffic jdbcTemplate.execute("SELECT 1"); return Health.up() .withDetail("database", "Ready") .withDetail("service", "Ready to serve traffic") .build(); } catch (Exception e) { return Health.down() .withDetail("database", "Not ready: " + e.getMessage()) .build(); } } }

Application Properties Configuration

// ApplicationProperties.java package com.example.myapp.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "app") public class ApplicationProperties { private String version = "1.0.0"; private String environment = "development"; private Database database = new Database(); private Security security = new Security(); private Kubernetes kubernetes = new Kubernetes(); // Getters and setters public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getEnvironment() { return environment; } public void setEnvironment(String environment) { this.environment = environment; } public Database getDatabase() { return database; } public void setDatabase(Database database) { this.database = database; } public Security getSecurity() { return security; } public void setSecurity(Security security) { this.security = security; } public Kubernetes getKubernetes() { return kubernetes; } public void setKubernetes(Kubernetes kubernetes) { this.kubernetes = kubernetes; } public static class Database { private int connectionTimeout = 30000; private int maxPoolSize = 10; public int getConnectionTimeout() { return connectionTimeout; } public void setConnectionTimeout(int connectionTimeout) { this.connectionTimeout = connectionTimeout; } public int getMaxPoolSize() { return maxPoolSize; } public void setMaxPoolSize(int maxPoolSize) { this.maxPoolSize = maxPoolSize; } } public static class Security { private boolean enabled = true; private String jwtSecret; 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 static class Kubernetes { private boolean enabled = false; private String namespace = "default"; private String clusterName; public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public String getNamespace() { return namespace; } public void setNamespace(String namespace) { this.namespace = namespace; } public String getClusterName() { return clusterName; } public void setClusterName(String clusterName) { this.clusterName = clusterName; } } }

4. Dockerfile for Java Application

# Multi-stage build for Java application FROM maven:3.8.6-openjdk-11 AS builder # Install additional tools RUN apt-get update && apt-get install -y \ curl \ && rm -rf /var/lib/apt/lists/* WORKDIR /app # Copy pom.xml and download dependencies COPY pom.xml . RUN mvn dependency:go-offline -B # Copy source code and build application COPY src ./src RUN mvn clean package -DskipTests -Pprod # Runtime stage FROM openjdk:11-jre-slim # Install required packages RUN apt-get update && apt-get install -y \ curl \ wait-for-it \ && rm -rf /var/lib/apt/lists/* # Create non-root user RUN groupadd -r appuser && useradd -r -g appuser appuser WORKDIR /app # Copy built artifact from builder stage COPY --from=builder /app/target/*.jar app.jar # Copy entrypoint script COPY entrypoint.sh . RUN chmod +x entrypoint.sh # Create necessary directories and set permissions RUN mkdir -p /app/logs && \ chown -R appuser:appuser /app # Switch to non-root user USER appuser # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \ CMD curl -f http://localhost:8080/actuator/health/liveness || exit 1 # JVM options for container ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+UseG1GC -XX:+ExitOnOutOfMemoryError" # Expose port EXPOSE 8080 # Use entrypoint script ENTRYPOINT ["/app/entrypoint.sh"]

Entrypoint Script

#!/bin/bash # entrypoint.sh set -e echo "Starting Java application..." # Wait for dependencies if needed if [ -n "$WAIT_FOR_HOSTS" ]; then IFS=',' read -ra HOSTS <<< "$WAIT_FOR_HOSTS" for host in "${HOSTS[@]}"; do echo "Waiting for $host..." /usr/bin/wait-for-it "$host" --timeout=60 done fi # Set default JVM options if not provided if [ -z "$JAVA_OPTS" ]; then JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0" fi # Start the application with exec to replace the shell process exec java $JAVA_OPTS -jar app.jar "$@"

5. Maven Configuration with Jenkins X Profiles

pom.xml

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>my-java-app</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <name>My Java Application</name> <description>Spring Boot application for Kubernetes</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.0</version> <relativePath/> </parent> <properties> <java.version>11</java.version> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- Test dependencies --> <junit.version>5.8.2</junit.version> <testcontainers.version>1.17.3</testcontainers.version> <!-- Quality tools --> <jacoco.version>0.8.8</jacoco.version> <sonar-maven-plugin.version>3.9.1.2184</sonar-maven-plugin.version> <owasp-dependency-check.version>7.1.1</owasp-dependency-check.version> <!-- Performance testing --> <gatling.version>3.9.0</gatling.version> </properties> <dependencies> <!-- Spring Boot Starters --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <!-- Database --> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <!-- Kubernetes --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-kubernetes-client-all</artifactId> <version>2.1.3</version> </dependency> <!-- Monitoring --> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency> <!-- Test Dependencies --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>junit-jupiter</artifactId> <version>${testcontainers.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>postgresql</artifactId> <version>${testcontainers.version}</version> <scope>test</scope> </dependency> <!-- Performance Testing --> <dependency> <groupId>io.gatling.highcharts</groupId> <artifactId>gatling-charts-highcharts</artifactId> <version>${gatling.version}</version> <scope>test</scope> </dependency> </dependencies> <profiles> <!-- Production profile --> <profile> <id>prod</id> <properties> <spring.profiles.active>prod</spring.profiles.active> </properties> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <executable>true</executable> <layers> <enabled>true</enabled> </layers> </configuration> <executions> <execution> <goals> <goal>build-info</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </profile> <!-- Integration Tests Profile --> <profile> <id>integration-tests</id> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <version>3.0.0-M7</version> <executions> <execution> <goals> <goal>integration-test</goal> <goal>verify</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </profile> <!-- Smoke Tests Profile --> <profile> <id>smoke-tests</id> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M7</version> <configuration> <includes> <include>**/*SmokeTest.java</include> </includes> </configuration> </plugin> </plugins> </build> </profile> <!-- Performance Tests Profile --> <profile> <id>performance-tests</id> <build> <plugins> <plugin> <groupId>io.gatling</groupId> <artifactId>gatling-maven-plugin</artifactId> <version>${gatling.version}</version> <configuration> <simulationsFolder>src/test/gatling/simulations</simulationsFolder> <resourcesFolder>src/test/gatling/resources</resourcesFolder> </configuration> </plugin> </plugins> </build> </profile> </profiles> <build> <plugins> <!-- JaCoCo Code Coverage --> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>${jacoco.version}</version> <executions> <execution> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>report</id> <phase>test</phase> <goals> <goal>report</goal> </goals> </execution> <execution> <id>check</id> <phase>verify</phase> <goals> <goal>check</goal> </goals> <configuration> <rules> <rule> <element>BUNDLE</element> <limits> <limit> <counter>INSTRUCTION</counter> <value>COVEREDRATIO</value> <minimum>0.80</minimum> </limit> </limits> </rule> </rules> </configuration> </execution> </executions> </plugin> <!-- SonarQube Analysis --> <plugin> <groupId>org.sonarsource.scanner.maven</groupId> <artifactId>sonar-maven-plugin</artifactId> <version>${sonar-maven-plugin.version}</version> </plugin> <!-- OWASP Dependency Check --> <plugin> <groupId>org.owasp</groupId> <artifactId>dependency-check-maven</artifactId> <version>${owasp-dependency-check.version}</version> <executions> <execution> <goals> <goal>check</goal> </goals> </execution> </executions> <configuration> <failBuildOnAnyVulnerability>true</failBuildOnAnyVulnerability> <suppressionFile>dependency-check-suppressions.xml</suppressionFile> </configuration> </plugin> <!-- Git commit info --> <plugin> <groupId>pl.project13.maven</groupId> <artifactId>git-commit-id-plugin</artifactId> <version>4.9.10</version> <executions> <execution> <goals> <goal>revision</goal> </goals> </execution> </executions> <configuration> <generateGitPropertiesFile>true</generateGitPropertiesFile> <injectAllReactorProjects>true</injectAllReactorProjects> <failOnNoGitDirectory>false</failOnNoGitDirectory> </configuration> </plugin> </plugins> </build> </project>

6. Jenkins X Quickstart Location Configuration

jx-requirements.yml

cluster: clusterName: my-java-cluster environmentGitOwner: my-org project: my-java-project provider: gke zone: us-central1-a cluster: project: my-gcp-project zone: us-central1-a environments: - key: dev - key: staging - key: production ingress: domain: example.com externalDNS: true tls: email: [email protected] enabled: true production: true storage: logs: enabled: true url: gs://my-java-app-logs reports: enabled: true url: gs://my-java-app-reports repository: enabled: true url: gs://my-java-app-repository versionStream: url: https://github.com/jenkins-x/jenkins-x-versions ref: v1.0.0 webhook: lighthouse

jx-edit.yml

buildPack: maven-java11 dockerRegistry: ghcr.io quickstartLocations: - owner: jenkins-x includes: - name: spring-boot-* - name: java-* excludes: - name: *-nodejs - name: *-python

7. Custom Pipeline Extensions

jenkins-x-custom-steps.yaml

apiVersion: jenkins.io/v1 kind: PipelineActivity spec: pipeline: custom-java-pipeline steps: - name: security-scan command: mvn org.owasp:dependency-check-maven:check - name: performance-test command: mvn gatling:test -Pperformance-tests - name: container-scan command: | trivy image ${DOCKER_REGISTRY}/${ORG}/${APP_NAME}:${VERSION} - name: database-migration command: | mvn liquibase:update -Pprod - name: api-docs command: | mvn springdoc:generate-docs

8. Application Configuration Files

application-kubernetes.yml

# Kubernetes-specific configuration spring: config: activate: on-profile: "kubernetes" datasource: url: jdbc:postgresql://${POSTGRES_HOST:localhost}:${POSTGRES_PORT:5432}/${POSTGRES_DB:mydatabase} username: ${POSTGRES_USER:myuser} password: ${POSTGRES_PASSWORD:mypassword} hikari: maximum-pool-size: 10 connection-timeout: 30000 jpa: hibernate: ddl-auto: validate show-sql: false properties: hibernate: dialect: org.hibernate.dialect.PostgreSQLDialect jdbc: lob: non_contextual_creation: true cloud: kubernetes: reload: enabled: true mode: event management: endpoints: web: exposure: include: health,info,prometheus,metrics endpoint: health: show-details: always probes: enabled: true prometheus: enabled: true health: livenessstate: enabled: true readinessstate: enabled: true app: environment: kubernetes kubernetes: enabled: true namespace: ${KUBERNETES_NAMESPACE:default}

application-prod.yml

# Production configuration spring: config: activate: on-profile: "prod" jackson: default-property-inclusion: non_null logging: level: com.example: INFO org.springframework: WARN org.hibernate: WARN pattern: level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]" app: environment: production security: enabled: true

9. Integration Tests with Testcontainers

// BaseIntegrationTest.java package com.example.myapp.integration; import org.junit.jupiter.api.BeforeAll; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Testcontainers @ActiveProfiles("integration-test") public abstract class BaseIntegrationTest { @Container static PostgreSQLContainer<?> postgreSQLContainer = new PostgreSQLContainer<>("postgres:13") .withDatabaseName("testdb") .withUsername("testuser") .withPassword("testpass"); @DynamicPropertySource static void configureProperties(DynamicPropertyRegistry registry) { registry.add("spring.datasource.url", postgreSQLContainer::getJdbcUrl); registry.add("spring.datasource.username", postgreSQLContainer::getUsername); registry.add("spring.datasource.password", postgreSQLContainer::getPassword); } @BeforeAll static void beforeAll() { postgreSQLContainer.start(); } } // UserControllerIntegrationTest.java package com.example.myapp.integration; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; class UserControllerIntegrationTest extends BaseIntegrationTest { @Autowired private TestRestTemplate restTemplate; @Test void shouldReturnHealthStatus() { ResponseEntity<String> response = restTemplate.getForEntity("/actuator/health", String.class); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); } @Test void shouldCreateAndRetrieveUser() { // Integration test implementation // This would test the full flow from controller to database } }

This comprehensive Jenkins X setup provides:

  • Complete CI/CD pipeline with multiple stages
  • Kubernetes-optimized Java application
  • Multi-environment deployment (dev, staging, production)
  • Preview environments for each PR
  • Comprehensive testing strategy
  • Security scanning and quality gates
  • Performance testing integration
  • Health checks and monitoring
  • Auto-scaling configuration
  • GitOps approach for environment management

The setup ensures your Java applications are built, tested, and deployed efficiently on Kubernetes using Jenkins X best practices.

Leave a Reply

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


Macro Nepal Helper