Basic pipeline to build and deploy on Kubernetes

In this post I will show show an example of a basic pipeline for building and deploying an application on EKS, the managed Kubernetes service from AWS.

We will also use ECR, the container registry provided by AWS to store container images and Jenkins to orchestrate the pipeline.

Pipeline steps

The pipeline will be composed of the following stages:

  1. Checkout application code for the selected branch from GitHub

  2. Build Java application with Maven

  3. Create image including the application using Docker

  4. Upload image to the container registry (ECR)

  5. Deploy application on Kubernetes using Ansible and k8s module

Requirements

For the pipeline to work, the following Jenkins plugins are required to be installed:

In addition, the following tools must be installed on the server where the pipeline is going to run:

  • Maven 3.5.0 o higher

  • JDK 8 o higher

  • Ansible and k8s module

  • Docker 1.18 or higher

ECR Plugin configuration

This plugin basically helps you to generate a Docker login token based on valid AWS ECR credentials.

Once it is installed you have to create a new Global credential of type AWS Credentials, providing both you AWS Access Key ID  and Secret Access Key

Jenkins Location

Once the user is created, a convenient way of generating the Docker push step is using the snippet generator provided by Jenkins in the following URL:

<Jenkins Location URL>/pipeline-syntax

You have to select the step withDockerRegistry in the first dropdown list and then enter your AWS ECR URL and the user AWS/* related to the region where your ECR is located (EU_WEST_1 in this case)

Jenkins Location

We will use the generated snippet in the last stage of the pipeline

Ansible k8s

In order to use k8s module, you will need Ansible 2.6 or higher installed.

In addition, the openshift python package is also required:

pip install openshift

Since k8s module needs to know how to connect to your kubernetes cluster, the correponding kube-config file must be located in $HOME/.kube directory.

With EKS you can generate this file using the AWS CLI:

aws eks --region <YOUR REGION> update-kubeconfig --name <YOUR CLUSTER NAME>

So now everything is setup and we can start creating our pipeline.

Pipeline

You can find the Jenkinsfile in declarative syntax and some comments below:

Build and deploy
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
properties([
    parameters([
        gitParameter(branch: '',
                     branchFilter: 'origin/(.*)',
                     defaultValue: 'master',
                     description: '',
                     name: 'BRANCH',
                     quickFilterEnabled: false,
                     selectedValue: 'NONE',
                     sortMode: 'NONE',
                     tagFilter: '*',
                     type: 'PT_BRANCH')
    ])
])

pipeline {
   agent any

   stages {
      stage('Build application') {
         steps {
            withMaven(maven: 'Maven 3.5.0', jdk: 'JDK8') {
                sh "mvn versions:set -DnewVersion=$BRANCH"   (1)
                sh "mvn clean package -U"                    (2)
            }
         }
      }
      stage('Build docker image') {
         steps {
            sh "docker build . -t helloworld:$BRANCH"        (3)
            sh "docker tag helloworld:$BRANCH 757992231822.dkr.ecr.eu-west-1.amazonaws.com/helloworld:$BRANCH"     (4)
         }
      }
      stage('Push image to AWS') {
         steps {
            withDockerRegistry(credentialsId: 'ecr:eu-west-1:jenkins_aws', url: 'https://757992231822.dkr.ecr.eu-west-1.amazonaws.com') {
                sh "docker push 757992231822.dkr.ecr.eu-west-1.amazonaws.com/helloworld:$BRANCH"                   (5)
            }
         }
      }
      stage('Deploy to k8s') {
         steps {
            ansiblePlaybook(installation: 'Ansible 2.8',          (6)
                            playbook: 'ansible-deploy.yml',
                            extraVars: [
                                version: "$BRANCH"
                            ]);
         }
      }
   }
}
1 Best practice for setting POM version
2 Builds application artifact
3 Builds image including previous artifact and tag it according to the git branch selected
4 Updates the tag to change from default Docker registry (Docker Hub) to AWS ECR
5 Using credentials created in previous step, image is pushed to ECR. Uses withDockerRegistry step provided by Docker Pipeline plugin
6 Deploy to K8S using Ansible playbook with k8s plugin. Branch (tag) is passed as parameter to the playbook. You can find below the playbook used in the last stage.
ansible-deploy.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
---
  - hosts: localhost
    gather_facts: false
    vars:
      version: "{{version}}"    (1)
    tasks:
      - name: Update deploy
        k8s:
          state: present
          definition: "{{ lookup('file', 'helloworld-k8s.yml') | replace ('TAG',version) }}"     (2)
          namespace: default
1 Local variable set from version parameter
2 File helloworld-k8s.yml is used as a template, replacing TAG text with image tag (version parameter).

Conclusion

As you have seen, it’s pretty easy to write a basic pipeline to deploy an application on EKS. The hard part actually is to install and setup all the requirements and dependencies to get the pipeline working.

There are some interesting improvements to this basic example like deploying permanent branches, handling different environments and managing secrets. I can tackle this in a follow-up post is you find it interesting.

You can find project sources in GitHub

comments powered by Disqus