Building a Docker-Jenkins CI/CD Pipeline for a Python App (Part 2)

Nyukeit
6 min readDec 13, 2022

--

This is a continuation of the tutorial for building a Docker Jenkins pipeline to deploy a simple Python app using Git and GitHub. The first part of the tutorial can be found here.

Installing Jenkins

We now have the basics ready for deploying our app. Let’s install the remaining software to complete our pipeline.

We begin by importing the GPG key which will verify the integrity of the package.

curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo tee \ /usr/share/keyrings/jenkins-keyring.asc > /dev/null

Next, we add the Jenkins softwarey repository to the sources list and provide the authentication key.

echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \ https://pkg.jenkins.io/debian-stable binary/ | sudo tee \ /etc/apt/sources.list.d/jenkins.list > /dev/null

Now, we install Jenkins

Wait till the entire installation process is over and you get back control of the terminal.

To verify if Jenkins was installed correctly, we will check if the Jenkins service is running.

Press Q to regain control.

Jenkins Configuration

We have verified that the Jenkins service is now running. This means we can go ahead and configure it using our browser.

Open your browser and type this in the address bar:

You should see the Unlock Jenkins page.

Jenkins generated a default password when we installed it. To locate this password we will use the command:

Copy this password and paste it into the box on the welcome page.

On the next page, select ‘Install Suggested plugins’

You should see Jenkins installing the plugins.

Once the installation has completed, click on Continue.

On the Create Admin User page, click ‘Skip and Continue as Admin’. You can alternatively create a separate Admin user, but be sure to add it to Docker group.

Click on ‘Save and Continue’

On the Instance Configuration page, Jenkins will show the URL where it can be accessed. Leave it and click ‘Save and Finish’

Click on ‘Start Using Jenkins’. You will land on a welcome page like this:

We have now successfully setup Jenkins. Let’s go back to the terminal to install Docker.

Installing Docker

First we need to uninstall any previous Docker stuff, if any.

Most likely, nothing will be removed since we are working with a fresh install of Ubuntu.

We will use the command line to install Docker.

Next, we will add Docker’s GPG key, just like we did with Jenkins.

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

Now, we will setup the repository

echo \ "deb [arch= $(dpkg --print-architecture ) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs ) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Next we will install the Docker Engine.

Now verify the installation by typing

Notice that you will get an error for permission denied while connecting to Docker daemon socket. This is because it requires a root user. This means you would need to prefix sudo every time you want to run Docker commands. This is not ideal. We can fix this by making a docker group.

The docker group may already exist. Now let’s add the user to this group.

Apply changes to Unix groups by typing the following:

Note: If you are following this tutorial on a VM, you may need to restart your instance for changes to take effect.

Let’s verify that we can now connect to the Docker Engine.

As we can see, Docker is now fully functional with a connection to the Docker Engine.

We will now create the Dockerfile that will build the Docker image.

Creating the Dockerfile

Inside your terminal, within your folder, create the Dockerfile using the nano editor.

Type this text inside the editor:

FROM python:3.8 WORKDIR /src COPY . /src RUN pip install flask RUN pip install flask_restful EXPOSE 3333 ENTRYPOINT [ "python" ] CMD [ "./src/helloworld.py" ]

Building the Docker Image

From the Dockerfile, we will now build a Docker image.

Now let’s create a test container and run it a browser to check if our app is displaying correctly.

Open your browser and go to

In the nano editor, we will use the following code as our Jenkinsfile.

Explaining the Jenkinsfile

Our Jenkins pipeline is divided in 5 stages as you can see from the code.

  • Stage 1 — Clones our Github repo
  • Stage 2 — Builds our Docker image from the Docker File
  • Stage 3 — Pushes the image to Docker Hub
  • Stage 4 — Deploys the image as a container by pulling it from Docker Hub
  • Stage 5 — Removes the old image to stop image pile up.

Now that our Jenkinsfile is ready, let’s push all of our source code to GitHub.

Pushing files to GitHub

First, let’s check the status of our local repo.

As we can see, there are no commits yet and there are untracked files and folders. Let’s tell Git to track them so we can push them to our remote repo.

This will add all the files present in the git scope.

Git is now tracking our files and they are ready to be commit. The commit function pushes the files to the staging area where they will be ready to be pushed.

Now, it’s time to push our files.

Let’s go to our repo on GitHub to verify that our push was successful.

Creating Jenkins Credentials

In the Jenkins dashboard, go to Manage Jenkins.

In the Security section, go to Manage Credentials.

In the credentials section, click on System. On the page that opens, click on Global Credentials Unrestricted

Now click on Add Credentials.

Keep ‘Kind’ as ‘Username and Password’

In ‘username’ type your Docker Hub username.

In ‘password’ type your Docker Hub password.

Note: If you have enabled 2FA in your Docker Hub account, you need to create an access token and use it as a password here.

In ‘ID’, type ‘dockerHub’

Finally, click on Create

Creating a Jenkins Job

To close our pipeline, we will create a Jenkins job which will be triggered when there are changes to our GitHub repo.

Note: In Jenkins, if not already installed, install the plugins Docker and Docker Pipeline. Restart your Jenkins instance after installation.

Click on New Item in your Jenkins dashboard. Enter any name you like. Select Pipeline and click okay.

In the configuration page, type in any description that you want.

In ‘Build Triggers’ select Poll SCM.

In ‘Schedule’, type

* * * * *

(with spaces in between. This will poll our GitHub repo every minute to check if there any changes. This is mostly too quick for any project, but we are just testing our code.

In the ‘Pipeline’ section, in ‘definition’ select Pipeline Script from SCM. This will look for the Jenkinsfile that we uploaded to our repo in GitHub and apply it.

Next, in SCM in the Repositories section, copy and paste your GitHub repo HTTPS URL.

In ‘Branches to Build’, by default, it will have master. Change it to main, since our branch is called main.

Make sure the ‘Script Path’ has ‘Jenkinsfile’ already populated. If not, you can type it out.

Click on Save.

Now our Jenkins job is created. It is time to see the whole pipeline in action.

Click on ‘Build Now’. This will trigger all the steps and if we have all the configurations correct, it should have our container running with the python app and our custom image uploaded on Docker Hub. Let’s verify this.

As we can see, our custom built image is now available in our Docker Hub account.

Now let’s verify if the container is running.

Committing changes to Python App

To see the full automated flow in action, let’s change the python app a bit and go back to our browser to see the changes being reflected automatically.

We have changed the output text from Hello World! to Hello World! I am learning DevOps!

Save the file and push the file to GitHub.

As we can see, this action triggered an automatic job creation on Jenkins, which resulted in Build №2 of our app.

We can now see that our app has 2 builds. In the first build, we can see ‘no changes’ because we manually triggered the first build after creating our repository. All subsequent commits will result in a new build.

We can see that Build No 2 mentions there was 1 commit.

As for our webapp, the message displayed has now changed.

This is how we can create a Docker-Jenkins automation.

--

--

Nyukeit

DevOps Engineer | Terraform, Ansible, Jenkins, Docker, Kubernetes, Cloud | I help in streamlining IT operations automation