google-site-verification: google61fe8ba583a51912.html
Kaniko for Container Builds in Java Applications

Comprehensive Kaniko Implementation Guide

1. Kaniko Build Configuration

Maven Plugin Configuration

<!-- pom.xml --> <project> <properties> <docker.image.name>my-java-app</docker.image.name> <docker.registry>ghcr.io</docker.registry> <docker.tag>${project.version}</docker.tag> <jib-maven-plugin.version>3.3.2</jib-maven-plugin.version> </properties> <build> <plugins> <!-- Kaniko build preparation plugin --> <plugin> <groupId>com.google.cloud.tools</groupId> <artifactId>jib-maven-plugin</artifactId> <version>${jib-maven-plugin.version}</version> <configuration> <from> <image>eclipse-temurin:11-jre</image> </from> <to> <image>${docker.registry}/${docker.image.name}:${docker.tag}</image> </to> <container> <ports> <port>8080</port> </ports> <environment> <JAVA_OPTS>-XX:+UseContainerSupport</JAVA_OPTS> </environment> <labels> <version>${project.version}</version> <built-by>kaniko</built-by> </labels> </container> </configuration> </plugin> <!-- Build helper for Docker context --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <version>3.1.0</version> <executions> <execution> <id>prepare-docker-context</id> <phase>package</phase> <configuration> <target> <copy file="${project.build.directory}/${project.build.finalName}.jar" tofile="docker-context/app.jar"/> <copy file="Dockerfile" todir="docker-context"/> <copy file="entrypoint.sh" todir="docker-context"/> </target> </configuration> <goals> <goal>run</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>

2. Dockerfile Optimized for Kaniko

# Multi-stage build optimized for Kaniko caching FROM maven:3.8.6-eclipse-temurin-11 AS builder # Create layer for dependencies WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline # Create layer for source code and build COPY src ./src RUN mvn clean package -DskipTests # Runtime stage FROM eclipse-temurin:11-jre-jammy # Install security updates and required packages RUN apt-get update && \ apt-get upgrade -y && \ apt-get install -y --no-install-recommends \ curl \ ca-certificates \ && rm -rf /var/lib/apt/lists/* # Create non-root user RUN groupadd -r appuser && useradd -r -g appuser appuser # Create application directory WORKDIR /app # Copy built artifact from builder stage COPY --from=builder /app/target/*.jar app.jar COPY --chown=appuser:appuser entrypoint.sh . # Set permissions RUN chmod +x entrypoint.sh && \ 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 || exit 1 # JVM options for container ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+UseG1GC -XX:+ExitOnOutOfMemoryError" # Expose port EXPOSE 8080 # Use shell form for entrypoint to allow variable expansion ENTRYPOINT ["/app/entrypoint.sh"]

3. Entrypoint Script

#!/bin/bash # entrypoint.sh set -e # Wait for dependencies (if any) if [ -n "$WAIT_FOR_HOSTS" ]; then for host in $(echo $WAIT_FOR_HOSTS | sed "s/,/ /g"); do echo "Waiting for $host..." while ! nc -z $(echo $host | sed "s/:/ /"); do sleep 1 done echo "$host is available" 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 exec java $JAVA_OPTS -jar app.jar "$@"

4. Kubernetes Kaniko Build Configuration

Kaniko Pod Template for Kubernetes

# kaniko-pod-template.yaml apiVersion: v1 kind: Pod metadata: name: kaniko-build-pod annotations: sidecar.istio.io/inject: "false" spec: containers: - name: kaniko image: gcr.io/kaniko-project/executor:v1.9.1 args: - --dockerfile=Dockerfile - --context=dir:///workspace - --destination=$(DOCKER_REGISTRY)/$(APP_NAME):$(VERSION) - --cache=true - --cache-ttl=24h - --cache-repo=$(DOCKER_REGISTRY)/cache/$(APP_NAME) - --build-arg=BUILD_DATE=$(BUILD_DATE) - --build-arg=GIT_COMMIT=$(GIT_COMMIT) - --verbosity=info - --snapshotMode=redo - --use-new-run env: - name: DOCKER_CONFIG value: /kaniko/.docker resources: requests: cpu: 1000m memory: 2Gi limits: cpu: 2000m memory: 4Gi volumeMounts: - name: kaniko-secret mountPath: /kaniko/.docker - name: workspace mountPath: /workspace - name: maven-cache mountPath: /root/.m2 volumes: - name: kaniko-secret secret: secretName: docker-registry-secret items: - key: .dockerconfigjson path: config.json - name: workspace persistentVolumeClaim: claimName: build-workspace-pvc - name: maven-cache persistentVolumeClaim: claimName: maven-cache-pvc restartPolicy: Never

5. Jenkins Pipeline with Kaniko

// Jenkinsfile-kaniko pipeline { agent { kubernetes { yaml """ apiVersion: v1 kind: Pod spec: containers: - name: jnlp image: jenkins/inbound-agent:4.11.2-4 args: ['\$(JENKINS_SECRET)', '\$(JENKINS_NAME)'] resources: requests: cpu: 200m memory: 256Mi limits: cpu: 500m memory: 512Mi - name: maven image: maven:3.8.6-eclipse-temurin-11 command: ['cat'] tty: true resources: requests: cpu: 1000m memory: 2Gi limits: cpu: 2000m memory: 4Gi volumeMounts: - name: maven-cache mountPath: /root/.m2 - name: kaniko image: gcr.io/kaniko-project/executor:v1.9.1 command: ['cat'] tty: true resources: requests: cpu: 1000m memory: 2Gi limits: cpu: 2000m memory: 4Gi volumeMounts: - name: kaniko-secret mountPath: /kaniko/.docker volumes: - name: maven-cache persistentVolumeClaim: claimName: maven-cache-pvc - name: kaniko-secret secret: secretName: docker-registry-secret items: - key: .dockerconfigjson path: config.json """ } } environment { DOCKER_REGISTRY = 'ghcr.io' APP_NAME = 'my-java-app' VERSION = "${env.BUILD_NUMBER}-${env.GIT_COMMIT.substring(0, 7)}" MAVEN_OPTS = '-Dmaven.repo.local=/root/.m2/repository' } stages { stage('Checkout') { steps { checkout scm } } stage('Build and Test') { steps { container('maven') { sh ''' mvn clean compile mvn test mvn jacoco:report ''' } } post { always { junit 'target/surefire-reports/*.xml' jacoco exclusionPattern: '**/test-classes/**' } } } stage('Security Scan') { steps { container('maven') { sh 'mvn org.owasp:dependency-check-maven:check' } } } stage('Prepare Docker Context') { steps { container('maven') { sh ''' mvn package -DskipTests mkdir -p docker-context cp target/*.jar docker-context/app.jar cp Dockerfile docker-context/ cp entrypoint.sh docker-context/ chmod +x docker-context/entrypoint.sh ''' } } } stage('Build Container with Kaniko') { steps { container('kaniko') { sh ''' /kaniko/executor \ --context=/home/jenkins/agent/workspace/${JOB_NAME}/docker-context \ --destination=${DOCKER_REGISTRY}/${APP_NAME}:${VERSION} \ --destination=${DOCKER_REGISTRY}/${APP_NAME}:latest \ --cache=true \ --cache-ttl=72h \ --cache-repo=${DOCKER_REGISTRY}/cache/${APP_NAME} \ --build-arg=BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ --build-arg=GIT_COMMIT=${GIT_COMMIT} \ --verbosity=info ''' } } } stage('Scan Container') { steps { container('maven') { sh ''' docker run --rm \ -v /var/run/docker.sock:/var/run/docker.sock \ aquasec/trivy:latest \ image --exit-code 1 \ ${DOCKER_REGISTRY}/${APP_NAME}:${VERSION} ''' } } } stage('Deploy to Staging') { when { branch 'main' } steps { container('maven') { sh ''' kubectl set image deployment/${APP_NAME} \ ${APP_NAME}=${DOCKER_REGISTRY}/${APP_NAME}:${VERSION} \ -n staging ''' } } } } post { always { container('maven') { archiveArtifacts artifacts: 'target/*.jar', fingerprint: true } } success { emailext ( subject: "SUCCESS: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'", body: "Build successful: ${env.BUILD_URL}", to: "${env.CHANGE_AUTHOR_EMAIL}" ) } failure { emailext ( subject: "FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'", body: "Build failed: ${env.BUILD_URL}", to: "${env.CHANGE_AUTHOR_EMAIL}" ) } } }

6. Kubernetes Resources for Kaniko

Persistent Volume Claims

# pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: maven-cache-pvc namespace: build spec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi storageClassName: fast-ssd --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: build-workspace-pvc namespace: build spec: accessModes: - ReadWriteOnce resources: requests: storage: 5Gi storageClassName: standard

Docker Registry Secret

# docker-secret.yaml apiVersion: v1 kind: Secret metadata: name: docker-registry-secret namespace: build type: kubernetes.io/dockerconfigjson data: .dockerconfigjson: <base64-encoded-docker-config>

7. Java Application with Build Info

Build Info Actuator Endpoint

@Component public class BuildInfoContributor implements InfoContributor { private final BuildProperties buildProperties; public BuildInfoContributor(BuildProperties buildProperties) { this.buildProperties = buildProperties; } @Override public void contribute(Info.Builder builder) { Map<String, Object> buildInfo = new HashMap<>(); buildInfo.put("version", buildProperties.getVersion()); buildInfo.put("time", buildProperties.getTime()); buildInfo.put("builder", "kaniko"); buildInfo.put("java", System.getProperty("java.version")); builder.withDetail("build", buildInfo); // Add git info if available try { ClassPathResource gitProperties = new ClassPathResource("git.properties"); if (gitProperties.exists()) { Properties props = new Properties(); props.load(gitProperties.getInputStream()); builder.withDetail("git", props); } } catch (IOException e) { // Git properties not available } } }

Health Check with Build Info

@RestController @RequestMapping("/actuator") public class BuildInfoController { @GetMapping("/build-info") public ResponseEntity<Map<String, Object>> getBuildInfo() { Map<String, Object> buildInfo = new HashMap<>(); buildInfo.put("version", getClass().getPackage().getImplementationVersion()); buildInfo.put("buildTime", getClass().getPackage().getImplementationVendor()); buildInfo.put("builder", "kaniko"); buildInfo.put("jvm", System.getProperty("java.version")); return ResponseEntity.ok(buildInfo); } }

8. Maven Build Properties

Git Information Plugin

<!-- pom.xml snippet --> <build> <plugins> <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> <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>

9. Kaniko Build Script

#!/bin/bash # build-with-kaniko.sh set -e # Configuration APP_NAME="my-java-app" DOCKER_REGISTRY="ghcr.io" VERSION="${1:-latest}" CACHE_TTL="72h" # Build arguments BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") GIT_COMMIT=$(git rev-parse --short HEAD) echo "Building ${APP_NAME}:${VERSION} with Kaniko" /kaniko/executor \ --context="${WORKSPACE}" \ --dockerfile="${WORKSPACE}/Dockerfile" \ --destination="${DOCKER_REGISTRY}/${APP_NAME}:${VERSION}" \ --destination="${DOCKER_REGISTRY}/${APP_NAME}:latest" \ --cache=true \ --cache-ttl="${CACHE_TTL}" \ --cache-repo="${DOCKER_REGISTRY}/cache/${APP_NAME}" \ --build-arg="BUILD_DATE=${BUILD_DATE}" \ --build-arg="GIT_COMMIT=${GIT_COMMIT}" \ --build-arg="VERSION=${VERSION}" \ --label="org.opencontainers.image.created=${BUILD_DATE}" \ --label="org.opencontainers.image.revision=${GIT_COMMIT}" \ --label="org.opencontainers.image.version=${VERSION}" \ --verbosity="info" \ --snapshotMode="redo" \ --use-new-run echo "Build completed: ${DOCKER_REGISTRY}/${APP_NAME}:${VERSION}"

10. Kubernetes Job for Kaniko Build

# kaniko-job.yaml apiVersion: batch/v1 kind: Job metadata: name: kaniko-build-${BUILD_NUMBER} namespace: build spec: backoffLimit: 1 template: spec: containers: - name: kaniko image: gcr.io/kaniko-project/executor:v1.9.1 args: - --dockerfile=Dockerfile - --context=git://${GIT_REPO}#${GIT_BRANCH} - --destination=${DOCKER_REGISTRY}/${APP_NAME}:${VERSION} - --destination=${DOCKER_REGISTRY}/${APP_NAME}:latest - --cache=true - --cache-ttl=72h - --cache-repo=${DOCKER_REGISTRY}/cache/${APP_NAME} - --build-arg=BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - --build-arg=GIT_COMMIT=${GIT_COMMIT} - --build-arg=VERSION=${VERSION} - --verbosity=info - --snapshotMode=redo env: - name: DOCKER_CONFIG value: /kaniko/.docker resources: requests: cpu: 1000m memory: 2Gi limits: cpu: 2000m memory: 4Gi volumeMounts: - name: kaniko-secret mountPath: /kaniko/.docker volumes: - name: kaniko-secret secret: secretName: docker-registry-secret items: - key: .dockerconfigjson path: config.json restartPolicy: Never

This comprehensive Kaniko setup provides:

  • Dockerless container builds within Kubernetes
  • Efficient caching for faster builds
  • Security scanning integration
  • Multi-stage builds optimized for Java
  • GitOps-friendly configuration
  • Persistent caching for Maven dependencies
  • Build information tracking
  • Kubernetes-native execution

The setup ensures secure, efficient, and reproducible container builds for Java applications without requiring Docker daemon access.

Leave a Reply

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


Macro Nepal Helper