Jenkins X is a CI / CD platform for Kubernetes. Jenkins X offers you the possibility to create or import applications, automatically built them and deploy them to Kubernetes. Each code change will results in a new deploy of your application, all automated because of the hooks. When your satisfied with your application you can use a CLI command to promote the application to a different environment using a GitOps approach.

In this tutorial I’ll show you create a new Kubernetes cluster using Jenkins X. After the installation we will introduce CI / CD for a basic Python application. You don’t need to know Python. The main goal is to show that Jenkins X can be used for every type of application. We will deploy our application in a pod in our staging environment. We will do commits, scale the amount of pods and promote our application to our production environment.

This tutorial was performed on Google Cloud which offers $300 free credit to get started with any GCP product.

Prerequisites

Install gcloud SDK

Create a Google Cloud account

Link billing account in Console

Install a Kubernetes cluster using Jenkins X

First we will check if our package manager ( brew in my case) is up to date.

$ brew update

Install the jx binary on your machine. It’s the command line tool for working with Jenkins X. Here you can find the details about the installation on Linux or different platforms.

$ brew tap jenkins-x/jx

$ brew install jx

$ jx --version

1.3.106

Now create a GCP project in which we will work.

$ gcloud projects create some-unique-id --name jenkins-x

$ gcloud projects list

PROJECT_ID NAME PROJECT_NUMBER

xxxx-jenkins-x jenkins-x xxxxxxxx

Don’t forget to link your billing account with this project. It can be done using the console. There is also some alpha command available.

Create a default Kubernetes cluster using jx .

$ jx create cluster gke --skip-login --default-admin-password=xxx -n jenkins-x-kubernetes

Use your arrow keys to configure your cluster.

Here are my personal configurations for this tutorial.

Google Cloud Zone: europe-west1-b

Google Cloud Machine Type: n1-standard-2

Minimum number of Nodes 3

Maximum number of Nodes 4

Creating the cluster can take a few minutes. It will ask for our GitHub credentials. At the end we’ll also need to define a GitHub API token which we can create on GitHub itself. Go to “Settings -> Developer Settings -> Personal access tokens” and generate a new token. Provide the “repo” permission and click generate:

Now let’s move on with the install. We also choose to install an ingress controller in the kube-system namespace. Don’t forget to confirm the domain. Again we need to wait for a few minutes so our Kubernetes and Jenkins X components can be pulled/created.

When everything is running, your browser will automatically open http://jenkins.jx.xx.xx.xx.xx.nip.io . Just be patient. There will follow an automated login and your Jenkins instance will be configured automatically.

Finally our full Jenkins X environment is installed on our 3-node Kubernetes cluster on Google Cloud Platform.

The install will also create two repositories and push them to your GitHub account.

The README.md of the repositories tells us: “The default git repository used when creating new GitOps based Environments”. Here you can find a lot more information about GitOps. In very short, GitOps is a declarative approach where your entire environment state is stored in Git repository (application, version, environment configuration, …). These repositories contain a Jenkinsfile ( = Jenkins pipelines) which will be used during your GitOps based deployments.

Time to open our Jenkins console from the CLI. Switch to the namespace where Jenkins is deployed and open the console. Play a bit around with the CLI and the arrow keys.



$ jx ns jx --> now you choose your namespace immediately

Using namespace 'jx' from context named 'gke_xxx-jenkins-x_europe-west1-b_jenkins-x-kubernetes' on server ' $ jx ns --> now you can choose your namespace using the arrow keys$ jx ns jx --> now you choose your namespace immediatelyUsing namespace 'jx' from context named 'gke_xxx-jenkins-x_europe-west1-b_jenkins-x-kubernetes' on server ' https://xx.xx.xx.xx' $ jx console

We can login as user ‘admin’ with the password we defined in our jx create cluster command.

These pipelines are created from Jenkinsfiles which are stored in the two git repositories we’ve just discussed. These pipelines are multi branch pipelines, and will be used during promotion and versioning of applications. Multi branch pipelines enables you to implement different Jenkinsfiles for different branches of the same project.

During the initial run your personal environment configurations are added.

The new Blue Ocean layout provides some clear info about the stages in the pipeline

When both pipelines are okay you can be sure your that your environment works. Now we can start looking to our own already existing python application.

Run the basic Python application

I’ve created some very basic Dockerized Python application which you need to fork from this GitHub repository. In the end it’s just a Docker container which prints its name and IP by using Python and Flask which is a small micro-service framework.

It’s important to fork the repository because Jenkins X will create PR’s (Pull Requests) and merge them. Of course you don’t have the permission to do that on my repository, that’s why you need to fork it.

We will test the basic application first. Clone your forked repository build the Docker image and start it. The instructions are in the README.md.

The app is running on port 8080 (not default port 5000 of Flask). The reason here for is that an import of the application with Jenkins X (see later) will automatically expose container port 8080. It’s possible to edit this after an initial deploy to port 5000 but we want to perform a minimum of manual steps. That’s the reason why we just run our application on port 8080.

Import our basic Python application as a pipeline with Jenkins X

Now we will import our project inside Jenkins. First we need to cd inside the local repository (cloned from the fork). Now execute jx import .

It’s also possible to import a git project using a URL.

Execute the commands which are returned in the output above.

Check the pipeline in the Jenkins console.

It starts with cloning the repository, versioning, creating a Git tag, testing (if there are tests) and building the application.

It’s building the Docker application and pushing the image to the Docker Registry.

During the promote stage a PR (Pull Request) is created on the environment-jenkins-x-kubernetes-staging repository.

This PR will add our application and its version in the env/requirements.yaml file inside the Git Repository.

In the logs of the python-flask-docker pipeline:

Now you can see the multi branch environment-jenkins-x-kubernetetes-staging pipeline is triggered for the Pull Request (branch PR-1). It will checkout the PR and perform a helm build , execute tests on the environment along with code review and approval. When it’s successful it will merge the PR with the master.

The merge of the PR with the master will trigger the pipeline again but this time from the master branch. This pipeline will checkout the master branch and again perform a helm build and a helm apply .

This environment-jenkins-x-kubernetetes-staging pipeline started from the master branch will deploy our pod. When it succeeds both pipelines (environment-jenkins-x-kubernetetes-staging pipeline + our python-flask-docker pipeline pipeline) exit successfully.

Now check the existing application. Here for we need to use the jx CLI first to access our jx-staging namespace. Our pod is running in this namespace.

Get the applications from that namespace

$ jx ns jx-staging

$ jx get applications

# in short: jx get apps -n jx-staging

We can even use jx to check our logs.

Visit the URL to check the application.

The pod its hostname and IP are printed.

We can also take a look in the Kubernetes Engine console where we see our new application (go to workloads).

our application is deployed in the jx-staging namespace

Here you can also see other components deployed by Jenkins X like a Nexus, Heapster, a Docker Registry, … .

We can even check if our image exists in the Docker Registry.

Now we will update our application and see what will happen!

Redeploy and promote the application

We know Jenkins X creates a GitHub web hook during the import of our application. This means we can just commit a change and our application will be updated automatically.

We add a bit of text to our template page of the application and commit + push it to the remote GitHub repository.

The ‘That’s it!’ is added.

The python-flask-docker pipeline pipeline starts automatically. It will create a new release (0.0.2) and and with help of the environment-jenkins-x-kubernetetes-staging pipeline (see above), version 0.0.2 is promoted to our jx-staging environment.

Verify the result. A new pod is deployed with our updated application.

This way we can also update the charts/python-flask-docker/values.yaml of Helm to increase the amount of pods. We can set the replicaCount to 4. Our pipeline will be triggered again and we will deploy version 0.0.3 in our staging environment.

Remember tags are created, we can these tags also as releases in GitHub.

After the deploy we can curl our application. We can use some docker image with curl installed.

$ docker run --rm tutum/curl /bin/bash -c 'for i in $(seq 1 10); do curl -s http://python-flask-docker.jx-staging.xx.xx.xx.xx.nip.io/ && echo " " ; done'

Short output:

The hostname of the container is <b>jx-staging-python-flask-docker-6d64d66f96-nm678</b> and its IP is <b>10.36.1.19</b>. That's it! The hostname of the container is <b>jx-staging-python-flask-docker-6d64d66f96-844r4</b> and its IP is <b>10.36.1.20</b>. That's it! The hostname of the container is <b>jx-staging-python-flask-docker-6d64d66f96-zmzb5</b> and its IP is <b>10.36.0.16</b>. That's it! The hostname of the container is <b>jx-staging-python-flask-docker-6d64d66f96-7sj6v</b> and its IP is <b>10.36.1.17</b>. That's it! The hostname of the container is <b>jx-staging-python-flask-docker-6d64d66f96-nm678</b> and its IP is <b>10.36.1.19</b>. That's it! The hostname of the container is <b>jx-staging-python-flask-docker-6d64d66f96-844r4</b> and its IP is <b>10.36.1.20</b>. That's it! ...

We are load balanced through the 4 pods and probably through different nodes too. When we’re satisfied we can promote our application to our production environment.

Get our application name:



APPLICATION STAGING PODS URL

python-flask-docker 0.0.3 4/4 $ jx get app -n jx-stagingAPPLICATION STAGING PODS URLpython-flask-docker 0.0.3 4/4 http://python-flask-docker.jx-staging.xx.xx..xx.nip.io

Promote our app to production. We can promote a specific version. Be sure you’re still inside the git repository!

$ jx promote python-flask-docker --version 0.0.3 --env production

A new PR is created, but this time on our production repository. The environment-jenkins-x-kubernetes-production pipeline is triggered. The PR is tested and merged, now that same pipeline starts again from the master branch which is triggered by the merge. It will perform a helm apply (deploy).

Let’s go check the application in the jx-production environment and visit the URL (it does not contain “staging” anymore!).



APPLICATION PRODUCTION PODS URL

python-flask-docker 0.0.3 4/4 jx get apps -n jx-productionAPPLICATION PRODUCTION PODS URLpython-flask-docker 0.0.3 4/4 http://python-flask-docker.jx-production.xx.xx.xx.xx.nip.io

We are now accessing our application in producion. Check the pod name. There are also 4 pods running in production because of our GitOps approach.

Conclusion

In this blog we learned how to install a Kubernetes cluster using jx and how we can import any type of application. We saw how a fully automated deployment pipeline was created in seconds. We didn’t had to care anymore about hooks or about creating our application environment. All configuration is stored in Git repositories. Promoting applications between environments isn’t a bottleneck anymore because of the GitOps based approach.

I hope you enjoyed it and understand the very basics of Jenkins X. When I’ve the time, I’ll write a second blog about Jenkins X, with a deeper focus on all the other different possibilities it offers.