The task is to create a Jenkins job to deploy Redis to Dev/Stage/Prod Kubernetes clusters.
In the Redis: running Master-Slave replication in Kubernetes we did it manually to see how it’s working, now it’s time to automate it.
The main question is how to pass parameters for different environments during the deployment? I’d like to use an existing chart for Bitnami, and at the same time to deploy it from our own Helm chart with a values.yaml
file fie each of the Dev/Stage/Prod environments.
Well, we still can create our Helm chart and add the Redis Bitnami chart as a Helm dependency, and pass a values.yaml
from the parent chart.
The documentation is
So, what we will do:
- the parent chart – backend-redis
- with the
bitnami/redis dependency - with the
env
directory- with the
dev
catalog- with the
values.yaml
file inside- where the Redis options will be kept
- with the
- with the
- with the
- and in Jenkins, it will be deployed as
helm install backend-redis -f env/${ENV}/values.yaml
Let’s go.
Contents
Creating parent Helm chart
In a repository with our services create a new chart:
Remove all template files – we don’t need them here:
Create directories for the values.yaml
:
Copy a config from the previous post (Rus):
Check its content:
Okay, just need to delete the password from here as it will be passed from a Jenkins Password parameter with the helm install --set
during deployment.
Helm: a values.yaml
for a subchart
Also, what do we need to change here is to add a child subchart’s name at the very beginning so we can use it with the parent’s chart and Helm will apply those values to the Redis chart from Bitnami which is our child chart.
Add the redis word to the beginning of the file and remove the password
‘s value:
redis: global: redis: password: "" metrics: enabled: true serviceMonitor: enabled: true namespace: "monitoring" master: persistence: enabled: false service: type: LoadBalancer annotations: service.beta.kubernetes.io/aws-load-balancer-internal: "true" ...
Now, we can add a dependency to our parent backend-redis chart, add the Redis chart:
Let’s use the bitnami/redis 11.2.3, add dependencies
to the Chart.yaml
of the parent’s chart:
... dependencies: - name: redis version: ~11.2 repository: "@bitnami"
Add the repository:
Run for the test:
“Error: found in Chart.yaml, but missing in charts/ directory: redis” – ah, yeah, forgot. Update dependencies:
Check the subchart’s archive:
Run again:
And if everything is good here – run the installation:
Pay attention, that in the --set
the password
parameter also passed with the subchart’s name, and then the bloc’s name – redis.global.redis.password
.
Check the services:
Pods:
And check if Redis is working:
Cool – now we can add a Jenkins job.
Here is everything similar to the jobs from the Helm: пошаговое создание чарта и деплоймента из Jenkins post (Rus).
Jenkins deploy job
Create a Jenkins Pipeline Job.
Parameters
Add parameters:
Тут будут:
APP_CHART_NAME
: parent’s chart nameAWS_EKS_NAMESPACE
: a Kubernetes Namespace to deploy toAWS_EKS_CLUSTER
: a Kubernetes cluster to deploy to, also used to generate the.kube/config
for thekubectl
APP_ENV
: used to substitute in thebackend-redis/env/$APP_ENV/values.yaml
and for theverify()
function call, see the Jenkins: Scripted Pipeline – Production environment job confirmation stepAPP_REPO_URL
: the chart’s repositoryAPP_REPO_BRANCH
: a branch in this repository
The directories structure in our repository is the following:
Create a Password Parameter with a password for Redis:
Pipeline script
And write the pipeline script:
// ask confirmation for build if APP_ENV == prod def verify() { def userInput = input( id: 'userInput', message: 'This is PRODUCTION!', parameters: [ [$class: 'BooleanParameterDefinition', defaultValue: false, description: '', name: 'Please confirm you sure to proceed'] ]) if(!userInput) { error "Build wasn't confirmed" } } // Add slack Notification def notifySlack(String buildStatus = 'STARTED') { // Build status of null means success. buildStatus = buildStatus ?: 'SUCCESS' def color //change for another slack chanel def token = 'devops-alarms-ci-slack-notification' if (buildStatus == 'STARTED') { color = '#D4DADF' } else if (buildStatus == 'SUCCESS') { color = '#BDFFC3' } else if (buildStatus == 'UNSTABLE') { color = '#FFFE89' } else { color = '#FF9FA1' } def msg = "${buildStatus}: `${env.JOB_NAME}` #${env.BUILD_NUMBER}:\n${env.BUILD_URL}" slackSend(color: color, message: msg, tokenCredentialId: token) } node { try { docker.image('projectname/kubectl-aws:4.1').inside('-v /var/run/docker.sock:/var/run/docker.sock') { stage('Verify') { switch("${env.APP_ENV}") { case "prod": verify() echo "Run job" break; default: echo "Dev deploy" break; } } stage('Init') { gitenv = git branch: '${APP_REPO_BRANCH}', credentialsId: 'jenkins-projectname-github', url: '${APP_REPO_URL}' GIT_COMMIT_SHORT = gitenv.GIT_COMMIT.take(8) RELEASE_VERSION = "${BUILD_NUMBER}" APP_VERSION = "${BUILD_NUMBER}.${GIT_COMMIT_SHORT}" AWS_EKS_REGION = "us-east-2" echo "APP_VERSION: ${APP_VERSION}" echo "RELEASE_VERSION: ${RELEASE_VERSION}" sh "aws eks update-kubeconfig --region ${AWS_EKS_REGION} --name ${AWS_EKS_CLUSTER}" sh "kubectl cluster-info" sh "helm version" sh "helm -n ${AWS_EKS_NAMESPACE} ls " } // install dependencies stage("Helm dependencies") { dir("projects/backend/services/${APP_CHART_NAME}") { sh "helm repo add bitnami https://charts.bitnami.com/bitnami" sh "helm dependency update" } } // lint the chart stage("Helm lint") { dir("projects/backend/services/") { sh "helm lint ${APP_CHART_NAME} -f ${APP_CHART_NAME}/env/${APP_ENV}/values.yaml" } } // just to create --app-version stage("Helm package") { dir("projects/backend/services/") { sh "helm package ${APP_CHART_NAME} --version ${RELEASE_VERSION} --app-version ${APP_VERSION}" } } // --dry-run first, if OK then run install stage("Helm install") { dir("projects/backend/services/") { sh "helm secrets upgrade --install --namespace ${AWS_EKS_NAMESPACE} --create-namespace --atomic ${APP_CHART_NAME} ${APP_CHART_NAME}-${RELEASE_VERSION}.tgz -f ${APP_CHART_NAME}/env/${APP_ENV}/values.yaml --set redis.global.redis.password=${REDIS_PASSWORD} --debug --dry-run" sh "helm secrets upgrade --install --namespace ${AWS_EKS_NAMESPACE} --create-namespace --atomic ${APP_CHART_NAME} ${APP_CHART_NAME}-${RELEASE_VERSION}.tgz -f ${APP_CHART_NAME}/env/${APP_ENV}/values.yaml --set redis.global.redis.password=${REDIS_PASSWORD} --debug" } } stage("Helm info") { dir("projects/backend/services/") { sh "helm ls -a -d --namespace ${AWS_EKS_NAMESPACE}" sh "helm get manifest --namespace ${AWS_EKS_NAMESPACE} ${APP_CHART_NAME}" } } } // send Slack notification if Jenkins build fails } catch (e) { currentBuild.result = 'FAILURE' notifySlack(currentBuild.result) throw e } }
Slack notifications are described in the Jenkins: уведомление в Slack из Jenkins Scripted Pipeline post (Rus).
Run the Job:
Check the result:
Done.
Similar posts
Also published on