CI/CD Tutorial for Beginners
Get up and running with your very first CI/CD pipeline by building, testing, and deploying a simple “Hello World!” web app using Docker and Jenkins—no prior experience required.
Get up and running with your very first CI/CD pipeline by building, testing, and deploying a simple “Hello World!” web app using Docker and Jenkins—no prior experience required.
The purpose of this blog is to serve as a step-by-step guide for beginners looking to implement their first CI/CD pipeline using Docker and Jenkins. By following this tutorial, readers will create a simple "Hello World!" web application and automate its build, test, and deployment processes. This hands-on approach introduces the core concepts of Continuous Integration and Continuous Deployment, equipping readers with practical skills for modern software development practices.
CI/CD, which stands for Continuous Integration and Continuous Deployment (or Delivery), is a set of practices that automates the process of software development, testing, and deployment. Continuous Integration (CI) involves developers frequently merging their code changes into a shared repository, often multiple times a day. Automated tests are then run to ensure that new code integrates smoothly with the existing codebase, identifying any issues early in the development cycle.
Continuous Deployment (CD) goes a step further by automatically deploying every change that passes the CI process to production. In environments where Continuous Delivery is practiced, the deployment step is automated, but it requires manual approval to go live.
CI/CD is essential because it reduces the time it takes to deliver software, ensures higher quality through automated testing, and minimizes the risk of bugs and errors in production. By continuously integrating and deploying, teams can respond faster to changes in the market or customer requirements, allowing for more agile development processes.
Practical applications of CI/CD are widespread in today's technology landscape. For example, companies like Amazon and Netflix deploy updates to their production environments hundreds or even thousands of times per day, ensuring they can deliver new features and fixes to users quickly. In web development, CI/CD pipelines are used to automatically test and deploy web applications, ensuring that they remain up-to-date and functional across all environments. This approach is also critical in DevOps practices, where collaboration between development and operations teams is streamlined to enhance the overall software lifecycle.
Before starting, ensure you have the following installed:
jdk-11.0.24.8-hotspot
of the JDK in Jenkins, not the /bin
git clone https://github.com/your-username/your-repo-name.git
npm init -y
npm install express
app.js
, and add the following code:
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(port, () => {
console.log(App listening at http://localhost:${port}
);
});
mocha
and chai
for testing:
npm install --save-dev mocha chai
/test
directory with a file test.js
with the following content:
const chai = require('chai');
const chaiHttp = require('chai-http');
const server = require('../app');
chai.use(chaiHttp);
const should = chai.should();
describe('Main Route', () => {
it('should return Hello World on / GET', (done) => {
chai.request(server)
.get('/')
.end((err, res) => {
res.should.have.status(200);
res.text.should.equal('Hello World!');
done();
});
});
});
app.js
to export the app (for testing purposes):
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(port, () => {
console.log(App listening at http://localhost:${port}
);
});
// Export the app for testing purposes
module.exports = app;
package.json
to add a test script:
{
"name": "your-app-name",
"version": "1.0.0",
"description": "A simple Node.js app",
"main": "app.js",
"scripts": {
"start": "node app.js",
"test": "mocha"
},
"author": "Your Name",
"license": "ISC",
"dependencies": {
"express": "^4.17.1"
},
"devDependencies": {
"mocha": "^9.0.0",
"chai": "^4.0.0"
}
}
Dockerfile
with the following content:
# Use an official Node.js runtime as a parent image
FROM node:14
# Set the working directory in the container
WORKDIR /usr/src/app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy the rest of the application code
COPY . .
# Expose the port the app runs on
EXPOSE 3000
# Command to run the application
CMD ["node", "app.js"]
If your application consists of multiple services (e.g., a database), you can create a docker-compose.yml
file. For now, we'll keep it simple and just use the Dockerfile. See here for more details.
docker build -t your-app-name .
docker run -p 3000:3000 your-app-name
You should be able to access your app at http://localhost:3000.
Jenkinsfile
in the root of your project with the following content:
pipeline {
agent any
environment {
DOCKER_IMAGE = "your-dockerhub-username/your-app-name"
HEROKU_APP_NAME = 'your-heroku-app-name'
HEROKU_API_KEY = credentials('HEROKU_API_KEY')
}
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://github.com/your-username/your-repo-name.git'
}
}
stage('Build Docker Image') {
steps {
script {
dockerImage = docker.build("$DOCKER_IMAGE:$BUILD_NUMBER")
}
}
}
stage('Run Tests') {
steps {
script {
dockerImage.inside {
sh 'npm install'
sh 'npm test'
}
}
}
}
stage('Push Docker Image') {
steps {
script {
docker.withRegistry('https://registry.hub.docker.com', 'dockerhub-credentials') {
dockerImage.push("$BUILD_NUMBER")
dockerImage.push("latest")
}
}
}
}
stage('Deploy to Heroku') {
steps {
sh 'heroku container:login'
sh 'heroku container:push web --app $HEROKU_APP_NAME'
sh 'heroku container:release web --app $HEROKU_APP_NAME'
}
}
}
post {
success {
echo 'Deployment Successful!'
}
failure {
echo 'Deployment Failed!'
}
}
}
dockerhub-credentials
.HEROKU_API_KEY
.heroku create your-heroku-app-name
heroku container:push web --app your-heroku-app-name
heroku container:release web --app your-heroku-app-name
Now that you have a Docker-based CI/CD pipeline set up, consider expanding the tutorial by:
nyc
and display the results in Jenkins.This interactive learning program introduces you to the basics of CI/CD using Docker alongside other industry-standard tools. By following this tutorial, you should gain a solid understanding of how to set up and maintain a Docker-based CI/CD pipeline, enabling you to develop and deploy applications more efficiently in a containerized environment.
node_modules
npm-debug.log
.env
.vscode/
.idea/
.git
.gitignore
Dockerfile
docker-compose.yml
test/
# Node.js dependencies
node_modules/
npm-debug.log
yarn-error.log
package-lock.json
yarn.lock
# Logs
logs/
*.log
logs/*.log
# Runtime data
pids/
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov/
# Coverage directory used by tools like istanbul
coverage/
*.lcov
# Dependency directories
node_modules/
jspm_packages/
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
# IDE specific files
.vscode/
.idea/
*.sublime-project
*.sublime-workspace
# Environment variable files
.env
.env.test
.env.production
.env.local
# Docker
Dockerfile
docker-compose.yml
.dockerignore
# Build artifacts
dist/
build/
tmp/
temp/
# Generated files
*.swp
*.swo
# Operating system specific files
.DS_Store
Thumbs.db
# Git specific files
.git/
.gitignore
# Jenkins-specific files
jenkins/
Do you have questions, or are you interested in a particular service offered by myTech.Today?
If so, please click the button below, and one of our friendly and knowledgeable Technology Advisors will call you within 15 minutes (Monday through Friday, 8:30 am to 5:00 pm Central Time).