Jenkins: Redis deployment, and Helm subchart values
Tags: kubernetes, tutorial, devops
Published: 2020-11-01 15:36:05

---
title: Jenkins: Redis deployment, and Helm subchart values
published: true
date: 2020-11-01 07:28:17 UTC
tags: kubernetes,tutorial,devops
canonical_url: https://rtfm.co.ua/en/jenkins-redis-deployment-and-helm-subchart-values/
---

![](https://cdn-images-1.medium.com/max/130/0*Ipn2LdthoX4YYVuf.png)

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](https://rtfm.co.ua/en/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](https://rtfm.co.ua/helm-dependencies-aka-subcharts-obzor-i-primer/), and pass a values.yaml from the parent chart.

The documentation is [here>>>](https://helm.sh/docs/chart_template_guide/subcharts_and_globals/#:~:text=A%20subchart%20is%20considered%20%22stand,be%20accessed%20by%20all%20charts.).

So, what we will do:

- the parent chart — _backend-redis_
- with the [bitnami/redis](https://bitnami.com/stack/redis) dependency
- with the `env` directory
- with the `dev` catalog inside
- with the `values.yaml` file inside
- where the [Redis options](https://rtfm.co.ua/redis-master-slave-replikaciya-i-zapusk-v-kubernetes/#Redis_Options) will be kept
- and in Jenkins, it will be deployed as `helm install backend-redis -f env/${ENV}/values.yaml`

Let’s go.

### Creating parent Helm chart

In a repository with our services create a new chart:

```
$ helm create backend-redis
```

Creating backend-redis

Remove all template files — we don’t need them here:

```
$ rm -rf backend-redis/templates/*
```

Create directories for the `values.yaml`:

```
$ mkdir -p backend-redis/env/{dev,stage,prod}
```

Copy a config from the [previous post](https://rtfm.co.ua/redis-master-slave-replikaciya-i-zapusk-v-kubernetes/) (_Rus_):

```
$ cp ~/Temp/redis-opts.yaml backend-redis/env/dev/values.yaml
```

Check its content:

```
$ head backend-redis/env/dev/values.yaml
global:
  redis:
    password: “blablacar”

metrics:
  enabled: true
  serviceMonitor:
    enabled: true
    namespace: “monitoring”
```

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:

```
$ helm search repo redis
NAME CHART VERSION APP VERSION DESCRIPTION
bitnami/redis 11.2.3 6.0.9 Open source, advanced key-value store. It is of…
bitnami/redis-cluster 3.2.10 6.0.9 Open source, advanced key-value store. It is of…
stable/prometheus-redis-exporter 3.5.1 1.3.4 DEPRECATED Prometheus exporter for Redis metrics
stable/redis 10.5.7 5.0.7 DEPRECATED Open source, advanced key-value stor…
stable/redis-ha 4.4.4 5.0.6 Highly available Kubernetes implementation of R…
stable/sensu 0.2.3 0.28 Sensu monitoring framework backed by the Redis …
```

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:

```
$ helm repo add bitnami [https://charts.bitnami.com/bitnami](https://charts.bitnami.com/bitnami)
```

Run for the test:

```
$ helm install backend-redis . --dry-run -f env/dev/values.yaml --debug
install.go:172: [debug] Original chart version: “”
install.go:189: [debug] CHART PATH: /home/setevoy/Work/devops-kubernetes/projects/backend/services/backend-redis
Error: found in Chart.yaml, but missing in charts/ directory: redis
helm.go:94: [debug] found in Chart.yaml, but missing in charts/ directory: redis
…
```

**_“Error: found in Chart.yaml, but missing in charts/ directory: redis_** ” — ah, yeah, forgot. Update dependencies:

```
$ helm dependency update
Hang tight while we grab the latest from your chart repositories…
…Successfully got an update from the “equinor-charts” chart repository
…Successfully got an update from the “bitnami” chart repository
…Successfully got an update from the “stable” chart repository
Update Complete. ⎈Happy Helming!⎈
Saving 1 charts
Downloading redis from repo [https://charts.bitnami.com/bitnami](https://charts.bitnami.com/bitnami)
Deleting outdated charts
```

Check the subchart’s archive:

```
$ ll charts/
total 64
-rw-r — r — 1 setevoy setevoy 64195 Oct 28 16:30 redis-11.2.3.tgz
```

Run again:

```
$ helm install backend-redis . — dry-run -f env/dev/values.yaml — debug
```

And if everything is good here — run the installation:

```
$ helm upgrade --install --namespace eks-dev-1-backend-redis-ns --create-namespace --atomic backend-redis . -f env/dev/values.yaml --set redis.global.redis.password=p@ssw0rd --debug
history.go:53: [debug] getting history for release backend-redis
Release “backend-redis” does not exist. Installing it now.
install.go:172: [debug] Original chart version: “”
install.go:189: [debug] CHART PATH: /home/setevoy/Work/devops-kubernetes/projects/backend/services/backend-redis
client.go:108: [debug] creating 1 resource(s)
client.go:108: [debug] creating 9 resource(s)
wait.go:53: [debug] beginning wait for 9 resources with timeout of 5m0s
wait.go:206: [debug] Service does not have load balancer ingress IP address: eks-dev-1-backend-redis-ns/backend-redis
wait.go:329: [debug] StatefulSet is not ready: eks-dev-1-backend-redis-ns/backend-redis-node. 1 out of 2 expected pods have been scheduled…
```

Pay attention, that in the `--set` the password parameter also passed with the subchart's name, and then the block's name - `redis.global.redis.password`.

Check the services:

```
$ kubectl -n eks-dev-1-backend-redis-ns get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
backend-redis LoadBalancer 172.20.153.106 internal-a5d0d8f5a5d4a4438a2ca06610886d2f-1400935130.us-east-2.elb.amazonaws.com 6379:30362/TCP,26379:31326/TCP 70s
backend-redis-headless ClusterIP None <none> 6379/TCP,26379/TCP 70s
backend-redis-metrics ClusterIP 172.20.50.77 <none> 9121/TCP 70s 9121/TCP 21s
```

Pods:

```
$ kubectl -n eks-dev-1-backend-redis-ns get pod
NAME READY STATUS RESTARTS AGE
backend-redis-node-0 3/3 Running 0 37s
backend-redis-node-1 3/3 Running 0 21s
```

And check if Redis is working:

```
$ admin@bttrm-dev-app-1:~$ redis-cli -h internal-a5d0d8f5a5d4a4438a2ca06610886d2f-1400935130.us-east-2.elb.amazonaws.com -p 6379 -a p@ssw0rd info replication
Replication
role:master
connected_slaves:1
slave0:ip=10.3.45.195,port=6379,state=online,offset=15261,lag=1
…
```

Cool — now we can add a Jenkins job.

Here is everything similar to the jobs from the [Helm: пошаговое создание чарта и деплоймента из Jenkins](https://rtfm.co.ua/helm-poshagovoe-sozdanie-charta-i-deplojmenta-iz-jenkins/) post (_Rus_).

### Jenkins deploy job

Create a Jenkins Pipeline Job.

#### Parameters

Add parameters:

![](https://cdn-images-1.medium.com/max/1024/0*-L9ZhKKCbTTbkozk.png)

Here:

- `APP_CHART_NAME`: parent's chart name
- `AWS_EKS_NAMESPACE`: a Kubernetes Namespace to deploy to
- `AWS_EKS_CLUSTER`: a Kubernetes cluster to deploy to, also used to generate the .kube/config for the kubectl
- `APP_ENV`: used to substitute in the `backend-redis/env/$APP_ENV/values.yaml` and for the `verify()` function call, see the [Jenkins: Scripted Pipeline – Production environment job confirmation step](https://rtfm.co.ua/en/jenkins-scripted-pipeline-production-environment-job-confirmation-step/)
- `APP_REPO_URL`: the chart's repository
- `APP_REPO_BRANCH`: a branch in this repository

The directories structure in our repository is the following:

```
projects/
└── backend
└── services
└── backend-redis
├── Chart.lock
├── charts
│ └── redis-11.2.3.tgz
├── Chart.yaml
├── env
│ ├── dev
│ │ └── values.yaml
│ ├── prod
│ └── stage
├── templates└── values.yaml
```

Create a Password Parameter with a password for Redis:

![](https://cdn-images-1.medium.com/max/478/0*o-Ol_QBkel5RHlUH.png)

#### 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](https://rtfm.co.ua/jenkins-uvedomlenie-v-slack-iz-jenkins-scripted-pipeline/) post (_Rus_).

Run the Job:

![](https://cdn-images-1.medium.com/max/811/0*74TR-a1UVfjBhQVg.png)

Check the result:

```
$ helm -n eks-dev-1-backend-redis-ns ls

NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION

backend-redis eks-dev-1-backend-redis-ns 2 2020–10–29 14:19:52.407047243 +0000 UTC deployed backend-redis-13 13.6da15d3e
```

Done.

_Originally published at_ [_RTFM: Linux, DevOps and system administration_](https://rtfm.co.ua/en/jenkins-redis-deployment-and-helm-subchart-values/)_._

* * *