Deploying Jenkins with Helm on Kubernetes (AKS)

In our last blog, we showed how to deploy a Kubernetes cluster in Azure Container Service (AKS). In this blog, we will cover how to deploy Jenkins with Helm in a Kubernetes cluster. Helm charts helps you define, install, and upgrade complex Kubernetes applications. Jenkins Helm chart installs a master and slave cluster utilizing the Jenkins Kubernetes plugin.

Jenkins is a very popular open source automation server written in Java. Jenkins helps to automate the non-human part of software development process, with continuous integration and facilitating technical aspects of continuous delivery and plays a key role in setting up an automated CI CD pipeline with Kubernetes.

Helm is an open-source packaging tool that helps you install and manage the lifecycle of Kubernetes applications.

Helm has two components:

  • The Helm CLI is a client that runs on your machine locally or in the cloud
  • Tiller is a server that runs on the Kubernetes cluster and manages the lifecycle of your Kubernetes applications

Prerequisites

Create a Kubernetes cluster in Azure Container Service (AKS)

Install Helm on a local computer

After you have installed Helm, install Tiller on your Kubernetes cluster by typing the following command:

[bash]helm init –upgrade[/bash]

Deploying Jenkins with Helm chart

Default Installation

To deploy Jenkins with Helm using default configuration, type the following command:

[bash]helm install –name coderise-dev stable/jenkins[/bash]

Custom Installation

To deploy Jenkins with Helm using custom configuration, clone the Jenkins Helm repository and then update stable/jenkins/values.yaml file:

[bash]coderiseio-MacBook-Pro:repos$ git clone https://github.com/helm/charts.git
coderiseio-MacBook-Pro:repos$ cd charts/stable/jenkins[/bash]

Here’s our sample values.yaml file:

[bash]
coderiseio–MacBook-Pro:jenkins$ cat values.yaml
# Default values for jenkins.
# This is a YAML-formatted file.
# Declare name/value pairs to be passed into your templates.
# name: value

Master:
Name: jenkins-master
Image: “jenkins/jenkins”
ImageTag: “lts”
ImagePullPolicy: “Always”
# ImagePullSecret: jenkins
Component: “jenkins-master”
UseSecurity: true
AdminUser: coderiseio
# AdminPassword: <defaults to random>
Cpu: “200m”
Memory: “1024Mi”
# Set min/max heap here if needed with:
# JavaOpts: “-Xms512m -Xmx512m”
# JenkinsOpts: “”
# JenkinsUriPrefix: “/jenkins”
# Set RunAsUser to 1000 to let Jenkins run as non-root user ‘jenkins’ which exists in ‘jenkins/jenkins’ docker image.
# When setting RunAsUser to a different value than 0 also set FsGroup to the same value:
# RunAsUser: <defaults to 0>
# FsGroup: <will be omitted in deployment if RunAsUser is 0>
ServicePort: 8080
# For minikube, set this to NodePort, elsewhere use LoadBalancer
# Use ClusterIP if your setup includes ingress controller
ServiceType: LoadBalancer
# Master Service annotations
ServiceAnnotations: {}
# service.beta.kubernetes.io/aws-load-balancer-backend-protocol: https
# Used to create Ingress record (should used with ServiceType: ClusterIP)
# HostName: jenkins.cluster.local
# NodePort: <to set explicitly, choose port between 30000-32767
ContainerPort: 8080
# Enable Kubernetes Liveness and Readiness Probes
HealthProbes: true
HealthProbesTimeout: 120
SlaveListenerPort: 50000
LoadBalancerSourceRanges:
– 0.0.0.0/0
# Optionally assign a known public LB IP
# LoadBalancerIP: 1.2.3.4
# Optionally configure a JMX port
# requires additional JavaOpts, ie
# JavaOpts: >
# -Dcom.sun.management.jmxremote.port=4000
# -Dcom.sun.management.jmxremote.authenticate=false
# -Dcom.sun.management.jmxremote.ssl=false
# JMXPort: 4000
# List of plugins to be install during Jenkins master start
InstallPlugins:
– kubernetes:1.1.3
– workflow-aggregator:2.5
– workflow-job:2.15
– credentials-binding:1.13
– git:3.6.4
# Used to approve a list of groovy functions in pipelines used the script-security plugin. Can be viewed under /scriptApproval
# ScriptApproval:
# – “method groovy.json.JsonSlurperClassic parseText java.lang.String”
# – “new groovy.json.JsonSlurperClassic”
# List of groovy init scripts to be executed during Jenkins master start
InitScripts:
# – |
# print ‘adding global pipeline libraries, register properties, bootstrap jobs…’
# Kubernetes secret that contains a ‘credentials.xml’ for Jenkins
# CredentialsXmlSecret: jenkins-credentials
# Kubernetes secret that contains files to be put in the Jenkins ‘secrets’ directory,
# useful to manage encryption keys used for credentials.xml for instance (such as
# master.key and hudson.util.Secret)
# SecretsFilesSecret: jenkins-secrets
# Jenkins XML job configs to provision
# Jobs: |-
# test: |-
# <<xml here>>
CustomConfigMap: false
# Node labels and tolerations for pod assignment
# ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector
# ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature
NodeSelector: {}
Tolerations: {}

Ingress:
Annotations:
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: “true”

TLS:
# – secretName: jenkins.cluster.local
# hosts:
# – jenkins.cluster.local

Agent:
Enabled: true
Image: jenkins/jnlp-slave
ImageTag: 3.16-1
# ImagePullSecret: jenkins
Component: “jenkins-slave”
Privileged: false
Cpu: “200m”
Memory: “1024Mi”
# You may want to change this to true while testing a new image
AlwaysPullImage: false
# You can define the volumes that you want to mount for this container
# Allowed types are: ConfigMap, EmptyDir, HostPath, Nfs, Pod, Secret
# Configure the attributes as they appear in the corresponding Java class for that type
# https://github.com/jenkinsci/kubernetes-plugin/tree/master/src/main/java/org/csanchez/jenkins/plugins/kubernetes/volumes
volumes:
# – type: Secret
# secretName: mysecret
# mountPath: /var/myapp/mysecret
NodeSelector: {}
# Key Value selectors. Ex:
# jenkins-agent: v1

Persistence:
Enabled: true
## A manually managed Persistent Volume and Claim
## Requires Persistence.Enabled: true
## If defined, PVC must be created manually before volume will be bound
# ExistingClaim:

## jenkins data Persistent Volume Storage Class
## If defined, storageClassName: <storageClass>
## If set to “-“, storageClassName: “”, which disables dynamic provisioning
## If undefined (the default) or set to null, no storageClassName spec is
## set, choosing the default provisioner. (gp2 on AWS, standard on
## GKE, AWS & OpenStack)
##
# StorageClass: “-”

Annotations: {}
AccessMode: ReadWriteOnce
Size: 8Gi
volumes:
# – name: nothing
# emptyDir: {}
mounts:
# – mountPath: /var/nothing
# name: nothing
# readOnly: true

NetworkPolicy:
# Enable creation of NetworkPolicy resources.
Enabled: false
# For Kubernetes v1.4, v1.5 and v1.6, use ‘extensions/v1beta1’
# For Kubernetes v1.7, use ‘networking.k8s.io/v1’
ApiVersion: extensions/v1beta1

## Install Default RBAC roles and bindings
rbac:
install: false
serviceAccountName: default
# RBAC api version (currently either v1beta1 or v1alpha1)
apiVersion: v1beta1
# Cluster role reference
roleRef: cluster-admin[/bash]

After the values.yaml file is updated, you can run the following command:

[bash]
coderiseio-MacBook-Pro:jenkins$ helm install –name cr-development -f values.yaml .
NAME: cr-development
LAST DEPLOYED: Sat Jan 20 17:00:48 2018
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/Secret
NAME TYPE DATA AGE
cr-development-jenkins Opaque 2 1s

==> v1/ConfigMap
NAME DATA AGE
cr-development-jenkins 3 1s
cr-development-jenkins-tests 1 1s

==> v1/PersistentVolumeClaim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
cr-development-jenkins Pending default 1s

==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
cr-development-jenkins-agent ClusterIP 10.0.218.236 50000/TCP 1s
cr-development-jenkins LoadBalancer 10.0.135.72 8080:30710/TCP 0s

==> v1beta1/Deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
cr-development-jenkins 1 1 1 0 0s

==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
cr-development-jenkins-2645363986-99n88 0/1 Pending 0 0s[/bash]

After updating values.yaml, you can deploy it using this command:

[bash]helm install –name coderise-dev .[/bash]

Notes:

1. Get your ‘coderiseio’ user password by running:
[bash]
printf $(kubectl get secret –namespace default cr-development-jenkins -o jsonpath=”{.data.jenkins-admin-password}” | base64 –decode);echo[/bash]

2. Get the Jenkins URL to visit by running these commands in the same shell:
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running:
[bash]’kubectl get svc –namespace default -w cr-development-jenkins’
export SERVICE_IP=$(kubectl get svc –namespace default cr-development-jenkins –template “{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}”)
echo http://$SERVICE_IP:8080/login[/bash]

3. Login with the password from step 1 and the username specified in values.yaml: admin(default). In our case, we set it to coderiseio.

jenkins-with-helm

Done!

In this blog, we showed you how to deploy Jenkins with Helm on Kubernetes cluster in Azure Container Service (AKS). The same commands should work fine on AWS, GCP and IBM Cloud as well. If you have any questions or have a better approach, please use comments section below to share with us!

References

Jenkins Helm Chart

Use Helm to deploy containers on a Kubernetes cluster



1 Comment

Leave a Reply