Jenkins Java Centric Pipelines in Kubernetes

This is a first post in a series, describing the problems my team has faced during implementation of Jenkins pipelines in Kubernetes.

  1. Jenkins Java Centric Pipelines in Kubernetes (This article)
  2. Building Maven Projects in Jenkins Docker workers
  3. Using Maven and Jenkins to perform modular Java builds
  4. Building Docker Images in Jenkins on Kubernetes

When I took this project, I’ve read a considerable number of guides and documentation pages, some of them official, others written by bloggers like me. The common theme was to show a single aspect of the project. This approach simplifies the effort to explain the solution, however ignores the biggest challenge: combining the various solutions together.

In each post I’ll also try to tackle a single aspect of the project, however I’ll make an emphasis on how these different technologies play together.


Running in a pod

One of the first challenges that you will face when trying to build in a dynamically allocated Jenkins worker on Kubernetes is that unless you haven’t configured the Jenkins right, it won’t be able to spawn the worker, leaving your jobs suspended forever waiting for an available executor.

Required plug-ins

  • kubernetes
  • workflow-job
  • credentials-binding
  • git
  • pipeline-maven
  • pipeline-github-lib
  • matrix-auth
  • pipeline-utility-steps

Required configuration

Under Manage Jenkins -> Configure System -> Cloud:
Assuming that the Jenkins service name is ci-jenkins, set the following values:

Name: `kubernetes`
Kubernetes URL: `https://kubernetes.default`
Jenkins URL: `http://ci-jenkins:80` where ci-jenkins is the name of your Jenkins pod.
Jenkins tunnel: `ci-jenkins-agent:50000`
Kubernetes Pod Template:
    Name: `jnlp`
    Docker image: `jenkins/jnlp-slave:3.27-1`
    Working directory: `/home/jenkins`
    Arguments to pass to the command: `${computer.jnlpmac} ${computer.name}`
    Env Vars:
        Name: `JENKINS_URL`, value: `http://ci-jenkins.default.svc.cluster.local:80`

Testing it out

Let’s create a test project:

Jenkinsfile:

pipeline {
    agent {
        kubernetes {
            // Defines the default container for build, Jenkins will use it if no specific container was defined.
            defaultContainer 'python'
            // Defines all of the containers available to the build as part of the pod. These containers share the mounted workspace.
            yamlFile 'python-builder.yaml'
        }
    }

    stages {
        stage('Mock Stage') {
            steps {
                echo "Test"
                echo "${python --version}"
            }
        }
    }
}

Deployment YAML (python-builder.yaml):

apiVersion: v1
kind: Pod
metadata:
  labels:
    buildType: python
spec:
  containers:
  - name: python
    image: python:3.7-alpine
    resources:
      requests:
        memory: "64Mi"
        cpu: "1"
      limits:
        memory: "1Gi"
        cpu: "2"
    imagePullPolicy: Always
    command:
    - cat
    tty: true

Expect to see something like this:

Started by user Joe Doe
Obtained Jenkinsfile from 314cde67718d21119a92e126a98c87d94c5c0096
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] podTemplate
[Pipeline] {
[Pipeline] node
Still waiting to schedule task
Waiting for next available executor
Agent my-project-my-branch-1-dv63f-90n14-dr58c is provisioned from template Kubernetes Pod Template
---
apiVersion: "v1"
kind: "Pod"
metadata:
    ...