Set up CI/CD with GitHub, Docker, and Jenkins.

In this tutorial, we are going to build and run a simple Node.js app on our (local or remote) server using docker, triggering the build from a GitHub repository git push event, using Jenkins.

Setup Docker:

https://docs.docker.com/engine/install

Setup Jenkins:

https://www.jenkins.io/doc/book/installing

Step 1) Node App:

4 Files to be created:

1- server.js

The app simply is an express app that prints ‘Hello World — version’

'use strict';
const express = require('express');

// Constants
const PORT = 8080;
const HOST = '0.0.0.0';

// App
const app = express();
app.get('/', (req, res) => {
  res.send('Hello World - v1.0');
}); 
app.listen(PORT, HOST, () => {
  console.log(`Running on http://${HOST}:${PORT}`);
});

2- package.json

Simply adds the express library to the app.

{
"name": "docker_web_app",
"version": "1.0.0",
"description": "Node.js on Docker",
"author": "Samer Al Saydali <samsaydali@gmail.com>",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.2"
}
}

3- Dockerfile

FROM node:18

WORKDIR /usr/src/app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 8080
CMD [ "node", "server.js" ]

The provided Dockerfile appears to be for building a Node.js application container. Let me explain each part of it:

  1. FROM node:18: This line specifies the base Docker image to use. In this case, it starts with an official Node.js image tagged with version 18. This image contains a pre-installed Node.js runtime.
  2. WORKDIR /usr/src/app: This line sets the working directory inside the container to /usr/src/app. All subsequent commands will be executed in this directory.
  3. COPY package*.json ./: This line copies the package.json and package-lock.json (if it exists) from the host machine to the container’s current working directory. These files are essential for installing application dependencies using npm.
  4. RUN npm install: This command installs the Node.js application’s dependencies using npm. It reads the package.json and package-lock.json files and installs all the required packages.
  5. COPY . .: This line copies the entire content of your application (all files and directories) from the host machine to the current working directory inside the container. This step is used to add your application code to the container.
  6. EXPOSE 8080: This instruction tells Docker that the container will listen on port 8080. However, it doesn’t actually map this port to any specific port on the host. You would need to do that when running the container using the -p or -P option.
  7. CMD [ "node", "server.js" ]: This specifies the command to run when the container starts. It runs the Node.js script server.js. This is typically the main entry point of your application.

4- .dockerignore

A wise idea to add .dockerignore file with the content:

node_modules
npm-debug.log

The .dockerignore file is used to specify files and directories that should be excluded when building a Docker image. It’s similar in concept to .gitignore, but it applies to the Docker build context, which is the set of files and directories sent to Docker for building an image.

Initial Testing

To use this Dockerfile, you would typically build an image from it and then run containers based on that image. Here are the basic Docker commands to build and run the container:

Build the Docker image:

docker build -t test-node-app .

Run a container based on the image, mapping port 8080 from the container to a port on the host (e.g., 8090 on the host):

docker run -p 8090:8080 test-node-app

This will start your Node.js application in a container, and you can access it by going to http://localhost:8090 in your web browser.

Step 2) Expose your Jenkins Service:

If you are using a remote server and can access your Jenkins [ip:port], skip this step.

If you are using your personal computer, you might want to let Github access your Jenkins service.

Ngrok is a tool that allows you to expose a local server running on your machine to the public internet. It creates a secure tunnel from a public endpoint to a port on your local machine, making it accessible from anywhere on the internet. Ngrok is commonly used for various purposes, including web development, testing, and debugging. Here’s how you can use ngrok:

Installation:

  • You can download and install ngrok from the official website (https://ngrok.com/download).
  • After installing it, you may need to authenticate using your Ngrok account to access some advanced features.

Basic Usage:

  • Open a terminal or command prompt and navigate to the directory where ngrok is installed.
  • To expose a local server running on a specific port (e.g., port 8080 for Jenkins), use the following command:
ngrok http 8080

Ngrok will generate a public URL (usually in the form of https://randomstring.ngrok.io) that you can use to access your local server from anywhere.

Step 3) Setup Github repository:

Create a repository that hosts the previously created files (server.js, package.json, Dockerfile, and .dockerignore).

Now add the webhook to jenkins:

Setting up a webhook between GitHub and Jenkins allows you to trigger Jenkins jobs automatically whenever certain events occur in your GitHub repository, such as a code push or a pull request.

In your GitHub repository, go to Settings -> Webhooks -> Add webhook.

Set the Payload URL to https://your-jenkins-url/github-webhook/. Replace your-jenkins-url with the actual URL of your Jenkins server.

Keep the /github-webhook/part.

Step 4) Setup Jenkins Project

  • Create new freestyle project
  • Set the Source Code Management to Git.
  • Add your Repository URL
  • To securely authenticate Jenkins with your GitHub repository, you should create a GitHub Personal Access Token. Go to your GitHub account settings -> Developer settings -> Personal access tokens -> Generate token. Make sure to give it the necessary permissions, like “repo” for private repositories or specific permissions required for your use case.
  • For private repositories add and use Jenkins Credentials Provider, set the username to your username, and the password to the Access token created.

Set Build Triggers to GitHub hook trigger for GITScm polling

Build Steps:

1- Execute Shell

#!/bin/bash
sudo docker rmi node-app-img -f
sudo docker build . -t node-app-img

Use Docker commands to remove a Docker image and then build a new Docker image from the Dockerfile.

2- Execute Shell

#!/bin/bash
PORT_TO_KILL=8080

CONTAINER_ID=$(sudo docker ps -q --filter "expose=$PORT_TO_KILL")

if [ -n "$CONTAINER_ID" ]; then
sudo docker kill "$CONTAINER_ID"
else
echo "No container using port $PORT_TO_KILL found."
fi

Used to find and kill a Docker container that is using a specific port (PORT_TO_KILL).

3- Execute Shell

sudo docker run -p 49160:8080 -d node-app-img

Runs the Image on port 49160 on your machine.

-p 49160:8080: This option is used to map ports between the host and the container. It specifies that port 49160 on the host should be mapped to port 8080 on the container. This means that you can access the application running inside the container on port 8080 by connecting to port 49160 on the host.

Testing:

Run the build manually on Jenkins then check your http://localhost:49160/, you should see:

Hello World — v1.0

Now try to make a code change, changing v1.0 to v2.0 for example.

Git add, commit, and push the changes to the remote GitHub repository.

Check your Jenkins Dashboard:

If you click on your project and view the last run console:

This means that the build was triggered and successful, now check your http://localhost:49160/, you should see:

Hello World — v2.0

Congratulations, you just have created your CI/CD pipeline!

Leave a Comment

Your email address will not be published. Required fields are marked *