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")
}
}
}
}
}
}
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
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)