DEV Community

Rafael Almeida for SWORD Health

Posted on • Originally published at Medium

Seamless CI/CD with Jenkins + Helm and Kubernetes

There are many ways and platforms you can use to solve CI/CD for your company nowadays. As we were already using Jenkins jobs for deployments and everyone at the team knew it well it was the obvious choice, but we shifted towards pipelines.

To implement this you need:

  • A Kubernetes cluster where you are going to deploy your applications
  • Helm deployment files for your application
  • Jenkins CI pipelines

The big picture

Jenkins will fetch branches and tags from the selected repository and run all steps defined for the pipeline inside your project's Jenskinsfile.

Kubernetes is an orchestration tool for containers so we need to be deploying applications as Docker containers so they can run and be managed inside this orchestration tool.

Helm is a “package manager” for Kubernetes. It reads, parses and injects values into a series of “Helm templates” that in the results in a series of Kubernetes objects. Helm then bundles all of these objects into a Helm release and installs it in the configured Kubernetes cluster. You can supply to helm some input values to configure each installation which is flexible enough to install for example the same application configured differently for multiple environments (e.g dev, staging, and production).

The jenkins pipeline

Here is an example of our declarative Jenkins pipeline file:

pipeline {
    agent {
        kubernetes {
            label 'my-app'
            yamlFile 'JenkinsKubernetesPod.yaml'
        }
    }
    stages {
        stage('Run unit tests') {
            steps {
              // The needed steps for your testing
            }
        }

        stage('Build application') {
            steps {
              // Build the app
            }
        }

        stage('Docker publish') {
            steps {
              // Publish a docker image for your application 
            }
        }

        stage('Deployment') {
            steps {
                script {
                  container('helm') {
                      // Init authentication and config for your kubernetes cluster
                      sh("helm init --client-only --skip-refresh")
                      sh("helm upgrade --install --wait prod-my-app ./helm --namespace prod")
                    }
                }
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

You should include a file with the name “Jenkinsfile” in your project repository and Jenkins will use it to define the pipeline. For brevity, I only included the deployment step. In this step, you will call the Helm installation or upgrade. This will apply a helm upgrade to your previously installed Helm release or if no release exists Helm will execute a helm install with the given release name (e.g. prod-my-app).

It’s a good idea to include the namespace as prefix or suffix for the helm release name because release names are only a name for helm and not installed as part of a Kubernetes namespace.

If you attempt to install a release-dummy on namespace dev and a release-dummy on namespace staging it will fail because release-dummy will already exist and Helm doesn’t care about namespace. To solve this we include a prefix of the namespace on the release name.

The deployment file

Make sure you have configured your app to run as a Kubernetes Deployment or StatefulSet or avoid having downtime between deploys. Check the deployment.yaml helm template below:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-dp
spec:
  replicas: 2
  minReadySeconds: 60
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  selector:
    matchLabels:
      app: {{ .Values.app.name }}
  template:
    metadata:
      labels:
        app: {{ .Values.app.name }}
    spec:
      containers:
      - name: {{ .Values.app.name }}
        image: {{ .Values.app.image.repository }}/{{ .Values.app.image.name }}:{{ .Values.app.image.tag }}
        imagePullPolicy: Always
        envFrom:
        - configMapRef:
            name: config-{{ .Values.app.name }}
        ports:
        - name: http-{{ .Values.app.name }}
          protocol: TCP
          containerPort: 80
Enter fullscreen mode Exit fullscreen mode

And that’s it, there are a lot of concepts to learn to put this all together but when you have it all covered up you will never have to put that much effort into deploying new applications and managing releases.

When you are done you push the commits into master or create a new tag and the code will appear in your servers without no more clicking.

Top comments (0)